Added 3rdpart QtSingleApplication class. Fixed some overall-status

issues.
This commit is contained in:
Klaas Freitag 2012-07-30 17:10:48 +03:00
parent ae847e46e7
commit 5275238f51
16 changed files with 1243 additions and 12 deletions

5
src/3rdparty/qtlockedfile/README.txt vendored Normal file
View file

@ -0,0 +1,5 @@
This is the src directory of the QtLockedFile
solution integrated over from Qt's Qt Creator.
It is required by the QtSingleApplication solution.

View file

@ -0,0 +1,159 @@
/**************************************************************************
**
** This file is part of Qt Creator
**
** Copyright (c) 2012 Nokia Corporation and/or its subsidiary(-ies).
**
** Contact: http://www.qt-project.org/
**
**
** GNU Lesser General Public License Usage
**
** This file may be used under the terms of the GNU Lesser General Public
** License version 2.1 as published by the Free Software Foundation and
** appearing in the file LICENSE.LGPL included in the packaging of this file.
** Please review the following information to ensure the GNU Lesser General
** Public License version 2.1 requirements will be met:
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Nokia gives you certain additional
** rights. These rights are described in the Nokia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
** Other Usage
**
** Alternatively, this file may be used in accordance with the terms and
** conditions contained in a signed written agreement between you and Nokia.
**
**
**************************************************************************/
#include "qtlockedfile.h"
namespace SharedTools {
/*!
\class QtLockedFile
\brief The QtLockedFile class extends QFile with advisory locking functions.
A file may be locked in read or write mode. Multiple instances of
\e QtLockedFile, created in multiple processes running on the same
machine, may have a file locked in read mode. Exactly one instance
may have it locked in write mode. A read and a write lock cannot
exist simultaneously on the same file.
The file locks are advisory. This means that nothing prevents
another process from manipulating a locked file using QFile or
file system functions offered by the OS. Serialization is only
guaranteed if all processes that access the file use
QtLockedFile. Also, while holding a lock on a file, a process
must not open the same file again (through any API), or locks
can be unexpectedly lost.
The lock provided by an instance of \e QtLockedFile is released
whenever the program terminates. This is true even when the
program crashes and no destructors are called.
*/
/*! \enum QtLockedFile::LockMode
This enum describes the available lock modes.
\value ReadLock A read lock.
\value WriteLock A write lock.
\value NoLock Neither a read lock nor a write lock.
*/
/*!
Constructs an unlocked \e QtLockedFile object. This constructor behaves in the same way
as \e QFile::QFile().
\sa QFile::QFile()
*/
QtLockedFile::QtLockedFile()
: QFile()
{
#ifdef Q_OS_WIN
m_semaphore_hnd = 0;
m_mutex_hnd = 0;
#endif
m_lock_mode = NoLock;
}
/*!
Constructs an unlocked QtLockedFile object with file \a name. This constructor behaves in
the same way as \e QFile::QFile(const QString&).
\sa QFile::QFile()
*/
QtLockedFile::QtLockedFile(const QString &name)
: QFile(name)
{
#ifdef Q_OS_WIN
m_semaphore_hnd = 0;
m_mutex_hnd = 0;
#endif
m_lock_mode = NoLock;
}
/*!
Returns \e true if this object has a in read or write lock;
otherwise returns \e false.
\sa lockMode()
*/
bool QtLockedFile::isLocked() const
{
return m_lock_mode != NoLock;
}
/*!
Returns the type of lock currently held by this object, or \e QtLockedFile::NoLock.
\sa isLocked()
*/
QtLockedFile::LockMode QtLockedFile::lockMode() const
{
return m_lock_mode;
}
/*!
\fn bool QtLockedFile::lock(LockMode mode, bool block = true)
Obtains a lock of type \a mode.
If \a block is true, this
function will block until the lock is acquired. If \a block is
false, this function returns \e false immediately if the lock cannot
be acquired.
If this object already has a lock of type \a mode, this function returns \e true immediately. If this object has a lock of a different type than \a mode, the lock
is first released and then a new lock is obtained.
This function returns \e true if, after it executes, the file is locked by this object,
and \e false otherwise.
\sa unlock(), isLocked(), lockMode()
*/
/*!
\fn bool QtLockedFile::unlock()
Releases a lock.
If the object has no lock, this function returns immediately.
This function returns \e true if, after it executes, the file is not locked by
this object, and \e false otherwise.
\sa lock(), isLocked(), lockMode()
*/
/*!
\fn QtLockedFile::~QtLockedFile()
Destroys the \e QtLockedFile object. If any locks were held, they are released.
*/
} // namespace SharedTools

View file

