summary refs log tree commit diff
path: root/third_party
diff options
context:
space:
mode:
authorNicolas Werner <nicolas.werner@hotmail.de>2020-02-23 13:40:04 +0100
committerNicolas Werner <nicolas.werner@hotmail.de>2020-02-23 13:44:05 +0100
commite96241265e31264eaa96fda34c7a69c1f1ecf6c0 (patch)
tree7a3147527b52eef15021452ee8696c27061e6131 /third_party
parentFix Registration (diff)
downloadnheko-e96241265e31264eaa96fda34c7a69c1f1ecf6c0.tar.xz
Use third_party singleapplication helper
This may still not raise the application correctly on some WMs, because
they don't allow disturbing the user with windows popping up. I don't
think we want to work around that.

fixes #84
fixes #113
Diffstat (limited to 'third_party')
-rw-r--r--third_party/SingleApplication-3.0.19/.gitignore9
-rw-r--r--third_party/SingleApplication-3.0.19/CHANGELOG.md237
-rw-r--r--third_party/SingleApplication-3.0.19/CMakeLists.txt45
-rw-r--r--third_party/SingleApplication-3.0.19/LICENSE24
-rw-r--r--third_party/SingleApplication-3.0.19/README.md277
-rw-r--r--third_party/SingleApplication-3.0.19/Windows.md46
-rw-r--r--third_party/SingleApplication-3.0.19/singleapplication.cpp189
-rw-r--r--third_party/SingleApplication-3.0.19/singleapplication.h135
-rw-r--r--third_party/SingleApplication-3.0.19/singleapplication_p.cpp405
-rw-r--r--third_party/SingleApplication-3.0.19/singleapplication_p.h99
10 files changed, 1466 insertions, 0 deletions
diff --git a/third_party/SingleApplication-3.0.19/.gitignore b/third_party/SingleApplication-3.0.19/.gitignore
new file mode 100644
index 00000000..ad390758
--- /dev/null
+++ b/third_party/SingleApplication-3.0.19/.gitignore
@@ -0,0 +1,9 @@
+/examples/*/*.o
+/examples/*/Makefile
+/examples/*/moc_*.cpp
+/examples/*/moc_predefs.h
+/examples/*/*.qmake.stash
+/examples/basic/basic
+/examples/calculator/calculator
+/examples/sending_arguments/sending_arguments
+CMakeLists.txt.user
diff --git a/third_party/SingleApplication-3.0.19/CHANGELOG.md b/third_party/SingleApplication-3.0.19/CHANGELOG.md
new file mode 100644
index 00000000..36f1e261
--- /dev/null
+++ b/third_party/SingleApplication-3.0.19/CHANGELOG.md
@@ -0,0 +1,237 @@
+Changelog
+=========
+
+__3.0.19__
+----------
+
+* 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_
+
+__3.0.17__
+----------
+
+* Fixed compilation warning/error caused by `geteuid()` on unix based systems.
+
+   _Iakov Kirilenko_
+
+* Added CMake support
+
+   _Hennadii Chernyshchyk_
+
+__3.0.16__
+----------
+
+* Use geteuid and getpwuid to get username on Unix, fallback to environment variable.
+
+   _Jonas Kvinge_
+
+__3.0.15__
+----------
+
+* Bug Fix: sendMessage() might return false even though data was actually written.
+
+   _Jonas Kvinge_
+
+__3.0.14__
+----------
+
+* Fixed uninitialised variables in the `SingleApplicationPrivate` constructor.
+
+__3.0.13a__
+----------
+
+* Process socket events asynchronously
+* Fix undefined variable error on Windows
+
+   _Francis Giraldeau_
+
+__3.0.12a__
+----------
+
+* Removed signal handling.
+
+__3.0.11a__
+----------
+
+* Fixed bug where the message sent by the second process was not received
+  correctly when the message is sent immediately following a connection.
+
+   _Francis Giraldeau_
+
+* Refactored code and implemented shared memory block consistency checks
+  via `qChecksum()` (CRC-16).
+* Explicit `qWarning` and `qCritical` when the library is unable to initialise
+  correctly.
+
+__3.0.10__
+----------
+
+* Removed C style casts and eliminated all clang warnings. Fixed `instanceId`
+  reading from only one byte in the message deserialization. Cleaned up
+  serialization code using `QDataStream`. Changed connection type to use
+  `quint8 enum` rather than `char`.
+* Renamed `SingleAppConnectionType` to `ConnectionType`. Added initialization
+  values to all `ConnectionType` enum cases.
+
+    _Jedidiah Buck McCready_
+
+__3.0.9__
+---------
+
+*   Added SingleApplicationPrivate::primaryPid() as a solution to allow
+    bringing the primary window of an application to the foreground on
+    Windows.
+
+    _Eelco van Dam from Peacs BV_
+
+__3.0.8__
+---------
+
+*   Bug fix - changed QApplication::instance() to QCoreApplication::instance()
+
+    _Evgeniy Bazhenov_
+
+__3.0.7a__
+----------
+
+*   Fixed compilation error with Mingw32 in MXE thanks to Vitaly Tonkacheyev.
+*   Removed QMutex used for thread safe behaviour. The implementation now uses
+    QCoreApplication::instance() to get an instance to SingleApplication for
+    memory deallocation.
+
+__3.0.6a__
+----------
+
+*   Reverted GetUserName API usage on Windows. Fixed bug with missing library.
+*   Fixed bug in the Calculator example, preventing it's window to be raised
+    on Windows.
+
+    Special thanks to Charles Gunawan.
+
+__3.0.5a__
+----------
+
+*   Fixed a memory leak in the SingleApplicationPrivate destructor.
+
+    _Sergei Moiseev_
+
+__3.0.4a__
+----------
+
+*   Fixed shadow and uninitialised variable warnings.
+
+    _Paul Walmsley_
+
+__3.0.3a__
+----------
+
+*   Removed Microsoft Windows specific code for getting username due to
+    multiple problems and compiler differences on Windows platforms. On
+    Windows the shared memory block in User mode now includes the user's
+    home path (which contains the user's username).
+
+*   Explicitly getting absolute path of the user's home directory as on Unix
+    a relative path (`~`) may be returned.
+
+__3.0.2a__
+----------
+
+*   Fixed bug on Windows when username containing wide characters causes the
+    library to crash.
+
+    _Le Liu_
+
+__3.0.1a__
+----------
+
+*   Allows the application path and version to be excluded from the server name
+    hash. The following flags were added for this purpose:
+      * `SingleApplication::Mode::ExcludeAppVersion`
+      * `SingleApplication::Mode::ExcludeAppPath`
+*   Allow a non elevated process to connect to a local server created by an
+    elevated process run by the same user on Windows
+*   Fixes a problem with upper case letters in paths on Windows
+
+    _Le Liu_
+
+__v3.0a__
+---------
+
+*   Deprecated secondary instances count.
+*   Added a sendMessage() method to send a message to the primary instance.
+*   Added a receivedMessage() signal, emitted when a message is received from a
+    secondary instance.
+*   The SingleApplication constructor's third parameter is now a bool
+    specifying if the current instance should be allowed to run as a secondary
+    instance if there is already a primary instance.
+*   The SingleApplication constructor accept a fourth parameter specifying if
+    the SingleApplication block should be User-wide or System-wide.
+*   SingleApplication no longer relies on `applicationName` and
+    `organizationName` to be set. It instead concatenates all of the following
+    data and computes a `SHA256` hash which is used as the key of the
+    `QSharedMemory` block and the `QLocalServer`. Since at least
+    `applicationFilePath` is always present there is no need to explicitly set
+    any of the following prior to initialising `SingleApplication`.
+      * `QCoreApplication::applicationName`
+      * `QCoreApplication::applicationVersion`
+      * `QCoreApplication::applicationFilePath`
+      * `QCoreApplication::organizationName`
+      * `QCoreApplication::organizationDomain`
+      * User name or home directory path if in User mode
+*   The primary instance is no longer notified when a secondary instance had
+    been started by default. A `Mode` flag for this feature exists.
+*   Added `instanceNumber()` which represents a unique identifier for each
+    secondary instance started. When called from the primary instance will
+    return `0`.
+
+__v2.4__
+--------
+
+*   Stability improvements
+*   Support for secondary instances.
+*   The library now recovers safely after the primary process has crashed
+and the shared memory had not been deleted.
+
+__v2.3__
+--------
+
+*   Improved pimpl design and inheritance safety.
+
+    _Vladislav Pyatnichenko_
+
+__v2.2__
+--------
+
+*   The `QAPPLICATION_CLASS` macro can now be defined in the file including the
+Single Application header or with a `DEFINES+=` statement in the project file.
+
+__v2.1__
+--------
+
+*   A race condition can no longer occur when starting two processes nearly
+    simultaneously.
+
+    Fix issue [#3](https://github.com/itay-grudev/SingleApplication/issues/3)
+
+__v2.0__
+--------
+
+*   SingleApplication is now being passed a reference to `argc` instead of a
+    copy.
+
+    Fix issue [#1](https://github.com/itay-grudev/SingleApplication/issues/1)
+
+*   Improved documentation.
diff --git a/third_party/SingleApplication-3.0.19/CMakeLists.txt b/third_party/SingleApplication-3.0.19/CMakeLists.txt
new file mode 100644
index 00000000..076d514d
--- /dev/null
+++ b/third_party/SingleApplication-3.0.19/CMakeLists.txt
@@ -0,0 +1,45 @@
+cmake_minimum_required(VERSION 3.1.0)
+
+project(SingleApplication)
+
+set(CMAKE_INCLUDE_CURRENT_DIR ON)
+set(CMAKE_AUTOMOC ON)
+
+# SingleApplication base class
+set(QAPPLICATION_CLASS QCoreApplication CACHE STRING "Inheritance class for SingleApplication")
+set_property(CACHE QAPPLICATION_CLASS PROPERTY STRINGS QApplication QGuiApplication QCoreApplication)
+
+# Libary target
+add_library(${PROJECT_NAME} STATIC
+    singleapplication.cpp
+    singleapplication_p.cpp
+    )
+
+# Find dependencies
+find_package(Qt5Network)
+if(QAPPLICATION_CLASS STREQUAL QApplication)
+    find_package(Qt5 COMPONENTS Widgets REQUIRED)
+elseif(QAPPLICATION_CLASS STREQUAL QGuiApplication)
+    find_package(Qt5 COMPONENTS Gui REQUIRED)
+else()
+    find_package(Qt5 COMPONENTS Core REQUIRED)
+endif()
+target_compile_definitions(${PROJECT_NAME} PUBLIC QAPPLICATION_CLASS=${QAPPLICATION_CLASS})
+
+# Link dependencies
+target_link_libraries(${PROJECT_NAME} PRIVATE Qt5::Network)
+if(QAPPLICATION_CLASS STREQUAL QApplication)
+    target_link_libraries(${PROJECT_NAME} PRIVATE Qt5::Widgets)
+elseif(QAPPLICATION_CLASS STREQUAL QGuiApplication)
+    target_link_libraries(${PROJECT_NAME} PRIVATE Qt5::Gui)
+else()
+    target_link_libraries(${PROJECT_NAME} PRIVATE Qt5::Core)
+endif()
+
+if(WIN32)
+    target_link_libraries(${PROJECT_NAME} PRIVATE advapi32)
+endif()
+
+target_include_directories(${PROJECT_NAME} PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
+
+add_library(${PROJECT_NAME}::${PROJECT_NAME} ALIAS ${PROJECT_NAME})
diff --git a/third_party/SingleApplication-3.0.19/LICENSE b/third_party/SingleApplication-3.0.19/LICENSE
new file mode 100644
index 00000000..85b2a149
--- /dev/null
+++ b/third_party/SingleApplication-3.0.19/LICENSE
@@ -0,0 +1,24 @@
+The MIT License (MIT)
+
+Copyright (c) Itay Grudev 2015 - 2016
+
+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.
+
+Note: Some of the examples include code not distributed under the terms of the
+MIT License.
diff --git a/third_party/SingleApplication-3.0.19/README.md b/third_party/SingleApplication-3.0.19/README.md
new file mode 100644
index 00000000..5d609865
--- /dev/null
+++ b/third_party/SingleApplication-3.0.19/README.md
@@ -0,0 +1,277 @@
+SingleApplication
+=================
+
+This is a replacement of the QtSingleApplication for `Qt5`.
+
+Keeps the Primary Instance of your Application and kills each subsequent
+instances. It can (if enabled) spawn secondary (non-related to the primary)
+instances and can send data to the primary instance from secondary instances.
+
+Usage
+-----
+
+The `SingleApplication` class inherits from whatever `Q[Core|Gui]Application`
+class you specify via the `QAPPLICATION_CLASS` macro (`QCoreApplication` is the
+default). Further usage is similar to the use of the `Q[Core|Gui]Application`
+classes.
+
+The library sets up a `QLocalServer` and a `QSharedMemory` block. The first
+instance of your Application is your Primary Instance. It would check if the
+shared memory block exists and if not it will start a `QLocalServer` and listen
+for connections. Each subsequent instance of your application would check if the
+shared memory block exists and if it does, it will connect to the QLocalServer
+to notify the primary instance that a new instance had been started, after which
+it would terminate with status code `0`. In the Primary Instance
+`SingleApplication` would emit the `instanceStarted()` signal upon detecting
+that a new instance had been started.
+
+The library uses `stdlib` to terminate the program with the `exit()` function.
+
+You can use the library as if you use any other `QCoreApplication` derived
+class:
+
+```cpp
+#include <QApplication>
+#include <SingleApplication.h>
+
+int main( int argc, char* argv[] )
+{
+    SingleApplication app( argc, argv );
+
+    return app.exec();
+}
+```
+
+To include the library files I would recommend that you add it as a git
+submodule to your project and include it's contents with a `.pri` file. Here is
+how:
+
+```bash
+git submodule add git@github.com:itay-grudev/SingleApplication.git singleapplication
+```
+
+**Qmake:**
+
+Then include the `singleapplication.pri` file in your `.pro` project file.
+
+```qmake
+include(singleapplication/singleapplication.pri)
+DEFINES += QAPPLICATION_CLASS=QApplication
+```
+
+**CMake:**
+
+Then include the subdirectory in your `CMakeLists.txt` project file.
+
+```cmake
+set(QAPPLICATION_CLASS QApplication CACHE STRING "Inheritance class for SingleApplication")
+add_subdirectory(src/third-party/singleapplication)
+```
+
+Also don't forget to specify which `QCoreApplication` class your app is using if it
+is not `QCoreApplication` as in examples above.
+
+The `Instance Started` signal
+------------------------
+
+The SingleApplication class implements a `instanceStarted()` signal. You can
+bind to that signal to raise your application's window when a new instance had
+been started, for example.
+
+```cpp
+// window is a QWindow instance
+QObject::connect(
+    &app,
+    &SingleApplication::instanceStarted,
+    &window,
+    &QWindow::raise
+);
+```
+
+Using `SingleApplication::instance()` is a neat way to get the
+`SingleApplication` instance for binding to it's signals anywhere in your
+program.
+
+__Note:__ On Windows the ability to bring the application windows to the
+foreground is restricted. See [Windows specific implementations](Windows.md)
+for a workaround and an example implementation.
+
+
+Secondary Instances
+-------------------
+
+If you want to be able to launch additional Secondary Instances (not related to
+your Primary Instance) you have to enable that with the third parameter of the
+`SingleApplication` constructor. The default is `false` meaning no Secondary
+Instances. Here is an example of how you would start a Secondary Instance send
+a message with the command line arguments to the primary instance and then shut
+down.
+
+```cpp
+int main(int argc, char *argv[])
+{
+    SingleApplication app( argc, argv, true );
+
+    if( app.isSecondary() ) {
+        app.sendMessage(  app.arguments().join(' ')).toUtf8() );
+        app.exit( 0 );
+    }
+
+    return app.exec();
+}
+```
+
+*__Note:__ A secondary instance won't cause the emission of the
+`instanceStarted()` signal by default. See `SingleApplication::Mode` for more
+details.*
+
+You can check whether your instance is a primary or secondary with the following
+methods:
+
+```cpp
+app.isPrimary();
+// or
+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.*
+
+API
+---
+
+### Members
+
+```cpp
+SingleApplication::SingleApplication( int &argc, char *argv[], bool allowSecondary = false, Options options = Mode::User, int timeout = 100 )
+```
+
+Depending on whether `allowSecondary` is set, this constructor may terminate
+your app if there is already a primary instance running. Additional `Options`
+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.
+
+*__Note:__ `argc` and `argv` may be changed as Qt removes arguments that it
+recognizes.*
+
+*__Note:__ `Mode::SecondaryNotification` only works if set on both the primary
+and the secondary instance.*
+
+*__Note:__ Operating system can restrict the shared memory blocks to the same
+user, in which case the User/System modes will have no effect and the block will
+be user wide.*
+
+---
+
+```cpp
+bool SingleApplication::sendMessage( QByteArray message, int timeout = 100 )
+```
+
+Sends `message` to the Primary Instance. Uses `timeout` as a the maximum timeout
+in milliseconds for blocking functions
+
+---
+
+```cpp
+bool SingleApplication::isPrimary()
+```
+
+Returns if the instance is the primary instance.
+
+---
+
+```cpp
+bool SingleApplication::isSecondary()
+```
+Returns if the instance is a secondary instance.
+
+---
+
+```cpp
+quint32 SingleApplication::instanceId()
+```
+
+Returns a unique identifier for the current instance.
+
+---
+
+```cpp
+qint64 SingleApplication::primaryPid()
+```
+
+Returns the process ID (PID) of the primary instance.
+
+### Signals
+
+```cpp
+void SingleApplication::instanceStarted()
+```
+
+Triggered whenever a new instance had been started, except for secondary
+instances if the `Mode::SecondaryNotification` flag is not specified.
+
+---
+
+```cpp
+void SingleApplication::receivedMessage( quint32 instanceId, QByteArray message )
+```
+
+Triggered whenever there is a message received from a secondary instance.
+
+---
+
+### Flags
+
+```cpp
+enum SingleApplication::Mode
+```
+
+*   `Mode::User` - The SingleApplication block should apply user wide. This adds
+    user specific data to the key used for the shared memory and server name.
+    This is the default functionality.
+*   `Mode::System` – The SingleApplication block applies system-wide.
+*   `Mode::SecondaryNotification` – Whether to trigger `instanceStarted()` even
+    whenever secondary instances are started.
+*   `Mode::ExcludeAppPath` – Excludes the application path from the server name
+    (and memory block) hash.
+*   `Mode::ExcludeAppVersion` – Excludes the application version from the server
+    name (and memory block) hash.
+
+*__Note:__ `Mode::SecondaryNotification` only works if set on both the primary
+and the secondary instance.*
+
+*__Note:__ Operating system can restrict the shared memory blocks to the same
+user, in which case the User/System modes will have no effect and the block will
+be user wide.*
+
+---
+
+Versioning
+----------
+
+Each major version introduces either very significant changes or is not
+backwards compatible with the previous version. Minor versions only add
+additional features, bug fixes or performance improvements and are backwards
+compatible with the previous release. See [`CHANGELOG.md`](CHANGELOG.md) for
+more details.
+
+Implementation
+--------------
+
+The library is implemented with a QSharedMemory block which is thread safe and
+guarantees a race condition will not occur. It also uses a QLocalSocket to
+notify the main process that a new instance had been spawned and thus invoke the
+`instanceStarted()` signal and for messaging the primary instance.
+
+Additionally the library can recover from being forcefully killed on *nix
+systems and will reset the memory block given that there are no other
+instances running.
+
+License
+-------
+This library and it's supporting documentation are released under
+`The MIT License (MIT)` with the exception of the Qt calculator examples which
+is distributed under the BSD license.
diff --git a/third_party/SingleApplication-3.0.19/Windows.md b/third_party/SingleApplication-3.0.19/Windows.md
new file mode 100644
index 00000000..13c52da0
--- /dev/null
+++ b/third_party/SingleApplication-3.0.19/Windows.md
@@ -0,0 +1,46 @@
+Windows Specific Implementations
+================================
+
+Setting the foreground window
+-----------------------------
+
+In the `instanceStarted()` example in the `README` we demonstrated how an
+application can bring it's primary instance window whenever a second copy
+of the application is started.
+
+On Windows the ability to bring the application windows to the foreground is
+restricted, see [`AllowSetForegroundWindow()`][AllowSetForegroundWindow] for more
+details.
+
+The background process (the primary instance) can bring its windows to the
+foreground if it is allowed by the current foreground process (the secondary
+instance). To bypass this `SingleApplication` must be initialized with the
+`allowSecondary` parameter set to `true` and the `options` parameter must
+include `Mode::SecondaryNotification`, See `SingleApplication::Mode` for more
+details.
+
+Here is an example:
+
+```cpp
+if( app.isSecondary() ) {
+    // This API requires LIBS += User32.lib to be added to the project
+    AllowSetForegroundWindow( DWORD( app.primaryPid() ) );
+}
+
+if( app.isPrimary() ) {
+    QObject::connect(
+        &app,
+        &SingleApplication::instanceStarted,
+        this,
+        &App::instanceStarted
+    );
+}
+```
+
+```cpp
+void App::instanceStarted() {
+    QApplication::setActiveWindow( [window/widget to set to the foreground] );
+}
+```
+
+[AllowSetForegroundWindow]: https://msdn.microsoft.com/en-us/library/windows/desktop/ms632668.aspx
diff --git a/third_party/SingleApplication-3.0.19/singleapplication.cpp b/third_party/SingleApplication-3.0.19/singleapplication.cpp
new file mode 100644
index 00000000..8ff8747a
--- /dev/null
+++ b/third_party/SingleApplication-3.0.19/singleapplication.cpp
@@ -0,0 +1,189 @@
+// The MIT License (MIT)
+//
+// Copyright (c) Itay Grudev 2015 - 2018
+//
+// 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 );
+        }
+    }
+
+    InstancesInfo* 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();
+}
+
+bool SingleApplication::sendMessage( 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.0.19/singleapplication.h b/third_party/SingleApplication-3.0.19/singleapplication.h
new file mode 100644
index 00000000..cb505971
--- /dev/null
+++ b/third_party/SingleApplication-3.0.19/singleapplication.h
@@ -0,0 +1,135 @@
+// The MIT License (MIT)
+//
+// Copyright (c) Itay Grudev 2015 - 2018
+//
+// 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.
+
+#ifndef SINGLE_APPLICATION_H
+#define SINGLE_APPLICATION_H
+
+#include <QtCore/QtGlobal>
+#include <QtNetwork/QLocalSocket>
+
+#ifndef QAPPLICATION_CLASS
+  #define QAPPLICATION_CLASS QCoreApplication
+#endif
+
+#include QT_STRINGIFY(QAPPLICATION_CLASS)
+
+class SingleApplicationPrivate;
+
+/**
+ * @brief The SingleApplication class handles multiple instances of the same
+ * Application
+ * @see QCoreApplication
+ */
+class SingleApplication : public QAPPLICATION_CLASS
+{
+    Q_OBJECT
+
+    typedef QAPPLICATION_CLASS app_t;
+
+public:
+    /**
+     * @brief Mode of operation of SingleApplication.
+     * Whether the block should be user-wide or system-wide and whether the
+     * primary instance should be notified when a secondary instance had been
+     * started.
+     * @note Operating system can restrict the shared memory blocks to the same
+     * user, in which case the User/System modes will have no effect and the
+     * block will be user wide.
+     * @enum
+     */
+    enum Mode {
+        User                    = 1 << 0,
+        System                  = 1 << 1,
+        SecondaryNotification   = 1 << 2,
+        ExcludeAppVersion       = 1 << 3,
+        ExcludeAppPath          = 1 << 4
+    };
+    Q_DECLARE_FLAGS(Options, Mode)
+
+    /**
+     * @brief Intitializes a SingleApplication instance with argc command line
+     * arguments in argv
+     * @arg {int &} argc - Number of arguments in argv
+     * @arg {const char *[]} argv - Supplied command line arguments
+     * @arg {bool} allowSecondary - Whether to start the instance as secondary
+     * if there is already a primary instance.
+     * @arg {Mode} mode - Whether for the SingleApplication block to be applied
+     * User wide or System wide.
+     * @arg {int} timeout - Timeout to wait in milliseconds.
+     * @note argc and argv may be changed as Qt removes arguments that it
+     * recognizes
+     * @note Mode::SecondaryNotification only works if set on both the primary
+     * instance and the secondary instance.
+     * @note The timeout is just a hint for the maximum time of blocking
+     * operations. It does not guarantee that the SingleApplication
+     * initialisation will be completed in given time, though is a good hint.
+     * 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 );
+    ~SingleApplication();
+
+    /**
+     * @brief Returns if the instance is the primary instance
+     * @returns {bool}
+     */
+    bool isPrimary();
+
+    /**
+     * @brief Returns if the instance is a secondary instance
+     * @returns {bool}
+     */
+    bool isSecondary();
+
+    /**
+     * @brief Returns a unique identifier for the current instance
+     * @returns {qint32}
+     */
+    quint32 instanceId();
+
+    /**
+     * @brief Returns the process ID (PID) of the primary instance
+     * @returns {qint64}
+     */
+    qint64 primaryPid();
+
+    /**
+     * @brief Sends a message to the primary instance. Returns true on success.
+     * @param {int} timeout - Timeout for connecting
+     * @returns {bool}
+     * @note sendMessage() will return false if invoked from the primary
+     * instance.
+     */
+    bool sendMessage( QByteArray message, int timeout = 100 );
+
+Q_SIGNALS:
+    void instanceStarted();
+    void receivedMessage( quint32 instanceId, QByteArray message );
+
+private:
+    SingleApplicationPrivate *d_ptr;
+    Q_DECLARE_PRIVATE(SingleApplication)
+};
+
+Q_DECLARE_OPERATORS_FOR_FLAGS(SingleApplication::Options)
+
+#endif // SINGLE_APPLICATION_H
diff --git a/third_party/SingleApplication-3.0.19/singleapplication_p.cpp b/third_party/SingleApplication-3.0.19/singleapplication_p.cpp
new file mode 100644
index 00000000..884fe631
--- /dev/null
+++ b/third_party/SingleApplication-3.0.19/singleapplication_p.cpp
@@ -0,0 +1,405 @@
+// The MIT License (MIT)
+//
+// Copyright (c) Itay Grudev 2015 - 2018
+//
+// 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.
+
+//
+//  W A R N I N G !!!
+//  -----------------
+//
+// This file is not part of the SingleApplication API. It is used purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or may even be removed.
+//
+
+#include <cstdlib>
+#include <cstddef>
+
+#include <QtCore/QDir>
+#include <QtCore/QByteArray>
+#include <QtCore/QDataStream>
+#include <QtCore/QCryptographicHash>
+#include <QtNetwork/QLocalServer>
+#include <QtNetwork/QLocalSocket>
+
+#include "singleapplication.h"
+#include "singleapplication_p.h"
+
+#ifdef Q_OS_UNIX
+    #include <unistd.h>
+    #include <sys/types.h>
+    #include <pwd.h>
+#endif
+
+#ifdef Q_OS_WIN
+    #include <windows.h>
+    #include <lmcons.h>
+#endif
+
+SingleApplicationPrivate::SingleApplicationPrivate( SingleApplication *q_ptr )
+    : q_ptr( q_ptr )
+{
+    server = nullptr;
+    socket = nullptr;
+    memory = nullptr;
+    instanceNumber = -1;
+}
+
+SingleApplicationPrivate::~SingleApplicationPrivate()
+{
+    if( socket != nullptr ) {
+        socket->close();
+        delete socket;
+    }
+
+    memory->lock();
+    InstancesInfo* inst = static_cast<InstancesInfo*>(memory->data());
+    if( server != nullptr ) {
+        server->close();
+        delete server;
+        inst->primary = false;
+        inst->primaryPid = -1;
+        inst->checksum = blockChecksum();
+    }
+    memory->unlock();
+
+    delete memory;
+}
+
+void SingleApplicationPrivate::genBlockServerName()
+{
+    QCryptographicHash appData( QCryptographicHash::Sha256 );
+    appData.addData( "SingleApplication", 17 );
+    appData.addData( SingleApplication::app_t::applicationName().toUtf8() );
+    appData.addData( SingleApplication::app_t::organizationName().toUtf8() );
+    appData.addData( SingleApplication::app_t::organizationDomain().toUtf8() );
+
+    if( ! (options & SingleApplication::Mode::ExcludeAppVersion) ) {
+        appData.addData( SingleApplication::app_t::applicationVersion().toUtf8() );
+    }
+
+    if( ! (options & SingleApplication::Mode::ExcludeAppPath) ) {
+#ifdef Q_OS_WIN
+        appData.addData( SingleApplication::app_t::applicationFilePath().toLower().toUtf8() );
+#else
+        appData.addData( SingleApplication::app_t::applicationFilePath().toUtf8() );
+#endif
+    }
+
+    // User level block requires a user specific data in the hash
+    if( options & SingleApplication::Mode::User ) {
+#ifdef Q_OS_WIN
+        wchar_t username [ UNLEN + 1 ];
+        // Specifies size of the buffer on input
+        DWORD usernameLength = UNLEN + 1;
+        if( GetUserNameW( username, &usernameLength ) ) {
+            appData.addData( QString::fromWCharArray(username).toUtf8() );
+        } else {
+            appData.addData( qgetenv("USERNAME") );
+        }
+#endif
+#ifdef Q_OS_UNIX
+        QByteArray username;
+        uid_t uid = geteuid();
+        struct passwd *pw = getpwuid(uid);
+        if( pw ) {
+            username = pw->pw_name;
+        }
+        if( username.isEmpty() ) {
+            username = qgetenv("USER");
+        }
+        appData.addData(username);
+#endif
+    }
+
+    // Replace the backslash in RFC 2045 Base64 [a-zA-Z0-9+/=] to comply with
+    // server naming requirements.
+    blockServerName = appData.result().toBase64().replace("/", "_");
+}
+
+void SingleApplicationPrivate::initializeMemoryBlock()
+{
+    InstancesInfo* inst = static_cast<InstancesInfo*>( memory->data() );
+    inst->primary = false;
+    inst->secondary = 0;
+    inst->primaryPid = -1;
+    inst->checksum = blockChecksum();
+}
+
+void SingleApplicationPrivate::startPrimary()
+{
+    Q_Q(SingleApplication);
+
+    // Successful creation means that no main process exists
+    // So we start a QLocalServer to listen for connections
+    QLocalServer::removeServer( blockServerName );
+    server = new QLocalServer();
+
+    // 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 );
+    } else {
+      server->setSocketOptions( QLocalServer::WorldAccessOption );
+    }
+
+    server->listen( blockServerName );
+    QObject::connect(
+        server,
+        &QLocalServer::newConnection,
+        this,
+        &SingleApplicationPrivate::slotConnectionEstablished
+    );
+
+    // Reset the number of connections
+    InstancesInfo* inst = static_cast <InstancesInfo*>( memory->data() );
+
+    inst->primary = true;
+    inst->primaryPid = q->applicationPid();
+    inst->checksum = blockChecksum();
+
+    instanceNumber = 0;
+}
+
+void SingleApplicationPrivate::startSecondary()
+{
+}
+
+void SingleApplicationPrivate::connectToPrimary( int msecs, ConnectionType connectionType )
+{
+    // Connect to the Local Server of the Primary Instance if not already
+    // connected.
+    if( socket == nullptr ) {
+        socket = new QLocalSocket();
+    }
+
+    // If already connected - we are done;
+    if( socket->state() == QLocalSocket::ConnectedState )
+        return;
+
+    // If not connect
+    if( socket->state() == QLocalSocket::UnconnectedState ||
+        socket->state() == QLocalSocket::ClosingState ) {
+        socket->connectToServer( blockServerName );
+    }
+
+    // Wait for being connected
+    if( socket->state() == QLocalSocket::ConnectingState ) {
+        socket->waitForConnected( msecs );
+    }
+
+    // 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);
+
+#if (QT_VERSION >= QT_VERSION_CHECK(5, 6, 0))
+        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;
+
+        // 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);
+#endif
+        headerStream << static_cast <quint64>( initMsg.length() );
+
+        socket->write( header );
+        socket->write( initMsg );
+        socket->flush();
+        socket->waitForBytesWritten( msecs );
+    }
+}
+
+quint16 SingleApplicationPrivate::blockChecksum()
+{
+    return qChecksum(
+       static_cast <const char *>( memory->data() ),
+       offsetof( InstancesInfo, checksum )
+   );
+}
+
+qint64 SingleApplicationPrivate::primaryPid()
+{
+    qint64 pid;
+
+    memory->lock();
+    InstancesInfo* inst = static_cast<InstancesInfo*>( memory->data() );
+    pid = inst->primaryPid;
+    memory->unlock();
+
+    return pid;
+}
+
+/**
+ * @brief Executed when a connection has been made to the LocalServer
+ */
+void SingleApplicationPrivate::slotConnectionEstablished()
+{
+    QLocalSocket *nextConnSocket = server->nextPendingConnection();
+    connectionMap.insert(nextConnSocket, ConnectionInfo());
+
+    QObject::connect(nextConnSocket, &QLocalSocket::aboutToClose,
+        [nextConnSocket, this]() {
+            auto &info = connectionMap[nextConnSocket];
+            Q_EMIT this->slotClientConnectionClosed( nextConnSocket, info.instanceId );
+        }
+    );
+
+    QObject::connect(nextConnSocket, &QLocalSocket::disconnected,
+        [nextConnSocket, this](){
+            connectionMap.remove(nextConnSocket);
+            nextConnSocket->deleteLater();
+        }
+    );
+
+    QObject::connect(nextConnSocket, &QLocalSocket::readyRead,
+        [nextConnSocket, this]() {
+            auto &info = connectionMap[nextConnSocket];
+            switch(info.stage) {
+            case StageHeader:
+                readInitMessageHeader(nextConnSocket);
+                break;
+            case StageBody:
+                readInitMessageBody(nextConnSocket);
+                break;
+            case StageConnected:
+                Q_EMIT this->slotDataAvailable( nextConnSocket, info.instanceId );
+                break;
+            default:
+                break;
+            };
+        }
+    );
+}
+
+void SingleApplicationPrivate::readInitMessageHeader( QLocalSocket *sock )
+{
+    if (!connectionMap.contains( sock )) {
+        return;
+    }
+
+    if( sock->bytesAvailable() < ( qint64 )sizeof( quint64 ) ) {
+        return;
+    }
+
+    QDataStream headerStream( sock );
+
+#if (QT_VERSION >= QT_VERSION_CHECK(5, 6, 0))
+    headerStream.setVersion( QDataStream::Qt_5_6 );
+#endif
+
+    // Read the header to know the message length
+    quint64 msgLen = 0;
+    headerStream >> msgLen;
+    ConnectionInfo &info = connectionMap[sock];
+    info.stage = StageBody;
+    info.msgLen = msgLen;
+
+    if ( sock->bytesAvailable() >= (qint64) msgLen ) {
+        readInitMessageBody( sock );
+    }
+}
+
+void SingleApplicationPrivate::readInitMessageBody( QLocalSocket *sock )
+{
+    Q_Q(SingleApplication);
+
+    if (!connectionMap.contains( sock )) {
+        return;
+    }
+
+    ConnectionInfo &info = connectionMap[sock];
+    if( sock->bytesAvailable() < ( qint64 )info.msgLen ) {
+        return;
+    }
+
+    // Read the message body
+    QByteArray msgBytes = sock->read(info.msgLen);
+    QDataStream readStream(msgBytes);
+
+#if (QT_VERSION >= QT_VERSION_CHECK(5, 6, 0))
+    readStream.setVersion( QDataStream::Qt_5_6 );
+#endif
+
+    // server name
+    QByteArray latin1Name;
+    readStream >> latin1Name;
+
+    // connection type
+    ConnectionType connectionType = InvalidConnection;
+    quint8 connTypeVal = InvalidConnection;
+    readStream >> connTypeVal;
+    connectionType = static_cast <ConnectionType>( connTypeVal );
+
+    // instance id
+    quint32 instanceId = 0;
+    readStream >> instanceId;
+
+    // checksum
+    quint16 msgChecksum = 0;
+    readStream >> msgChecksum;
+
+    const quint16 actualChecksum = qChecksum( msgBytes.constData(), static_cast<quint32>( msgBytes.length() - sizeof( quint16 ) ) );
+
+    bool isValid = readStream.status() == QDataStream::Ok &&
+                   QLatin1String(latin1Name) == blockServerName &&
+                   msgChecksum == actualChecksum;
+
+    if( !isValid ) {
+        sock->close();
+        return;
+    }
+
+    info.instanceId = instanceId;
+    info.stage = StageConnected;
+
+    if( connectionType == NewInstance ||
+        ( connectionType == SecondaryInstance &&
+          options & SingleApplication::Mode::SecondaryNotification ) )
+    {
+        Q_EMIT q->instanceStarted();
+    }
+
+    if (sock->bytesAvailable() > 0) {
+        Q_EMIT this->slotDataAvailable( sock, instanceId );
+    }
+}
+
+void SingleApplicationPrivate::slotDataAvailable( QLocalSocket *dataSocket, quint32 instanceId )
+{
+    Q_Q(SingleApplication);
+    Q_EMIT q->receivedMessage( instanceId, dataSocket->readAll() );
+}
+
+void SingleApplicationPrivate::slotClientConnectionClosed( QLocalSocket *closedSocket, quint32 instanceId )
+{
+    if( closedSocket->bytesAvailable() > 0 )
+        Q_EMIT slotDataAvailable( closedSocket, instanceId  );
+}
diff --git a/third_party/SingleApplication-3.0.19/singleapplication_p.h b/third_party/SingleApplication-3.0.19/singleapplication_p.h
new file mode 100644
index 00000000..e2c361fb
--- /dev/null
+++ b/third_party/SingleApplication-3.0.19/singleapplication_p.h
@@ -0,0 +1,99 @@
+// The MIT License (MIT)
+//
+// Copyright (c) Itay Grudev 2015 - 2016
+//
+// 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.
+
+//
+//  W A R N I N G !!!
+//  -----------------
+//
+// This file is not part of the SingleApplication API. It is used purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or may even be removed.
+//
+
+#ifndef SINGLEAPPLICATION_P_H
+#define SINGLEAPPLICATION_P_H
+
+#include <QtCore/QSharedMemory>
+#include <QtNetwork/QLocalServer>
+#include <QtNetwork/QLocalSocket>
+#include "singleapplication.h"
+
+struct InstancesInfo {
+    bool primary;
+    quint32 secondary;
+    qint64 primaryPid;
+    quint16 checksum;
+};
+
+struct ConnectionInfo {
+    explicit ConnectionInfo() :
+        msgLen(0), instanceId(0), stage(0) {}
+    qint64 msgLen;
+    quint32 instanceId;
+    quint8 stage;
+};
+
+class SingleApplicationPrivate : public QObject {
+Q_OBJECT
+public:
+    enum ConnectionType : quint8 {
+        InvalidConnection = 0,
+        NewInstance = 1,
+        SecondaryInstance = 2,
+        Reconnect = 3
+    };
+    enum ConnectionStage : quint8 {
+        StageHeader = 0,
+        StageBody = 1,
+        StageConnected = 2,
+    };
+    Q_DECLARE_PUBLIC(SingleApplication)
+
+    SingleApplicationPrivate( SingleApplication *q_ptr );
+     ~SingleApplicationPrivate();
+
+    void genBlockServerName();
+    void initializeMemoryBlock();
+    void startPrimary();
+    void startSecondary();
+    void connectToPrimary(int msecs, ConnectionType connectionType );
+    quint16 blockChecksum();
+    qint64 primaryPid();
+    void readInitMessageHeader(QLocalSocket *socket);
+    void readInitMessageBody(QLocalSocket *socket);
+
+    SingleApplication *q_ptr;
+    QSharedMemory *memory;
+    QLocalSocket *socket;
+    QLocalServer *server;
+    quint32 instanceNumber;
+    QString blockServerName;
+    SingleApplication::Options options;
+    QMap<QLocalSocket*, ConnectionInfo> connectionMap;
+
+public Q_SLOTS:
+    void slotConnectionEstablished();
+    void slotDataAvailable( QLocalSocket*, quint32 );
+    void slotClientConnectionClosed( QLocalSocket*, quint32 );
+};
+
+#endif // SINGLEAPPLICATION_P_H