SingleApplication v3.0a

This commit is contained in:
Itay Grudev 2016-08-10 02:42:46 +01:00
parent b459c9247b
commit 4e5c1647cc
No known key found for this signature in database
GPG Key ID: 913C021BA6F9DE98
6 changed files with 522 additions and 187 deletions

View File

@ -1,13 +1,43 @@
Changelog
=========
__v2.4__
__v3.0a__
--------
* Depricated secondary instances count.
* Added a sendMessage() method to send a message to the primary instance.
* Added a receivedMessage() signal, emmited 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 of 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 uses as the key for the
`QSharedMemory` block and the `QLocalServer`. Since at least
`applicationFilePath` is always present there is no need to explicitly set
any of these 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. An setting 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.
and the shared memory had not been deleted.
__v2.3__
--------

View File

@ -4,8 +4,8 @@ SingleApplication
This is a replacement of the QSingleApplication for `Qt5`.
Keeps the Primary Instance of your Application and kills each subsequent
instances. It can (if enabled) spawn a certain number of secondary instances
(with the `--secondary` command line argument).
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
-----
@ -15,32 +15,26 @@ 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 uses your `Organization Name` and `Application Name` to set 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 then listen for connections
on it. 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 it that a new instance had been started, after which it would terminate
with status code `0`. The Primary Instance, `SingleApplication` would emit the
`showUp()` signal upon detecting that a new instance had been started.
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 then
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 it that a new instance had been started, after which it
would terminate with status code `0`. 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.
Here is an example usage of the library:
In your main you need to set the the `applicationName` and `organizationName` of
the `QCoreApplication` class like so:
You can use the library as if you use any other `QCoreApplication` class:
```cpp
#include <QApplication>
#include "singleapplication.h"
#include <SingleApplication.h>
int main( int argc, char* argv[] )
{
QApplication::setApplicationName("{Your App Name}");
QApplication::setOrganizationName("{Your Organization Name}");
SingleApplication app( argc, argv );
return app.exec();
@ -55,24 +49,28 @@ how:
git submodule add git@github.com:itay-grudev/SingleApplication.git singleapplication
```
And include the `singleapplication.pri` file in your `.pro` project file:
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 `Show Up` signal
The `Instance Started` signal
------------------------
The SingleApplication class implements a `showUp()` signal. You can bind to that
signal to raise your application's window when a new instance had been started.
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.
```cpp
// window is a QWindow instance
QObject::connect( &app, &SingleApplication::showUp, window, &QWindow::raise );
QObject::connect( &app, &SingleApplication::instanceStarted, window, &QWindow::raise );
```
Using `QCoreApplication::instance()` is a neat way to get the
Using `SingleApplication::instance()` is a neat way to get the
`SingleApplication` instance for binding to it's signals anywhere in your
program.
@ -81,15 +79,27 @@ 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 `0` meaning no Secondary
Instances. Here is an example allowing spawning up to `2` Secondary Instances.
`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
SingleApplication app( argc, argv, 2 );
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();
}
```
After which just call your program with the `--secondary` argument to launch a
secondary instance.
___Note:__ A secondary instance won't cause the emission of the
`instanceStarted()` signal.
You can check whether your instance is a primary or secondary with the following
methods:
@ -100,20 +110,18 @@ app.isPrimary();
app.isSecondary();
```
__*Note:*__ If your Primary Instance is terminated upon launch of a new one it
will replace it as Primary even if the `--secondary` argument has been set.
*P.S. If you think this behavior could be improved create an issue and explain
why.*
__*Note:*__ If your Primary Instance is terminated a newly launched instance
will replace the Primary one even if the Secondary flag has been set.
Versioning
----------
The current library versions is `2.4`.
The current library versions is `3.0a`.
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.
compatible with the previous release. See `CHANGELOG.md` for more details.
Implementation
--------------
@ -121,7 +129,7 @@ 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
`showUp()` signal.
`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

View File