@ -0,0 +1,78 @@
/**************************************************************************
**
** This file is part of Qt Creator
**
** Copyright (c) 2012 Nokia Corporation and/or its subsidiary(-ies).
**
** Contact: http://www.qt-project.org/
**
**
** GNU Lesser General Public License Usage
**
** This file may be used under the terms of the GNU Lesser General Public
** License version 2.1 as published by the Free Software Foundation and
** appearing in the file LICENSE.LGPL included in the packaging of this file.
** Please review the following information to ensure the GNU Lesser General
** Public License version 2.1 requirements will be met:
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Nokia gives you certain additional
** rights. These rights are described in the Nokia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
** Other Usage
**
** Alternatively, this file may be used in accordance with the terms and
** conditions contained in a signed written agreement between you and Nokia.
**
**
**************************************************************************/
#ifndef QTLOCKEDFILE_H
#define QTLOCKEDFILE_H
#include <QFile>
#if defined(Q_OS_WIN)
# if !defined(QT_QTLOCKEDFILE_EXPORT) && !defined(QT_QTLOCKEDFILE_IMPORT)
# define QT_QTLOCKEDFILE_EXPORT
# elif defined(QT_QTLOCKEDFILE_IMPORT)
# if defined(QT_QTLOCKEDFILE_EXPORT)
# undef QT_QTLOCKEDFILE_EXPORT
# endif
# define QT_QTLOCKEDFILE_EXPORT __declspec(dllimport)
# elif defined(QT_QTLOCKEDFILE_EXPORT)
# undef QT_QTLOCKEDFILE_EXPORT
# define QT_QTLOCKEDFILE_EXPORT __declspec(dllexport)
# endif
#else
# define QT_QTLOCKEDFILE_EXPORT
#endif
namespace SharedTools {
class QT_QTLOCKEDFILE_EXPORT QtLockedFile : public QFile
{
public:
enum LockMode { NoLock = 0, ReadLock, WriteLock };
QtLockedFile();
QtLockedFile(const QString &name);
~QtLockedFile();
bool lock(LockMode mode, bool block = true);
bool unlock();
bool isLocked() const;
LockMode lockMode() const;
private:
#ifdef Q_OS_WIN
Qt::HANDLE m_semaphore_hnd;
Qt::HANDLE m_mutex_hnd;
#endif
LockMode m_lock_mode;
};
} // namespace SharedTools
#endif // QTLOCKEDFILE_H

View file

@ -0,0 +1,108 @@
/**************************************************************************
**
** This file is part of Qt Creator
**
** Copyright (c) 2012 Nokia Corporation and/or its subsidiary(-ies).
**
** Contact: http://www.qt-project.org/
**
**
** GNU Lesser General Public License Usage
**
** This file may be used under the terms of the GNU Lesser General Public
** License version 2.1 as published by the Free Software Foundation and
** appearing in the file LICENSE.LGPL included in the packaging of this file.
** Please review the following information to ensure the GNU Lesser General
** Public License version 2.1 requirements will be met:
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Nokia gives you certain additional
** rights. These rights are described in the Nokia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
** Other Usage
**
** Alternatively, this file may be used in accordance with the terms and
** conditions contained in a signed written agreement between you and Nokia.
**
**
**************************************************************************/
#include "qtlockedfile.h"
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <fcntl.h>
namespace SharedTools {
bool QtLockedFile::lock(LockMode mode, bool block)
{
if (!isOpen()) {
qWarning("QtLockedFile::lock(): file is not opened");
return false;
}
if (mode == NoLock)
return unlock();
if (mode == m_lock_mode)
return true;
if (m_lock_mode != NoLock)
unlock();
struct flock fl;
fl.l_whence = SEEK_SET;
fl.l_start = 0;
fl.l_len = 0;
fl.l_type = (mode == ReadLock) ? F_RDLCK : F_WRLCK;
int cmd = block ? F_SETLKW : F_SETLK;
int ret = fcntl(handle(), cmd, &fl);
if (ret == -1) {
if (errno != EINTR && errno != EAGAIN)
qWarning("QtLockedFile::lock(): fcntl: %s", strerror(errno));
return false;
}
m_lock_mode = mode;
return true;
}
bool QtLockedFile::unlock()
{
if (!isOpen()) {
qWarning("QtLockedFile::unlock(): file is not opened");
return false;
}
if (!isLocked())
return true;
struct flock fl;
fl.l_whence = SEEK_SET;
fl.l_start = 0;
fl.l_len = 0;
fl.l_type = F_UNLCK;
int ret = fcntl(handle(), F_SETLKW, &fl);
if (ret == -1) {
qWarning("QtLockedFile::lock(): fcntl: %s", strerror(errno));
return false;
}
m_lock_mode = NoLock;
return true;
}
QtLockedFile::~QtLockedFile()
{
if (isOpen())
unlock();
}
} // namespace SharedTools

View file

