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