File indexing completed on 2024-05-19 04:38:46
0001 /**************************************************************************** 0002 ** 0003 ** Copyright (C) 2016 The Qt Company Ltd. 0004 ** Contact: https://www.qt.io/licensing/ 0005 ** 0006 ** This file is part of Qt Creator. 0007 ** 0008 ** Commercial License Usage 0009 ** Licensees holding valid commercial Qt licenses may use this file in 0010 ** accordance with the commercial license agreement provided with the 0011 ** Software or, alternatively, in accordance with the terms contained in 0012 ** a written agreement between you and The Qt Company. For licensing terms 0013 ** and conditions see https://www.qt.io/terms-conditions. For further 0014 ** information use the contact form at https://www.qt.io/contact-us. 0015 ** 0016 ** GNU General Public License Usage 0017 ** Alternatively, this file may be used under the terms of the GNU 0018 ** General Public License version 3 as published by the Free Software 0019 ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT 0020 ** included in the packaging of this file. Please review the following 0021 ** information to ensure the GNU General Public License requirements will 0022 ** be met: https://www.gnu.org/licenses/gpl-3.0.html. 0023 ** 0024 ****************************************************************************/ 0025 0026 #include "qtsingleapplication.h" 0027 #include "qtlocalpeer.h" 0028 0029 #include <qtlockedfile.h> 0030 0031 #include <QDir> 0032 #include <QFileOpenEvent> 0033 #include <QSharedMemory> 0034 #include <QWidget> 0035 0036 namespace SharedTools { 0037 0038 static const int instancesSize = 1024; 0039 0040 static QString instancesLockFilename(const QString &appSessionId) 0041 { 0042 const QChar slash(QLatin1Char('/')); 0043 QString res = QDir::tempPath(); 0044 if (!res.endsWith(slash)) 0045 res += slash; 0046 return res + appSessionId + QLatin1String("-instances"); 0047 } 0048 0049 QtSingleApplication::QtSingleApplication(const QString &appId, int &argc, char **argv) 0050 : QApplication(argc, argv), 0051 firstPeer(-1), 0052 pidPeer(0) 0053 { 0054 this->appId = appId; 0055 0056 const QString appSessionId = QtLocalPeer::appSessionId(appId); 0057 0058 // This shared memory holds a zero-terminated array of active (or crashed) instances 0059 instances = new QSharedMemory(appSessionId, this); 0060 actWin = 0; 0061 block = false; 0062 0063 // First instance creates the shared memory, later instances attach to it 0064 const bool created = instances->create(instancesSize); 0065 if (!created) { 0066 if (!instances->attach()) { 0067 qWarning() << "Failed to initialize instances shared memory: " 0068 << instances->errorString(); 0069 delete instances; 0070 instances = 0; 0071 return; 0072 } 0073 } 0074 0075 // QtLockedFile is used to workaround QTBUG-10364 0076 QtLockedFile lockfile(instancesLockFilename(appSessionId)); 0077 0078 lockfile.open(QtLockedFile::ReadWrite); 0079 lockfile.lock(QtLockedFile::WriteLock); 0080 qint64 *pids = static_cast<qint64 *>(instances->data()); 0081 if (!created) { 0082 // Find the first instance that it still running 0083 // The whole list needs to be iterated in order to append to it 0084 for (; *pids; ++pids) { 0085 if (firstPeer == -1 && isRunning(*pids)) 0086 firstPeer = *pids; 0087 } 0088 } 0089 // Add current pid to list and terminate it 0090 *pids++ = QCoreApplication::applicationPid(); 0091 *pids = 0; 0092 pidPeer = new QtLocalPeer(this, appId + QLatin1Char('-') + 0093 QString::number(QCoreApplication::applicationPid())); 0094 connect(pidPeer, SIGNAL(messageReceived(QString,QObject*)), SIGNAL(messageReceived(QString,QObject*))); 0095 pidPeer->isClient(); 0096 lockfile.unlock(); 0097 } 0098 0099 QtSingleApplication::~QtSingleApplication() 0100 { 0101 if (!instances) 0102 return; 0103 const qint64 appPid = QCoreApplication::applicationPid(); 0104 QtLockedFile lockfile(instancesLockFilename(QtLocalPeer::appSessionId(appId))); 0105 lockfile.open(QtLockedFile::ReadWrite); 0106 lockfile.lock(QtLockedFile::WriteLock); 0107 // Rewrite array, removing current pid and previously crashed ones 0108 qint64 *pids = static_cast<qint64 *>(instances->data()); 0109 qint64 *newpids = pids; 0110 for (; *pids; ++pids) { 0111 if (*pids != appPid && isRunning(*pids)) 0112 *newpids++ = *pids; 0113 } 0114 *newpids = 0; 0115 lockfile.unlock(); 0116 } 0117 0118 bool QtSingleApplication::event(QEvent *event) 0119 { 0120 if (event->type() == QEvent::FileOpen) { 0121 QFileOpenEvent *foe = static_cast<QFileOpenEvent*>(event); 0122 emit fileOpenRequest(foe->file()); 0123 return true; 0124 } 0125 return QApplication::event(event); 0126 } 0127 0128 bool QtSingleApplication::isRunning(qint64 pid) 0129 { 0130 if (pid == -1) { 0131 pid = firstPeer; 0132 if (pid == -1) 0133 return false; 0134 } 0135 0136 QtLocalPeer peer(this, appId + QLatin1Char('-') + QString::number(pid, 10)); 0137 return peer.isClient(); 0138 } 0139 0140 bool QtSingleApplication::sendMessage(const QString &message, int timeout, qint64 pid) 0141 { 0142 if (pid == -1) { 0143 pid = firstPeer; 0144 if (pid == -1) 0145 return false; 0146 } 0147 0148 QtLocalPeer peer(this, appId + QLatin1Char('-') + QString::number(pid, 10)); 0149 return peer.sendMessage(message, timeout, block); 0150 } 0151 0152 QString QtSingleApplication::applicationId() const 0153 { 0154 return appId; 0155 } 0156 0157 void QtSingleApplication::setBlock(bool value) 0158 { 0159 block = value; 0160 } 0161 0162 void QtSingleApplication::setActivationWindow(QWidget *aw, bool activateOnMessage) 0163 { 0164 actWin = aw; 0165 if (!pidPeer) 0166 return; 0167 if (activateOnMessage) 0168 connect(pidPeer, SIGNAL(messageReceived(QString,QObject*)), this, SLOT(activateWindow())); 0169 else 0170 disconnect(pidPeer, SIGNAL(messageReceived(QString,QObject*)), this, SLOT(activateWindow())); 0171 } 0172 0173 0174 QWidget* QtSingleApplication::activationWindow() const 0175 { 0176 return actWin; 0177 } 0178 0179 0180 void QtSingleApplication::activateWindow() 0181 { 0182 if (actWin) { 0183 actWin->setWindowState(actWin->windowState() & ~Qt::WindowMinimized); 0184 actWin->raise(); 0185 actWin->activateWindow(); 0186 } 0187 } 0188 0189 } // namespace SharedTools