@ -0,0 +1,196 @@
/**************************************************************************
**
** This file is part of Qt Creator
**
** Copyright (c) 2012 Nokia Corporation and/or its subsidiary(-ies).
**
** Contact: http://www.qt-project.org/
**
**
** GNU Lesser General Public License Usage
**
** This file may be used under the terms of the GNU Lesser General Public
** License version 2.1 as published by the Free Software Foundation and
** appearing in the file LICENSE.LGPL included in the packaging of this file.
** Please review the following information to ensure the GNU Lesser General
** Public License version 2.1 requirements will be met:
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Nokia gives you certain additional
** rights. These rights are described in the Nokia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
** Other Usage
**
** Alternatively, this file may be used in accordance with the terms and
** conditions contained in a signed written agreement between you and Nokia.
**
**
**************************************************************************/
#include "qtlockedfile.h"
#include <qt_windows.h>
#include <QFileInfo>
namespace SharedTools {
#define SEMAPHORE_PREFIX "QtLockedFile semaphore "
#define MUTEX_PREFIX "QtLockedFile mutex "
#define SEMAPHORE_MAX 100
static QString errorCodeToString(DWORD errorCode)
{
QString result;
char *data = 0;
FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
0, errorCode, 0,
(char*)&data, 0, 0);
result = QString::fromLocal8Bit(data);
if (data != 0)
LocalFree(data);
if (result.endsWith('\n'))
result.truncate(result.length() - 1);
return result;
}
bool QtLockedFile::lock(LockMode mode, bool block)
{
if (!isOpen()) {
qWarning("QtLockedFile::lock(): file is not opened");
return false;
}
if (mode == m_lock_mode)
return true;
if (m_lock_mode != 0)
unlock();
if (m_semaphore_hnd == 0) {
QFileInfo fi(*this);
QString sem_name = QString::fromLatin1(SEMAPHORE_PREFIX)
+ fi.absoluteFilePath().toLower();
m_semaphore_hnd = CreateSemaphoreW(0, SEMAPHORE_MAX, SEMAPHORE_MAX,
(TCHAR*)sem_name.utf16());
if (m_semaphore_hnd == 0) {
qWarning("QtLockedFile::lock(): CreateSemaphore: %s",
errorCodeToString(GetLastError()).toLatin1().constData());
return false;
}
}
bool gotMutex = false;
int decrement;
if (mode == ReadLock) {
decrement = 1;
} else {
decrement = SEMAPHORE_MAX;
if (m_mutex_hnd == 0) {
QFileInfo fi(*this);
QString mut_name = QString::fromLatin1(MUTEX_PREFIX)
+ fi.absoluteFilePath().toLower();
m_mutex_hnd = CreateMutexW(NULL, FALSE, (TCHAR*)mut_name.utf16());
if (m_mutex_hnd == 0) {
qWarning("QtLockedFile::lock(): CreateMutex: %s",
errorCodeToString(GetLastError()).toLatin1().constData());
return false;
}
}
DWORD res = WaitForSingleObject(m_mutex_hnd, block ? INFINITE : 0);
if (res == WAIT_TIMEOUT)
return false;
if (res == WAIT_FAILED) {
qWarning("QtLockedFile::lock(): WaitForSingleObject (mutex): %s",
errorCodeToString(GetLastError()).toLatin1().constData());
return false;
}
gotMutex = true;
}
for (int i = 0; i < decrement; ++i) {
DWORD res = WaitForSingleObject(m_semaphore_hnd, block ? INFINITE : 0);
if (res == WAIT_TIMEOUT) {
if (i) {
// A failed nonblocking rw locking. Undo changes to semaphore.
if (ReleaseSemaphore(m_semaphore_hnd, i, NULL) == 0) {
qWarning("QtLockedFile::unlock(): ReleaseSemaphore: %s",
errorCodeToString(GetLastError()).toLatin1().constData());
// Fall through
}
}
if (gotMutex)
ReleaseMutex(m_mutex_hnd);
return false;
}
if (res != WAIT_OBJECT_0) {
if (gotMutex)
ReleaseMutex(m_mutex_hnd);
qWarning("QtLockedFile::lock(): WaitForSingleObject (semaphore): %s",
errorCodeToString(GetLastError()).toLatin1().constData());
return false;
}
}
m_lock_mode = mode;
if (gotMutex)
ReleaseMutex(m_mutex_hnd);
return true;
}
bool QtLockedFile::unlock()
{
if (!isOpen()) {
qWarning("QtLockedFile::unlock(): file is not opened");
return false;
}
if (!isLocked())
return true;
int increment;
if (m_lock_mode == ReadLock)
increment = 1;
else
increment = SEMAPHORE_MAX;
DWORD ret = ReleaseSemaphore(m_semaphore_hnd, increment, 0);
if (ret == 0) {
qWarning("QtLockedFile::unlock(): ReleaseSemaphore: %s",
errorCodeToString(GetLastError()).toLatin1().constData());
return false;
}
m_lock_mode = QtLockedFile::NoLock;
return true;
}
QtLockedFile::~QtLockedFile()
{
if (isOpen())
unlock();
if (m_mutex_hnd != 0) {
DWORD ret = CloseHandle(m_mutex_hnd);
if (ret == 0) {
qWarning("QtLockedFile::~QtLockedFile(): CloseHandle (mutex): %s",
errorCodeToString(GetLastError()).toLatin1().constData());
}
m_mutex_hnd = 0;
}
if (m_semaphore_hnd != 0) {
DWORD ret = CloseHandle(m_semaphore_hnd);
if (ret == 0) {
qWarning("QtLockedFile::~QtLockedFile(): CloseHandle (semaphore): %s",
errorCodeToString(GetLastError()).toLatin1().constData());
}
m_semaphore_hnd = 0;
}
}
} // namespace SharedTools

View file

