1 /****************************************************************************
2 **
3 ** Copyright (C) 2016 The Qt Company Ltd.
4 ** Contact: https://www.qt.io/licensing/
5 **
6 ** This file is part of Qt Creator.
7 **
8 ** Commercial License Usage
9 ** Licensees holding valid commercial Qt licenses may use this file in
10 ** accordance with the commercial license agreement provided with the
11 ** Software or, alternatively, in accordance with the terms contained in
12 ** a written agreement between you and The Qt Company. For licensing terms
13 ** and conditions see https://www.qt.io/terms-conditions. For further
14 ** information use the contact form at https://www.qt.io/contact-us.
15 **
16 ** GNU General Public License Usage
17 ** Alternatively, this file may be used under the terms of the GNU
18 ** General Public License version 3 as published by the Free Software
19 ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
20 ** included in the packaging of this file. Please review the following
21 ** information to ensure the GNU General Public License requirements will
22 ** be met: https://www.gnu.org/licenses/gpl-3.0.html.
23 **
24 ****************************************************************************/
25
26 #include "qtsingleapplication.h"
27 #include "qtlocalpeer.h"
28
29 #include <qtlockedfile.h>
30
31 #include <QDir>
32 #include <QFileOpenEvent>
33 #include <QSharedMemory>
34 #include <QWidget>
35
36 namespace SharedTools {
37
38 static const int instancesSize = 1024;
39
instancesLockFilename(const QString & appSessionId)40 static QString instancesLockFilename(const QString &appSessionId)
41 {
42 const QChar slash(QLatin1Char('/'));
43 QString res = QDir::tempPath();
44 if (!res.endsWith(slash))
45 res += slash;
46 return res + appSessionId + QLatin1String("-instances");
47 }
48
QtSingleApplication(const QString & appId,int & argc,char ** argv)49 QtSingleApplication::QtSingleApplication(const QString &appId, int &argc, char **argv)
50 : QApplication(argc, argv),
51 firstPeer(-1),
52 pidPeer(0)
53 {
54 this->appId = appId;
55
56 const QString appSessionId = QtLocalPeer::appSessionId(appId);
57
58 // This shared memory holds a zero-terminated array of active (or crashed) instances
59 instances = new QSharedMemory(appSessionId, this);
60 actWin = 0;
61 block = false;
62
63 // First instance creates the shared memory, later instances attach to it
64 const bool created = instances->create(instancesSize);
65 if (!created) {
66 if (!instances->attach()) {
67 qWarning() << "Failed to initialize instances shared memory: "
68 << instances->errorString();
69 delete instances;
70 instances = 0;
71 return;
72 }
73 }
74
75 // QtLockedFile is used to workaround QTBUG-10364
76 QtLockedFile lockfile(instancesLockFilename(appSessionId));
77
78 lockfile.open(QtLockedFile::ReadWrite);
79 lockfile.lock(QtLockedFile::WriteLock);
80 qint64 *pids = static_cast<qint64 *>(instances->data());
81 if (!created) {
82 // Find the first instance that it still running
83 // The whole list needs to be iterated in order to append to it
84 for (; *pids; ++pids) {
85 if (firstPeer == -1 && isRunning(*pids))
86 firstPeer = *pids;
87 }
88 }
89 // Add current pid to list and terminate it
90 *pids++ = QCoreApplication::applicationPid();
91 *pids = 0;
92 pidPeer = new QtLocalPeer(this, appId + QLatin1Char('-') +
93 QString::number(QCoreApplication::applicationPid()));
94 connect(pidPeer, SIGNAL(messageReceived(QString,QObject*)), SIGNAL(messageReceived(QString,QObject*)));
95 pidPeer->isClient();
96 lockfile.unlock();
97 }
98
~QtSingleApplication()99 QtSingleApplication::~QtSingleApplication()
100 {
101 if (!instances)
102 return;
103 const qint64 appPid = QCoreApplication::applicationPid();
104 QtLockedFile lockfile(instancesLockFilename(QtLocalPeer::appSessionId(appId)));
105 lockfile.open(QtLockedFile::ReadWrite);
106 lockfile.lock(QtLockedFile::WriteLock);
107 // Rewrite array, removing current pid and previously crashed ones
108 qint64 *pids = static_cast<qint64 *>(instances->data());
109 qint64 *newpids = pids;
110 for (; *pids; ++pids) {
111 if (*pids != appPid && isRunning(*pids))
112 *newpids++ = *pids;
113 }
114 *newpids = 0;
115 lockfile.unlock();
116 }
117
event(QEvent * event)118 bool QtSingleApplication::event(QEvent *event)
119 {
120 if (event->type() == QEvent::FileOpen) {
121 QFileOpenEvent *foe = static_cast<QFileOpenEvent*>(event);
122 emit fileOpenRequest(foe->file());
123 return true;
124 }
125 return QApplication::event(event);
126 }
127
isRunning(qint64 pid)128 bool QtSingleApplication::isRunning(qint64 pid)
129 {
130 if (pid == -1) {
131 pid = firstPeer;
132 if (pid == -1)
133 return false;
134 }
135
136 QtLocalPeer peer(this, appId + QLatin1Char('-') + QString::number(pid, 10));
137 return peer.isClient();
138 }
139
sendMessage(const QString & message,int timeout,qint64 pid)140 bool QtSingleApplication::sendMessage(const QString &message, int timeout, qint64 pid)
141 {
142 if (pid == -1) {
143 pid = firstPeer;
144 if (pid == -1)
145 return false;
146 }
147
148 QtLocalPeer peer(this, appId + QLatin1Char('-') + QString::number(pid, 10));
149 return peer.sendMessage(message, timeout, block);
150 }
151
applicationId() const152 QString QtSingleApplication::applicationId() const
153 {
154 return appId;
155 }
156
setBlock(bool value)157 void QtSingleApplication::setBlock(bool value)
158 {
159 block = value;
160 }
161
setActivationWindow(QWidget * aw,bool activateOnMessage)162 void QtSingleApplication::setActivationWindow(QWidget *aw, bool activateOnMessage)
163 {
164 actWin = aw;
165 if (!pidPeer)
166 return;
167 if (activateOnMessage)
168 connect(pidPeer, SIGNAL(messageReceived(QString,QObject*)), this, SLOT(activateWindow()));
169 else
170 disconnect(pidPeer, SIGNAL(messageReceived(QString,QObject*)), this, SLOT(activateWindow()));
171 }
172
173
activationWindow() const174 QWidget* QtSingleApplication::activationWindow() const
175 {
176 return actWin;
177 }
178
179
activateWindow()180 void QtSingleApplication::activateWindow()
181 {
182 if (actWin) {
183 actWin->setWindowState(actWin->windowState() & ~Qt::WindowMinimized);
184 actWin->raise();
185 actWin->activateWindow();
186 }
187 }
188
189 } // namespace SharedTools
190