kdelibs/kdeui/kernel/kuniqueapplication.cpp

219 lines
7 KiB
C++
Raw Normal View History

2014-11-13 01:04:59 +02:00
/* This file is part of the KDE libraries
Copyright (c) 1999 Preston Brown <pbrown@kde.org>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
#include "kuniqueapplication.h"
#include "kuniqueapplication_p.h"
#include "kmainwindow.h"
#include "kcmdlineargs.h"
#include "kaboutdata.h"
#include "kconfiggroup.h"
#include "kconfig.h"
#include "kstartupinfo.h"
#include "kdebug.h"
2014-11-13 01:04:59 +02:00
#include <QList>
#include <QTimer>
#include <QDBusConnection>
#include <QDBusConnectionInterface>
2014-11-13 01:04:59 +02:00
#include <sys/types.h>
#include <unistd.h>
#include <limits.h>
2014-11-13 01:04:59 +02:00
#if defined Q_WS_X11
# include <X11/Xlib.h>
2014-11-13 01:04:59 +02:00
#endif
bool KUniqueApplication::Private::s_multipleInstances = false;
bool s_kuniqueapplication_startCalled = false;
bool KUniqueApplication::start(StartFlags flags)
2014-11-13 01:04:59 +02:00
{
if (s_kuniqueapplication_startCalled) {
return true;
}
s_kuniqueapplication_startCalled = true;
QString appName = KCmdLineArgs::aboutData()->appName();
const QStringList parts = KCmdLineArgs::aboutData()->organizationDomain().split(QLatin1Char('.'), QString::SkipEmptyParts);
if (parts.isEmpty()) {
appName.prepend(QLatin1String("local."));
} else {
foreach (const QString &s, parts) {
appName.prepend(QLatin1Char('.'));
appName.prepend(s);
}
}
2014-11-13 01:04:59 +02:00
// Check the D-Bus connection health
QDBusConnectionInterface* dbusService = nullptr;
2014-11-13 01:04:59 +02:00
QDBusConnection sessionBus = QDBusConnection::sessionBus();
if (!sessionBus.isConnected() || !(dbusService = sessionBus.interface())) {
kError() << "KUniqueApplication: Cannot find the D-Bus session server: " << sessionBus.lastError().message();
2014-11-13 01:04:59 +02:00
::exit(255);
}
if (Private::s_multipleInstances || flags & KUniqueApplication::NonUniqueInstance) {
appName = appName + '-' + QString::number(::getpid());
}
2014-11-13 01:04:59 +02:00
// Check to make sure that we're actually able to register with the D-Bus session server.
bool registered = dbusService->registerService(appName) == QDBusConnectionInterface::ServiceRegistered;
if (!registered) {
// If already running call newInstance() on the app interface
QByteArray saved_args;
QDataStream ds(&saved_args, QIODevice::WriteOnly);
KCmdLineArgs::saveAppArgs(ds);
QByteArray new_asn_id;
KStartupInfoId id;
if (kapp != NULL) {
// KApplication constructor unsets the env. variable
id.initId(kapp->startupId());
} else {
id = KStartupInfo::currentStartupIdEnv();
}
if (!id.none()) {
new_asn_id = id.id();
}
QDBusMessage msg = QDBusMessage::createMethodCall(appName, "/MainApplication", "org.kde.KUniqueApplication", "newInstance");
msg << new_asn_id << saved_args;
QDBusReply<int> reply = QDBusConnection::sessionBus().call(msg, QDBus::Block, INT_MAX);
if (!reply.isValid()) {
QDBusError err = reply.error();
kError() << "Communication problem with " << KCmdLineArgs::aboutData()->appName() << ", it probably crashed.\n"
<< "Error message was: " << err.name() << ": \"" << err.message() << "\"";
::exit(255);
}
::exit(reply);
}
2014-11-13 01:04:59 +02:00
// We'll call newInstance in the constructor. Do nothing here.
return true;
2014-11-13 01:04:59 +02:00
}
KUniqueApplication::KUniqueApplication(bool configUnique)
: KApplication(Private::initHack(configUnique)),
2014-11-13 01:04:59 +02:00
d(new Private(this))
{
d->firstInstance = true;
2014-11-13 01:04:59 +02:00
// the sanity checking happened in initHack
new KUniqueApplicationAdaptor(this);
2014-11-13 01:04:59 +02:00
// Can't call newInstance directly from the constructor since it's virtual...
QTimer::singleShot(0, this, SLOT(_k_newInstance()));
2014-11-13 01:04:59 +02:00
}
#ifdef Q_WS_X11
KUniqueApplication::KUniqueApplication(Display *display, Qt::HANDLE visual,
Qt::HANDLE colormap, bool configUnique)
: KApplication(display, visual, colormap, Private::initHack(configUnique)),
2014-11-13 01:04:59 +02:00
d(new Private(this))
{
d->firstInstance = true;
2014-11-13 01:04:59 +02:00
// the sanity checking happened in initHack
new KUniqueApplicationAdaptor(this);
2014-11-13 01:04:59 +02:00
// Can't call newInstance directly from the constructor since it's virtual...
QTimer::singleShot(0, this, SLOT(_k_newInstance()));
2014-11-13 01:04:59 +02:00
}
#endif
KUniqueApplication::~KUniqueApplication()
{
delete d;
2014-11-13 01:04:59 +02:00
}
// this gets called before even entering QApplication::QApplication()
KComponentData KUniqueApplication::Private::initHack(bool configUnique)
{
KComponentData cData(KCmdLineArgs::aboutData());
if (configUnique) {
KConfigGroup cg(cData.config(), "KDE");
s_multipleInstances = cg.readEntry("MultipleInstances", false);
}
if (!KUniqueApplication::start()) {
// Already running
::exit(0);
}
return cData;
2014-11-13 01:04:59 +02:00
}
void KUniqueApplication::Private::_k_newInstance()
2014-11-13 01:04:59 +02:00
{
q->newInstance();
firstInstance = false;
2014-11-13 01:04:59 +02:00
}
bool KUniqueApplication::restoringSession()
{
return d->firstInstance && isSessionRestored();
2014-11-13 01:04:59 +02:00
}
int KUniqueApplication::newInstance()
{
if (!d->firstInstance) {
QList<KMainWindow*> allWindows = KMainWindow::memberList();
if (!allWindows.isEmpty()) {
// This method is documented to only work for applications
// with only one mainwindow.
KMainWindow* mainWindow = allWindows.first();
if (mainWindow) {
mainWindow->show();
#ifdef Q_WS_X11
// This is the line that handles window activation if necessary,
// and what's important, it does it properly. If you reimplement newInstance(),
// and don't call the inherited one, use this (but NOT when newInstance()
// is called for the first time, like here).
KStartupInfo::setNewStartupId(mainWindow, startupId());
#endif
}
}
}
// do nothing in default implementation
return 0;
2014-11-13 01:04:59 +02:00
}
////
int KUniqueApplicationAdaptor::newInstance(const QByteArray &asn_id, const QByteArray &args)
{
if (!asn_id.isEmpty()) {
parent()->setStartupId(asn_id);
}
2014-11-13 01:04:59 +02:00
QDataStream ds(args);
KCmdLineArgs::loadAppArgs(ds);
int ret = parent()->newInstance();
// Must be done out of the newInstance code, in case it is overloaded
parent()->d->firstInstance = false;
return ret;
}
#include "moc_kuniqueapplication.cpp"
#include "moc_kuniqueapplication_p.cpp"