@ -0,0 +1,5 @@
This is the src directory of the QtSingleApplication solution
integrated over from Qt's Qt Creator project.
It additionally requires the QtLockedFile solution.

View file

@ -0,0 +1,173 @@
/**************************************************************************
**
** This file is part of Qt Creator
**
** Copyright (c) 2012 Nokia Corporation and/or its subsidiary(-ies).
**
** Contact: http://www.qt-project.org/
**
**
** GNU Lesser General Public License Usage
**
** This file may be used under the terms of the GNU Lesser General Public
** License version 2.1 as published by the Free Software Foundation and
** appearing in the file LICENSE.LGPL included in the packaging of this file.
** Please review the following information to ensure the GNU Lesser General
** Public License version 2.1 requirements will be met:
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Nokia gives you certain additional
** rights. These rights are described in the Nokia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
** Other Usage
**
** Alternatively, this file may be used in accordance with the terms and
** conditions contained in a signed written agreement between you and Nokia.
**
**
**************************************************************************/
#include "qtlocalpeer.h"
#include <QCoreApplication>
#include <QTime>
#if defined(Q_OS_WIN)
#include <QLibrary>
#include <qt_windows.h>
typedef BOOL(WINAPI*PProcessIdToSessionId)(DWORD,DWORD*);
static PProcessIdToSessionId pProcessIdToSessionId = 0;
#endif
#if defined(Q_OS_UNIX)
#include <time.h>
#include <unistd.h>
#endif
namespace SharedTools {
const char *QtLocalPeer::ack = "ack";
QtLocalPeer::QtLocalPeer(QObject *parent, const QString &appId)
: QObject(parent), id(appId)
{
if (id.isEmpty())
id = QCoreApplication::applicationFilePath(); //### On win, check if this returns .../argv[0] without casefolding; .\MYAPP == .\myapp on Win
QByteArray idc = id.toUtf8();
quint16 idNum = qChecksum(idc.constData(), idc.size());
//### could do: two 16bit checksums over separate halves of id, for a 32bit result - improved uniqeness probability. Every-other-char split would be best.
socketName = QLatin1String("qtsingleapplication-")
+ QString::number(idNum, 16);
#if defined(Q_OS_WIN)
if (!pProcessIdToSessionId) {
QLibrary lib("kernel32");
pProcessIdToSessionId = (PProcessIdToSessionId)lib.resolve("ProcessIdToSessionId");
}
if (pProcessIdToSessionId) {
DWORD sessionId = 0;
pProcessIdToSessionId(GetCurrentProcessId(), &sessionId);
socketName += QLatin1Char('-') + QString::number(sessionId, 16);
}
#else
socketName += QLatin1Char('-') + QString::number(::getuid(), 16);
#endif
server = new QLocalServer(this);
QString lockName = QDir(QDir::tempPath()).absolutePath()
+ QLatin1Char('/') + socketName
+ QLatin1String("-lockfile");
lockFile.setFileName(lockName);
lockFile.open(QIODevice::ReadWrite);
}
bool QtLocalPeer::isClient()
{
if (lockFile.isLocked())
return false;
if (!lockFile.lock(QtLockedFile::WriteLock, false))
return true;
if (!QLocalServer::removeServer(socketName))
qWarning("QtSingleCoreApplication: could not cleanup socket");
bool res = server->listen(socketName);
if (!res)
qWarning("QtSingleCoreApplication: listen on local socket failed, %s", qPrintable(server->errorString()));
QObject::connect(server, SIGNAL(newConnection()), SLOT(receiveConnection()));
return false;
}
bool QtLocalPeer::sendMessage(const QString &message, int timeout)
{
if (!isClient())
return false;
QLocalSocket socket;
bool connOk = false;
for (int i = 0; i < 2; i++) {
// Try twice, in case the other instance is just starting up
socket.connectToServer(socketName);
connOk = socket.waitForConnected(timeout/2);
if (connOk || i)
break;
int ms = 250;
#if defined(Q_OS_WIN)
Sleep(DWORD(ms));
#else
struct timespec ts = { ms / 1000, (ms % 1000) * 1000 * 1000 };
nanosleep(&ts, NULL);
#endif
}
if (!connOk)
return false;
QByteArray uMsg(message.toUtf8());
QDataStream ds(&socket);
ds.writeBytes(uMsg.constData(), uMsg.size());
bool res = socket.waitForBytesWritten(timeout);
res &= socket.waitForReadyRead(timeout); // wait for ack
res &= (socket.read(qstrlen(ack)) == ack);
return res;
}
void QtLocalPeer::receiveConnection()
{
QLocalSocket* socket = server->nextPendingConnection();
if (!socket)
return;
// Why doesn't Qt have a blocking stream that takes care of this shait???
while (socket->bytesAvailable() < static_cast<int>(sizeof(quint32)))
socket->waitForReadyRead();
QDataStream ds(socket);
QByteArray uMsg;
quint32 remaining;
ds >> remaining;
uMsg.resize(remaining);
int got = 0;
char* uMsgBuf = uMsg.data();
//qDebug() << "RCV: remaining" << remaining;
do {
got = ds.readRawData(uMsgBuf, remaining);
remaining -= got;
uMsgBuf += got;
//qDebug() << "RCV: got" << got << "remaining" << remaining;
} while (remaining && got >= 0 && socket->waitForReadyRead(2000));
//### error check: got<0
if (got < 0) {
qWarning() << "QtLocalPeer: Message reception failed" << socket->errorString();
delete socket;
return;
}
// ### async this
QString message = QString::fromUtf8(uMsg.constData(), uMsg.size());
socket->write(ack, qstrlen(ack));
socket->waitForBytesWritten(1000);
delete socket;
emit messageReceived(message); // ##(might take a long time to return)
}
} // namespace SharedTools

