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"