• Skip to content
  • Skip to link menu
KDE 4.6 API Reference
  • KDE API Reference
  • kdelibs
  • KDE Home
  • Contact Us
 

KDEUI

kapplication.cpp

Go to the documentation of this file.
00001 /* This file is part of the KDE libraries
00002     Copyright (C) 1997 Matthias Kalle Dalheimer (kalle@kde.org)
00003     Copyright (C) 1998, 1999, 2000 KDE Team
00004 
00005     This library is free software; you can redistribute it and/or
00006     modify it under the terms of the GNU Library General Public
00007     License as published by the Free Software Foundation; either
00008     version 2 of the License, or (at your option) any later version.
00009 
00010     This library is distributed in the hope that it will be useful,
00011     but WITHOUT ANY WARRANTY; without even the implied warranty of
00012     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00013     Library General Public License for more details.
00014 
00015     You should have received a copy of the GNU Library General Public License
00016     along with this library; see the file COPYING.LIB.  If not, write to
00017     the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
00018     Boston, MA 02110-1301, USA.
00019         */
00020 
00021 #include "kapplication.h"
00022 // TODO: KDE5 +#include "kdeversion.h"
00023 
00024 #include <config.h>
00025 
00026 #include <QtCore/QDir>
00027 #include <QtCore/QFile>
00028 #include <QtGui/QSessionManager>
00029 #include <QtGui/QStyleFactory>
00030 #include <QtCore/QTimer>
00031 #include <QtGui/QWidget>
00032 #include <QtCore/QList>
00033 #include <QtDBus/QtDBus>
00034 #include <QtCore/QMetaType>
00035 
00036 #include "kauthorized.h"
00037 #include "kaboutdata.h"
00038 #include "kcheckaccelerators.h"
00039 #include "kcrash.h"
00040 #include "kconfig.h"
00041 #include "kcmdlineargs.h"
00042 #include "kclipboard.h"
00043 #include "kglobalsettings.h"
00044 #include "kdebug.h"
00045 #include "kglobal.h"
00046 #include "kicon.h"
00047 #include "klocale.h"
00048 #include "ksessionmanager.h"
00049 #include "kstandarddirs.h"
00050 #include "kstandardshortcut.h"
00051 #include "ktoolinvocation.h"
00052 #include "kgesturemap.h"
00053 #include "kurl.h"
00054 #include "kmessage.h"
00055 #include "kmessageboxmessagehandler.h"
00056 
00057 #if defined Q_WS_X11
00058 #include <QtGui/qx11info_x11.h>
00059 #include <kstartupinfo.h>
00060 #endif
00061 
00062 #include <sys/types.h>
00063 #ifdef HAVE_SYS_STAT_H
00064 #include <sys/stat.h>
00065 #endif
00066 #include <sys/wait.h>
00067 
00068 #ifndef Q_WS_WIN
00069 #include "kwindowsystem.h"
00070 #endif
00071 
00072 #include <fcntl.h>
00073 #include <stdlib.h> // srand(), rand()
00074 #include <unistd.h>
00075 #if defined Q_WS_X11
00076 //#ifndef Q_WS_QWS //FIXME(embedded): NetWM should talk to QWS...
00077 #include <netwm.h>
00078 #endif
00079 
00080 #ifdef HAVE_PATHS_H
00081 #include <paths.h>
00082 #endif
00083 
00084 #ifdef Q_WS_X11
00085 #include <X11/Xlib.h>
00086 #include <X11/Xutil.h>
00087 #include <X11/Xatom.h>
00088 #include <X11/SM/SMlib.h>
00089 #include <fixx11h.h>
00090 
00091 #include <QX11Info>
00092 #endif
00093 
00094 #ifdef Q_WS_MACX
00095 // ick
00096 #undef Status
00097 #include <Carbon/Carbon.h>
00098 #include <QImage>
00099 #include <ksystemtrayicon.h>
00100 #include <kkernel_mac.h>
00101 #endif
00102 
00103 #ifdef Q_OS_UNIX
00104 #include <signal.h>
00105 #endif
00106 
00107 #include <QtGui/QActionEvent>
00108 #include <kcomponentdata.h>
00109 
00110 KApplication* KApplication::KApp = 0L;
00111 bool KApplication::loadedByKdeinit = false;
00112 
00113 #ifdef Q_WS_X11
00114 static Atom atom_DesktopWindow;
00115 static Atom atom_NetSupported;
00116 static Atom kde_xdnd_drop;
00117 static QByteArray* startup_id_tmp;
00118 #endif
00119 
00120 template class QList<KSessionManager*>;
00121 
00122 #ifdef Q_WS_X11
00123 extern "C" {
00124 static int kde_xio_errhandler( Display * dpy )
00125 {
00126   return kapp->xioErrhandler( dpy );
00127 }
00128 
00129 static int kde_x_errhandler( Display *dpy, XErrorEvent *err )
00130 {
00131   return kapp->xErrhandler( dpy, err );
00132 }
00133 
00134 }
00135 #endif
00136 
00137 #ifdef Q_WS_WIN
00138 void KApplication_init_windows();
00139 #endif
00140 
00141 /*
00142   Private data to make keeping binary compatibility easier
00143  */
00144 class KApplicationPrivate
00145 {
00146 public:
00147   KApplicationPrivate(KApplication* q, const QByteArray &cName)
00148       : q(q)
00149       , componentData(cName)
00150       , startup_id("0")
00151       , app_started_timer(0)
00152       , session_save(false)
00153 #ifdef Q_WS_X11
00154       , oldIceIOErrorHandler(0)
00155       , oldXErrorHandler(0)
00156       , oldXIOErrorHandler(0)
00157 #endif
00158       , pSessionConfig( 0 )
00159       , bSessionManagement( true )
00160   {
00161   }
00162 
00163   KApplicationPrivate(KApplication* q, const KComponentData &cData)
00164       : q(q)
00165       , componentData(cData)
00166       , startup_id("0")
00167       , app_started_timer(0)
00168       , session_save(false)
00169 #ifdef Q_WS_X11
00170       , oldIceIOErrorHandler(0)
00171       , oldXErrorHandler(0)
00172       , oldXIOErrorHandler(0)
00173 #endif
00174       , pSessionConfig( 0 )
00175       , bSessionManagement( true )
00176   {
00177   }
00178 
00179   KApplicationPrivate(KApplication *q)
00180       : q(q)
00181       , componentData(KCmdLineArgs::aboutData())
00182       , startup_id( "0" )
00183       , app_started_timer( 0 )
00184       , session_save( false )
00185 #ifdef Q_WS_X11
00186       , oldIceIOErrorHandler( 0 )
00187       , oldXErrorHandler( 0 )
00188       , oldXIOErrorHandler( 0 )
00189 #endif
00190       , pSessionConfig( 0 )
00191       , bSessionManagement( true )
00192   {
00193   }
00194 
00195   ~KApplicationPrivate()
00196   {
00197   }
00198 
00199 #ifndef KDE3_SUPPORT
00200   KConfig *config() { return KGlobal::config().data(); }
00201 #endif
00202 
00203   void _k_x11FilterDestroyed();
00204   void _k_checkAppStartedSlot();
00205   void _k_slot_KToolInvocation_hook(QStringList&, QByteArray&);
00206 
00207   QString sessionConfigName() const;
00208   void init(bool GUIenabled=true);
00209   void parseCommandLine( ); // Handle KDE arguments (Using KCmdLineArgs)
00210   static void preqapplicationhack();
00211   static void preread_app_startup_id();
00212   void read_app_startup_id();
00213 
00214   KApplication *q;
00215   KComponentData componentData;
00216   QByteArray startup_id;
00217   QTimer* app_started_timer;
00218   bool session_save;
00219 
00220 #ifdef Q_WS_X11
00221   IceIOErrorHandler oldIceIOErrorHandler;
00222   int (*oldXErrorHandler)(Display*,XErrorEvent*);
00223   int (*oldXIOErrorHandler)(Display*);
00224 #endif
00225 
00226   QString sessionKey;
00227   QString pSessionConfigFile;
00228 
00229   KConfig* pSessionConfig; //instance specific application config object
00230   bool bSessionManagement;
00231 };
00232 
00233 
00234 static QList< QWeakPointer< QWidget > > *x11Filter = 0;
00235 
00243 static void installSigpipeHandler()
00244 {
00245 #ifdef Q_OS_UNIX
00246     struct sigaction act;
00247     act.sa_handler = SIG_IGN;
00248     sigemptyset( &act.sa_mask );
00249     act.sa_flags = 0;
00250     sigaction( SIGPIPE, &act, 0 );
00251 #endif
00252 }
00253 
00254 void KApplication::installX11EventFilter( QWidget* filter )
00255 {
00256     if ( !filter )
00257         return;
00258     if (!x11Filter)
00259         x11Filter = new QList< QWeakPointer< QWidget > >;
00260     connect ( filter, SIGNAL( destroyed() ), this, SLOT( _k_x11FilterDestroyed() ) );
00261     x11Filter->append( filter );
00262 }
00263 
00264 void KApplicationPrivate::_k_x11FilterDestroyed()
00265 {
00266     q->removeX11EventFilter( static_cast< const QWidget* >(q->sender()));
00267 }
00268 
00269 void KApplication::removeX11EventFilter( const QWidget* filter )
00270 {
00271     if ( !x11Filter || !filter )
00272         return;
00273     // removeAll doesn't work, creating QWeakPointer to something that's about to be deleted aborts
00274     // x11Filter->removeAll( const_cast< QWidget* >( filter ));
00275     for( QMutableListIterator< QWeakPointer< QWidget > > it( *x11Filter );
00276          it.hasNext();
00277          ) {
00278         QWidget* w = it.next().data();
00279         if( w == filter || w == NULL )
00280             it.remove();
00281     }
00282     if ( x11Filter->isEmpty() ) {
00283         delete x11Filter;
00284         x11Filter = 0;
00285     }
00286 }
00287 
00288 bool KApplication::notify(QObject *receiver, QEvent *event)
00289 {
00290     QEvent::Type t = event->type();
00291     if( t == QEvent::Show && receiver->isWidgetType())
00292     {
00293         QWidget* w = static_cast< QWidget* >( receiver );
00294 #if defined Q_WS_X11
00295         if( w->isTopLevel() && !startupId().isEmpty()) // TODO better done using window group leader?
00296             KStartupInfo::setWindowStartupId( w->winId(), startupId());
00297 #endif
00298         if( w->isTopLevel() && !( w->windowFlags() & Qt::X11BypassWindowManagerHint ) && w->windowType() != Qt::Popup && !event->spontaneous())
00299         {
00300             if( d->app_started_timer == NULL )
00301             {
00302                 d->app_started_timer = new QTimer( this );
00303                 connect( d->app_started_timer, SIGNAL( timeout()), SLOT( _k_checkAppStartedSlot()));
00304             }
00305             if( !d->app_started_timer->isActive()) {
00306                 d->app_started_timer->setSingleShot( true );
00307                 d->app_started_timer->start( 0 );
00308             }
00309         }
00310     }
00311     return QApplication::notify(receiver, event);
00312 }
00313 
00314 void KApplicationPrivate::_k_checkAppStartedSlot()
00315 {
00316 #if defined Q_WS_X11
00317     KStartupInfo::handleAutoAppStartedSending();
00318 #endif
00319 }
00320 
00321 /*
00322   Auxiliary function to calculate a a session config name used for the
00323   instance specific config object.
00324   Syntax:  "session/<appname>_<sessionId>"
00325  */
00326 QString KApplicationPrivate::sessionConfigName() const
00327 {
00328 #ifdef QT_NO_SESSIONMANAGER
00329 #error QT_NO_SESSIONMANAGER was set, this will not compile. Reconfigure Qt with Session management support.
00330 #endif
00331     QString sessKey = q->sessionKey();
00332     if ( sessKey.isEmpty() && !sessionKey.isEmpty() )
00333         sessKey = sessionKey;
00334     return QString(QLatin1String("session/%1_%2_%3")).arg(q->applicationName()).arg(q->sessionId()).arg(sessKey);
00335 }
00336 
00337 #ifdef Q_WS_X11
00338 static SmcConn mySmcConnection = 0;
00339 #else
00340 // FIXME(E): Implement for Qt Embedded
00341 // Possibly "steal" XFree86's libSM?
00342 #endif
00343 
00344 KApplication::KApplication(bool GUIenabled)
00345     : QApplication((KApplicationPrivate::preqapplicationhack(),KCmdLineArgs::qtArgc()), KCmdLineArgs::qtArgv(), GUIenabled),
00346     d(new KApplicationPrivate(this))
00347 {
00348     d->read_app_startup_id();
00349     setApplicationName(d->componentData.componentName());
00350     setOrganizationDomain(d->componentData.aboutData()->organizationDomain());
00351     installSigpipeHandler();
00352     d->init(GUIenabled);
00353 }
00354 
00355 #ifdef Q_WS_X11
00356 KApplication::KApplication(Display *dpy, Qt::HANDLE visual, Qt::HANDLE colormap)
00357     : QApplication((KApplicationPrivate::preqapplicationhack(),dpy), KCmdLineArgs::qtArgc(), KCmdLineArgs::qtArgv(), visual, colormap),
00358     d(new KApplicationPrivate(this))
00359 {
00360     d->read_app_startup_id();
00361     setApplicationName(d->componentData.componentName());
00362     setOrganizationDomain(d->componentData.aboutData()->organizationDomain());
00363     installSigpipeHandler();
00364     d->init();
00365 }
00366 
00367 KApplication::KApplication(Display *dpy, Qt::HANDLE visual, Qt::HANDLE colormap, const KComponentData &cData)
00368     : QApplication((KApplicationPrivate::preqapplicationhack(),dpy), KCmdLineArgs::qtArgc(), KCmdLineArgs::qtArgv(), visual, colormap),
00369     d (new KApplicationPrivate(this, cData))
00370 {
00371     d->read_app_startup_id();
00372     setApplicationName(d->componentData.componentName());
00373     setOrganizationDomain(d->componentData.aboutData()->organizationDomain());
00374     installSigpipeHandler();
00375     d->init();
00376 }
00377 #endif
00378 
00379 KApplication::KApplication(bool GUIenabled, const KComponentData &cData)
00380     : QApplication((KApplicationPrivate::preqapplicationhack(),KCmdLineArgs::qtArgc()), KCmdLineArgs::qtArgv(), GUIenabled),
00381     d (new KApplicationPrivate(this, cData))
00382 {
00383     d->read_app_startup_id();
00384     setApplicationName(d->componentData.componentName());
00385     setOrganizationDomain(d->componentData.aboutData()->organizationDomain());
00386     installSigpipeHandler();
00387     d->init(GUIenabled);
00388 }
00389 
00390 #ifdef Q_WS_X11
00391 KApplication::KApplication(Display *display, int& argc, char** argv, const QByteArray& rAppName,
00392         bool GUIenabled)
00393     : QApplication((KApplicationPrivate::preqapplicationhack(),display)),
00394     d(new KApplicationPrivate(this, rAppName))
00395 {
00396     Q_UNUSED(GUIenabled);
00397     d->read_app_startup_id();
00398     setApplicationName(QLatin1String(rAppName));
00399     installSigpipeHandler();
00400     KCmdLineArgs::initIgnore(argc, argv, rAppName.data());
00401     d->init();
00402 }
00403 #endif
00404 
00405 // this function is called in KApplication ctors while evaluating arguments to QApplication ctor,
00406 // i.e. before QApplication ctor is called
00407 void KApplicationPrivate::preqapplicationhack()
00408 {
00409     preread_app_startup_id();
00410 }
00411 
00412 int KApplication::xioErrhandler( Display* dpy )
00413 {
00414     if(kapp)
00415     {
00416 #ifdef Q_WS_X11
00417         d->oldXIOErrorHandler( dpy );
00418 #else
00419         Q_UNUSED(dpy);
00420 #endif
00421     }
00422     exit( 1 );
00423     return 0;
00424 }
00425 
00426 int KApplication::xErrhandler( Display* dpy, void* err_ )
00427 { // no idea how to make forward decl. for XErrorEvent
00428 #ifdef Q_WS_X11
00429     XErrorEvent* err = static_cast< XErrorEvent* >( err_ );
00430     if(kapp)
00431     {
00432         // add KDE specific stuff here
00433         d->oldXErrorHandler( dpy, err );
00434     }
00435     const QByteArray fatalXError = qgetenv("KDE_FATAL_X_ERROR");
00436     if (!fatalXError.isEmpty()) {
00437         abort();
00438     }
00439 #endif
00440     return 0;
00441 }
00442 
00443 void KApplication::iceIOErrorHandler( _IceConn *conn )
00444 {
00445     emit aboutToQuit();
00446 
00447 #ifdef Q_WS_X11
00448     if ( d->oldIceIOErrorHandler != NULL )
00449       (*d->oldIceIOErrorHandler)( conn );
00450 #endif
00451     exit( 1 );
00452 }
00453 
00454 void KApplicationPrivate::init(bool GUIenabled)
00455 {
00456   if ((getuid() != geteuid()) ||
00457       (getgid() != getegid()))
00458   {
00459      fprintf(stderr, "The KDE libraries are not designed to run with suid privileges.\n");
00460      ::exit(127);
00461   }
00462 
00463 #ifdef Q_WS_MAC
00464   mac_initialize_dbus();
00465 #endif
00466 
00467   KApplication::KApp = q;
00468 
00469   // make sure the clipboard is created before setting the window icon (bug 209263)
00470   if(GUIenabled)
00471     (void) QApplication::clipboard();
00472 
00473   parseCommandLine();
00474 
00475   if(GUIenabled)
00476     (void) KClipboardSynchronizer::self();
00477 
00478   extern KDECORE_EXPORT bool kde_kdebug_enable_dbus_interface;
00479   kde_kdebug_enable_dbus_interface = true;
00480 
00481   QApplication::setDesktopSettingsAware( false );
00482 
00483 #ifdef Q_WS_X11
00484   // create all required atoms in _one_ roundtrip to the X server
00485   if ( q->type() == KApplication::GuiClient ) {
00486       const int max = 20;
00487       Atom* atoms[max];
00488       char* names[max];
00489       Atom atoms_return[max];
00490       int n = 0;
00491 
00492       atoms[n] = &atom_DesktopWindow;
00493       names[n++] = (char *) "KDE_DESKTOP_WINDOW";
00494 
00495       atoms[n] = &atom_NetSupported;
00496       names[n++] = (char *) "_NET_SUPPORTED";
00497 
00498       atoms[n] = &kde_xdnd_drop;
00499       names[n++] = (char *) "XdndDrop";
00500 
00501       XInternAtoms( QX11Info::display(), names, n, false, atoms_return );
00502 
00503       for (int i = 0; i < n; i++ )
00504         *atoms[i] = atoms_return[i];
00505   }
00506 #endif
00507 
00508 
00509   // sanity checking, to make sure we've connected
00510   extern void qDBusBindToApplication();
00511   qDBusBindToApplication();
00512   QDBusConnectionInterface *bus = 0;
00513   if (!QDBusConnection::sessionBus().isConnected() || !(bus = QDBusConnection::sessionBus().interface())) {
00514       kFatal(101) << "Session bus not found" << endl <<
00515                      "To circumvent this problem try the following command (with Linux and bash)" << endl <<
00516                      "export $(dbus-launch)" << endl;
00517       ::exit(125);
00518   }
00519 
00520   extern bool s_kuniqueapplication_startCalled;
00521   if ( bus && !s_kuniqueapplication_startCalled ) // don't register again if KUniqueApplication did so already
00522   {
00523       QStringList parts = q->organizationDomain().split(QLatin1Char('.'), QString::SkipEmptyParts);
00524       QString reversedDomain;
00525       if (parts.isEmpty())
00526           reversedDomain = QLatin1String("local.");
00527       else
00528           foreach (const QString& s, parts)
00529           {
00530               reversedDomain.prepend(QLatin1Char('.'));
00531               reversedDomain.prepend(s);
00532           }
00533       const QString pidSuffix = QString::number( getpid() ).prepend( QLatin1String("-") );
00534       const QString serviceName = reversedDomain + q->applicationName() + pidSuffix;
00535       if ( bus->registerService(serviceName) == QDBusConnectionInterface::ServiceNotRegistered ) {
00536           kError(101) << "Couldn't register name '" << serviceName << "' with DBUS - another process owns it already!" << endl;
00537           ::exit(126);
00538       }
00539   }
00540   QDBusConnection::sessionBus().registerObject(QLatin1String("/MainApplication"), q,
00541                                                QDBusConnection::ExportScriptableSlots |
00542                                                QDBusConnection::ExportScriptableProperties |
00543                                                QDBusConnection::ExportAdaptors);
00544 
00545   // Trigger creation of locale.
00546   (void) KGlobal::locale();
00547 
00548   KSharedConfig::Ptr config = componentData.config();
00549   QByteArray readOnly = qgetenv("KDE_HOME_READONLY");
00550   if (readOnly.isEmpty() && q->applicationName() != QLatin1String("kdialog"))
00551   {
00552     if (KAuthorized::authorize(QLatin1String("warn_unwritable_config")))
00553        config->isConfigWritable(true);
00554   }
00555 
00556   if (q->type() == KApplication::GuiClient)
00557   {
00558 #ifdef Q_WS_X11
00559     // this is important since we fork() to launch the help (Matthias)
00560     fcntl(ConnectionNumber(QX11Info::display()), F_SETFD, FD_CLOEXEC);
00561     // set up the fancy (=robust and error ignoring ) KDE xio error handlers (Matthias)
00562     oldXErrorHandler = XSetErrorHandler( kde_x_errhandler );
00563     oldXIOErrorHandler = XSetIOErrorHandler( kde_xio_errhandler );
00564 #endif
00565 
00566     // Trigger initial settings
00567     KGlobalSettings::self()->activate();
00568 
00569     KMessage::setMessageHandler( new KMessageBoxMessageHandler(0) );
00570 
00571     KCheckAccelerators::initiateIfNeeded(q);
00572     KGestureMap::self()->installEventFilterOnMe( q );
00573 
00574     q->connect(KToolInvocation::self(), SIGNAL(kapplication_hook(QStringList&, QByteArray&)),
00575                q, SLOT(_k_slot_KToolInvocation_hook(QStringList&,QByteArray&)));
00576   }
00577 
00578 #ifdef Q_WS_MAC
00579   if (q->type() == KApplication::GuiClient) {
00580       // This is a QSystemTrayIcon instead of K* because we can't be sure q is a QWidget
00581       QSystemTrayIcon *trayIcon; //krazy:exclude=qclasses
00582       if (QSystemTrayIcon::isSystemTrayAvailable()) //krazy:exclude=qclasses
00583       {
00584           trayIcon = new QSystemTrayIcon(q); //krazy:exclude=qclasses
00585           trayIcon->setIcon(q->windowIcon());
00586           /* it's counter-intuitive, but once you do setIcon it's already set the
00587              dock icon... ->show actually shows an icon in the menu bar too  :P */
00588           // trayIcon->show();
00589       }
00590   }
00591 #endif
00592 
00593   qRegisterMetaType<KUrl>();
00594   qRegisterMetaType<KUrl::List>();
00595 
00596 #ifdef Q_WS_WIN
00597   KApplication_init_windows();
00598 #endif
00599 }
00600 
00601 KApplication* KApplication::kApplication()
00602 {
00603     return KApp;
00604 }
00605 
00606 KConfig* KApplication::sessionConfig()
00607 {
00608     if (!d->pSessionConfig) // create an instance specific config object
00609         d->pSessionConfig = new KConfig( d->sessionConfigName(), KConfig::SimpleConfig );
00610     return d->pSessionConfig;
00611 }
00612 
00613 void KApplication::reparseConfiguration()
00614 {
00615     KGlobal::config()->reparseConfiguration();
00616 }
00617 
00618 void KApplication::quit()
00619 {
00620     QApplication::quit();
00621 }
00622 
00623 void KApplication::disableSessionManagement() {
00624   d->bSessionManagement = false;
00625 }
00626 
00627 void KApplication::enableSessionManagement() {
00628   d->bSessionManagement = true;
00629 #ifdef Q_WS_X11
00630   // Session management support in Qt/KDE is awfully broken.
00631   // If konqueror disables session management right after its startup,
00632   // and enables it later (preloading stuff), it won't be properly
00633   // saved on session shutdown.
00634   // I'm not actually sure why it doesn't work, but saveState()
00635   // doesn't seem to be called on session shutdown, possibly
00636   // because disabling session management after konqueror startup
00637   // disabled it somehow. Forcing saveState() here for this application
00638   // seems to fix it.
00639   if( mySmcConnection ) {
00640         SmcRequestSaveYourself( mySmcConnection, SmSaveLocal, False,
00641                 SmInteractStyleAny,
00642                 False, False );
00643 
00644     // flush the request
00645     IceFlush(SmcGetIceConnection(mySmcConnection));
00646   }
00647 #endif
00648 }
00649 
00650 void KApplication::commitData( QSessionManager& sm )
00651 {
00652     d->session_save = true;
00653     bool canceled = false;
00654 
00655     foreach (KSessionManager *it, KSessionManager::sessionClients()) {
00656         if ( ( canceled = !it->commitData( sm ) ) )
00657             break;
00658     }
00659 
00660     if ( canceled )
00661         sm.cancel();
00662 
00663     if ( sm.allowsInteraction() ) {
00664         QWidgetList donelist, todolist;
00665         QWidget* w;
00666 
00667 commitDataRestart:
00668         todolist = QApplication::topLevelWidgets();
00669 
00670         for ( int i = 0; i < todolist.size(); ++i ) {
00671             w = todolist.at( i );
00672             if( !w )
00673                 break;
00674 
00675             if ( donelist.contains( w ) )
00676                 continue;
00677 
00678             if ( !w->isHidden() && !w->inherits( "KMainWindow" ) ) {
00679                 QCloseEvent e;
00680                 sendEvent( w, &e );
00681                 if ( !e.isAccepted() )
00682                     break; //canceled
00683 
00684                 donelist.append( w );
00685 
00686                 //grab the new list that was just modified by our closeevent
00687                 goto commitDataRestart;
00688             }
00689         }
00690     }
00691 
00692     if ( !d->bSessionManagement )
00693         sm.setRestartHint( QSessionManager::RestartNever );
00694     else
00695         sm.setRestartHint( QSessionManager::RestartIfRunning );
00696     d->session_save = false;
00697 }
00698 
00699 #ifdef Q_WS_X11
00700 static void checkRestartVersion( QSessionManager& sm )
00701 {
00702     Display* dpy = QX11Info::display();
00703     Atom type;
00704     int format;
00705     unsigned long nitems, after;
00706     unsigned char* data;
00707     if( dpy != NULL && XGetWindowProperty( dpy, RootWindow( dpy, 0 ), XInternAtom( dpy, "KDE_SESSION_VERSION", False ),
00708         0, 1, False, AnyPropertyType, &type, &format, &nitems, &after, &data ) == Success ) {
00709         if( type == XA_CARDINAL && format == 32 ) {
00710             int version = *( long* ) data;
00711             if( version == KDE_VERSION_MAJOR ) { // we run in our native session
00712                 XFree( data );
00713                 return; // no need to wrap
00714             }
00715         }
00716         XFree( data );
00717     }
00718     if( getenv( "KDE_SESSION_VERSION" ) != NULL && atoi( getenv( "KDE_SESSION_VERSION" )) == KDE_VERSION_MAJOR )
00719         return; // we run in our native session, no need to wrap
00720 #define NUM_TO_STRING2( num ) #num
00721 #define NUM_TO_STRING( num ) NUM_TO_STRING2( num )
00722     QString wrapper = KStandardDirs::findExe( "kde" NUM_TO_STRING( KDE_VERSION_MAJOR ) ); // "kde4", etc.
00723 #undef NUM_TO_STRING
00724 #undef NUM_TO_STRING2
00725     if( !wrapper.isEmpty()) {
00726         QStringList restartCommand = sm.restartCommand();
00727         restartCommand.prepend( wrapper );
00728         sm.setRestartCommand( restartCommand );
00729     }
00730 }
00731 #endif // Q_WS_X11
00732 
00733 void KApplication::saveState( QSessionManager& sm )
00734 {
00735     d->session_save = true;
00736 #ifdef Q_WS_X11
00737     static bool firstTime = true;
00738     mySmcConnection = (SmcConn) sm.handle();
00739 
00740     if ( !d->bSessionManagement ) {
00741         sm.setRestartHint( QSessionManager::RestartNever );
00742     d->session_save = false;
00743         return;
00744     }
00745     else
00746     sm.setRestartHint( QSessionManager::RestartIfRunning );
00747 
00748     if ( firstTime ) {
00749         firstTime = false;
00750     d->session_save = false;
00751         return; // no need to save the state.
00752     }
00753 
00754     // remove former session config if still existing, we want a new
00755     // and fresh one. Note that we do not delete the config file here,
00756     // this is done by the session manager when it executes the
00757     // discard commands. In fact it would be harmful to remove the
00758     // file here, as the session might be stored under a different
00759     // name, meaning the user still might need it eventually.
00760     delete d->pSessionConfig;
00761     d->pSessionConfig = 0;
00762 
00763     // tell the session manager about our new lifecycle
00764     QStringList restartCommand = sm.restartCommand();
00765 
00766     QByteArray multiHead = qgetenv("KDE_MULTIHEAD");
00767     if (multiHead.toLower() == "true") {
00768         // if multihead is enabled, we save our -display argument so that
00769         // we are restored onto the correct head... one problem with this
00770         // is that the display is hard coded, which means we cannot restore
00771         // to a different display (ie. if we are in a university lab and try,
00772         // try to restore a multihead session, our apps could be started on
00773         // someone else's display instead of our own)
00774         QByteArray displayname = qgetenv("DISPLAY");
00775         if (! displayname.isNull()) {
00776             // only store the command if we actually have a DISPLAY
00777             // environment variable
00778             restartCommand.append(QLatin1String("-display"));
00779             restartCommand.append(QLatin1String(displayname));
00780         }
00781         sm.setRestartCommand( restartCommand );
00782     }
00783 
00784 #ifdef Q_WS_X11
00785     checkRestartVersion( sm );
00786 #endif
00787 
00788     // finally: do session management
00789     emit saveYourself(); // for compatibility
00790     bool canceled = false;
00791     foreach(KSessionManager* it, KSessionManager::sessionClients()) {
00792       if(canceled) break;
00793       canceled = !it->saveState( sm );
00794     }
00795 
00796     // if we created a new session config object, register a proper discard command
00797     if ( d->pSessionConfig ) {
00798         d->pSessionConfig->sync();
00799         QStringList discard;
00800         discard  << QLatin1String("rm") << KStandardDirs::locateLocal("config", d->sessionConfigName());
00801         sm.setDiscardCommand( discard );
00802     } else {
00803     sm.setDiscardCommand( QStringList( QLatin1String("") ) );
00804     }
00805 
00806     if ( canceled )
00807         sm.cancel();
00808 #endif
00809     d->session_save = false;
00810 }
00811 
00812 bool KApplication::sessionSaving() const
00813 {
00814     return d->session_save;
00815 }
00816 
00817 void KApplicationPrivate::parseCommandLine( )
00818 {
00819     KCmdLineArgs *args = KCmdLineArgs::parsedArgs("kde");
00820 
00821     if (args && args->isSet("style"))
00822     {
00823         extern QString kde_overrideStyle; // see KGlobalSettings. Should we have a static setter?
00824         QString reqStyle(args->getOption("style").toLower());
00825         if (QStyleFactory::keys().contains(reqStyle, Qt::CaseInsensitive))
00826             kde_overrideStyle = reqStyle;
00827         else
00828             qWarning() << i18n("The style '%1' was not found", reqStyle);
00829     }
00830 
00831     if ( q->type() != KApplication::Tty ) {
00832         if (args && args->isSet("icon"))
00833         {
00834             q->setWindowIcon(KIcon(args->getOption("icon")));
00835         }
00836         else {
00837             q->setWindowIcon(KIcon(componentData.aboutData()->programIconName()));
00838         }
00839     }
00840 
00841     if (!args)
00842         return;
00843 
00844     if (args->isSet("config"))
00845     {
00846         QString config = args->getOption("config");
00847         componentData.setConfigName(config);
00848     }
00849 
00850     bool nocrashhandler = (!qgetenv("KDE_DEBUG").isEmpty());
00851     if (!nocrashhandler && args->isSet("crashhandler"))
00852     {
00853         // enable drkonqi
00854         KCrash::setDrKonqiEnabled(true);
00855     }
00856     // Always set the app name, can be usefuls for apps that call setEmergencySaveFunction or enable AutoRestart
00857     KCrash::setApplicationName(args->appName());
00858     if (!QCoreApplication::applicationDirPath().isEmpty()) {
00859         KCrash::setApplicationPath(QCoreApplication::applicationDirPath());
00860     }
00861 
00862 #ifdef Q_WS_X11
00863     if ( args->isSet( "waitforwm" ) ) {
00864         Atom type;
00865         (void) q->desktop(); // trigger desktop creation, we need PropertyNotify events for the root window
00866         int format;
00867         unsigned long length, after;
00868         unsigned char *data;
00869         while ( XGetWindowProperty( QX11Info::display(), QX11Info::appRootWindow(), atom_NetSupported,
00870                     0, 1, false, AnyPropertyType, &type, &format,
00871                                     &length, &after, &data ) != Success || !length ) {
00872             if ( data )
00873                 XFree( data );
00874             XEvent event;
00875             XWindowEvent( QX11Info::display(), QX11Info::appRootWindow(), PropertyChangeMask, &event );
00876         }
00877         if ( data )
00878             XFree( data );
00879     }
00880 #endif
00881 
00882 #ifndef Q_WS_WIN
00883     if (args->isSet("smkey"))
00884     {
00885         sessionKey = args->getOption("smkey");
00886     }
00887 #endif
00888 }
00889 
00890 extern void kDebugCleanup();
00891 
00892 KApplication::~KApplication()
00893 {
00894 #ifdef Q_WS_X11
00895   if ( d->oldXErrorHandler != NULL )
00896       XSetErrorHandler( d->oldXErrorHandler );
00897   if ( d->oldXIOErrorHandler != NULL )
00898       XSetIOErrorHandler( d->oldXIOErrorHandler );
00899   if ( d->oldIceIOErrorHandler != NULL )
00900       IceSetIOErrorHandler( d->oldIceIOErrorHandler );
00901 #endif
00902 
00903   delete d;
00904   KApp = 0;
00905 
00906 #ifdef Q_WS_X11
00907   mySmcConnection = 0;
00908 #endif
00909 }
00910 
00911 
00912 #ifdef Q_WS_X11
00913 class KAppX11HackWidget: public QWidget
00914 {
00915 public:
00916     bool publicx11Event( XEvent * e) { return x11Event( e ); }
00917 };
00918 #endif
00919 
00920 
00921 
00922 #ifdef Q_WS_X11
00923 bool KApplication::x11EventFilter( XEvent *_event )
00924 {
00925     switch ( _event->type ) {
00926         case ClientMessage:
00927         {
00928 #if KDE_IS_VERSION( 3, 90, 90 )
00929 #ifdef __GNUC__
00930 #warning This should be already in Qt, check.
00931 #endif
00932 #endif
00933         // Workaround for focus stealing prevention not working when dragging e.g. text from KWrite
00934         // to KDesktop -> the dialog asking for filename doesn't get activated. This is because
00935         // Qt-3.2.x doesn't have concept of qt_x_user_time at all, and Qt-3.3.0b1 passes the timestamp
00936         // in the XdndDrop message in incorrect field (and doesn't update qt_x_user_time either).
00937         // Patch already sent, future Qt version should have this fixed.
00938             if( _event->xclient.message_type == kde_xdnd_drop )
00939                 { // if the message is XdndDrop
00940                 if( _event->xclient.data.l[ 1 ] == 1 << 24     // and it's broken the way it's in Qt-3.2.x
00941                     && _event->xclient.data.l[ 2 ] == 0
00942                     && _event->xclient.data.l[ 4 ] == 0
00943                     && _event->xclient.data.l[ 3 ] != 0 )
00944                     {
00945                     if( QX11Info::appUserTime() == 0
00946                         || NET::timestampCompare( _event->xclient.data.l[ 3 ], QX11Info::appUserTime() ) > 0 )
00947                         { // and the timestamp looks reasonable
00948                         QX11Info::setAppUserTime(_event->xclient.data.l[ 3 ]); // update our qt_x_user_time from it
00949                         }
00950                     }
00951                 else // normal DND, only needed until Qt updates qt_x_user_time from XdndDrop
00952                     {
00953                     if( QX11Info::appUserTime() == 0
00954                         || NET::timestampCompare( _event->xclient.data.l[ 2 ], QX11Info::appUserTime() ) > 0 )
00955                         { // the timestamp looks reasonable
00956                         QX11Info::setAppUserTime(_event->xclient.data.l[ 2 ]); // update our qt_x_user_time from it
00957                         }
00958                     }
00959                 }
00960         }
00961         default: break;
00962     }
00963 
00964     if (x11Filter) {
00965         foreach (const QWeakPointer< QWidget >& wp, *x11Filter) {
00966             if( QWidget* w = wp.data())
00967                 if ( static_cast<KAppX11HackWidget*>( w )->publicx11Event(_event))
00968                     return true;
00969         }
00970     }
00971 
00972     return false;
00973 }
00974 #endif // Q_WS_X11
00975 
00976 void KApplication::updateUserTimestamp( int time )
00977 {
00978 #if defined Q_WS_X11
00979     if( time == 0 )
00980     { // get current X timestamp
00981         Window w = XCreateSimpleWindow( QX11Info::display(), QX11Info::appRootWindow(), 0, 0, 1, 1, 0, 0, 0 );
00982         XSelectInput( QX11Info::display(), w, PropertyChangeMask );
00983         unsigned char data[ 1 ];
00984         XChangeProperty( QX11Info::display(), w, XA_ATOM, XA_ATOM, 8, PropModeAppend, data, 1 );
00985         XEvent ev;
00986         XWindowEvent( QX11Info::display(), w, PropertyChangeMask, &ev );
00987         time = ev.xproperty.time;
00988         XDestroyWindow( QX11Info::display(), w );
00989     }
00990     if( QX11Info::appUserTime() == 0
00991         || NET::timestampCompare( time, QX11Info::appUserTime()) > 0 ) // time > appUserTime
00992         QX11Info::setAppUserTime(time);
00993     if( QX11Info::appTime() == 0
00994         || NET::timestampCompare( time, QX11Info::appTime()) > 0 ) // time > appTime
00995         QX11Info::setAppTime(time);
00996 #endif
00997 }
00998 
00999 unsigned long KApplication::userTimestamp() const
01000 {
01001 #if defined Q_WS_X11
01002     return QX11Info::appUserTime();
01003 #else
01004     return 0;
01005 #endif
01006 }
01007 
01008 void KApplication::updateRemoteUserTimestamp( const QString& service, int time )
01009 {
01010 #if defined Q_WS_X11
01011     Q_ASSERT(service.contains('.'));
01012     if( time == 0 )
01013         time = QX11Info::appUserTime();
01014     QDBusInterface(service, QLatin1String("/MainApplication"),
01015             QString(QLatin1String("org.kde.KApplication")))
01016         .call(QLatin1String("updateUserTimestamp"), time);
01017 #endif
01018 }
01019 
01020 
01021 #ifndef KDE_NO_DEPRECATED
01022 QString KApplication::tempSaveName( const QString& pFilename )
01023 {
01024   QString aFilename;
01025 
01026   if( QDir::isRelativePath(pFilename) )
01027     {
01028       kWarning(101) << "Relative filename passed to KApplication::tempSaveName";
01029       aFilename = QFileInfo( QDir( QLatin1String(".") ), pFilename ).absoluteFilePath();
01030     }
01031   else
01032     aFilename = pFilename;
01033 
01034   QDir aAutosaveDir( QDir::homePath() + QLatin1String("/autosave/") );
01035   if( !aAutosaveDir.exists() )
01036     {
01037       if( !aAutosaveDir.mkdir( aAutosaveDir.absolutePath() ) )
01038         {
01039           // Last chance: use temp dir
01040           aAutosaveDir.setPath( KGlobal::dirs()->saveLocation("tmp") );
01041         }
01042     }
01043 
01044   aFilename.replace( '/', QLatin1String("\\!") )
01045     .prepend( QLatin1Char('#') )
01046     .append( QLatin1Char('#') )
01047     .prepend( QLatin1Char('/') ).prepend( aAutosaveDir.absolutePath() );
01048 
01049   return aFilename;
01050 }
01051 #endif
01052 
01053 
01054 QString KApplication::checkRecoverFile( const QString& pFilename,
01055         bool& bRecover )
01056 {
01057   QString aFilename;
01058 
01059   if( QDir::isRelativePath(pFilename) )
01060     {
01061       kWarning(101) << "Relative filename passed to KApplication::tempSaveName";
01062       aFilename = QFileInfo( QDir( QLatin1String(".") ), pFilename ).absoluteFilePath();
01063     }
01064   else
01065     aFilename = pFilename;
01066 
01067   QDir aAutosaveDir( QDir::homePath() + QLatin1String("/autosave/") );
01068   if( !aAutosaveDir.exists() )
01069     {
01070       if( !aAutosaveDir.mkdir( aAutosaveDir.absolutePath() ) )
01071         {
01072           // Last chance: use temp dir
01073           aAutosaveDir.setPath( KGlobal::dirs()->saveLocation("tmp") );
01074         }
01075     }
01076 
01077   aFilename.replace( QLatin1String("/"), QLatin1String("\\!") )
01078       .prepend( QLatin1Char('#') )
01079       .append( QLatin1Char('#') )
01080       .prepend( QLatin1Char('/') )
01081       .prepend( aAutosaveDir.absolutePath() );
01082 
01083   if( QFile( aFilename ).exists() )
01084     {
01085       bRecover = true;
01086       return aFilename;
01087     }
01088   else
01089     {
01090       bRecover = false;
01091       return pFilename;
01092     }
01093 }
01094 
01095 
01096 void KApplication::setTopWidget( QWidget *topWidget )
01097 {
01098     if( !topWidget )
01099       return;
01100 
01101     // set the specified caption
01102     if ( !topWidget->inherits("KMainWindow") ) { // KMainWindow does this already for us
01103         topWidget->setWindowTitle(KGlobal::caption());
01104     }
01105 
01106 #ifdef Q_WS_X11
01107     // set the app startup notification window property
01108     KStartupInfo::setWindowStartupId(topWidget->winId(), startupId());
01109 #endif
01110 }
01111 
01112 QByteArray KApplication::startupId() const
01113 {
01114     return d->startup_id;
01115 }
01116 
01117 void KApplication::setStartupId( const QByteArray& startup_id )
01118 {
01119     if( startup_id == d->startup_id )
01120         return;
01121 #if defined Q_WS_X11
01122     KStartupInfo::handleAutoAppStartedSending(); // finish old startup notification if needed
01123 #endif
01124     if( startup_id.isEmpty())
01125         d->startup_id = "0";
01126     else
01127         {
01128         d->startup_id = startup_id;
01129 #if defined Q_WS_X11
01130         KStartupInfoId id;
01131         id.initId( startup_id );
01132         long timestamp = id.timestamp();
01133         if( timestamp != 0 )
01134             updateUserTimestamp( timestamp );
01135 #endif
01136         }
01137 }
01138 
01139 void KApplication::clearStartupId()
01140 {
01141     d->startup_id = "0";
01142 }
01143 
01144 // Qt reads and unsets the value and doesn't provide any way to reach the value,
01145 // so steal it from it beforehand. If Qt gets API for taking (reading and unsetting)
01146 // the startup id from it, this can be dumped.
01147 void KApplicationPrivate::preread_app_startup_id()
01148 {
01149 #if defined Q_WS_X11
01150     KStartupInfoId id = KStartupInfo::currentStartupIdEnv();
01151     KStartupInfo::resetStartupEnv();
01152     startup_id_tmp = new QByteArray( id.id());
01153 #endif
01154 }
01155 
01156 // read the startup notification env variable, save it and unset it in order
01157 // not to propagate it to processes started from this app
01158 void KApplicationPrivate::read_app_startup_id()
01159 {
01160 #if defined Q_WS_X11
01161     startup_id = *startup_id_tmp;
01162     delete startup_id_tmp;
01163     startup_id_tmp = NULL;
01164 #endif
01165 }
01166 
01167 // Hook called by KToolInvocation
01168 void KApplicationPrivate::_k_slot_KToolInvocation_hook(QStringList& envs,QByteArray& startup_id)
01169 {
01170 #ifdef Q_WS_X11
01171     if (QX11Info::display()) {
01172         QByteArray dpystring(XDisplayString(QX11Info::display()));
01173         envs << QString::fromLatin1( QByteArray("DISPLAY=") + dpystring );
01174     } else {
01175         const QByteArray dpystring( qgetenv( "DISPLAY" ));
01176         if(!dpystring.isEmpty())
01177             envs << QString::fromLatin1( QByteArray("DISPLAY=") + dpystring );
01178     }
01179 
01180     if(startup_id.isEmpty())
01181         startup_id = KStartupInfo::createNewStartupId();
01182 #else
01183     Q_UNUSED(envs);
01184     Q_UNUSED(startup_id);
01185 #endif
01186 }
01187 
01188 void KApplication::setSynchronizeClipboard(bool synchronize)
01189 {
01190     KClipboardSynchronizer::self()->setSynchronizing(synchronize);
01191     KClipboardSynchronizer::self()->setReverseSynchronizing(synchronize);
01192 }
01193 
01194 #include "kapplication.moc"
01195 

KDEUI

Skip menu "KDEUI"
  • Main Page
  • Modules
  • Namespace List
  • Class Hierarchy
  • Alphabetical List
  • Class List
  • File List
  • Namespace Members
  • Class Members
  • Related Pages

kdelibs

Skip menu "kdelibs"
  • DNSSD
  • Interfaces
  •   KHexEdit
  •   KMediaPlayer
  •   KSpeech
  •   KTextEditor
  • Kate
  • kconf_update
  • KDE3Support
  •   KUnitTest
  • KDECore
  • KDED
  • KDEsu
  • KDEUI
  • KDEWebKit
  • KDocTools
  • KFile
  • KHTML
  • KImgIO
  • KInit
  • kio
  • KIOSlave
  • KJS
  •   KJS-API
  •   WTF
  • kjsembed
  • KNewStuff
  • KParts
  • KPty
  • Kross
  • KUnitConversion
  • KUtils
  • Nepomuk
  • Plasma
  • Solid
  • Sonnet
  • ThreadWeaver
Generated for kdelibs by doxygen 1.7.3
This website is maintained by Adriaan de Groot and Allen Winter.
KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal