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 }