View file

@ -0,0 +1,66 @@
/**************************************************************************
**
** This file is part of Qt Creator
**
** Copyright (c) 2012 Nokia Corporation and/or its subsidiary(-ies).
**
** Contact: http://www.qt-project.org/
**
**
** GNU Lesser General Public License Usage
**
** This file may be used under the terms of the GNU Lesser General Public
** License version 2.1 as published by the Free Software Foundation and
** appearing in the file LICENSE.LGPL included in the packaging of this file.
** Please review the following information to ensure the GNU Lesser General
** Public License version 2.1 requirements will be met:
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Nokia gives you certain additional
** rights. These rights are described in the Nokia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
** Other Usage
**
** Alternatively, this file may be used in accordance with the terms and
** conditions contained in a signed written agreement between you and Nokia.
**
**
**************************************************************************/
#include "qtlockedfile.h"
#include <QLocalServer>
#include <QLocalSocket>
#include <QDir>
namespace SharedTools {
class QtLocalPeer : public QObject
{
Q_OBJECT
public:
explicit QtLocalPeer(QObject *parent = 0, const QString &appId = QString());
bool isClient();
bool sendMessage(const QString &message, int timeout);
QString applicationId() const
{ return id; }
Q_SIGNALS:
void messageReceived(const QString &message);
protected Q_SLOTS:
void receiveConnection();
protected:
QString id;
QString socketName;
QLocalServer* server;
QtLockedFile lockFile;
private:
static const char* ack;
};
} // namespace SharedTools

View file

@ -0,0 +1,165 @@
/**************************************************************************
**
** This file is part of Qt Creator
**
** Copyright (c) 2012 Nokia Corporation and/or its subsidiary(-ies).
**
** Contact: http://www.qt-project.org/
**
**
** GNU Lesser General Public License Usage
**
** This file may be used under the terms of the GNU Lesser General Public
** License version 2.1 as published by the Free Software Foundation and
** appearing in the file LICENSE.LGPL included in the packaging of this file.
** Please review the following information to ensure the GNU Lesser General
** Public License version 2.1 requirements will be met:
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Nokia gives you certain additional
** rights. These rights are described in the Nokia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
** Other Usage
**
** Alternatively, this file may be used in accordance with the terms and
** conditions contained in a signed written agreement between you and Nokia.
**
**
**************************************************************************/
#include "qtsingleapplication.h"
#include "qtlocalpeer.h"
#include <QWidget>
#include <QFileOpenEvent>
namespace SharedTools {
void QtSingleApplication::sysInit(const QString &appId)
{
actWin = 0;
firstPeer = new QtLocalPeer(this, appId);
connect(firstPeer, SIGNAL(messageReceived(QString)), SIGNAL(messageReceived(QString)));
pidPeer = new QtLocalPeer(this, appId + QLatin1Char('-') + QString::number(QCoreApplication::applicationPid(), 10));
connect(pidPeer, SIGNAL(messageReceived(QString)), SIGNAL(messageReceived(QString)));
}
QtSingleApplication::QtSingleApplication(int &argc, char **argv, bool GUIenabled)
: QApplication(argc, argv, GUIenabled)
{
sysInit();
}
QtSingleApplication::QtSingleApplication(const QString &appId, int &argc, char **argv)
: QApplication(argc, argv)
{
this->appId = appId;
sysInit(appId);
}
QtSingleApplication::QtSingleApplication(int &argc, char **argv, Type type)
: QApplication(argc, argv, type)
{
sysInit();
}
#if defined(Q_WS_X11)
QtSingleApplication::QtSingleApplication(Display* dpy, Qt::HANDLE visual, Qt::HANDLE colormap)
: QApplication(dpy, visual, colormap)
{
sysInit();
}
QtSingleApplication::QtSingleApplication(Display *dpy, int &argc, char **argv, Qt::HANDLE visual, Qt::HANDLE cmap)
: QApplication(dpy, argc, argv, visual, cmap)
{
sysInit();
}
QtSingleApplication::QtSingleApplication(Display* dpy, const QString &appId,
int argc, char **argv, Qt::HANDLE visual, Qt::HANDLE colormap)
: QApplication(dpy, argc, argv, visual, colormap)
{
this->appId = appId;
sysInit(appId);
}
#endif
bool QtSingleApplication::event(QEvent *event)
{
if (event->type() == QEvent::FileOpen) {
QFileOpenEvent *foe = static_cast<QFileOpenEvent*>(event);
emit fileOpenRequest(foe->file());
return true;
}
return QApplication::event(event);
}
bool QtSingleApplication::isRunning(qint64 pid)
{
if (pid == -1)
return firstPeer->isClient();
QtLocalPeer peer(this, appId + QLatin1Char('-') + QString::number(pid, 10));
return peer.isClient();
}
void QtSingleApplication::initialize(bool)
{
firstPeer->isClient();
pidPeer->isClient();
}
bool QtSingleApplication::sendMessage(const QString &message, int timeout, qint64 pid)
{
if (pid == -1)
return firstPeer->sendMessage(message, timeout);
QtLocalPeer peer(this, appId + QLatin1Char('-') + QString::number(pid, 10));
return peer.sendMessage(message, timeout);
}
QString QtSingleApplication::id() const
{
return firstPeer->applicationId();
}
QString QtSingleApplication::applicationId() const
{
return appId;
}
void QtSingleApplication::setActivationWindow(QWidget *aw, bool activateOnMessage)
{
actWin = aw;
if (activateOnMessage) {
connect(firstPeer, SIGNAL(messageReceived(QString)), this, SLOT(activateWindow()));
connect(pidPeer, SIGNAL(messageReceived(QString)), this, SLOT(activateWindow()));
} else {
disconnect(firstPeer, SIGNAL(messageReceived(QString)), this, SLOT(activateWindow()));
disconnect(pidPeer, SIGNAL(messageReceived(QString)), this, SLOT(activateWindow()));
}
}
QWidget* QtSingleApplication::activationWindow() const
{
return actWin;
}
void QtSingleApplication::activateWindow()
{
if (actWin) {
actWin->setWindowState(actWin->windowState() & ~Qt::WindowMinimized);
actWin->raise();
actWin->activateWindow();
}
}
} // namespace SharedTools

