diff options
author | Aqua-sama <aqua@iserlohn-fortress.net> | 2018-10-02 14:12:01 +0200 |
---|---|---|
committer | Aqua-sama <aqua@iserlohn-fortress.net> | 2018-10-02 14:12:01 +0200 |
commit | c409efea81d6cdf5fd0e36671455267a0049df13 (patch) | |
tree | 9ce243c7ea1498449e667aead541e31311705d77 /3rd-party/SingleApplication | |
parent | Update remaining repository paths in license headers (diff) | |
download | smolbote-c409efea81d6cdf5fd0e36671455267a0049df13.tar.xz |
Add SingleApplication as a git submodule
Diffstat (limited to '3rd-party/SingleApplication')
-rw-r--r-- | 3rd-party/SingleApplication/CHANGELOG.md | 162 | ||||
-rw-r--r-- | 3rd-party/SingleApplication/CMakeLists.txt | 12 | ||||
-rw-r--r-- | 3rd-party/SingleApplication/LICENSE | 24 | ||||
-rw-r--r-- | 3rd-party/SingleApplication/README.md | 287 | ||||
m--------- | 3rd-party/SingleApplication/SingleApplication.git | 0 | ||||
-rw-r--r-- | 3rd-party/SingleApplication/Windows.md | 46 | ||||
-rw-r--r-- | 3rd-party/SingleApplication/singleapplication.cpp | 468 | ||||
-rw-r--r-- | 3rd-party/SingleApplication/singleapplication.h | 135 | ||||
-rw-r--r-- | 3rd-party/SingleApplication/singleapplication.pri | 18 | ||||
-rw-r--r-- | 3rd-party/SingleApplication/singleapplication_p.h | 85 |
10 files changed, 8 insertions, 1229 deletions
diff --git a/3rd-party/SingleApplication/CHANGELOG.md b/3rd-party/SingleApplication/CHANGELOG.md deleted file mode 100644 index 4665222..0000000 --- a/3rd-party/SingleApplication/CHANGELOG.md +++ /dev/null @@ -1,162 +0,0 @@ -Changelog -========= - -__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__ ---------- - -* Depricated 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/3rd-party/SingleApplication/CMakeLists.txt b/3rd-party/SingleApplication/CMakeLists.txt index 5de0253..97fe7d7 100644 --- a/3rd-party/SingleApplication/CMakeLists.txt +++ b/3rd-party/SingleApplication/CMakeLists.txt @@ -7,12 +7,16 @@ set(CMAKE_AUTOMOC ON) #set(CMAKE_AUTORCC ON) add_library(SingleApplication - singleapplication.cpp - singleapplication.h - singleapplication_p.h) + SingleApplication.git/singleapplication.cpp + SingleApplication.git/singleapplication.h + SingleApplication.git/singleapplication_p.cpp + SingleApplication.git/singleapplication_p.h +) + +target_include_directories(SingleApplication PUBLIC SingleApplication.git) target_link_libraries(SingleApplication - Qt5::Core Qt5::Network Qt5::Widgets + Qt5::Core Qt5::Network Qt5::Widgets ) target_compile_definitions(SingleApplication diff --git a/3rd-party/SingleApplication/LICENSE b/3rd-party/SingleApplication/LICENSE deleted file mode 100644 index 85b2a14..0000000 --- a/3rd-party/SingleApplication/LICENSE +++ /dev/null @@ -1,24 +0,0 @@ -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/3rd-party/SingleApplication/README.md b/3rd-party/SingleApplication/README.md deleted file mode 100644 index 82e7a5b..0000000 --- a/3rd-party/SingleApplication/README.md +++ /dev/null @@ -1,287 +0,0 @@ -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 -``` - -Then include the `singleapplication.pri` file in your `.pro` project file. Also -don't forget to specify which `QCoreApplication` class your app is using if it -is not `QCoreApplication`. - -```qmake -include(singleapplication/singleapplication.pri) -DEFINES += QAPPLICATION_CLASS=QApplication -``` - -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. - -To handle an issue on `*nix` systems, where the operating system owns the shared -memory block and if the program crashes the memory remains untouched, the -library binds to the following signals and closes the program with -`error code = 128 + signum` where signum is the number representation of the -signal listed below. Handling the signal is required in order to safely delete -the `QSharedMemory` block. Each of these signals are potentially lethal and will -results in process termination. - -* `SIGHUP` - `1`, Hangup. -* `SIGINT` - `2`, Terminal interrupt signal -* `SIGQUIT` - `3`, Terminal quit signal. -* `SIGILL` - `4`, Illegal instruction. -* `SIGABRT` - `6`, Process abort signal. -* `SIGBUS` - `7`, Access to an undefined portion of a memory object. -* `SIGFPE` - `8`, Erroneous arithmetic operation (such as division by zero). -* `SIGSEGV` - `11`, Invalid memory reference. -* `SIGSYS` - `12`, Bad system call. -* `SIGPIPE` - `13`, Write on a pipe with no one to read it. -* `SIGALRM` - `14`, Alarm clock. -* `SIGTERM` - `15`, Termination signal. -* `SIGXCPU` - `24`, CPU time limit exceeded. -* `SIGXFSZ` - `25`, File size limit exceeded. - -Additionally the library can recover from being killed with uncatchable signals -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 some of the examples distributed -under the BSD license. diff --git a/3rd-party/SingleApplication/SingleApplication.git b/3rd-party/SingleApplication/SingleApplication.git new file mode 160000 +Subproject 0db27016b0470b989f4fa114ae9dde5aa2a325f diff --git a/3rd-party/SingleApplication/Windows.md b/3rd-party/SingleApplication/Windows.md deleted file mode 100644 index 48b0748..0000000 --- a/3rd-party/SingleApplication/Windows.md +++ /dev/null @@ -1,46 +0,0 @@ -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.getPrimaryPid() ) ); -} - -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/3rd-party/SingleApplication/singleapplication.cpp b/3rd-party/SingleApplication/singleapplication.cpp deleted file mode 100644 index c74d6db..0000000 --- a/3rd-party/SingleApplication/singleapplication.cpp +++ /dev/null @@ -1,468 +0,0 @@ -// 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. - -#include <cstdlib> - -#include <QtCore/QDir> -#include <QtCore/QProcess> -#include <QtCore/QByteArray> -#include <QtCore/QSemaphore> -#include <QtCore/QSharedMemory> -#include <QtCore/QStandardPaths> -#include <QtCore/QCryptographicHash> -#include <QtCore/QDataStream> -#include <QtNetwork/QLocalServer> -#include <QtNetwork/QLocalSocket> - -#ifdef Q_OS_UNIX - #include <signal.h> - #include <unistd.h> -#endif - -#ifdef Q_OS_WIN - #include <windows.h> - #include <lmcons.h> -#endif - -#include "singleapplication.h" -#include "singleapplication_p.h" - - -SingleApplicationPrivate::SingleApplicationPrivate( SingleApplication *q_ptr ) : q_ptr( q_ptr ) { - server = nullptr; - socket = nullptr; -} - -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; - } - memory->unlock(); - - delete memory; -} - -void SingleApplicationPrivate::genBlockServerName( int timeout ) -{ - 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 - Q_UNUSED(timeout); - 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( QStandardPaths::standardLocations( QStandardPaths::HomeLocation ).join("").toUtf8() ); - } -#endif -#ifdef Q_OS_UNIX - QProcess process; - process.start( "whoami" ); - if( process.waitForFinished( timeout ) && - process.exitCode() == QProcess::NormalExit) { - appData.addData( process.readLine() ); - } else { - appData.addData( - QDir( - QStandardPaths::standardLocations( QStandardPaths::HomeLocation ).first() - ).absolutePath().toUtf8() - ); - } -#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::startPrimary( bool resetMemory ) -{ - Q_Q(SingleApplication); - -#ifdef Q_OS_UNIX - // Handle any further termination signals to ensure the - // QSharedMemory block is deleted even if the process crashes - crashHandler(); -#endif - // 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 - memory->lock(); - InstancesInfo* inst = static_cast<InstancesInfo*>(memory->data()); - - if( resetMemory ) { - inst->secondary = 0; - } - - inst->primary = true; - inst->primaryPid = q->applicationPid(); - - memory->unlock(); - - instanceNumber = 0; -} - -void SingleApplicationPrivate::startSecondary() -{ -#ifdef Q_OS_UNIX - // Handle any further termination signals to ensure the - // QSharedMemory block is deleted even if the process crashes - crashHandler(); -#endif -} - -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); - writeStream.setVersion(QDataStream::Qt_5_6); - writeStream << blockServerName.toLatin1(); - writeStream << static_cast<quint8>(connectionType); - writeStream << instanceNumber; - quint16 checksum = qChecksum(initMsg.constData(), static_cast<quint32>(initMsg.length())); - writeStream << checksum; - - socket->write( initMsg ); - socket->flush(); - socket->waitForBytesWritten( msecs ); - } -} - -qint64 SingleApplicationPrivate::primaryPid() -{ - qint64 pid; - - memory->lock(); - InstancesInfo* inst = static_cast<InstancesInfo*>(memory->data()); - pid = inst->primaryPid; - memory->unlock(); - - return pid; -} - -#ifdef Q_OS_UNIX - void SingleApplicationPrivate::crashHandler() - { - // Handle any further termination signals to ensure the - // QSharedMemory block is deleted even if the process crashes - signal( SIGHUP, SingleApplicationPrivate::terminate ); // 1 - signal( SIGINT, SingleApplicationPrivate::terminate ); // 2 - signal( SIGQUIT, SingleApplicationPrivate::terminate ); // 3 - signal( SIGILL, SingleApplicationPrivate::terminate ); // 4 - signal( SIGABRT, SingleApplicationPrivate::terminate ); // 6 - signal( SIGFPE, SingleApplicationPrivate::terminate ); // 8 - signal( SIGBUS, SingleApplicationPrivate::terminate ); // 10 - signal( SIGSEGV, SingleApplicationPrivate::terminate ); // 11 - signal( SIGSYS, SingleApplicationPrivate::terminate ); // 12 - signal( SIGPIPE, SingleApplicationPrivate::terminate ); // 13 - signal( SIGALRM, SingleApplicationPrivate::terminate ); // 14 - signal( SIGTERM, SingleApplicationPrivate::terminate ); // 15 - signal( SIGXCPU, SingleApplicationPrivate::terminate ); // 24 - signal( SIGXFSZ, SingleApplicationPrivate::terminate ); // 25 - } - - void SingleApplicationPrivate::terminate( int signum ) - { - delete ((SingleApplication*)QCoreApplication::instance())->d_ptr; - ::exit( 128 + signum ); - } -#endif - -/** - * @brief Executed when a connection has been made to the LocalServer - */ -void SingleApplicationPrivate::slotConnectionEstablished() -{ - Q_Q(SingleApplication); - - QLocalSocket *nextConnSocket = server->nextPendingConnection(); - - quint32 instanceId = 0; - ConnectionType connectionType = InvalidConnection; - if( nextConnSocket->waitForReadyRead( 100 ) ) { - // read all data from message in same order/format as written - QByteArray msgBytes = nextConnSocket->read(nextConnSocket->bytesAvailable() - static_cast<qint64>(sizeof(quint16))); - QByteArray checksumBytes = nextConnSocket->read(sizeof(quint16)); - QDataStream readStream(msgBytes); - readStream.setVersion(QDataStream::Qt_5_6); - - // server name - QByteArray latin1Name; - readStream >> latin1Name; - // connectioon type - quint8 connType = InvalidConnection; - readStream >> connType; - connectionType = static_cast<ConnectionType>(connType); - // instance id - readStream >> instanceId; - // checksum - quint16 msgChecksum = 0; - QDataStream checksumStream(checksumBytes); - checksumStream.setVersion(QDataStream::Qt_5_6); - checksumStream >> msgChecksum; - - const quint16 actualChecksum = qChecksum(msgBytes.constData(), static_cast<quint32>(msgBytes.length())); - - if (readStream.status() != QDataStream::Ok || QLatin1String(latin1Name) != blockServerName || msgChecksum != actualChecksum) { - connectionType = InvalidConnection; - } - } - - if( connectionType == InvalidConnection ) { - nextConnSocket->close(); - delete nextConnSocket; - return; - } - - QObject::connect( - nextConnSocket, - &QLocalSocket::aboutToClose, - this, - [nextConnSocket, instanceId, this]() { - Q_EMIT this->slotClientConnectionClosed( nextConnSocket, instanceId ); - } - ); - - QObject::connect( - nextConnSocket, - &QLocalSocket::readyRead, - this, - [nextConnSocket, instanceId, this]() { - Q_EMIT this->slotDataAvailable( nextConnSocket, instanceId ); - } - ); - - if( connectionType == NewInstance || ( - connectionType == SecondaryInstance && - options & SingleApplication::Mode::SecondaryNotification - ) - ) { - Q_EMIT q->instanceStarted(); - } - - if( nextConnSocket->bytesAvailable() > 0 ) { - Q_EMIT this->slotDataAvailable( nextConnSocket, 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 ); - closedSocket->deleteLater(); -} - -/** - * @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); - - // 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( timeout ); - - // Guarantee thread safe behaviour with a shared memory block. Also by - // explicitly attaching it and then deleting it we make sure that the - // memory is deleted even if the process had crashed on Unix. -#ifdef Q_OS_UNIX - d->memory = new QSharedMemory( d->blockServerName ); - d->memory->attach(); - delete d->memory; -#endif - d->memory = new QSharedMemory( d->blockServerName ); - - // Create a shared memory block - if( d->memory->create( sizeof( InstancesInfo ) ) ) { - d->startPrimary( true ); - return; - } else { - // Attempt to attach to the memory segment - if( d->memory->attach() ) { - d->memory->lock(); - InstancesInfo* inst = static_cast<InstancesInfo*>(d->memory->data()); - - if( ! inst->primary ) { - d->startPrimary( false ); - d->memory->unlock(); - return; - } - - // Check if another instance can be started - if( allowSecondary ) { - inst->secondary += 1; - 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->flush(); - d->socket->waitForBytesWritten( timeout ); - return dataWritten; -} diff --git a/3rd-party/SingleApplication/singleapplication.h b/3rd-party/SingleApplication/singleapplication.h deleted file mode 100644 index 33a9898..0000000 --- a/3rd-party/SingleApplication/singleapplication.h +++ /dev/null @@ -1,135 +0,0 @@ -// 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. - -#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 multipe 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 miliseconds. - * @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 = 100 ); - ~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/3rd-party/SingleApplication/singleapplication.pri b/3rd-party/SingleApplication/singleapplication.pri deleted file mode 100644 index a82ff28..0000000 --- a/3rd-party/SingleApplication/singleapplication.pri +++ /dev/null @@ -1,18 +0,0 @@ -QT += core network -CONFIG += c++11 - -HEADERS += $$PWD/singleapplication.h \ - $$PWD/singleapplication_p.h -SOURCES += $$PWD/singleapplication.cpp - -INCLUDEPATH += $$PWD - -win32 { - msvc:LIBS += Advapi32.lib - gcc:LIBS += -lAdvapi32 -} - -DISTFILES += \ - $$PWD/README.md \ - $$PWD/CHANGELOG.md \ - $$PWD/Windows.md diff --git a/3rd-party/SingleApplication/singleapplication_p.h b/3rd-party/SingleApplication/singleapplication_p.h deleted file mode 100644 index a990a53..0000000 --- a/3rd-party/SingleApplication/singleapplication_p.h +++ /dev/null @@ -1,85 +0,0 @@ -// 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; -}; - -class SingleApplicationPrivate : public QObject { -Q_OBJECT -public: - enum ConnectionType : quint8 { - InvalidConnection = 0, - NewInstance = 1, - SecondaryInstance = 2, - Reconnect = 3 - }; - Q_DECLARE_PUBLIC(SingleApplication) - - SingleApplicationPrivate( SingleApplication *q_ptr ); - ~SingleApplicationPrivate(); - - void genBlockServerName( int msecs ); - void startPrimary( bool resetMemory ); - void startSecondary(); - void connectToPrimary(int msecs, ConnectionType connectionType ); - qint64 primaryPid(); - -#ifdef Q_OS_UNIX - void crashHandler(); - static void terminate( int signum ); -#endif - - QSharedMemory *memory; - SingleApplication *q_ptr; - QLocalSocket *socket; - QLocalServer *server; - quint32 instanceNumber; - QString blockServerName; - SingleApplication::Options options; - -public Q_SLOTS: - void slotConnectionEstablished(); - void slotDataAvailable( QLocalSocket*, quint32 ); - void slotClientConnectionClosed( QLocalSocket*, quint32 ); -}; - -#endif // SINGLEAPPLICATION_P_H |