File indexing completed on 2024-04-14 11:03:53

0001 /* This file is part of the KDE libraries
0002     Copyright (C) 1997 Matthias Kalle Dalheimer (kalle@kde.org)
0003     Copyright (C) 1998, 1999, 2000 KDE Team
0004 
0005     This library is free software; you can redistribute it and/or
0006     modify it under the terms of the GNU Library General Public
0007     License as published by the Free Software Foundation; either
0008     version 2 of the License, or (at your option) any later version.
0009 
0010     This library is distributed in the hope that it will be useful,
0011     but WITHOUT ANY WARRANTY; without even the implied warranty of
0012     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
0013     Library General Public License for more details.
0014 
0015     You should have received a copy of the GNU Library General Public License
0016     along with this library; see the file COPYING.LIB.  If not, write to
0017     the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
0018     Boston, MA 02110-1301, USA.
0019         */
0020 
0021 #include "kapplication.h"
0022 
0023 #include "kdeversion.h"
0024 #include <config-kdelibs4support.h>
0025 
0026 #include <QDir>
0027 #include <QFile>
0028 #include <QSessionManager>
0029 #include <QStyleFactory>
0030 #include <QTimer>
0031 #include <QWidget>
0032 #include <QPointer>
0033 #include <QList>
0034 #include <QDBusConnection>
0035 #include <QDBusConnectionInterface>
0036 #include <QDBusInterface>
0037 #include <QMetaType>
0038 
0039 #include "kauthorized.h"
0040 #include "k4aboutdata.h"
0041 #include "kcrash.h"
0042 #include "kconfig.h"
0043 #include "kcmdlineargs.h"
0044 #include "kglobalsettings.h"
0045 #include "kdebug.h"
0046 #include <kglobal.h>
0047 #include "klocale.h"
0048 #include "klocalizedstring.h"
0049 #include "ksessionmanager.h"
0050 #include "ktoolinvocation.h"
0051 #include "kurl.h"
0052 #include "kmessage.h"
0053 #include "kmessageboxmessagehandler.h"
0054 #include <kconfiggui.h>
0055 #include <kusertimestamp.h>
0056 
0057 #if HAVE_X11
0058 #include <qx11info_x11.h>
0059 #endif
0060 #include <kstartupinfo.h>
0061 
0062 #include <sys/types.h>
0063 #if HAVE_SYS_STAT_H
0064 #include <sys/stat.h>
0065 #endif
0066 #include <sys/wait.h>
0067 
0068 #ifndef Q_OS_WIN
0069 #include "kwindowsystem.h"
0070 #endif
0071 
0072 #include <fcntl.h>
0073 #include <stdlib.h> // srand(), rand()
0074 #include <unistd.h>
0075 #if HAVE_X11
0076 #include <netwm.h>
0077 #endif
0078 
0079 #if HAVE_PATHS_H
0080 #include <paths.h>
0081 #endif
0082 
0083 #if HAVE_X11
0084 #include <X11/Xlib.h>
0085 #include <X11/Xutil.h>
0086 #include <X11/Xatom.h>
0087 #include <X11/SM/SMlib.h>
0088 #include <fixx11h.h>
0089 
0090 #include <QX11Info>
0091 #endif
0092 
0093 #ifdef Q_OS_MAC
0094 // ick
0095 #undef Status
0096 #include <Carbon/Carbon.h>
0097 #include <QImage>
0098 #include <ksystemtrayicon.h>
0099 #include <kkernel_mac.h>
0100 #endif
0101 
0102 #ifdef Q_OS_UNIX
0103 #include <signal.h>
0104 #endif
0105 
0106 #include <qstandardpaths.h>
0107 #include <QActionEvent>
0108 
0109 KApplication *KApplication::KApp = nullptr;
0110 
0111 #if HAVE_X11
0112 static Atom atom_DesktopWindow;
0113 static Atom atom_NetSupported;
0114 static Atom kde_xdnd_drop;
0115 #endif
0116 
0117 template class QList<KSessionManager *>;
0118 
0119 #ifdef Q_OS_WIN
0120 void KApplication_init_windows();
0121 #endif
0122 
0123 static KApplicationPrivate *kapp_priv = nullptr;
0124 
0125 /*
0126   Private data to make keeping binary compatibility easier
0127  */
0128 class KApplicationPrivate
0129 {
0130 public:
0131     KApplicationPrivate(KApplication *q, const QByteArray &cName)
0132         : q(q)
0133         , componentData(cName)
0134         , session_save(false)
0135 #if HAVE_X11
0136         , oldIceIOErrorHandler(nullptr)
0137         , oldXErrorHandler(nullptr)
0138         , oldXIOErrorHandler(nullptr)
0139         , isX11(false)
0140 #endif
0141         , pSessionConfig(nullptr)
0142         , bSessionManagement(true)
0143     {
0144         kapp_priv = this;
0145     }
0146 
0147     KApplicationPrivate(KApplication *q, const KComponentData &cData)
0148         : q(q)
0149         , componentData(cData)
0150         , session_save(false)
0151 #if HAVE_X11
0152         , oldIceIOErrorHandler(nullptr)
0153         , oldXErrorHandler(nullptr)
0154         , oldXIOErrorHandler(nullptr)
0155         , isX11(false)
0156 #endif
0157         , pSessionConfig(nullptr)
0158         , bSessionManagement(true)
0159     {
0160         kapp_priv = this;
0161     }
0162 
0163     KApplicationPrivate(KApplication *q)
0164         : q(q)
0165         , componentData(KCmdLineArgs::aboutData())
0166         , session_save(false)
0167 #if HAVE_X11
0168         , oldIceIOErrorHandler(nullptr)
0169         , oldXErrorHandler(nullptr)
0170         , oldXIOErrorHandler(nullptr)
0171         , isX11(false)
0172 #endif
0173         , pSessionConfig(nullptr)
0174         , bSessionManagement(true)
0175     {
0176         kapp_priv = this;
0177     }
0178 
0179     ~KApplicationPrivate()
0180     {
0181     }
0182 
0183 #ifndef KDE3_SUPPORT
0184     KConfig *config()
0185     {
0186         return KSharedConfig::openConfig().data();
0187     }
0188 #endif
0189 
0190 #if HAVE_X11
0191     int xErrhandler(Display *, void *);
0192     int xioErrhandler(Display *);
0193     void iceIOErrorHandler(_IceConn *conn);
0194 #endif
0195 
0196     void _k_x11FilterDestroyed();
0197     void _k_slot_KToolInvocation_hook(QStringList &, QByteArray &);
0198 
0199     void init(bool GUIenabled = true);
0200     void parseCommandLine();  // Handle KDE arguments (Using KCmdLineArgs)
0201 
0202     KApplication *q;
0203     KComponentData componentData;
0204     bool session_save;
0205 
0206 #if HAVE_X11
0207     IceIOErrorHandler oldIceIOErrorHandler;
0208     int (*oldXErrorHandler)(Display *, XErrorEvent *);
0209     int (*oldXIOErrorHandler)(Display *);
0210     bool isX11;
0211 #endif
0212 
0213     QString sessionKey;
0214     QString pSessionConfigFile;
0215 
0216     KConfig *pSessionConfig; //instance specific application config object
0217     bool bSessionManagement;
0218 };
0219 
0220 #if HAVE_X11
0221 
0222 extern "C" {
0223     static int kde_xio_errhandler(Display *dpy)
0224     {
0225         return kapp_priv->xioErrhandler(dpy);
0226     }
0227 
0228     static int kde_x_errhandler(Display *dpy, XErrorEvent *err)
0229     {
0230         return kapp_priv->xErrhandler(dpy, err);
0231     }
0232 }
0233 #endif
0234 
0235 static QList< QPointer< QWidget > > *x11Filter = nullptr;
0236 
0237 /**
0238    * Installs a handler for the SIGPIPE signal. It is thrown when you write to
0239    * a pipe or socket that has been closed.
0240    * The handler is installed automatically in the constructor, but you may
0241    * need it if your application or component does not have a KApplication
0242    * instance.
0243    */
0244 static void installSigpipeHandler()
0245 {
0246 #ifdef Q_OS_UNIX
0247     struct sigaction act;
0248     act.sa_handler = SIG_IGN;
0249     sigemptyset(&act.sa_mask);
0250     act.sa_flags = 0;
0251     sigaction(SIGPIPE, &act, nullptr);
0252 #endif
0253 }
0254 
0255 void KApplication::installX11EventFilter(QWidget *filter)
0256 {
0257     if (!filter) {
0258         return;
0259     }
0260     if (!x11Filter) {
0261         x11Filter = new QList< QPointer< QWidget > >;
0262     }
0263     connect(filter, SIGNAL(destroyed()), this, SLOT(_k_x11FilterDestroyed()));
0264     x11Filter->append(filter);
0265 }
0266 
0267 void KApplicationPrivate::_k_x11FilterDestroyed()
0268 {
0269     q->removeX11EventFilter(static_cast< const QWidget * >(q->sender()));
0270 }
0271 
0272 void KApplication::removeX11EventFilter(const QWidget *filter)
0273 {
0274     if (!x11Filter || !filter) {
0275         return;
0276     }
0277     // removeAll doesn't work, creating QPointer to something that's about to be deleted aborts
0278     // x11Filter->removeAll( const_cast< QWidget* >( filter ));
0279     for (QMutableListIterator< QPointer< QWidget > > it(*x11Filter);
0280             it.hasNext();
0281         ) {
0282         QWidget *w = it.next().data();
0283         if (w == filter || w == nullptr) {
0284             it.remove();
0285         }
0286     }
0287     if (x11Filter->isEmpty()) {
0288         delete x11Filter;
0289         x11Filter = nullptr;
0290     }
0291 }
0292 
0293 #if HAVE_X11
0294 static SmcConn mySmcConnection = nullptr;
0295 #else
0296 // FIXME(E): Implement for Qt Embedded
0297 // Possibly "steal" XFree86's libSM?
0298 #endif
0299 
0300 KApplication::KApplication(bool GUIenabled)
0301     : QApplication(KCmdLineArgs::qtArgc(), KCmdLineArgs::qtArgv(), GUIenabled),
0302       d(new KApplicationPrivate(this))
0303 {
0304     setApplicationName(d->componentData.componentName());
0305     setOrganizationDomain(d->componentData.aboutData()->organizationDomain());
0306     installSigpipeHandler();
0307     d->init(GUIenabled);
0308 }
0309 
0310 KApplication::KApplication(bool GUIenabled, const KComponentData &cData)
0311     : QApplication(KCmdLineArgs::qtArgc(), KCmdLineArgs::qtArgv(), GUIenabled),
0312       d(new KApplicationPrivate(this, cData))
0313 {
0314     setApplicationName(d->componentData.componentName());
0315     setOrganizationDomain(d->componentData.aboutData()->organizationDomain());
0316     installSigpipeHandler();
0317     d->init(GUIenabled);
0318 }
0319 
0320 #if HAVE_X11
0321 int KApplicationPrivate::xioErrhandler(Display *dpy)
0322 {
0323     oldXIOErrorHandler(dpy);
0324     exit(1);
0325     return 0;
0326 }
0327 
0328 int KApplicationPrivate::xErrhandler(Display *dpy, void *err_)
0329 {
0330     XErrorEvent *err = static_cast< XErrorEvent * >(err_);
0331     if (kapp) {
0332         // add KDE specific stuff here
0333         oldXErrorHandler(dpy, err);
0334     }
0335     const QByteArray fatalXError = qgetenv("KDE_FATAL_X_ERROR");
0336     if (!fatalXError.isEmpty()) {
0337         abort();
0338     }
0339     return 0;
0340 }
0341 
0342 void KApplicationPrivate::iceIOErrorHandler(_IceConn *conn)
0343 {
0344     if (oldIceIOErrorHandler != nullptr) {
0345         (*oldIceIOErrorHandler)(conn);
0346     }
0347     exit(1);
0348 }
0349 #endif
0350 
0351 // (only called by KUniqueApplication, no need to export)
0352 bool s_kuniqueapplication_startCalled = false;
0353 
0354 void KApplicationPrivate::init(bool GUIenabled)
0355 {
0356     Q_UNUSED(GUIenabled)
0357     if ((getuid() != geteuid()) ||
0358             (getgid() != getegid())) {
0359         fprintf(stderr, "The KDE libraries are not designed to run with suid privileges.\n");
0360         ::exit(127);
0361     }
0362 
0363 #ifdef Q_OS_MAC
0364     mac_initialize_dbus();
0365 #endif
0366 
0367     KApplication::KApp = q;
0368 
0369     extern KDELIBS4SUPPORT_DEPRECATED_EXPORT bool kde_kdebug_enable_dbus_interface;
0370     kde_kdebug_enable_dbus_interface = true;
0371 
0372     parseCommandLine();
0373 
0374     QGuiApplication::setDesktopSettingsAware(false);
0375     QGuiApplication::setFallbackSessionManagementEnabled(false);
0376 
0377 #if HAVE_X11
0378     isX11 = (QGuiApplication::platformName() == QStringLiteral("xcb"));
0379     if (isX11) {
0380         // create all required atoms in _one_ roundtrip to the X server
0381         const int max = 20;
0382         Atom *atoms[max];
0383         char *names[max];
0384         Atom atoms_return[max];
0385         int n = 0;
0386 
0387         atoms[n] = &atom_DesktopWindow;
0388         names[n++] = (char *) "KDE_DESKTOP_WINDOW";
0389 
0390         atoms[n] = &atom_NetSupported;
0391         names[n++] = (char *) "_NET_SUPPORTED";
0392 
0393         atoms[n] = &kde_xdnd_drop;
0394         names[n++] = (char *) "XdndDrop";
0395 
0396         XInternAtoms(QX11Info::display(), names, n, false, atoms_return);
0397 
0398         for (int i = 0; i < n; i++) {
0399             *atoms[i] = atoms_return[i];
0400         }
0401     }
0402 #endif
0403 
0404     // sanity checking, to make sure we've connected
0405     extern void qDBusBindToApplication();
0406     qDBusBindToApplication();
0407     QDBusConnectionInterface *bus = nullptr;
0408     if (!QDBusConnection::sessionBus().isConnected() || !(bus = QDBusConnection::sessionBus().interface())) {
0409         kFatal(240) << "Session bus not found" << endl <<
0410                     "To circumvent this problem try the following command (with Linux and bash)" << endl <<
0411                     "export $(dbus-launch)";
0412         ::exit(125);
0413     }
0414 
0415     if (bus && !s_kuniqueapplication_startCalled) { // don't register again if KUniqueApplication did so already
0416         QStringList parts = q->organizationDomain().split(QLatin1Char('.'), QString::SkipEmptyParts);
0417         QString reversedDomain;
0418         if (parts.isEmpty()) {
0419             reversedDomain = QLatin1String("local.");
0420         } else
0421             foreach (const QString &s, parts) {
0422                 reversedDomain.prepend(QLatin1Char('.'));
0423                 reversedDomain.prepend(s);
0424             }
0425         const QString pidSuffix = QString::number(getpid()).prepend(QLatin1String("-"));
0426         const QString serviceName = reversedDomain + q->applicationName() + pidSuffix;
0427         if (bus->registerService(serviceName) == QDBusConnectionInterface::ServiceNotRegistered) {
0428             kError(240) << "Couldn't register name '" << serviceName << "' with DBUS - another process owns it already!" << endl;
0429             ::exit(126);
0430         }
0431     }
0432     QDBusConnection::sessionBus().registerObject(QLatin1String("/MainApplication"), q,
0433             QDBusConnection::ExportScriptableSlots |
0434             QDBusConnection::ExportScriptableProperties |
0435             QDBusConnection::ExportAdaptors);
0436 
0437     // Trigger creation of locale.
0438     (void) KLocale::global();
0439 
0440     KSharedConfig::Ptr config = componentData.config();
0441     QByteArray readOnly = qgetenv("KDE_HOME_READONLY");
0442     if (readOnly.isEmpty() && q->applicationName() != QLatin1String("kdialog")) {
0443         if (KAuthorized::authorize(QLatin1String("warn_unwritable_config"))) {
0444             config->isConfigWritable(true);
0445         }
0446     }
0447 
0448 #if HAVE_X11
0449     if (isX11) {
0450         // this is important since we fork() to launch the help (Matthias)
0451         fcntl(ConnectionNumber(QX11Info::display()), F_SETFD, FD_CLOEXEC);
0452         // set up the fancy (=robust and error ignoring ) KDE xio error handlers (Matthias)
0453         oldXErrorHandler = XSetErrorHandler(kde_x_errhandler);
0454         oldXIOErrorHandler = XSetIOErrorHandler(kde_xio_errhandler);
0455     }
0456 #endif
0457 
0458     // Trigger initial settings
0459     KGlobalSettings::self()->activate();
0460 
0461     KMessage::setMessageHandler(new KMessageBoxMessageHandler(nullptr));
0462 
0463     q->connect(KToolInvocation::self(), SIGNAL(kapplication_hook(QStringList&,QByteArray&)),
0464                q, SLOT(_k_slot_KToolInvocation_hook(QStringList&,QByteArray&)));
0465 
0466     q->connect(q, SIGNAL(commitDataRequest(QSessionManager&)),
0467                q, SLOT(commitData(QSessionManager&)));
0468     q->connect(q, SIGNAL(saveStateRequest(QSessionManager&)),
0469                q, SLOT(saveState(QSessionManager&)));
0470 
0471 
0472 #ifdef Q_OS_MAC
0473     // This is a QSystemTrayIcon instead of K* because we can't be sure q is a QWidget
0474     QSystemTrayIcon *trayIcon; //krazy:exclude=qclasses
0475     if (QSystemTrayIcon::isSystemTrayAvailable()) { //krazy:exclude=qclasses
0476         trayIcon = new QSystemTrayIcon(q); //krazy:exclude=qclasses
0477         trayIcon->setIcon(q->windowIcon());
0478         /* it's counter-intuitive, but once you do setIcon it's already set the
0479            dock icon... ->show actually shows an icon in the menu bar too  :P */
0480         // trayIcon->show();
0481     }
0482 #endif
0483 
0484     qRegisterMetaType<KUrl>();
0485     qRegisterMetaType<QList<KUrl> >();
0486     qRegisterMetaType<QList<QUrl> >();
0487 
0488 #ifdef Q_OS_WIN
0489     KApplication_init_windows();
0490 #endif
0491 }
0492 
0493 KApplication *KApplication::kApplication()
0494 {
0495     return KApp;
0496 }
0497 
0498 KConfig *KApplication::sessionConfig()
0499 {
0500     return KConfigGui::sessionConfig();
0501 }
0502 
0503 void KApplication::reparseConfiguration()
0504 {
0505     KSharedConfig::openConfig()->reparseConfiguration();
0506 }
0507 
0508 void KApplication::quit()
0509 {
0510     QApplication::quit();
0511 }
0512 
0513 void KApplication::disableSessionManagement()
0514 {
0515     d->bSessionManagement = false;
0516 }
0517 
0518 void KApplication::enableSessionManagement()
0519 {
0520     d->bSessionManagement = true;
0521 #if HAVE_X11
0522     if (!d->isX11) {
0523         return;
0524     }
0525     // Session management support in Qt/KDE is awfully broken.
0526     // If konqueror disables session management right after its startup,
0527     // and enables it later (preloading stuff), it won't be properly
0528     // saved on session shutdown.
0529     // I'm not actually sure why it doesn't work, but saveState()
0530     // doesn't seem to be called on session shutdown, possibly
0531     // because disabling session management after konqueror startup
0532     // disabled it somehow. Forcing saveState() here for this application
0533     // seems to fix it.
0534     if (mySmcConnection) {
0535         SmcRequestSaveYourself(mySmcConnection, SmSaveLocal, False,
0536                                SmInteractStyleAny,
0537                                False, False);
0538 
0539         // flush the request
0540         IceFlush(SmcGetIceConnection(mySmcConnection));
0541     }
0542 #endif
0543 }
0544 
0545 void KApplication::commitData(QSessionManager &sm)
0546 {
0547     d->session_save = true;
0548     bool canceled = false;
0549 
0550     foreach (KSessionManager *it, KSessionManager::sessionClients()) {
0551         if ((canceled = !it->commitData(sm))) {
0552             break;
0553         }
0554     }
0555 
0556     if (canceled) {
0557         sm.cancel();
0558     }
0559 
0560     if (sm.allowsInteraction()) {
0561         QWidgetList donelist, todolist;
0562         QWidget *w;
0563 
0564     commitDataRestart:
0565         todolist = QApplication::topLevelWidgets();
0566 
0567         for (int i = 0; i < todolist.size(); ++i) {
0568             w = todolist.at(i);
0569             if (!w) {
0570                 break;
0571             }
0572 
0573             if (donelist.contains(w)) {
0574                 continue;
0575             }
0576 
0577             // leave KMainWindows alone because they are handled by KMWSessionManager
0578             if (!w->isHidden() && !w->inherits("KMainWindow")) {
0579                 QCloseEvent e;
0580                 sendEvent(w, &e);
0581                 if (!e.isAccepted()) {
0582                     canceled = true;
0583                     break;
0584                 }
0585 
0586                 donelist.append(w);
0587 
0588                 //grab the new list that was just modified by our closeevent
0589                 goto commitDataRestart;
0590             }
0591         }
0592     }
0593 
0594     if (!d->bSessionManagement) {
0595         sm.setRestartHint(QSessionManager::RestartNever);
0596     } else {
0597         sm.setRestartHint(QSessionManager::RestartIfRunning);
0598     }
0599 
0600     if (canceled) {
0601         sm.cancel();
0602     }
0603     d->session_save = false;
0604 }
0605 
0606 #if HAVE_X11 && QT_VERSION < QT_VERSION_CHECK(5, 0, 0)
0607 static void checkRestartVersion(QSessionManager &sm)
0608 {
0609     Display *dpy = QX11Info::display();
0610     Atom type;
0611     int format;
0612     unsigned long nitems, after;
0613     unsigned char *data;
0614     if (dpy != NULL && XGetWindowProperty(dpy, RootWindow(dpy, 0), XInternAtom(dpy, "KDE_SESSION_VERSION", False),
0615                                           0, 1, False, AnyPropertyType, &type, &format, &nitems, &after, &data) == Success) {
0616         if (type == XA_CARDINAL && format == 32) {
0617             int version = *(long *) data;
0618             if (version == KDE_VERSION_MAJOR) {  // we run in our native session
0619                 XFree(data);
0620                 return; // no need to wrap
0621             }
0622         }
0623         XFree(data);
0624     }
0625     if (getenv("KDE_SESSION_VERSION") != NULL && atoi(getenv("KDE_SESSION_VERSION")) == KDE_VERSION_MAJOR) {
0626         return;    // we run in our native session, no need to wrap
0627     }
0628 #define NUM_TO_STRING2( num ) #num
0629 #define NUM_TO_STRING( num ) NUM_TO_STRING2( num )
0630     QString wrapper = QStandardPaths::findExecutable("kde" NUM_TO_STRING(KDE_VERSION_MAJOR));     // "kde4", etc.
0631 #undef NUM_TO_STRING
0632 #undef NUM_TO_STRING2
0633     if (!wrapper.isEmpty()) {
0634         QStringList restartCommand = sm.restartCommand();
0635         restartCommand.prepend(wrapper);
0636         sm.setRestartCommand(restartCommand);
0637     }
0638 }
0639 #endif // HAVE_X11
0640 
0641 void KApplication::saveState(QSessionManager &sm)
0642 {
0643     d->session_save = true;
0644 #ifdef __GNUC__
0645 #warning TODO: QSessionManager::handle() is gone in Qt5!
0646 #endif
0647 #if HAVE_X11 && QT_VERSION < QT_VERSION_CHECK(5, 0, 0)
0648     static bool firstTime = true;
0649     mySmcConnection = (SmcConn) sm.handle();
0650 
0651     if (!d->bSessionManagement) {
0652         sm.setRestartHint(QSessionManager::RestartNever);
0653         d->session_save = false;
0654         return;
0655     } else {
0656         sm.setRestartHint(QSessionManager::RestartIfRunning);
0657     }
0658 
0659     if (firstTime) {
0660         firstTime = false;
0661         d->session_save = false;
0662         return; // no need to save the state.
0663     }
0664 
0665     // remove former session config if still existing, we want a new
0666     // and fresh one. Note that we do not delete the config file here,
0667     // this is done by the session manager when it executes the
0668     // discard commands. In fact it would be harmful to remove the
0669     // file here, as the session might be stored under a different
0670     // name, meaning the user still might need it eventually.
0671     delete d->pSessionConfig;
0672     d->pSessionConfig = 0;
0673 
0674     // tell the session manager about our new lifecycle
0675     QStringList restartCommand = sm.restartCommand();
0676 
0677     QByteArray multiHead = qgetenv("KDE_MULTIHEAD");
0678     if (multiHead.toLower() == "true") {
0679         // if multihead is enabled, we save our -display argument so that
0680         // we are restored onto the correct head... one problem with this
0681         // is that the display is hard coded, which means we cannot restore
0682         // to a different display (ie. if we are in a university lab and try,
0683         // try to restore a multihead session, our apps could be started on
0684         // someone else's display instead of our own)
0685         QByteArray displayname = qgetenv("DISPLAY");
0686         if (! displayname.isNull()) {
0687             // only store the command if we actually have a DISPLAY
0688             // environment variable
0689             restartCommand.append(QLatin1String("-display"));
0690             restartCommand.append(QLatin1String(displayname));
0691         }
0692         sm.setRestartCommand(restartCommand);
0693     }
0694 
0695     checkRestartVersion(sm);
0696 
0697     // finally: do session management
0698     emit saveYourself(); // for compatibility
0699     bool canceled = false;
0700     foreach (KSessionManager *it, KSessionManager::sessionClients()) {
0701         if (canceled) {
0702             break;
0703         }
0704         canceled = !it->saveState(sm);
0705     }
0706 
0707     // if we created a new session config object, register a proper discard command
0708     if (KConfigGui::hasSessionConfig()) {
0709         KConfigGui::sessionConfig()->sync();
0710         QStringList discard;
0711         discard  << QLatin1String("rm") << QStandardPaths::writableLocation(QStandardPaths::GenericConfigLocation) + '/' + KConfigGui::sessionConfigName();
0712         sm.setDiscardCommand(discard);
0713     } else {
0714         sm.setDiscardCommand(QStringList(QLatin1String("")));
0715     }
0716 
0717     if (canceled) {
0718         sm.cancel();
0719     }
0720 #else
0721     Q_UNUSED(sm);
0722 #endif
0723     d->session_save = false;
0724 }
0725 
0726 bool KApplication::sessionSaving() const
0727 {
0728     return d->session_save;
0729 }
0730 
0731 void KApplicationPrivate::parseCommandLine()
0732 {
0733     KCmdLineArgs *args = KCmdLineArgs::parsedArgs("kde");
0734 
0735     if (args && args->isSet("style")) {
0736         extern QString kde_overrideStyle; // see KGlobalSettings
0737         QString reqStyle(args->getOption("style").toLower());
0738         if (QStyleFactory::keys().contains(reqStyle, Qt::CaseInsensitive)) {
0739             kde_overrideStyle = reqStyle;
0740         } else {
0741             qWarning() << i18n("The style '%1' was not found", reqStyle);
0742         }
0743     }
0744 
0745     if (args && args->isSet("config")) {
0746         QString config = args->getOption("config");
0747         componentData.setConfigName(config);
0748     }
0749 
0750     if (args && args->isSet("icon")) {
0751         q->setWindowIcon(QIcon::fromTheme(args->getOption("icon")));
0752     } else {
0753         q->setWindowIcon(QIcon::fromTheme(componentData.aboutData()->programIconName()));
0754     }
0755 
0756     if (!args) {
0757         return;
0758     }
0759 
0760     if (!args->isSet("crashhandler")) {
0761         // disable drkonqi (the old way)
0762         KCrash::setDrKonqiEnabled(false);
0763     }
0764 
0765 #if HAVE_X11
0766     if (args->isSet("waitforwm")) {
0767         Atom type;
0768         (void) q->desktop(); // trigger desktop creation, we need PropertyNotify events for the root window
0769         int format;
0770         unsigned long length, after;
0771         unsigned char *data;
0772         while (XGetWindowProperty(QX11Info::display(), QX11Info::appRootWindow(), atom_NetSupported,
0773                                   0, 1, false, AnyPropertyType, &type, &format,
0774                                   &length, &after, &data) != Success || !length) {
0775             if (data) {
0776                 XFree(data);
0777             }
0778             XEvent event;
0779             XWindowEvent(QX11Info::display(), QX11Info::appRootWindow(), PropertyChangeMask, &event);
0780         }
0781         if (data) {
0782             XFree(data);
0783         }
0784     }
0785 #endif
0786 
0787 #ifndef Q_OS_WIN
0788     if (args->isSet("smkey")) {
0789         sessionKey = args->getOption("smkey");
0790     }
0791 #endif
0792 }
0793 
0794 extern void kDebugCleanup();
0795 
0796 KApplication::~KApplication()
0797 {
0798 #if HAVE_X11
0799     if (d->isX11) {
0800         if (d->oldXErrorHandler != nullptr) {
0801             XSetErrorHandler(d->oldXErrorHandler);
0802         }
0803         if (d->oldXIOErrorHandler != nullptr) {
0804             XSetIOErrorHandler(d->oldXIOErrorHandler);
0805         }
0806         if (d->oldIceIOErrorHandler != nullptr) {
0807             IceSetIOErrorHandler(d->oldIceIOErrorHandler);
0808         }
0809     }
0810 #endif
0811 
0812     delete d;
0813     KApp = nullptr;
0814 
0815 #if HAVE_X11
0816     mySmcConnection = nullptr;
0817 #endif
0818 }
0819 
0820 #ifdef __GNUC__
0821 #warning TODO kapp->installX11EventFilter needs to be ported to nativeEvent filters.
0822 #endif
0823 #if 0 // replaced with QWidget::nativeEvent in Qt5
0824 class KAppX11HackWidget: public QWidget
0825 {
0826 public:
0827     bool publicx11Event(XEvent *e)
0828     {
0829         return x11Event(e);    // no such method anymore!
0830     }
0831 };
0832 bool KApplication::x11EventFilter(XEvent *_event)
0833 {
0834     if (x11Filter) {
0835         foreach (const QPointer< QWidget > &wp, *x11Filter) {
0836             if (QWidget *w = wp.data())
0837                 if (static_cast<KAppX11HackWidget *>(w)->publicx11Event(_event)) {
0838                     return true;
0839                 }
0840         }
0841     }
0842 
0843     return false;
0844 }
0845 #endif
0846 
0847 void KApplication::updateUserTimestamp(int time)
0848 {
0849     KUserTimestamp::updateUserTimestamp(time);
0850 }
0851 
0852 unsigned long KApplication::userTimestamp() const
0853 {
0854     return KUserTimestamp::userTimestamp();
0855 }
0856 
0857 void KApplication::updateRemoteUserTimestamp(const QString &service, int time)
0858 {
0859 #if HAVE_X11
0860     if (!d->isX11) {
0861         return;
0862     }
0863     Q_ASSERT(service.contains('.'));
0864     if (time == 0) {
0865         time = QX11Info::appUserTime();
0866     }
0867     QDBusInterface(service, QLatin1String("/MainApplication"),
0868                    QString(QLatin1String("org.kde.KApplication")))
0869     .call(QLatin1String("updateUserTimestamp"), time);
0870 #endif
0871 }
0872 
0873 #ifndef KDELIBS4SUPPORT_NO_DEPRECATED
0874 QString KApplication::tempSaveName(const QString &pFilename)
0875 {
0876     QString aFilename;
0877 
0878     if (QDir::isRelativePath(pFilename)) {
0879         kWarning(240) << "Relative filename passed to KApplication::tempSaveName";
0880         aFilename = QFileInfo(QDir(QLatin1String(".")), pFilename).absoluteFilePath();
0881     } else {
0882         aFilename = pFilename;
0883     }
0884 
0885     QDir aAutosaveDir(QDir::homePath() + QLatin1String("/autosave/"));
0886     if (!aAutosaveDir.exists()) {
0887         if (!aAutosaveDir.mkdir(aAutosaveDir.absolutePath())) {
0888             // Last chance: use temp dir
0889             aAutosaveDir.setPath(QDir::tempPath());
0890         }
0891     }
0892 
0893     aFilename.replace('/', QLatin1String("\\!"))
0894     .prepend(QLatin1Char('#'))
0895     .append(QLatin1Char('#'))
0896     .prepend(QLatin1Char('/')).prepend(aAutosaveDir.absolutePath());
0897 
0898     return aFilename;
0899 }
0900 #endif
0901 
0902 QString KApplication::checkRecoverFile(const QString &pFilename,
0903                                        bool &bRecover)
0904 {
0905     QString aFilename;
0906 
0907     if (QDir::isRelativePath(pFilename)) {
0908         kWarning(240) << "Relative filename passed to KApplication::tempSaveName";
0909         aFilename = QFileInfo(QDir(QLatin1String(".")), pFilename).absoluteFilePath();
0910     } else {
0911         aFilename = pFilename;
0912     }
0913 
0914     QDir aAutosaveDir(QDir::homePath() + QLatin1String("/autosave/"));
0915     if (!aAutosaveDir.exists()) {
0916         if (!aAutosaveDir.mkdir(aAutosaveDir.absolutePath())) {
0917             // Last chance: use temp dir
0918             aAutosaveDir.setPath(QDir::tempPath());
0919         }
0920     }
0921 
0922     aFilename.replace(QLatin1String("/"), QLatin1String("\\!"))
0923     .prepend(QLatin1Char('#'))
0924     .append(QLatin1Char('#'))
0925     .prepend(QLatin1Char('/'))
0926     .prepend(aAutosaveDir.absolutePath());
0927 
0928     if (QFile(aFilename).exists()) {
0929         bRecover = true;
0930         return aFilename;
0931     } else {
0932         bRecover = false;
0933         return pFilename;
0934     }
0935 }
0936 
0937 void KApplication::setTopWidget(QWidget *topWidget)
0938 {
0939     if (!topWidget) {
0940         return;
0941     }
0942 
0943     // set the specified caption
0944     if (!topWidget->inherits("KMainWindow")) {   // KMainWindow does this already for us
0945         topWidget->setWindowTitle(KGlobal::caption());
0946     }
0947 
0948 #if HAVE_X11
0949     // set the app startup notification window property
0950     KStartupInfo::setWindowStartupId(topWidget->winId(), startupId());
0951 #endif
0952 }
0953 
0954 QByteArray KApplication::startupId() const
0955 {
0956     return KStartupInfo::startupId();
0957 }
0958 
0959 void KApplication::setStartupId(const QByteArray &startup_id)
0960 {
0961     KStartupInfo::setStartupId(startup_id);
0962 }
0963 
0964 void KApplication::clearStartupId()
0965 {
0966     KStartupInfo::setStartupId("0");
0967 }
0968 
0969 // Hook called by KToolInvocation
0970 void KApplicationPrivate::_k_slot_KToolInvocation_hook(QStringList &envs, QByteArray &startup_id)
0971 {
0972 #if HAVE_X11
0973     if (!isX11) {
0974         return;
0975     }
0976     if (QX11Info::display()) {
0977         QByteArray dpystring(XDisplayString(QX11Info::display()));
0978         envs << QLatin1String("DISPLAY=") + dpystring;
0979     } else {
0980         const QByteArray dpystring(qgetenv("DISPLAY"));
0981         if (!dpystring.isEmpty()) {
0982             envs << QLatin1String("DISPLAY=") + dpystring;
0983         }
0984     }
0985 
0986     if (startup_id.isEmpty()) {
0987         startup_id = KStartupInfo::createNewStartupId();
0988     }
0989 #else
0990     Q_UNUSED(envs);
0991     Q_UNUSED(startup_id);
0992 #endif
0993 }
0994 
0995 #include "moc_kapplication.cpp"