View file

@ -0,0 +1,85 @@
/**************************************************************************
**
** This file is part of Qt Creator
**
** Copyright (c) 2012 Nokia Corporation and/or its subsidiary(-ies).
**
** Contact: http://www.qt-project.org/
**
**
** GNU Lesser General Public License Usage
**
** This file may be used under the terms of the GNU Lesser General Public
** License version 2.1 as published by the Free Software Foundation and
** appearing in the file LICENSE.LGPL included in the packaging of this file.
** Please review the following information to ensure the GNU Lesser General
** Public License version 2.1 requirements will be met:
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Nokia gives you certain additional
** rights. These rights are described in the Nokia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
** Other Usage
**
** Alternatively, this file may be used in accordance with the terms and
** conditions contained in a signed written agreement between you and Nokia.
**
**
**************************************************************************/
#include <QApplication>
namespace SharedTools {
class QtLocalPeer;
class QtSingleApplication : public QApplication
{
Q_OBJECT
public:
QtSingleApplication(int &argc, char **argv, bool GUIenabled = true);
QtSingleApplication(const QString &id, int &argc, char **argv);
QtSingleApplication(int &argc, char **argv, Type type);
#if defined(Q_WS_X11)
explicit QtSingleApplication(Display *dpy, Qt::HANDLE visual = 0, Qt::HANDLE colormap = 0);
QtSingleApplication(Display *dpy, int &argc, char **argv, Qt::HANDLE visual = 0, Qt::HANDLE cmap = 0);
#endif
bool isRunning(qint64 pid = -1);
QString id() const;
void setActivationWindow(QWidget* aw, bool activateOnMessage = true);
QWidget* activationWindow() const;
bool event(QEvent *event);
QString applicationId() const;
public Q_SLOTS:
bool sendMessage(const QString &message, int timeout = 5000, qint64 pid = -1);
void activateWindow();
//Obsolete methods:
public:
void initialize(bool = true);
#if defined(Q_WS_X11)
QtSingleApplication(Display* dpy, const QString &id, int argc, char **argv, Qt::HANDLE visual = 0, Qt::HANDLE colormap = 0);
#endif
// end obsolete methods
Q_SIGNALS:
void messageReceived(const QString &message);
void fileOpenRequest(const QString &file);
private:
void sysInit(const QString &appId = QString());
QtLocalPeer *firstPeer;
QtLocalPeer *pidPeer;
QWidget *actWin;
QString appId;
};
} // namespace SharedTools

View file