@ -23,39 +23,81 @@
#include <cstdlib>
#include <QtCore/QMutex>
#include <QtCore/QProcess>
#include <QtCore/QByteArray>
#include <QtCore/QSemaphore>
#include <QtCore/QSharedMemory>
#include <QtNetwork/QLocalSocket>
#include <QtCore/QStandardPaths>
#include <QtCore/QCryptographicHash>
#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"
struct InstancesInfo {
bool primary;
uint8_t secondary;
};
SingleApplicationPrivate::SingleApplicationPrivate( SingleApplication *q_ptr ) : q_ptr( q_ptr ) {
server = nullptr;
socket = nullptr;
}
class SingleApplicationPrivate {
public:
Q_DECLARE_PUBLIC(SingleApplication)
SingleApplicationPrivate( SingleApplication *q_ptr ) : q_ptr( q_ptr ) {
server = NULL;
}
~SingleApplicationPrivate()
{
SingleApplicationPrivate::~SingleApplicationPrivate()
{
cleanUp();
}
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::applicationVersion().toUtf8() );
appData.addData( SingleApplication::app_t::applicationFilePath().toUtf8() );
appData.addData( SingleApplication::app_t::organizationName().toUtf8() );
appData.addData( SingleApplication::app_t::organizationDomain().toUtf8() );
// User level block requires a user specific data in the hash
if( options & SingleApplication::Mode::User ) {
#ifdef Q_OS_WIN
char username[UNLEN + 1];
// Specifies size of the buffer on input
DWORD usernameLength = sizeof( username );
if( GetUserName( username, &usernameLength ) ) {
// usernameLength includes the null terminating character
appData.addData( username, usernameLength - 1 );
} else {
appData.addData( QStandardPaths::standardLocations( QStandardPaths::HomeLocation ).join("").toUtf8() );
}
#endif
#ifdef Q_OS_UNIX
QString username;
QProcess process;
process.start( "whoami" );
if( process.waitForFinished( timeout ) &&
process.exitCode() == QProcess::NormalExit) {
appData.addData( process.readLine() );
} else {
appData.addData( QStandardPaths::standardLocations( QStandardPaths::HomeLocation ).join("").toUtf8() );
}
#endif
}
void startPrimary( bool resetMemory )
{
Q_Q(SingleApplication);
// 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 )
{
#ifdef Q_OS_UNIX
// Handle any further termination signals to ensure the
// QSharedMemory block is deleted even if the process crashes
@ -63,14 +105,14 @@ public:
#endif
// Successful creation means that no main process exists
// So we start a QLocalServer to listen for connections
QLocalServer::removeServer( memory->key() );
QLocalServer::removeServer( blockServerName );
server = new QLocalServer();
server->listen( memory->key() );
server->listen( blockServerName );
QObject::connect(
server,
&QLocalServer::newConnection,
q,
&SingleApplication::slotConnectionEstablished
this,
&SingleApplicationPrivate::slotConnectionEstablished
);
// Reset the number of connections
@ -85,33 +127,59 @@ public:
}
memory->unlock();
}
void startSecondary()
{
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
}
notifyPrimary();
void SingleApplicationPrivate::connectToPrimary( int msecs, char connectionType )
{
// Connect to the Local Server of the Primary Instance if not already
// connected.
if( socket == nullptr ) {
socket = new QLocalSocket();
}
void notifyPrimary()
{
// Connect to the Local Server of the main process to notify it
// that a new process had been started
QLocalSocket socket;
socket.connectToServer( memory->key() );
// 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;
socket.waitForConnected( 100 );
socket.close();
QByteArray initMsg = blockServerName.toLatin1();
initMsg.append( connectionType );
initMsg.append( (const char *)&instanceNumber, sizeof(quint32) );
initMsg.append( QByteArray::number( qChecksum( initMsg.constData(), initMsg.length() ), 256) );
socket->write( initMsg );
socket->flush();
socket->waitForBytesWritten( msecs );
}
}
#ifdef Q_OS_UNIX
void crashHandler()
void SingleApplicationPrivate::crashHandler()
{
// This guarantees the program will work even with multiple
// instances of SingleApplication in different threads.
@ -140,7 +208,7 @@ public:
signal( SIGXFSZ, SingleApplicationPrivate::terminate ); // 25
}
static void terminate( int signum )
void SingleApplicationPrivate::terminate( int signum )
{
while( ! sharedMem.empty() ) {
delete sharedMem.back();
@ -149,83 +217,154 @@ public:
::exit( 128 + signum );
}
static QList<SingleApplicationPrivate*> sharedMem;
static QMutex sharedMemMutex;
#endif
void cleanUp() {
memory->lock();
InstancesInfo* inst = (InstancesInfo*)memory->data();
if( server != NULL ) {
server->close();
inst->primary = false;
} else {
if( inst->secondary > 0 )
inst->secondary -= 1;
}
memory->unlock();
delete memory;
}
QSharedMemory *memory;
SingleApplication *q_ptr;
QLocalServer *server;
};
#ifdef Q_OS_UNIX
QList<SingleApplicationPrivate*> SingleApplicationPrivate::sharedMem;
QMutex SingleApplicationPrivate::sharedMemMutex;
#endif
void SingleApplicationPrivate::cleanUp() {
if( socket != nullptr ) {
socket->close();
delete socket;
}
memory->lock();
InstancesInfo* inst = (InstancesInfo*)memory->data();
if( server != nullptr ) {
server->close();
inst->primary = false;
}
memory->unlock();
delete memory;
}
/**
* @brief Executed when a connection has been made to the LocalServer
*/
void SingleApplicationPrivate::slotConnectionEstablished()
{
Q_Q(SingleApplication);
QLocalSocket *socket = server->nextPendingConnection();
// Verify that the new connection follows the SingleApplication protocol
char connectionType;
quint32 instanceId;
QByteArray initMsg, tmp;
bool invalidConnection = false;
if( socket->waitForReadyRead( 100 ) ) {
tmp = socket->read( blockServerName.length() );
// Verify that the socket data start with blockServerName
if( tmp == blockServerName.toLatin1() ) {
initMsg = tmp;
tmp = socket->read(1);
// Verify that the next charecter is N/S/R (connecion type)
// Stands for New Instance/Secondary Instance/Reconnect
if( tmp == "N" || tmp == "S" || tmp == "R" ) {
connectionType = tmp.at(0);
initMsg += tmp;
tmp = socket->read( sizeof(quint32) );
const char * data = tmp.constData();
instanceId = (quint32)*data;
initMsg += tmp;
// Verify the checksum of the initMsg
QByteArray checksum = QByteArray::number(
qChecksum( initMsg.constData(), initMsg.length() ),
256
);
tmp = socket->read( checksum.length() );
if( checksum != tmp ) {
invalidConnection = true;
}
} else {
invalidConnection = true;
}
} else {
invalidConnection = true;
}
} else {
invalidConnection = true;
}
if( invalidConnection ) {
socket->close();
delete socket;
return;
}
QObject::connect(
socket,
&QLocalSocket::aboutToClose,
this,
[socket, instanceId, this]() {
Q_EMIT this->slotClientConnectionClosed( socket, instanceId );
}
);
QObject::connect(
socket,
&QLocalSocket::readyRead,
this,
[socket, instanceId, this]() {
Q_EMIT this->slotDataAvailable( socket, instanceId );
}
);
if( connectionType == 'N' || (
connectionType == 'S' &&
options & SingleApplication::Mode::SecondaryNotification
)
) {
Q_EMIT q->instanceStarted();
}
if( socket->bytesAvailable() > 0 ) {
Q_EMIT this->slotDataAvailable( socket, instanceId );
}
}
void SingleApplicationPrivate::slotDataAvailable( QLocalSocket *socket, quint32 instanceId )
{
Q_Q(SingleApplication);
Q_EMIT q->receivedMessage( instanceId, socket->readAll() );
}
void SingleApplicationPrivate::slotClientConnectionClosed( QLocalSocket *socket, quint32 instanceId )
{
if( socket->bytesAvailable() > 0 )
Q_EMIT slotDataAvailable( socket, instanceId );
socket->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[], uint8_t secondaryInstances )
SingleApplication::SingleApplication( int &argc, char *argv[], bool allowSecondary, Options options, int timeout )
: app_t( argc, argv ), d_ptr( new SingleApplicationPrivate( this ) )
{
Q_D(SingleApplication);
// Check command line arguments for the force primary and secondary flags
#ifdef Q_OS_UNIX
bool forcePrimary = false;
#endif
bool secondary = false;
for( int i = 0; i < argc; ++i ) {
if( strcmp( argv[i], "--secondary" ) == 0 ) {
secondary = true;
#ifndef Q_OS_UNIX
break;
#endif
}
#ifdef Q_OS_UNIX
if( strcmp( argv[i], "--primary" ) == 0 ) {
secondary = false;
forcePrimary = true;
break;
}
#endif
}
// Store the current mode of the program
d->options = options;
QString serverName = app_t::organizationName() + app_t::applicationName();
serverName.replace( QRegExp("[^\\w\\-. ]"), "" );
// 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
// attaching to it and deleting it we make sure that the memory i deleted
// even if the process had crashed
d->memory = new QSharedMemory( serverName );
// 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;
d->memory = new QSharedMemory( serverName );
// Create a shared memory block with a minimum size of 1 byte
#ifdef Q_OS_UNIX
if( d->memory->create( sizeof(InstancesInfo) ) || forcePrimary ) {
#else
if( d->memory->create( sizeof(InstancesInfo) ) ) {
#endif
d->memory = new QSharedMemory( d->blockServerName );
// Create a shared memory block
if( d->memory->create( sizeof( InstancesInfo ) ) ) {
d->startPrimary( true );
return;
} else {
@ -241,9 +380,13 @@ SingleApplication::SingleApplication( int &argc, char *argv[], uint8_t secondary
}
// Check if another instance can be started
if( secondary && inst->secondary < secondaryInstances ) {
if( allowSecondary ) {
inst->secondary += 1;
d->instanceNumber = inst->secondary;
d->startSecondary();
if( d->options & Mode::SecondaryNotification ) {
d->connectToPrimary( timeout, 'S' );
}
d->memory->unlock();
return;
}
@ -252,9 +395,9 @@ SingleApplication::SingleApplication( int &argc, char *argv[], uint8_t secondary
}
}
d->notifyPrimary();
d->connectToPrimary( timeout, 'N' );
delete d;
::exit(EXIT_SUCCESS);
::exit( EXIT_SUCCESS );
}
/**
@ -269,24 +412,33 @@ SingleApplication::~SingleApplication()
bool SingleApplication::isPrimary()
{
Q_D(SingleApplication);
return d->server != NULL;
return d->server != nullptr;
}
bool SingleApplication::isSecondary()
{
Q_D(SingleApplication);
return d->server == NULL;
return d->server == nullptr;
}
/**
* @brief Executed when a connection has been made to the LocalServer
*/
void SingleApplication::slotConnectionEstablished()
quint32 SingleApplication::instanceId()
{
Q_D(SingleApplication);
return d->instanceNumber;
}
bool SingleApplication::sendMessage( QByteArray message, int timeout )
{
Q_D(SingleApplication);
QLocalSocket *socket = d->server->nextPendingConnection();
socket->close();
delete socket;
Q_EMIT showUp();
// Nobody to connect to
if( isPrimary() ) return false;
// Make sure the socket is connected
d->connectToPrimary( timeout, 'R' );
d->socket->write( message );
bool dataWritten = d->socket->flush();
d->socket->waitForBytesWritten( timeout );
return dataWritten;
}

View File

@ -24,6 +24,7 @@
#define SINGLE_APPLICATION_H
#include <QtCore/QtGlobal>
#include <QtNetwork/QLocalSocket>
#ifndef QAPPLICATION_CLASS
#define QAPPLICATION_CLASS QCoreApplication
@ -34,31 +35,93 @@
class SingleApplicationPrivate;
/**
* @brief The SingleApplication class handles multipe instances of the same Application
* @see QApplication
* @brief The SingleApplication class handles multipe instances of the same
* Application
* @see QCoreApplication
*/
class SingleApplication : public QAPPLICATION_CLASS
{
Q_OBJECT
Q_DECLARE_PRIVATE(SingleApplication)
typedef QAPPLICATION_CLASS app_t;
public:
explicit SingleApplication( int &argc, char *argv[], uint8_t secondaryInstances = 0 );
/**
* @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
};
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();
Q_SIGNALS:
void showUp();
/**
* @brief Returns a unique identifier for the current instance
* @returns {int}
*/
quint32 instanceId();
private Q_SLOTS:
void slotConnectionEstablished();
/**
* @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

View File

@ -1,6 +1,8 @@
DEFINES += QAPPLICATION_CLASS=QApplication
QT += core network
CONFIG += c++11
HEADERS += $$PWD/singleapplication.h
HEADERS += $$PWD/singleapplication.h \
$$PWD/singleapplication_p.h
SOURCES += $$PWD/singleapplication.cpp
QT += core network
INCLUDEPATH += $$PWD

80
singleapplication_p.h Normal file
View File

@ -0,0 +1,80 @@
// 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;
};
class SingleApplicationPrivate : public QObject {
Q_OBJECT
public:
Q_DECLARE_PUBLIC(SingleApplication)
SingleApplicationPrivate( SingleApplication *q_ptr );
~SingleApplicationPrivate();
void genBlockServerName( int msecs );
void startPrimary( bool resetMemory );
void startSecondary();
void connectToPrimary( int msecs, char connectionType );
void cleanUp();
#ifdef Q_OS_UNIX
void crashHandler();
static void terminate( int signum );
static QList<SingleApplicationPrivate*> sharedMem;
static QMutex sharedMemMutex;
#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