Revision | 60fe0c2787df0450c763f9c3158885e0baceaab5 (tree) |
---|---|
Time | 2020-07-04 19:14:08 |
Author | simphone |
Commiter | simphone |
qtsingleapplication 2.6
@@ -0,0 +1,1 @@ | ||
1 | +#include "qtlockedfile.h" |
@@ -0,0 +1,1 @@ | ||
1 | +#include "qtsingleapplication.h" |
@@ -0,0 +1,33 @@ | ||
1 | +Qt Solutions Component: Single Application | |
2 | + | |
3 | +The QtSingleApplication component provides support for | |
4 | +applications that can be only started once per user. | |
5 | + | |
6 | + | |
7 | + | |
8 | +Version history: | |
9 | + | |
10 | +2.0: - Version 1.3 ported to Qt 4. | |
11 | + | |
12 | +2.1: - Fix compilation problem on Mac. | |
13 | + | |
14 | +2.2: - Really fix the Mac compilation problem. | |
15 | + - Mac: fix crash due to wrong object releasing. | |
16 | + - Mac: Fix memory leak. | |
17 | + | |
18 | +2.3: - Windows: Force creation of internal widget to make it work | |
19 | + with Qt 4.2. | |
20 | + | |
21 | +2.4: - Fix the system for automatic window raising on message | |
22 | + reception. NOTE: minor API change. | |
23 | + | |
24 | +2.5: - Mac: Fix isRunning() to work and report correctly. | |
25 | + | |
26 | +2.6: - - initialize() is now obsolete, no longer necessary to call | |
27 | + it | |
28 | + - - Fixed race condition where multiple instances migth be started | |
29 | + - - QtSingleCoreApplication variant provided for non-GUI (console) | |
30 | + usage | |
31 | + - Complete reimplementation. Visible changes: | |
32 | + - LGPL release. | |
33 | + |
@@ -0,0 +1,213 @@ | ||
1 | +/**************************************************************************** | |
2 | +** | |
3 | +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). | |
4 | +** Contact: http://www.qt-project.org/legal | |
5 | +** | |
6 | +** This file is part of the Qt Solutions component. | |
7 | +** | |
8 | +** $QT_BEGIN_LICENSE:BSD$ | |
9 | +** You may use this file under the terms of the BSD license as follows: | |
10 | +** | |
11 | +** "Redistribution and use in source and binary forms, with or without | |
12 | +** modification, are permitted provided that the following conditions are | |
13 | +** met: | |
14 | +** * Redistributions of source code must retain the above copyright | |
15 | +** notice, this list of conditions and the following disclaimer. | |
16 | +** * Redistributions in binary form must reproduce the above copyright | |
17 | +** notice, this list of conditions and the following disclaimer in | |
18 | +** the documentation and/or other materials provided with the | |
19 | +** distribution. | |
20 | +** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names | |
21 | +** of its contributors may be used to endorse or promote products derived | |
22 | +** from this software without specific prior written permission. | |
23 | +** | |
24 | +** | |
25 | +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | |
26 | +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | |
27 | +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | |
28 | +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | |
29 | +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | |
30 | +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | |
31 | +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | |
32 | +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | |
33 | +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |
34 | +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | |
35 | +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." | |
36 | +** | |
37 | +** $QT_END_LICENSE$ | |
38 | +** | |
39 | +****************************************************************************/ | |
40 | + | |
41 | + | |
42 | +#include "qtlocalpeer.h" | |
43 | +#include <QCoreApplication> | |
44 | +#include <QDataStream> | |
45 | +#include <QTime> | |
46 | + | |
47 | +#if defined(Q_OS_WIN) | |
48 | +#include <QLibrary> | |
49 | +#include <qt_windows.h> | |
50 | +typedef BOOL(WINAPI*PProcessIdToSessionId)(DWORD,DWORD*); | |
51 | +static PProcessIdToSessionId pProcessIdToSessionId = 0; | |
52 | +#endif | |
53 | +#if defined(Q_OS_UNIX) | |
54 | +#include <sys/types.h> | |
55 | +#include <time.h> | |
56 | +#include <unistd.h> | |
57 | +#endif | |
58 | + | |
59 | +namespace QtLP_Private { | |
60 | +#include "qtlockedfile.cpp" | |
61 | +#if defined(Q_OS_WIN) | |
62 | +#include "qtlockedfile_win.cpp" | |
63 | +#else | |
64 | +#include "qtlockedfile_unix.cpp" | |
65 | +#endif | |
66 | +} | |
67 | + | |
68 | +const char* QtLocalPeer::ack = "ack"; | |
69 | + | |
70 | +QtLocalPeer::QtLocalPeer(QObject* parent, const QString &appId) | |
71 | + : QObject(parent), id(appId) | |
72 | +{ | |
73 | + QString prefix = id; | |
74 | + if (id.isEmpty()) { | |
75 | + id = QCoreApplication::applicationFilePath(); | |
76 | +#if defined(Q_OS_WIN) | |
77 | + id = id.toLower(); | |
78 | +#endif | |
79 | + prefix = id.section(QLatin1Char('/'), -1); | |
80 | + } | |
81 | + prefix.remove(QRegExp("[^a-zA-Z]")); | |
82 | + prefix.truncate(6); | |
83 | + | |
84 | + QByteArray idc = id.toUtf8(); | |
85 | + quint16 idNum = qChecksum(idc.constData(), idc.size()); | |
86 | + socketName = QLatin1String("qtsingleapp-") + prefix | |
87 | + + QLatin1Char('-') + QString::number(idNum, 16); | |
88 | + | |
89 | +#if defined(Q_OS_WIN) | |
90 | + if (!pProcessIdToSessionId) { | |
91 | + QLibrary lib("kernel32"); | |
92 | + pProcessIdToSessionId = (PProcessIdToSessionId)lib.resolve("ProcessIdToSessionId"); | |
93 | + } | |
94 | + if (pProcessIdToSessionId) { | |
95 | + DWORD sessionId = 0; | |
96 | + pProcessIdToSessionId(GetCurrentProcessId(), &sessionId); | |
97 | + socketName += QLatin1Char('-') + QString::number(sessionId, 16); | |
98 | + } | |
99 | +#else | |
100 | + socketName += QLatin1Char('-') + QString::number(::getuid(), 16); | |
101 | +#endif | |
102 | + | |
103 | + server = new QLocalServer(this); | |
104 | + QString lockName = QDir(QDir::tempPath()).absolutePath() | |
105 | + + QLatin1Char('/') + socketName | |
106 | + + QLatin1String("-lockfile"); | |
107 | + lockFile.setFileName(lockName); | |
108 | + lockFile.open(QIODevice::ReadWrite); | |
109 | +} | |
110 | + | |
111 | + | |
112 | + | |
113 | +bool QtLocalPeer::isClient() | |
114 | +{ | |
115 | + if (lockFile.isLocked()) | |
116 | + return false; | |
117 | + | |
118 | + if (!lockFile.lock(QtLP_Private::QtLockedFile::WriteLock, false)) | |
119 | + return true; | |
120 | + | |
121 | + bool res = server->listen(socketName); | |
122 | +#if defined(Q_OS_UNIX) && (QT_VERSION >= QT_VERSION_CHECK(4,5,0)) | |
123 | + // ### Workaround | |
124 | + if (!res && server->serverError() == QAbstractSocket::AddressInUseError) { | |
125 | + QFile::remove(QDir::cleanPath(QDir::tempPath())+QLatin1Char('/')+socketName); | |
126 | + res = server->listen(socketName); | |
127 | + } | |
128 | +#endif | |
129 | + if (!res) | |
130 | + qWarning("QtSingleCoreApplication: listen on local socket failed, %s", qPrintable(server->errorString())); | |
131 | + QObject::connect(server, SIGNAL(newConnection()), SLOT(receiveConnection())); | |
132 | + return false; | |
133 | +} | |
134 | + | |
135 | + | |
136 | +bool QtLocalPeer::sendMessage(const QString &message, int timeout) | |
137 | +{ | |
138 | + if (!isClient()) | |
139 | + return false; | |
140 | + | |
141 | + QLocalSocket socket; | |
142 | + bool connOk = false; | |
143 | + for(int i = 0; i < 2; i++) { | |
144 | + // Try twice, in case the other instance is just starting up | |
145 | + socket.connectToServer(socketName); | |
146 | + connOk = socket.waitForConnected(timeout/2); | |
147 | + if (connOk || i) | |
148 | + break; | |
149 | + int ms = 250; | |
150 | +#if defined(Q_OS_WIN) | |
151 | + Sleep(DWORD(ms)); | |
152 | +#else | |
153 | + struct timespec ts = { ms / 1000, (ms % 1000) * 1000 * 1000 }; | |
154 | + nanosleep(&ts, NULL); | |
155 | +#endif | |
156 | + } | |
157 | + if (!connOk) | |
158 | + return false; | |
159 | + | |
160 | + QByteArray uMsg(message.toUtf8()); | |
161 | + QDataStream ds(&socket); | |
162 | + ds.writeBytes(uMsg.constData(), uMsg.size()); | |
163 | + bool res = socket.waitForBytesWritten(timeout); | |
164 | + if (res) { | |
165 | + res &= socket.waitForReadyRead(timeout); // wait for ack | |
166 | + if (res) | |
167 | + res &= (socket.read(qstrlen(ack)) == ack); | |
168 | + } | |
169 | + return res; | |
170 | +} | |
171 | + | |
172 | + | |
173 | +void QtLocalPeer::receiveConnection() | |
174 | +{ | |
175 | + QLocalSocket* socket = server->nextPendingConnection(); | |
176 | + if (!socket) | |
177 | + return; | |
178 | + | |
179 | + while (true) { | |
180 | + if (socket->state() == QLocalSocket::UnconnectedState) { | |
181 | + qWarning("QtLocalPeer: Peer disconnected"); | |
182 | + delete socket; | |
183 | + return; | |
184 | + } | |
185 | + if (socket->bytesAvailable() >= qint64(sizeof(quint32))) | |
186 | + break; | |
187 | + socket->waitForReadyRead(); | |
188 | + } | |
189 | + | |
190 | + QDataStream ds(socket); | |
191 | + QByteArray uMsg; | |
192 | + quint32 remaining; | |
193 | + ds >> remaining; | |
194 | + uMsg.resize(remaining); | |
195 | + int got = 0; | |
196 | + char* uMsgBuf = uMsg.data(); | |
197 | + do { | |
198 | + got = ds.readRawData(uMsgBuf, remaining); | |
199 | + remaining -= got; | |
200 | + uMsgBuf += got; | |
201 | + } while (remaining && got >= 0 && socket->waitForReadyRead(2000)); | |
202 | + if (got < 0) { | |
203 | + qWarning("QtLocalPeer: Message reception failed %s", socket->errorString().toLatin1().constData()); | |
204 | + delete socket; | |
205 | + return; | |
206 | + } | |
207 | + QString message(QString::fromUtf8(uMsg)); | |
208 | + socket->write(ack, qstrlen(ack)); | |
209 | + socket->waitForBytesWritten(1000); | |
210 | + socket->waitForDisconnected(1000); // make sure client reads ack | |
211 | + delete socket; | |
212 | + emit messageReceived(message); //### (might take a long time to return) | |
213 | +} |
@@ -0,0 +1,77 @@ | ||
1 | +/**************************************************************************** | |
2 | +** | |
3 | +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). | |
4 | +** Contact: http://www.qt-project.org/legal | |
5 | +** | |
6 | +** This file is part of the Qt Solutions component. | |
7 | +** | |
8 | +** $QT_BEGIN_LICENSE:BSD$ | |
9 | +** You may use this file under the terms of the BSD license as follows: | |
10 | +** | |
11 | +** "Redistribution and use in source and binary forms, with or without | |
12 | +** modification, are permitted provided that the following conditions are | |
13 | +** met: | |
14 | +** * Redistributions of source code must retain the above copyright | |
15 | +** notice, this list of conditions and the following disclaimer. | |
16 | +** * Redistributions in binary form must reproduce the above copyright | |
17 | +** notice, this list of conditions and the following disclaimer in | |
18 | +** the documentation and/or other materials provided with the | |
19 | +** distribution. | |
20 | +** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names | |
21 | +** of its contributors may be used to endorse or promote products derived | |
22 | +** from this software without specific prior written permission. | |
23 | +** | |
24 | +** | |
25 | +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | |
26 | +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | |
27 | +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | |
28 | +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | |
29 | +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | |
30 | +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | |
31 | +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | |
32 | +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | |
33 | +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |
34 | +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | |
35 | +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." | |
36 | +** | |
37 | +** $QT_END_LICENSE$ | |
38 | +** | |
39 | +****************************************************************************/ | |
40 | + | |
41 | +#ifndef QTLOCALPEER_H | |
42 | +#define QTLOCALPEER_H | |
43 | + | |
44 | +#include <QLocalServer> | |
45 | +#include <QLocalSocket> | |
46 | +#include <QDir> | |
47 | + | |
48 | +#include "qtlockedfile.h" | |
49 | + | |
50 | +class QtLocalPeer : public QObject | |
51 | +{ | |
52 | + Q_OBJECT | |
53 | + | |
54 | +public: | |
55 | + QtLocalPeer(QObject *parent = 0, const QString &appId = QString()); | |
56 | + bool isClient(); | |
57 | + bool sendMessage(const QString &message, int timeout); | |
58 | + QString applicationId() const | |
59 | + { return id; } | |
60 | + | |
61 | +Q_SIGNALS: | |
62 | + void messageReceived(const QString &message); | |
63 | + | |
64 | +protected Q_SLOTS: | |
65 | + void receiveConnection(); | |
66 | + | |
67 | +protected: | |
68 | + QString id; | |
69 | + QString socketName; | |
70 | + QLocalServer* server; | |
71 | + QtLP_Private::QtLockedFile lockFile; | |
72 | + | |
73 | +private: | |
74 | + static const char* ack; | |
75 | +}; | |
76 | + | |
77 | +#endif // QTLOCALPEER_H |
@@ -0,0 +1,193 @@ | ||
1 | +/**************************************************************************** | |
2 | +** | |
3 | +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). | |
4 | +** Contact: http://www.qt-project.org/legal | |
5 | +** | |
6 | +** This file is part of the Qt Solutions component. | |
7 | +** | |
8 | +** $QT_BEGIN_LICENSE:BSD$ | |
9 | +** You may use this file under the terms of the BSD license as follows: | |
10 | +** | |
11 | +** "Redistribution and use in source and binary forms, with or without | |
12 | +** modification, are permitted provided that the following conditions are | |
13 | +** met: | |
14 | +** * Redistributions of source code must retain the above copyright | |
15 | +** notice, this list of conditions and the following disclaimer. | |
16 | +** * Redistributions in binary form must reproduce the above copyright | |
17 | +** notice, this list of conditions and the following disclaimer in | |
18 | +** the documentation and/or other materials provided with the | |
19 | +** distribution. | |
20 | +** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names | |
21 | +** of its contributors may be used to endorse or promote products derived | |
22 | +** from this software without specific prior written permission. | |
23 | +** | |
24 | +** | |
25 | +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | |
26 | +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | |
27 | +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | |
28 | +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | |
29 | +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | |
30 | +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | |
31 | +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | |
32 | +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | |
33 | +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |
34 | +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | |
35 | +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." | |
36 | +** | |
37 | +** $QT_END_LICENSE$ | |
38 | +** | |
39 | +****************************************************************************/ | |
40 | + | |
41 | +#include "qtlockedfile.h" | |
42 | + | |
43 | +/*! | |
44 | + \class QtLockedFile | |
45 | + | |
46 | + \brief The QtLockedFile class extends QFile with advisory locking | |
47 | + functions. | |
48 | + | |
49 | + A file may be locked in read or write mode. Multiple instances of | |
50 | + \e QtLockedFile, created in multiple processes running on the same | |
51 | + machine, may have a file locked in read mode. Exactly one instance | |
52 | + may have it locked in write mode. A read and a write lock cannot | |
53 | + exist simultaneously on the same file. | |
54 | + | |
55 | + The file locks are advisory. This means that nothing prevents | |
56 | + another process from manipulating a locked file using QFile or | |
57 | + file system functions offered by the OS. Serialization is only | |
58 | + guaranteed if all processes that access the file use | |
59 | + QLockedFile. Also, while holding a lock on a file, a process | |
60 | + must not open the same file again (through any API), or locks | |
61 | + can be unexpectedly lost. | |
62 | + | |
63 | + The lock provided by an instance of \e QtLockedFile is released | |
64 | + whenever the program terminates. This is true even when the | |
65 | + program crashes and no destructors are called. | |
66 | +*/ | |
67 | + | |
68 | +/*! \enum QtLockedFile::LockMode | |
69 | + | |
70 | + This enum describes the available lock modes. | |
71 | + | |
72 | + \value ReadLock A read lock. | |
73 | + \value WriteLock A write lock. | |
74 | + \value NoLock Neither a read lock nor a write lock. | |
75 | +*/ | |
76 | + | |
77 | +/*! | |
78 | + Constructs an unlocked \e QtLockedFile object. This constructor | |
79 | + behaves in the same way as \e QFile::QFile(). | |
80 | + | |
81 | + \sa QFile::QFile() | |
82 | +*/ | |
83 | +QtLockedFile::QtLockedFile() | |
84 | + : QFile() | |
85 | +{ | |
86 | +#ifdef Q_OS_WIN | |
87 | + wmutex = 0; | |
88 | + rmutex = 0; | |
89 | +#endif | |
90 | + m_lock_mode = NoLock; | |
91 | +} | |
92 | + | |
93 | +/*! | |
94 | + Constructs an unlocked QtLockedFile object with file \a name. This | |
95 | + constructor behaves in the same way as \e QFile::QFile(const | |
96 | + QString&). | |
97 | + | |
98 | + \sa QFile::QFile() | |
99 | +*/ | |
100 | +QtLockedFile::QtLockedFile(const QString &name) | |
101 | + : QFile(name) | |
102 | +{ | |
103 | +#ifdef Q_OS_WIN | |
104 | + wmutex = 0; | |
105 | + rmutex = 0; | |
106 | +#endif | |
107 | + m_lock_mode = NoLock; | |
108 | +} | |
109 | + | |
110 | +/*! | |
111 | + Opens the file in OpenMode \a mode. | |
112 | + | |
113 | + This is identical to QFile::open(), with the one exception that the | |
114 | + Truncate mode flag is disallowed. Truncation would conflict with the | |
115 | + advisory file locking, since the file would be modified before the | |
116 | + write lock is obtained. If truncation is required, use resize(0) | |
117 | + after obtaining the write lock. | |
118 | + | |
119 | + Returns true if successful; otherwise false. | |
120 | + | |
121 | + \sa QFile::open(), QFile::resize() | |
122 | +*/ | |
123 | +bool QtLockedFile::open(OpenMode mode) | |
124 | +{ | |
125 | + if (mode & QIODevice::Truncate) { | |
126 | + qWarning("QtLockedFile::open(): Truncate mode not allowed."); | |
127 | + return false; | |
128 | + } | |
129 | + return QFile::open(mode); | |
130 | +} | |
131 | + | |
132 | +/*! | |
133 | + Returns \e true if this object has a in read or write lock; | |
134 | + otherwise returns \e false. | |
135 | + | |
136 | + \sa lockMode() | |
137 | +*/ | |
138 | +bool QtLockedFile::isLocked() const | |
139 | +{ | |
140 | + return m_lock_mode != NoLock; | |
141 | +} | |
142 | + | |
143 | +/*! | |
144 | + Returns the type of lock currently held by this object, or \e | |
145 | + QtLockedFile::NoLock. | |
146 | + | |
147 | + \sa isLocked() | |
148 | +*/ | |
149 | +QtLockedFile::LockMode QtLockedFile::lockMode() const | |
150 | +{ | |
151 | + return m_lock_mode; | |
152 | +} | |
153 | + | |
154 | +/*! | |
155 | + \fn bool QtLockedFile::lock(LockMode mode, bool block = true) | |
156 | + | |
157 | + Obtains a lock of type \a mode. The file must be opened before it | |
158 | + can be locked. | |
159 | + | |
160 | + If \a block is true, this function will block until the lock is | |
161 | + aquired. If \a block is false, this function returns \e false | |
162 | + immediately if the lock cannot be aquired. | |
163 | + | |
164 | + If this object already has a lock of type \a mode, this function | |
165 | + returns \e true immediately. If this object has a lock of a | |
166 | + different type than \a mode, the lock is first released and then a | |
167 | + new lock is obtained. | |
168 | + | |
169 | + This function returns \e true if, after it executes, the file is | |
170 | + locked by this object, and \e false otherwise. | |
171 | + | |
172 | + \sa unlock(), isLocked(), lockMode() | |
173 | +*/ | |
174 | + | |
175 | +/*! | |
176 | + \fn bool QtLockedFile::unlock() | |
177 | + | |
178 | + Releases a lock. | |
179 | + | |
180 | + If the object has no lock, this function returns immediately. | |
181 | + | |
182 | + This function returns \e true if, after it executes, the file is | |
183 | + not locked by this object, and \e false otherwise. | |
184 | + | |
185 | + \sa lock(), isLocked(), lockMode() | |
186 | +*/ | |
187 | + | |
188 | +/*! | |
189 | + \fn QtLockedFile::~QtLockedFile() | |
190 | + | |
191 | + Destroys the \e QtLockedFile object. If any locks were held, they | |
192 | + are released. | |
193 | +*/ |
@@ -0,0 +1,97 @@ | ||
1 | +/**************************************************************************** | |
2 | +** | |
3 | +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). | |
4 | +** Contact: http://www.qt-project.org/legal | |
5 | +** | |
6 | +** This file is part of the Qt Solutions component. | |
7 | +** | |
8 | +** $QT_BEGIN_LICENSE:BSD$ | |
9 | +** You may use this file under the terms of the BSD license as follows: | |
10 | +** | |
11 | +** "Redistribution and use in source and binary forms, with or without | |
12 | +** modification, are permitted provided that the following conditions are | |
13 | +** met: | |
14 | +** * Redistributions of source code must retain the above copyright | |
15 | +** notice, this list of conditions and the following disclaimer. | |
16 | +** * Redistributions in binary form must reproduce the above copyright | |
17 | +** notice, this list of conditions and the following disclaimer in | |
18 | +** the documentation and/or other materials provided with the | |
19 | +** distribution. | |
20 | +** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names | |
21 | +** of its contributors may be used to endorse or promote products derived | |
22 | +** from this software without specific prior written permission. | |
23 | +** | |
24 | +** | |
25 | +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | |
26 | +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | |
27 | +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | |
28 | +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | |
29 | +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | |
30 | +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | |
31 | +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | |
32 | +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | |
33 | +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |
34 | +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | |
35 | +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." | |
36 | +** | |
37 | +** $QT_END_LICENSE$ | |
38 | +** | |
39 | +****************************************************************************/ | |
40 | + | |
41 | +#ifndef QTLOCKEDFILE_H | |
42 | +#define QTLOCKEDFILE_H | |
43 | + | |
44 | +#include <QFile> | |
45 | +#ifdef Q_OS_WIN | |
46 | +#include <QVector> | |
47 | +#endif | |
48 | + | |
49 | +#if defined(Q_OS_WIN) | |
50 | +# if !defined(QT_QTLOCKEDFILE_EXPORT) && !defined(QT_QTLOCKEDFILE_IMPORT) | |
51 | +# define QT_QTLOCKEDFILE_EXPORT | |
52 | +# elif defined(QT_QTLOCKEDFILE_IMPORT) | |
53 | +# if defined(QT_QTLOCKEDFILE_EXPORT) | |
54 | +# undef QT_QTLOCKEDFILE_EXPORT | |
55 | +# endif | |
56 | +# define QT_QTLOCKEDFILE_EXPORT __declspec(dllimport) | |
57 | +# elif defined(QT_QTLOCKEDFILE_EXPORT) | |
58 | +# undef QT_QTLOCKEDFILE_EXPORT | |
59 | +# define QT_QTLOCKEDFILE_EXPORT __declspec(dllexport) | |
60 | +# endif | |
61 | +#else | |
62 | +# define QT_QTLOCKEDFILE_EXPORT | |
63 | +#endif | |
64 | + | |
65 | +namespace QtLP_Private { | |
66 | + | |
67 | +class QT_QTLOCKEDFILE_EXPORT QtLockedFile : public QFile | |
68 | +{ | |
69 | +public: | |
70 | + enum LockMode { NoLock = 0, ReadLock, WriteLock }; | |
71 | + | |
72 | + QtLockedFile(); | |
73 | + QtLockedFile(const QString &name); | |
74 | + ~QtLockedFile(); | |
75 | + | |
76 | + bool open(OpenMode mode); | |
77 | + | |
78 | + bool lock(LockMode mode, bool block = true); | |
79 | + bool unlock(); | |
80 | + bool isLocked() const; | |
81 | + LockMode lockMode() const; | |
82 | + | |
83 | +private: | |
84 | +#ifdef Q_OS_WIN | |
85 | + Qt::HANDLE wmutex; | |
86 | + Qt::HANDLE rmutex; | |
87 | + QVector<Qt::HANDLE> rmutexes; | |
88 | + QString mutexname; | |
89 | + | |
90 | + Qt::HANDLE getMutexHandle(int idx, bool doCreate); | |
91 | + bool waitMutex(Qt::HANDLE mutex, bool doBlock); | |
92 | + | |
93 | +#endif | |
94 | + LockMode m_lock_mode; | |
95 | +}; | |
96 | +} | |
97 | +#endif |
@@ -0,0 +1,115 @@ | ||
1 | +/**************************************************************************** | |
2 | +** | |
3 | +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). | |
4 | +** Contact: http://www.qt-project.org/legal | |
5 | +** | |
6 | +** This file is part of the Qt Solutions component. | |
7 | +** | |
8 | +** $QT_BEGIN_LICENSE:BSD$ | |
9 | +** You may use this file under the terms of the BSD license as follows: | |
10 | +** | |
11 | +** "Redistribution and use in source and binary forms, with or without | |
12 | +** modification, are permitted provided that the following conditions are | |
13 | +** met: | |
14 | +** * Redistributions of source code must retain the above copyright | |
15 | +** notice, this list of conditions and the following disclaimer. | |
16 | +** * Redistributions in binary form must reproduce the above copyright | |
17 | +** notice, this list of conditions and the following disclaimer in | |
18 | +** the documentation and/or other materials provided with the | |
19 | +** distribution. | |
20 | +** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names | |
21 | +** of its contributors may be used to endorse or promote products derived | |
22 | +** from this software without specific prior written permission. | |
23 | +** | |
24 | +** | |
25 | +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | |
26 | +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | |
27 | +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | |
28 | +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | |
29 | +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | |
30 | +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | |
31 | +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | |
32 | +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | |
33 | +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |
34 | +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | |
35 | +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." | |
36 | +** | |
37 | +** $QT_END_LICENSE$ | |
38 | +** | |
39 | +****************************************************************************/ | |
40 | + | |
41 | +#include <string.h> | |
42 | +#include <errno.h> | |
43 | +#include <unistd.h> | |
44 | +#include <fcntl.h> | |
45 | + | |
46 | +#include "qtlockedfile.h" | |
47 | + | |
48 | +bool QtLockedFile::lock(LockMode mode, bool block) | |
49 | +{ | |
50 | + if (!isOpen()) { | |
51 | + qWarning("QtLockedFile::lock(): file is not opened"); | |
52 | + return false; | |
53 | + } | |
54 | + | |
55 | + if (mode == NoLock) | |
56 | + return unlock(); | |
57 | + | |
58 | + if (mode == m_lock_mode) | |
59 | + return true; | |
60 | + | |
61 | + if (m_lock_mode != NoLock) | |
62 | + unlock(); | |
63 | + | |
64 | + struct flock fl; | |
65 | + fl.l_whence = SEEK_SET; | |
66 | + fl.l_start = 0; | |
67 | + fl.l_len = 0; | |
68 | + fl.l_type = (mode == ReadLock) ? F_RDLCK : F_WRLCK; | |
69 | + int cmd = block ? F_SETLKW : F_SETLK; | |
70 | + int ret = fcntl(handle(), cmd, &fl); | |
71 | + | |
72 | + if (ret == -1) { | |
73 | + if (errno != EINTR && errno != EAGAIN) | |
74 | + qWarning("QtLockedFile::lock(): fcntl: %s", strerror(errno)); | |
75 | + return false; | |
76 | + } | |
77 | + | |
78 | + | |
79 | + m_lock_mode = mode; | |
80 | + return true; | |
81 | +} | |
82 | + | |
83 | + | |
84 | +bool QtLockedFile::unlock() | |
85 | +{ | |
86 | + if (!isOpen()) { | |
87 | + qWarning("QtLockedFile::unlock(): file is not opened"); | |
88 | + return false; | |
89 | + } | |
90 | + | |
91 | + if (!isLocked()) | |
92 | + return true; | |
93 | + | |
94 | + struct flock fl; | |
95 | + fl.l_whence = SEEK_SET; | |
96 | + fl.l_start = 0; | |
97 | + fl.l_len = 0; | |
98 | + fl.l_type = F_UNLCK; | |
99 | + int ret = fcntl(handle(), F_SETLKW, &fl); | |
100 | + | |
101 | + if (ret == -1) { | |
102 | + qWarning("QtLockedFile::lock(): fcntl: %s", strerror(errno)); | |
103 | + return false; | |
104 | + } | |
105 | + | |
106 | + m_lock_mode = NoLock; | |
107 | + return true; | |
108 | +} | |
109 | + | |
110 | +QtLockedFile::~QtLockedFile() | |
111 | +{ | |
112 | + if (isOpen()) | |
113 | + unlock(); | |
114 | +} | |
115 | + |
@@ -0,0 +1,211 @@ | ||
1 | +/**************************************************************************** | |
2 | +** | |
3 | +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). | |
4 | +** Contact: http://www.qt-project.org/legal | |
5 | +** | |
6 | +** This file is part of the Qt Solutions component. | |
7 | +** | |
8 | +** $QT_BEGIN_LICENSE:BSD$ | |
9 | +** You may use this file under the terms of the BSD license as follows: | |
10 | +** | |
11 | +** "Redistribution and use in source and binary forms, with or without | |
12 | +** modification, are permitted provided that the following conditions are | |
13 | +** met: | |
14 | +** * Redistributions of source code must retain the above copyright | |
15 | +** notice, this list of conditions and the following disclaimer. | |
16 | +** * Redistributions in binary form must reproduce the above copyright | |
17 | +** notice, this list of conditions and the following disclaimer in | |
18 | +** the documentation and/or other materials provided with the | |
19 | +** distribution. | |
20 | +** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names | |
21 | +** of its contributors may be used to endorse or promote products derived | |
22 | +** from this software without specific prior written permission. | |
23 | +** | |
24 | +** | |
25 | +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | |
26 | +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | |
27 | +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | |
28 | +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | |
29 | +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | |
30 | +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | |
31 | +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | |
32 | +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | |
33 | +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |
34 | +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | |
35 | +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." | |
36 | +** | |
37 | +** $QT_END_LICENSE$ | |
38 | +** | |
39 | +****************************************************************************/ | |
40 | + | |
41 | +#include "qtlockedfile.h" | |
42 | +#include <qt_windows.h> | |
43 | +#include <QFileInfo> | |
44 | + | |
45 | +#define MUTEX_PREFIX "QtLockedFile mutex " | |
46 | +// Maximum number of concurrent read locks. Must not be greater than MAXIMUM_WAIT_OBJECTS | |
47 | +#define MAX_READERS MAXIMUM_WAIT_OBJECTS | |
48 | + | |
49 | +#if QT_VERSION >= 0x050000 | |
50 | +#define QT_WA(unicode, ansi) unicode | |
51 | +#endif | |
52 | + | |
53 | +Qt::HANDLE QtLockedFile::getMutexHandle(int idx, bool doCreate) | |
54 | +{ | |
55 | + if (mutexname.isEmpty()) { | |
56 | + QFileInfo fi(*this); | |
57 | + mutexname = QString::fromLatin1(MUTEX_PREFIX) | |
58 | + + fi.absoluteFilePath().toLower(); | |
59 | + } | |
60 | + QString mname(mutexname); | |
61 | + if (idx >= 0) | |
62 | + mname += QString::number(idx); | |
63 | + | |
64 | + Qt::HANDLE mutex; | |
65 | + if (doCreate) { | |
66 | + QT_WA( { mutex = CreateMutexW(NULL, FALSE, (TCHAR*)mname.utf16()); }, | |
67 | + { mutex = CreateMutexA(NULL, FALSE, mname.toLocal8Bit().constData()); } ); | |
68 | + if (!mutex) { | |
69 | + qErrnoWarning("QtLockedFile::lock(): CreateMutex failed"); | |
70 | + return 0; | |
71 | + } | |
72 | + } | |
73 | + else { | |
74 | + QT_WA( { mutex = OpenMutexW(SYNCHRONIZE | MUTEX_MODIFY_STATE, FALSE, (TCHAR*)mname.utf16()); }, | |
75 | + { mutex = OpenMutexA(SYNCHRONIZE | MUTEX_MODIFY_STATE, FALSE, mname.toLocal8Bit().constData()); } ); | |
76 | + if (!mutex) { | |
77 | + if (GetLastError() != ERROR_FILE_NOT_FOUND) | |
78 | + qErrnoWarning("QtLockedFile::lock(): OpenMutex failed"); | |
79 | + return 0; | |
80 | + } | |
81 | + } | |
82 | + return mutex; | |
83 | +} | |
84 | + | |
85 | +bool QtLockedFile::waitMutex(Qt::HANDLE mutex, bool doBlock) | |
86 | +{ | |
87 | + Q_ASSERT(mutex); | |
88 | + DWORD res = WaitForSingleObject(mutex, doBlock ? INFINITE : 0); | |
89 | + switch (res) { | |
90 | + case WAIT_OBJECT_0: | |
91 | + case WAIT_ABANDONED: | |
92 | + return true; | |
93 | + break; | |
94 | + case WAIT_TIMEOUT: | |
95 | + break; | |
96 | + default: | |
97 | + qErrnoWarning("QtLockedFile::lock(): WaitForSingleObject failed"); | |
98 | + } | |
99 | + return false; | |
100 | +} | |
101 | + | |
102 | + | |
103 | + | |
104 | +bool QtLockedFile::lock(LockMode mode, bool block) | |
105 | +{ | |
106 | + if (!isOpen()) { | |
107 | + qWarning("QtLockedFile::lock(): file is not opened"); | |
108 | + return false; | |
109 | + } | |
110 | + | |
111 | + if (mode == NoLock) | |
112 | + return unlock(); | |
113 | + | |
114 | + if (mode == m_lock_mode) | |
115 | + return true; | |
116 | + | |
117 | + if (m_lock_mode != NoLock) | |
118 | + unlock(); | |
119 | + | |
120 | + if (!wmutex && !(wmutex = getMutexHandle(-1, true))) | |
121 | + return false; | |
122 | + | |
123 | + if (!waitMutex(wmutex, block)) | |
124 | + return false; | |
125 | + | |
126 | + if (mode == ReadLock) { | |
127 | + int idx = 0; | |
128 | + for (; idx < MAX_READERS; idx++) { | |
129 | + rmutex = getMutexHandle(idx, false); | |
130 | + if (!rmutex || waitMutex(rmutex, false)) | |
131 | + break; | |
132 | + CloseHandle(rmutex); | |
133 | + } | |
134 | + bool ok = true; | |
135 | + if (idx >= MAX_READERS) { | |
136 | + qWarning("QtLockedFile::lock(): too many readers"); | |
137 | + rmutex = 0; | |
138 | + ok = false; | |
139 | + } | |
140 | + else if (!rmutex) { | |
141 | + rmutex = getMutexHandle(idx, true); | |
142 | + if (!rmutex || !waitMutex(rmutex, false)) | |
143 | + ok = false; | |
144 | + } | |
145 | + if (!ok && rmutex) { | |
146 | + CloseHandle(rmutex); | |
147 | + rmutex = 0; | |
148 | + } | |
149 | + ReleaseMutex(wmutex); | |
150 | + if (!ok) | |
151 | + return false; | |
152 | + } | |
153 | + else { | |
154 | + Q_ASSERT(rmutexes.isEmpty()); | |
155 | + for (int i = 0; i < MAX_READERS; i++) { | |
156 | + Qt::HANDLE mutex = getMutexHandle(i, false); | |
157 | + if (mutex) | |
158 | + rmutexes.append(mutex); | |
159 | + } | |
160 | + if (rmutexes.size()) { | |
161 | + DWORD res = WaitForMultipleObjects(rmutexes.size(), rmutexes.constData(), | |
162 | + TRUE, block ? INFINITE : 0); | |
163 | + if (res != WAIT_OBJECT_0 && res != WAIT_ABANDONED) { | |
164 | + if (res != WAIT_TIMEOUT) | |
165 | + qErrnoWarning("QtLockedFile::lock(): WaitForMultipleObjects failed"); | |
166 | + m_lock_mode = WriteLock; // trick unlock() to clean up - semiyucky | |
167 | + unlock(); | |
168 | + return false; | |
169 | + } | |
170 | + } | |
171 | + } | |
172 | + | |
173 | + m_lock_mode = mode; | |
174 | + return true; | |
175 | +} | |
176 | + | |
177 | +bool QtLockedFile::unlock() | |
178 | +{ | |
179 | + if (!isOpen()) { | |
180 | + qWarning("QtLockedFile::unlock(): file is not opened"); | |
181 | + return false; | |
182 | + } | |
183 | + | |
184 | + if (!isLocked()) | |
185 | + return true; | |
186 | + | |
187 | + if (m_lock_mode == ReadLock) { | |
188 | + ReleaseMutex(rmutex); | |
189 | + CloseHandle(rmutex); | |
190 | + rmutex = 0; | |
191 | + } | |
192 | + else { | |
193 | + foreach(Qt::HANDLE mutex, rmutexes) { | |
194 | + ReleaseMutex(mutex); | |
195 | + CloseHandle(mutex); | |
196 | + } | |
197 | + rmutexes.clear(); | |
198 | + ReleaseMutex(wmutex); | |
199 | + } | |
200 | + | |
201 | + m_lock_mode = QtLockedFile::NoLock; | |
202 | + return true; | |
203 | +} | |
204 | + | |
205 | +QtLockedFile::~QtLockedFile() | |
206 | +{ | |
207 | + if (isOpen()) | |
208 | + unlock(); | |
209 | + if (wmutex) | |
210 | + CloseHandle(wmutex); | |
211 | +} |
@@ -0,0 +1,347 @@ | ||
1 | +/**************************************************************************** | |
2 | +** | |
3 | +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). | |
4 | +** Contact: http://www.qt-project.org/legal | |
5 | +** | |
6 | +** This file is part of the Qt Solutions component. | |
7 | +** | |
8 | +** $QT_BEGIN_LICENSE:BSD$ | |
9 | +** You may use this file under the terms of the BSD license as follows: | |
10 | +** | |
11 | +** "Redistribution and use in source and binary forms, with or without | |
12 | +** modification, are permitted provided that the following conditions are | |
13 | +** met: | |
14 | +** * Redistributions of source code must retain the above copyright | |
15 | +** notice, this list of conditions and the following disclaimer. | |
16 | +** * Redistributions in binary form must reproduce the above copyright | |
17 | +** notice, this list of conditions and the following disclaimer in | |
18 | +** the documentation and/or other materials provided with the | |
19 | +** distribution. | |
20 | +** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names | |
21 | +** of its contributors may be used to endorse or promote products derived | |
22 | +** from this software without specific prior written permission. | |
23 | +** | |
24 | +** | |
25 | +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | |
26 | +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | |
27 | +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | |
28 | +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | |
29 | +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | |
30 | +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | |
31 | +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | |
32 | +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | |
33 | +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |
34 | +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | |
35 | +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." | |
36 | +** | |
37 | +** $QT_END_LICENSE$ | |
38 | +** | |
39 | +****************************************************************************/ | |
40 | + | |
41 | + | |
42 | +#include "qtsingleapplication.h" | |
43 | +#include "qtlocalpeer.h" | |
44 | +#include <QWidget> | |
45 | + | |
46 | + | |
47 | +/*! | |
48 | + \class QtSingleApplication qtsingleapplication.h | |
49 | + \brief The QtSingleApplication class provides an API to detect and | |
50 | + communicate with running instances of an application. | |
51 | + | |
52 | + This class allows you to create applications where only one | |
53 | + instance should be running at a time. I.e., if the user tries to | |
54 | + launch another instance, the already running instance will be | |
55 | + activated instead. Another usecase is a client-server system, | |
56 | + where the first started instance will assume the role of server, | |
57 | + and the later instances will act as clients of that server. | |
58 | + | |
59 | + By default, the full path of the executable file is used to | |
60 | + determine whether two processes are instances of the same | |
61 | + application. You can also provide an explicit identifier string | |
62 | + that will be compared instead. | |
63 | + | |
64 | + The application should create the QtSingleApplication object early | |
65 | + in the startup phase, and call isRunning() to find out if another | |
66 | + instance of this application is already running. If isRunning() | |
67 | + returns false, it means that no other instance is running, and | |
68 | + this instance has assumed the role as the running instance. In | |
69 | + this case, the application should continue with the initialization | |
70 | + of the application user interface before entering the event loop | |
71 | + with exec(), as normal. | |
72 | + | |
73 | + The messageReceived() signal will be emitted when the running | |
74 | + application receives messages from another instance of the same | |
75 | + application. When a message is received it might be helpful to the | |
76 | + user to raise the application so that it becomes visible. To | |
77 | + facilitate this, QtSingleApplication provides the | |
78 | + setActivationWindow() function and the activateWindow() slot. | |
79 | + | |
80 | + If isRunning() returns true, another instance is already | |
81 | + running. It may be alerted to the fact that another instance has | |
82 | + started by using the sendMessage() function. Also data such as | |
83 | + startup parameters (e.g. the name of the file the user wanted this | |
84 | + new instance to open) can be passed to the running instance with | |
85 | + this function. Then, the application should terminate (or enter | |
86 | + client mode). | |
87 | + | |
88 | + If isRunning() returns true, but sendMessage() fails, that is an | |
89 | + indication that the running instance is frozen. | |
90 | + | |
91 | + Here's an example that shows how to convert an existing | |
92 | + application to use QtSingleApplication. It is very simple and does | |
93 | + not make use of all QtSingleApplication's functionality (see the | |
94 | + examples for that). | |
95 | + | |
96 | + \code | |
97 | + // Original | |
98 | + int main(int argc, char **argv) | |
99 | + { | |
100 | + QApplication app(argc, argv); | |
101 | + | |
102 | + MyMainWidget mmw; | |
103 | + mmw.show(); | |
104 | + return app.exec(); | |
105 | + } | |
106 | + | |
107 | + // Single instance | |
108 | + int main(int argc, char **argv) | |
109 | + { | |
110 | + QtSingleApplication app(argc, argv); | |
111 | + | |
112 | + if (app.isRunning()) | |
113 | + return !app.sendMessage(someDataString); | |
114 | + | |
115 | + MyMainWidget mmw; | |
116 | + app.setActivationWindow(&mmw); | |
117 | + mmw.show(); | |
118 | + return app.exec(); | |
119 | + } | |
120 | + \endcode | |
121 | + | |
122 | + Once this QtSingleApplication instance is destroyed (normally when | |
123 | + the process exits or crashes), when the user next attempts to run the | |
124 | + application this instance will not, of course, be encountered. The | |
125 | + next instance to call isRunning() or sendMessage() will assume the | |
126 | + role as the new running instance. | |
127 | + | |
128 | + For console (non-GUI) applications, QtSingleCoreApplication may be | |
129 | + used instead of this class, to avoid the dependency on the QtGui | |
130 | + library. | |
131 | + | |
132 | + \sa QtSingleCoreApplication | |
133 | +*/ | |
134 | + | |
135 | + | |
136 | +void QtSingleApplication::sysInit(const QString &appId) | |
137 | +{ | |
138 | + actWin = 0; | |
139 | + peer = new QtLocalPeer(this, appId); | |
140 | + connect(peer, SIGNAL(messageReceived(const QString&)), SIGNAL(messageReceived(const QString&))); | |
141 | +} | |
142 | + | |
143 | + | |
144 | +/*! | |
145 | + Creates a QtSingleApplication object. The application identifier | |
146 | + will be QCoreApplication::applicationFilePath(). \a argc, \a | |
147 | + argv, and \a GUIenabled are passed on to the QAppliation constructor. | |
148 | + | |
149 | + If you are creating a console application (i.e. setting \a | |
150 | + GUIenabled to false), you may consider using | |
151 | + QtSingleCoreApplication instead. | |
152 | +*/ | |
153 | + | |
154 | +QtSingleApplication::QtSingleApplication(int &argc, char **argv, bool GUIenabled) | |
155 | + : QApplication(argc, argv, GUIenabled) | |
156 | +{ | |
157 | + sysInit(); | |
158 | +} | |
159 | + | |
160 | + | |
161 | +/*! | |
162 | + Creates a QtSingleApplication object with the application | |
163 | + identifier \a appId. \a argc and \a argv are passed on to the | |
164 | + QAppliation constructor. | |
165 | +*/ | |
166 | + | |
167 | +QtSingleApplication::QtSingleApplication(const QString &appId, int &argc, char **argv) | |
168 | + : QApplication(argc, argv) | |
169 | +{ | |
170 | + sysInit(appId); | |
171 | +} | |
172 | + | |
173 | +#if QT_VERSION < 0x050000 | |
174 | + | |
175 | +/*! | |
176 | + Creates a QtSingleApplication object. The application identifier | |
177 | + will be QCoreApplication::applicationFilePath(). \a argc, \a | |
178 | + argv, and \a type are passed on to the QAppliation constructor. | |
179 | +*/ | |
180 | +QtSingleApplication::QtSingleApplication(int &argc, char **argv, Type type) | |
181 | + : QApplication(argc, argv, type) | |
182 | +{ | |
183 | + sysInit(); | |
184 | +} | |
185 | + | |
186 | + | |
187 | +# if defined(Q_WS_X11) | |
188 | +/*! | |
189 | + Special constructor for X11, ref. the documentation of | |
190 | + QApplication's corresponding constructor. The application identifier | |
191 | + will be QCoreApplication::applicationFilePath(). \a dpy, \a visual, | |
192 | + and \a cmap are passed on to the QApplication constructor. | |
193 | +*/ | |
194 | +QtSingleApplication::QtSingleApplication(Display* dpy, Qt::HANDLE visual, Qt::HANDLE cmap) | |
195 | + : QApplication(dpy, visual, cmap) | |
196 | +{ | |
197 | + sysInit(); | |
198 | +} | |
199 | + | |
200 | +/*! | |
201 | + Special constructor for X11, ref. the documentation of | |
202 | + QApplication's corresponding constructor. The application identifier | |
203 | + will be QCoreApplication::applicationFilePath(). \a dpy, \a argc, \a | |
204 | + argv, \a visual, and \a cmap are passed on to the QApplication | |
205 | + constructor. | |
206 | +*/ | |
207 | +QtSingleApplication::QtSingleApplication(Display *dpy, int &argc, char **argv, Qt::HANDLE visual, Qt::HANDLE cmap) | |
208 | + : QApplication(dpy, argc, argv, visual, cmap) | |
209 | +{ | |
210 | + sysInit(); | |
211 | +} | |
212 | + | |
213 | +/*! | |
214 | + Special constructor for X11, ref. the documentation of | |
215 | + QApplication's corresponding constructor. The application identifier | |
216 | + will be \a appId. \a dpy, \a argc, \a | |
217 | + argv, \a visual, and \a cmap are passed on to the QApplication | |
218 | + constructor. | |
219 | +*/ | |
220 | +QtSingleApplication::QtSingleApplication(Display* dpy, const QString &appId, int argc, char **argv, Qt::HANDLE visual, Qt::HANDLE cmap) | |
221 | + : QApplication(dpy, argc, argv, visual, cmap) | |
222 | +{ | |
223 | + sysInit(appId); | |
224 | +} | |
225 | +# endif // Q_WS_X11 | |
226 | +#endif // QT_VERSION < 0x050000 | |
227 | + | |
228 | + | |
229 | +/*! | |
230 | + Returns true if another instance of this application is running; | |
231 | + otherwise false. | |
232 | + | |
233 | + This function does not find instances of this application that are | |
234 | + being run by a different user (on Windows: that are running in | |
235 | + another session). | |
236 | + | |
237 | + \sa sendMessage() | |
238 | +*/ | |
239 | + | |
240 | +bool QtSingleApplication::isRunning() | |
241 | +{ | |
242 | + return peer->isClient(); | |
243 | +} | |
244 | + | |
245 | + | |
246 | +/*! | |
247 | + Tries to send the text \a message to the currently running | |
248 | + instance. The QtSingleApplication object in the running instance | |
249 | + will emit the messageReceived() signal when it receives the | |
250 | + message. | |
251 | + | |
252 | + This function returns true if the message has been sent to, and | |
253 | + processed by, the current instance. If there is no instance | |
254 | + currently running, or if the running instance fails to process the | |
255 | + message within \a timeout milliseconds, this function return false. | |
256 | + | |
257 | + \sa isRunning(), messageReceived() | |
258 | +*/ | |
259 | +bool QtSingleApplication::sendMessage(const QString &message, int timeout) | |
260 | +{ | |
261 | + return peer->sendMessage(message, timeout); | |
262 | +} | |
263 | + | |
264 | + | |
265 | +/*! | |
266 | + Returns the application identifier. Two processes with the same | |
267 | + identifier will be regarded as instances of the same application. | |
268 | +*/ | |
269 | +QString QtSingleApplication::id() const | |
270 | +{ | |
271 | + return peer->applicationId(); | |
272 | +} | |
273 | + | |
274 | + | |
275 | +/*! | |
276 | + Sets the activation window of this application to \a aw. The | |
277 | + activation window is the widget that will be activated by | |
278 | + activateWindow(). This is typically the application's main window. | |
279 | + | |
280 | + If \a activateOnMessage is true (the default), the window will be | |
281 | + activated automatically every time a message is received, just prior | |
282 | + to the messageReceived() signal being emitted. | |
283 | + | |
284 | + \sa activateWindow(), messageReceived() | |
285 | +*/ | |
286 | + | |
287 | +void QtSingleApplication::setActivationWindow(QWidget* aw, bool activateOnMessage) | |
288 | +{ | |
289 | + actWin = aw; | |
290 | + if (activateOnMessage) | |
291 | + connect(peer, SIGNAL(messageReceived(const QString&)), this, SLOT(activateWindow())); | |
292 | + else | |
293 | + disconnect(peer, SIGNAL(messageReceived(const QString&)), this, SLOT(activateWindow())); | |
294 | +} | |
295 | + | |
296 | + | |
297 | +/*! | |
298 | + Returns the applications activation window if one has been set by | |
299 | + calling setActivationWindow(), otherwise returns 0. | |
300 | + | |
301 | + \sa setActivationWindow() | |
302 | +*/ | |
303 | +QWidget* QtSingleApplication::activationWindow() const | |
304 | +{ | |
305 | + return actWin; | |
306 | +} | |
307 | + | |
308 | + | |
309 | +/*! | |
310 | + De-minimizes, raises, and activates this application's activation window. | |
311 | + This function does nothing if no activation window has been set. | |
312 | + | |
313 | + This is a convenience function to show the user that this | |
314 | + application instance has been activated when he has tried to start | |
315 | + another instance. | |
316 | + | |
317 | + This function should typically be called in response to the | |
318 | + messageReceived() signal. By default, that will happen | |
319 | + automatically, if an activation window has been set. | |
320 | + | |
321 | + \sa setActivationWindow(), messageReceived(), initialize() | |
322 | +*/ | |
323 | +void QtSingleApplication::activateWindow() | |
324 | +{ | |
325 | + if (actWin) { | |
326 | + actWin->setWindowState(actWin->windowState() & ~Qt::WindowMinimized); | |
327 | + actWin->raise(); | |
328 | + actWin->activateWindow(); | |
329 | + } | |
330 | +} | |
331 | + | |
332 | + | |
333 | +/*! | |
334 | + \fn void QtSingleApplication::messageReceived(const QString& message) | |
335 | + | |
336 | + This signal is emitted when the current instance receives a \a | |
337 | + message from another instance of this application. | |
338 | + | |
339 | + \sa sendMessage(), setActivationWindow(), activateWindow() | |
340 | +*/ | |
341 | + | |
342 | + | |
343 | +/*! | |
344 | + \fn void QtSingleApplication::initialize(bool dummy = true) | |
345 | + | |
346 | + \obsolete | |
347 | +*/ |
@@ -0,0 +1,105 @@ | ||
1 | +/**************************************************************************** | |
2 | +** | |
3 | +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). | |
4 | +** Contact: http://www.qt-project.org/legal | |
5 | +** | |
6 | +** This file is part of the Qt Solutions component. | |
7 | +** | |
8 | +** $QT_BEGIN_LICENSE:BSD$ | |
9 | +** You may use this file under the terms of the BSD license as follows: | |
10 | +** | |
11 | +** "Redistribution and use in source and binary forms, with or without | |
12 | +** modification, are permitted provided that the following conditions are | |
13 | +** met: | |
14 | +** * Redistributions of source code must retain the above copyright | |
15 | +** notice, this list of conditions and the following disclaimer. | |
16 | +** * Redistributions in binary form must reproduce the above copyright | |
17 | +** notice, this list of conditions and the following disclaimer in | |
18 | +** the documentation and/or other materials provided with the | |
19 | +** distribution. | |
20 | +** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names | |
21 | +** of its contributors may be used to endorse or promote products derived | |
22 | +** from this software without specific prior written permission. | |
23 | +** | |
24 | +** | |
25 | +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | |
26 | +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | |
27 | +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | |
28 | +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | |
29 | +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | |
30 | +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | |
31 | +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | |
32 | +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | |
33 | +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |
34 | +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | |
35 | +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." | |
36 | +** | |
37 | +** $QT_END_LICENSE$ | |
38 | +** | |
39 | +****************************************************************************/ | |
40 | + | |
41 | +#ifndef QTSINGLEAPPLICATION_H | |
42 | +#define QTSINGLEAPPLICATION_H | |
43 | + | |
44 | +#include <QApplication> | |
45 | + | |
46 | +class QtLocalPeer; | |
47 | + | |
48 | +#if defined(Q_OS_WIN) | |
49 | +# if !defined(QT_QTSINGLEAPPLICATION_EXPORT) && !defined(QT_QTSINGLEAPPLICATION_IMPORT) | |
50 | +# define QT_QTSINGLEAPPLICATION_EXPORT | |
51 | +# elif defined(QT_QTSINGLEAPPLICATION_IMPORT) | |
52 | +# if defined(QT_QTSINGLEAPPLICATION_EXPORT) | |
53 | +# undef QT_QTSINGLEAPPLICATION_EXPORT | |
54 | +# endif | |
55 | +# define QT_QTSINGLEAPPLICATION_EXPORT __declspec(dllimport) | |
56 | +# elif defined(QT_QTSINGLEAPPLICATION_EXPORT) | |
57 | +# undef QT_QTSINGLEAPPLICATION_EXPORT | |
58 | +# define QT_QTSINGLEAPPLICATION_EXPORT __declspec(dllexport) | |
59 | +# endif | |
60 | +#else | |
61 | +# define QT_QTSINGLEAPPLICATION_EXPORT | |
62 | +#endif | |
63 | + | |
64 | +class QT_QTSINGLEAPPLICATION_EXPORT QtSingleApplication : public QApplication | |
65 | +{ | |
66 | + Q_OBJECT | |
67 | + | |
68 | +public: | |
69 | + QtSingleApplication(int &argc, char **argv, bool GUIenabled = true); | |
70 | + QtSingleApplication(const QString &id, int &argc, char **argv); | |
71 | +#if QT_VERSION < 0x050000 | |
72 | + QtSingleApplication(int &argc, char **argv, Type type); | |
73 | +# if defined(Q_WS_X11) | |
74 | + QtSingleApplication(Display* dpy, Qt::HANDLE visual = 0, Qt::HANDLE colormap = 0); | |
75 | + QtSingleApplication(Display *dpy, int &argc, char **argv, Qt::HANDLE visual = 0, Qt::HANDLE cmap= 0); | |
76 | + QtSingleApplication(Display* dpy, const QString &appId, int argc, char **argv, Qt::HANDLE visual = 0, Qt::HANDLE colormap = 0); | |
77 | +# endif // Q_WS_X11 | |
78 | +#endif // QT_VERSION < 0x050000 | |
79 | + | |
80 | + bool isRunning(); | |
81 | + QString id() const; | |
82 | + | |
83 | + void setActivationWindow(QWidget* aw, bool activateOnMessage = true); | |
84 | + QWidget* activationWindow() const; | |
85 | + | |
86 | + // Obsolete: | |
87 | + void initialize(bool dummy = true) | |
88 | + { isRunning(); Q_UNUSED(dummy) } | |
89 | + | |
90 | +public Q_SLOTS: | |
91 | + bool sendMessage(const QString &message, int timeout = 5000); | |
92 | + void activateWindow(); | |
93 | + | |
94 | + | |
95 | +Q_SIGNALS: | |
96 | + void messageReceived(const QString &message); | |
97 | + | |
98 | + | |
99 | +private: | |
100 | + void sysInit(const QString &appId = QString()); | |
101 | + QtLocalPeer *peer; | |
102 | + QWidget *actWin; | |
103 | +}; | |
104 | + | |
105 | +#endif // QTSINGLEAPPLICATION_H |
@@ -0,0 +1,17 @@ | ||
1 | +include(../common.pri) | |
2 | +INCLUDEPATH += $$PWD | |
3 | +DEPENDPATH += $$PWD | |
4 | +QT *= network | |
5 | +greaterThan(QT_MAJOR_VERSION, 4): QT *= widgets | |
6 | + | |
7 | +qtsingleapplication-uselib:!qtsingleapplication-buildlib { | |
8 | + LIBS += -L$$QTSINGLEAPPLICATION_LIBDIR -l$$QTSINGLEAPPLICATION_LIBNAME | |
9 | +} else { | |
10 | + SOURCES += $$PWD/qtsingleapplication.cpp $$PWD/qtlocalpeer.cpp | |
11 | + HEADERS += $$PWD/qtsingleapplication.h $$PWD/qtlocalpeer.h | |
12 | +} | |
13 | + | |
14 | +win32 { | |
15 | + contains(TEMPLATE, lib):contains(CONFIG, shared):DEFINES += QT_QTSINGLEAPPLICATION_EXPORT | |
16 | + else:qtsingleapplication-uselib:DEFINES += QT_QTSINGLEAPPLICATION_IMPORT | |
17 | +} |
@@ -0,0 +1,149 @@ | ||
1 | +/**************************************************************************** | |
2 | +** | |
3 | +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). | |
4 | +** Contact: http://www.qt-project.org/legal | |
5 | +** | |
6 | +** This file is part of the Qt Solutions component. | |
7 | +** | |
8 | +** $QT_BEGIN_LICENSE:BSD$ | |
9 | +** You may use this file under the terms of the BSD license as follows: | |
10 | +** | |
11 | +** "Redistribution and use in source and binary forms, with or without | |
12 | +** modification, are permitted provided that the following conditions are | |
13 | +** met: | |
14 | +** * Redistributions of source code must retain the above copyright | |
15 | +** notice, this list of conditions and the following disclaimer. | |
16 | +** * Redistributions in binary form must reproduce the above copyright | |
17 | +** notice, this list of conditions and the following disclaimer in | |
18 | +** the documentation and/or other materials provided with the | |
19 | +** distribution. | |
20 | +** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names | |
21 | +** of its contributors may be used to endorse or promote products derived | |
22 | +** from this software without specific prior written permission. | |
23 | +** | |
24 | +** | |
25 | +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | |
26 | +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | |
27 | +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | |
28 | +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | |
29 | +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | |
30 | +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | |
31 | +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | |
32 | +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | |
33 | +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |
34 | +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | |
35 | +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." | |
36 | +** | |
37 | +** $QT_END_LICENSE$ | |
38 | +** | |
39 | +****************************************************************************/ | |
40 | + | |
41 | + | |
42 | +#include "qtsinglecoreapplication.h" | |
43 | +#include "qtlocalpeer.h" | |
44 | + | |
45 | +/*! | |
46 | + \class QtSingleCoreApplication qtsinglecoreapplication.h | |
47 | + \brief A variant of the QtSingleApplication class for non-GUI applications. | |
48 | + | |
49 | + This class is a variant of QtSingleApplication suited for use in | |
50 | + console (non-GUI) applications. It is an extension of | |
51 | + QCoreApplication (instead of QApplication). It does not require | |
52 | + the QtGui library. | |
53 | + | |
54 | + The API and usage is identical to QtSingleApplication, except that | |
55 | + functions relating to the "activation window" are not present, for | |
56 | + obvious reasons. Please refer to the QtSingleApplication | |
57 | + documentation for explanation of the usage. | |
58 | + | |
59 | + A QtSingleCoreApplication instance can communicate to a | |
60 | + QtSingleApplication instance if they share the same application | |
61 | + id. Hence, this class can be used to create a light-weight | |
62 | + command-line tool that sends commands to a GUI application. | |
63 | + | |
64 | + \sa QtSingleApplication | |
65 | +*/ | |
66 | + | |
67 | +/*! | |
68 | + Creates a QtSingleCoreApplication object. The application identifier | |
69 | + will be QCoreApplication::applicationFilePath(). \a argc and \a | |
70 | + argv are passed on to the QCoreAppliation constructor. | |
71 | +*/ | |
72 | + | |
73 | +QtSingleCoreApplication::QtSingleCoreApplication(int &argc, char **argv) | |
74 | + : QCoreApplication(argc, argv) | |
75 | +{ | |
76 | + peer = new QtLocalPeer(this); | |
77 | + connect(peer, SIGNAL(messageReceived(const QString&)), SIGNAL(messageReceived(const QString&))); | |
78 | +} | |
79 | + | |
80 | + | |
81 | +/*! | |
82 | + Creates a QtSingleCoreApplication object with the application | |
83 | + identifier \a appId. \a argc and \a argv are passed on to the | |
84 | + QCoreAppliation constructor. | |
85 | +*/ | |
86 | +QtSingleCoreApplication::QtSingleCoreApplication(const QString &appId, int &argc, char **argv) | |
87 | + : QCoreApplication(argc, argv) | |
88 | +{ | |
89 | + peer = new QtLocalPeer(this, appId); | |
90 | + connect(peer, SIGNAL(messageReceived(const QString&)), SIGNAL(messageReceived(const QString&))); | |
91 | +} | |
92 | + | |
93 | + | |
94 | +/*! | |
95 | + Returns true if another instance of this application is running; | |
96 | + otherwise false. | |
97 | + | |
98 | + This function does not find instances of this application that are | |
99 | + being run by a different user (on Windows: that are running in | |
100 | + another session). | |
101 | + | |
102 | + \sa sendMessage() | |
103 | +*/ | |
104 | + | |
105 | +bool QtSingleCoreApplication::isRunning() | |
106 | +{ | |
107 | + return peer->isClient(); | |
108 | +} | |
109 | + | |
110 | + | |
111 | +/*! | |
112 | + Tries to send the text \a message to the currently running | |
113 | + instance. The QtSingleCoreApplication object in the running instance | |
114 | + will emit the messageReceived() signal when it receives the | |
115 | + message. | |
116 | + | |
117 | + This function returns true if the message has been sent to, and | |
118 | + processed by, the current instance. If there is no instance | |
119 | + currently running, or if the running instance fails to process the | |
120 | + message within \a timeout milliseconds, this function return false. | |
121 | + | |
122 | + \sa isRunning(), messageReceived() | |
123 | +*/ | |
124 | + | |
125 | +bool QtSingleCoreApplication::sendMessage(const QString &message, int timeout) | |
126 | +{ | |
127 | + return peer->sendMessage(message, timeout); | |
128 | +} | |
129 | + | |
130 | + | |
131 | +/*! | |
132 | + Returns the application identifier. Two processes with the same | |
133 | + identifier will be regarded as instances of the same application. | |
134 | +*/ | |
135 | + | |
136 | +QString QtSingleCoreApplication::id() const | |
137 | +{ | |
138 | + return peer->applicationId(); | |
139 | +} | |
140 | + | |
141 | + | |
142 | +/*! | |
143 | + \fn void QtSingleCoreApplication::messageReceived(const QString& message) | |
144 | + | |
145 | + This signal is emitted when the current instance receives a \a | |
146 | + message from another instance of this application. | |
147 | + | |
148 | + \sa sendMessage() | |
149 | +*/ |
@@ -0,0 +1,71 @@ | ||
1 | +/**************************************************************************** | |
2 | +** | |
3 | +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). | |
4 | +** Contact: http://www.qt-project.org/legal | |
5 | +** | |
6 | +** This file is part of the Qt Solutions component. | |
7 | +** | |
8 | +** $QT_BEGIN_LICENSE:BSD$ | |
9 | +** You may use this file under the terms of the BSD license as follows: | |
10 | +** | |
11 | +** "Redistribution and use in source and binary forms, with or without | |
12 | +** modification, are permitted provided that the following conditions are | |
13 | +** met: | |
14 | +** * Redistributions of source code must retain the above copyright | |
15 | +** notice, this list of conditions and the following disclaimer. | |
16 | +** * Redistributions in binary form must reproduce the above copyright | |
17 | +** notice, this list of conditions and the following disclaimer in | |
18 | +** the documentation and/or other materials provided with the | |
19 | +** distribution. | |
20 | +** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names | |
21 | +** of its contributors may be used to endorse or promote products derived | |
22 | +** from this software without specific prior written permission. | |
23 | +** | |
24 | +** | |
25 | +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | |
26 | +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | |
27 | +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | |
28 | +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | |
29 | +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | |
30 | +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | |
31 | +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | |
32 | +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | |
33 | +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |
34 | +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | |
35 | +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." | |
36 | +** | |
37 | +** $QT_END_LICENSE$ | |
38 | +** | |
39 | +****************************************************************************/ | |
40 | + | |
41 | +#ifndef QTSINGLECOREAPPLICATION_H | |
42 | +#define QTSINGLECOREAPPLICATION_H | |
43 | + | |
44 | +#include <QCoreApplication> | |
45 | + | |
46 | +class QtLocalPeer; | |
47 | + | |
48 | +class QtSingleCoreApplication : public QCoreApplication | |
49 | +{ | |
50 | + Q_OBJECT | |
51 | + | |
52 | +public: | |
53 | + QtSingleCoreApplication(int &argc, char **argv); | |
54 | + QtSingleCoreApplication(const QString &id, int &argc, char **argv); | |
55 | + | |
56 | + bool isRunning(); | |
57 | + QString id() const; | |
58 | + | |
59 | +public Q_SLOTS: | |
60 | + bool sendMessage(const QString &message, int timeout = 5000); | |
61 | + | |
62 | + | |
63 | +Q_SIGNALS: | |
64 | + void messageReceived(const QString &message); | |
65 | + | |
66 | + | |
67 | +private: | |
68 | + QtLocalPeer* peer; | |
69 | +}; | |
70 | + | |
71 | +#endif // QTSINGLECOREAPPLICATION_H |
@@ -0,0 +1,10 @@ | ||
1 | +INCLUDEPATH += $$PWD | |
2 | +DEPENDPATH += $$PWD | |
3 | +HEADERS += $$PWD/qtsinglecoreapplication.h $$PWD/qtlocalpeer.h | |
4 | +SOURCES += $$PWD/qtsinglecoreapplication.cpp $$PWD/qtlocalpeer.cpp | |
5 | + | |
6 | +QT *= network | |
7 | + | |
8 | +win32:contains(TEMPLATE, lib):contains(CONFIG, shared) { | |
9 | + DEFINES += QT_QTSINGLECOREAPPLICATION_EXPORT=__declspec(dllexport) | |
10 | +} |