@ -0,0 +1,69 @@
/**************************************************************************
**
** This file is part of Qt Creator
**
** Copyright (c) 2012 Nokia Corporation and/or its subsidiary(-ies).
**
** Contact: http://www.qt-project.org/
**
**
** GNU Lesser General Public License Usage
**
** This file may be used under the terms of the GNU Lesser General Public
** License version 2.1 as published by the Free Software Foundation and
** appearing in the file LICENSE.LGPL included in the packaging of this file.
** Please review the following information to ensure the GNU Lesser General
** Public License version 2.1 requirements will be met:
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Nokia gives you certain additional
** rights. These rights are described in the Nokia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
** Other Usage
**
** Alternatively, this file may be used in accordance with the terms and
** conditions contained in a signed written agreement between you and Nokia.
**
**
**************************************************************************/
#include "qtsinglecoreapplication.h"
#include "qtlocalpeer.h"
namespace SharedTools {
QtSingleCoreApplication::QtSingleCoreApplication(int &argc, char **argv)
: QCoreApplication(argc, argv)
{
peer = new QtLocalPeer(this);
connect(peer, SIGNAL(messageReceived(QString)), SIGNAL(messageReceived(QString)));
}
QtSingleCoreApplication::QtSingleCoreApplication(const QString &appId, int &argc, char **argv)
: QCoreApplication(argc, argv)
{
peer = new QtLocalPeer(this, appId);
connect(peer, SIGNAL(messageReceived(QString)), SIGNAL(messageReceived(QString)));
}
bool QtSingleCoreApplication::isRunning()
{
return peer->isClient();
}
bool QtSingleCoreApplication::sendMessage(const QString &message, int timeout)
{
return peer->sendMessage(message, timeout);
}
QString QtSingleCoreApplication::id() const
{
return peer->applicationId();
}
} // namespace SharedTools

View file

@ -0,0 +1,60 @@
/**************************************************************************
**
** This file is part of Qt Creator
**
** Copyright (c) 2012 Nokia Corporation and/or its subsidiary(-ies).
**
** Contact: http://www.qt-project.org/
**
**
** GNU Lesser General Public License Usage
**
** This file may be used under the terms of the GNU Lesser General Public
** License version 2.1 as published by the Free Software Foundation and
** appearing in the file LICENSE.LGPL included in the packaging of this file.
** Please review the following information to ensure the GNU Lesser General
** Public License version 2.1 requirements will be met:
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Nokia gives you certain additional
** rights. These rights are described in the Nokia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
** Other Usage
**
** Alternatively, this file may be used in accordance with the terms and
** conditions contained in a signed written agreement between you and Nokia.
**
**
**************************************************************************/
#include <QCoreApplication>
namespace SharedTools {
class QtLocalPeer;
class QtSingleCoreApplication : public QCoreApplication
{
Q_OBJECT
public:
QtSingleCoreApplication(int &argc, char **argv);
QtSingleCoreApplication(const QString &id, int &argc, char **argv);
bool isRunning();
QString id() const;
public Q_SLOTS:
bool sendMessage(const QString &message, int timeout = 5000);
Q_SIGNALS:
void messageReceived(const QString &message);
private:
QtLocalPeer* peer;
};
} // namespace SharedTools

View file

@ -19,6 +19,36 @@ mirall/owncloudcredentialspage.ui
mirall/sslerrorsdialog.ui
)
set(3rdparty_SRC
3rdparty/qtsingleapplication/qtsingleapplication.cpp
3rdparty/qtsingleapplication/qtlocalpeer.h
3rdparty/qtsingleapplication/qtlocalpeer.cpp
3rdparty/qtsingleapplication/qtsingleapplication.h
3rdparty/qtsingleapplication/qtsinglecoreapplication.h
3rdparty/qtsingleapplication/qtsinglecoreapplication.cpp
3rdparty/qtlockedfile/qtlockedfile.h
3rdparty/qtlockedfile/qtlockedfile.cpp
)
set(3rdparty_HEADER
3rdparty/qtsingleapplication/qtlocalpeer.h
3rdparty/qtsingleapplication/qtsingleapplication.h
3rdparty/qtsingleapplication/qtsinglecoreapplication.h
)
qt4_wrap_cpp(3rdparty_MOC ${3rdparty_HEADER})
if(NOT WIN32)
list(APPEND 3rdparty_SRC 3rdparty/qtlockedfile/qtlockedfile_unix.cpp)
else()
list(APPEND 3rdparty_SRC 3rdparty/qtlockedfile/qtlockedfile_win.cpp )
endif()
set(3rdparty_INC
${CMAKE_CURRENT_SOURCE_DIR}/3rdparty/qtlockedfile
${CMAKE_CURRENT_SOURCE_DIR}/3rdparty/qtsingleapplication
)
qt4_wrap_ui(mirall_UI_SRCS ${mirall_UI})
set(libsync_SRCS
@ -106,6 +136,7 @@ endif()
# csync is required.
include_directories(${CSYNC_INCLUDE_DIR}/csync ${CSYNC_INCLUDE_DIR} ${CMAKE_BINARY_DIR})
include_directories(${3rdparty_INC})
qt4_wrap_cpp(mirallMoc ${mirall_HEADERS})
@ -119,6 +150,8 @@ set( final_src
${mirallMoc}
${mirall_HEADERS}
${mirall_I18N}
${3rdparty_SRC}
${3rdparty_MOC}
)
# add executable icon on windows and osx

View file

@ -19,7 +19,16 @@ int main(int argc, char **argv)
Q_INIT_RESOURCE(mirall);
Mirall::Application app(argc, argv);
app.initialize();
qint64 pid = -1;
// if the application is already running, notify it.
if( app.isRunning() ) {
if( app.sendMessage( QLatin1String("A message to the master"), 5000, pid ))
return 0;
}
// if help requested, show on command line and exit.
if( ! app.giveHelp() )
return app.exec();

View file

@ -66,7 +66,7 @@ void csyncLogCatcher(const char *msg)
// ----------------------------------------------------------------------------------
Application::Application(int &argc, char **argv) :
QApplication(argc, argv),
SharedTools::QtSingleApplication(argc, argv),
_tray(0),
#if QT_VERSION >= 0x040700
_networkMgr(new QNetworkConfigurationManager(this)),
@ -167,6 +167,9 @@ Application::Application(int &argc, char **argv) :
setupSystemTray();
processEvents();
QObject::connect( this, SIGNAL(messageReceived(QString)),
this, SLOT(slotOpenStatus()) );
QTimer::singleShot( 0, this, SLOT( slotStartFolderSetup() ));
MirallConfigFile cfg;
@ -221,6 +224,13 @@ void Application::slotOwnCloudFound( const QString& url, const QString& versionS
MirallConfigFile cfgFile;
cfgFile.setOwnCloudVersion( version );
// disconnect from ownCloudInfo
disconnect( ownCloudInfo::instance(),SIGNAL(ownCloudInfoFound(QString,QString,QString,QString)),
this, SLOT(slotOwnCloudFound(QString,QString,QString,QString)));
disconnect( ownCloudInfo::instance(),SIGNAL(noOwncloudFound(QNetworkReply*)),
this, SLOT(slotNoOwnCloudFound(QNetworkReply*)));
QTimer::singleShot( 0, this, SLOT( slotCheckAuthentication() ));
}
@ -277,6 +287,10 @@ void Application::slotAuthCheck( const QString& ,QNetworkReply *reply )
}
_actionAddFolder->setEnabled( true );
}
// disconnect from ownCloud Info signals
disconnect( ownCloudInfo::instance(),SIGNAL(ownCloudDirExists(QString,QNetworkReply*)),
this,SLOT(slotAuthCheck(QString,QNetworkReply*)));
setupContextMenu();
}
@ -524,8 +538,8 @@ void Application::slotRemoveFolder( const QString& alias )
return;
}
Folder *f = _folderMan->folder(alias);
if( f && _overallStatusStrings.contains( f )) {
_overallStatusStrings.remove( f );
if( f && _overallStatusStrings.contains( f->alias() )) {
_overallStatusStrings.remove( f->alias() );
}
_folderMan->slotRemoveFolder( alias );
@ -650,7 +664,7 @@ void Application::slotSyncStateChange( const QString& alias )
}
computeOverallSyncStatus();
qDebug() << "Sync state changed for folder " << alias << ": " << result.localRunOnly();
qDebug() << "Sync state changed for folder " << alias << ": " << result.statusString();
}
void Application::computeOverallSyncStatus()
@ -661,8 +675,8 @@ void Application::computeOverallSyncStatus()
QString trayMessage;
Folder::Map map = _folderMan->map();
foreach ( Folder *syncedFolder, map ) {
QString folderMessage = _overallStatusStrings[syncedFolder];
foreach ( Folder *syncedFolder, map.values() ) {
QString folderMessage = _overallStatusStrings[syncedFolder->alias()];
SyncResult folderResult = syncedFolder->syncResult();
SyncResult::Status syncStatus = folderResult.status();
@ -685,8 +699,10 @@ void Application::computeOverallSyncStatus()
overallResult.setStatus( SyncResult::SyncRunning );
break;
case SyncResult::Success:
folderMessage = tr( "Last Sync was successful." );
overallResult.setStatus( SyncResult::Success );
if( overallResult.status() == SyncResult::Undefined ) {
folderMessage = tr( "Last Sync was successful." );
overallResult.setStatus( SyncResult::Success );
}
break;
case SyncResult::Error:
overallResult.setStatus( SyncResult::Error );
@ -707,8 +723,10 @@ void Application::computeOverallSyncStatus()
folderMessage = tr( "Sync is paused." );
}
}
if( folderMessage != _overallStatusStrings[syncedFolder] ) {
_overallStatusStrings[syncedFolder] = QString("Folder %1: %2").arg(syncedFolder->alias()).arg(folderMessage);
qDebug() << "Folder in overallStatus Message: " << syncedFolder << " with name " << syncedFolder->alias();
QString msg = QString("Folder %1: %2").arg(syncedFolder->alias()).arg(folderMessage);
if( msg != _overallStatusStrings[syncedFolder->alias()] ) {
_overallStatusStrings[syncedFolder->alias()] = msg;
}
}

View file

@ -19,6 +19,8 @@
#include <QSystemTrayIcon>
#include <QNetworkReply>
#include "qtsingleapplication.h"
#include "mirall/syncresult.h"
#include "mirall/folder.h"
#include "mirall/logbrowser.h"
@ -40,7 +42,7 @@ class OwncloudSetupWizard;
class ownCloudInfo;
class UpdateDetector;
class Application : public QApplication
class Application : public SharedTools::QtSingleApplication
{
Q_OBJECT
public:
@ -109,7 +111,7 @@ private:
Theme *_theme;
QSignalMapper *_folderOpenActionMapper;
UpdateDetector *_updateDetector;
QMap<Folder*, QString> _overallStatusStrings;
QMap<QString, QString> _overallStatusStrings;
LogBrowser *_logBrowser;
bool _helpOnly;
};