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