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 KGlobal::config(); // initialize qt plugin path (see KComponentDataPrivate::lazyInit) 00412 } 00413 00414 int KApplication::xioErrhandler( Display* dpy ) 00415 { 00416 if(kapp) 00417 { 00418 #ifdef Q_WS_X11 00419 d->oldXIOErrorHandler( dpy ); 00420 #else 00421 Q_UNUSED(dpy); 00422 #endif 00423 } 00424 exit( 1 ); 00425 return 0; 00426 } 00427 00428 int KApplication::xErrhandler( Display* dpy, void* err_ ) 00429 { // no idea how to make forward decl. for XErrorEvent 00430 #ifdef Q_WS_X11 00431 XErrorEvent* err = static_cast< XErrorEvent* >( err_ ); 00432 if(kapp) 00433 { 00434 // add KDE specific stuff here 00435 d->oldXErrorHandler( dpy, err ); 00436 } 00437 const QByteArray fatalXError = qgetenv("KDE_FATAL_X_ERROR"); 00438 if (!fatalXError.isEmpty()) { 00439 abort(); 00440 } 00441 #endif 00442 return 0; 00443 } 00444 00445 void KApplication::iceIOErrorHandler( _IceConn *conn ) 00446 { 00447 emit aboutToQuit(); 00448 00449 #ifdef Q_WS_X11 00450 if ( d->oldIceIOErrorHandler != NULL ) 00451 (*d->oldIceIOErrorHandler)( conn ); 00452 #endif 00453 exit( 1 ); 00454 } 00455 00456 void KApplicationPrivate::init(bool GUIenabled) 00457 { 00458 if ((getuid() != geteuid()) || 00459 (getgid() != getegid())) 00460 { 00461 fprintf(stderr, "The KDE libraries are not designed to run with suid privileges.\n"); 00462 ::exit(127); 00463 } 00464 00465 #ifdef Q_WS_MAC 00466 mac_initialize_dbus(); 00467 #endif 00468 00469 KApplication::KApp = q; 00470 00471 // make sure the clipboard is created before setting the window icon (bug 209263) 00472 if(GUIenabled) 00473 (void) QApplication::clipboard(); 00474 00475 extern KDECORE_EXPORT bool kde_kdebug_enable_dbus_interface; 00476 kde_kdebug_enable_dbus_interface = true; 00477 00478 parseCommandLine(); 00479 00480 if(GUIenabled) 00481 (void) KClipboardSynchronizer::self(); 00482 00483 QApplication::setDesktopSettingsAware( false ); 00484 00485 #ifdef Q_WS_X11 00486 // create all required atoms in _one_ roundtrip to the X server 00487 if ( q->type() == KApplication::GuiClient ) { 00488 const int max = 20; 00489 Atom* atoms[max]; 00490 char* names[max]; 00491 Atom atoms_return[max]; 00492 int n = 0; 00493 00494 atoms[n] = &atom_DesktopWindow; 00495 names[n++] = (char *) "KDE_DESKTOP_WINDOW"; 00496 00497 atoms[n] = &atom_NetSupported; 00498 names[n++] = (char *) "_NET_SUPPORTED"; 00499 00500 atoms[n] = &kde_xdnd_drop; 00501 names[n++] = (char *) "XdndDrop"; 00502 00503 XInternAtoms( QX11Info::display(), names, n, false, atoms_return ); 00504 00505 for (int i = 0; i < n; i++ ) 00506 *atoms[i] = atoms_return[i]; 00507 } 00508 #endif 00509 00510 00511 // sanity checking, to make sure we've connected 00512 extern void qDBusBindToApplication(); 00513 qDBusBindToApplication(); 00514 QDBusConnectionInterface *bus = 0; 00515 if (!QDBusConnection::sessionBus().isConnected() || !(bus = QDBusConnection::sessionBus().interface())) { 00516 kFatal(240) << "Session bus not found" << endl << 00517 "To circumvent this problem try the following command (with Linux and bash)" << endl << 00518 "export $(dbus-launch)"; 00519 ::exit(125); 00520 } 00521 00522 extern bool s_kuniqueapplication_startCalled; 00523 if ( bus && !s_kuniqueapplication_startCalled ) // don't register again if KUniqueApplication did so already 00524 { 00525 QStringList parts = q->organizationDomain().split(QLatin1Char('.'), QString::SkipEmptyParts); 00526 QString reversedDomain; 00527 if (parts.isEmpty()) 00528 reversedDomain = QLatin1String("local."); 00529 else 00530 foreach (const QString& s, parts) 00531 { 00532 reversedDomain.prepend(QLatin1Char('.')); 00533 reversedDomain.prepend(s); 00534 } 00535 const QString pidSuffix = QString::number( getpid() ).prepend( QLatin1String("-") ); 00536 const QString serviceName = reversedDomain + q->applicationName() + pidSuffix; 00537 if ( bus->registerService(serviceName) == QDBusConnectionInterface::ServiceNotRegistered ) { 00538 kError(240) << "Couldn't register name '" << serviceName << "' with DBUS - another process owns it already!" << endl; 00539 ::exit(126); 00540 } 00541 } 00542 QDBusConnection::sessionBus().registerObject(QLatin1String("/MainApplication"), q, 00543 QDBusConnection::ExportScriptableSlots | 00544 QDBusConnection::ExportScriptableProperties | 00545 QDBusConnection::ExportAdaptors); 00546 00547 // Trigger creation of locale. 00548 (void) KGlobal::locale(); 00549 00550 KSharedConfig::Ptr config = componentData.config(); 00551 QByteArray readOnly = qgetenv("KDE_HOME_READONLY"); 00552 if (readOnly.isEmpty() && q->applicationName() != QLatin1String("kdialog")) 00553 { 00554 if (KAuthorized::authorize(QLatin1String("warn_unwritable_config"))) 00555 config->isConfigWritable(true); 00556 } 00557 00558 if (q->type() == KApplication::GuiClient) 00559 { 00560 #ifdef Q_WS_X11 00561 // this is important since we fork() to launch the help (Matthias) 00562 fcntl(ConnectionNumber(QX11Info::display()), F_SETFD, FD_CLOEXEC); 00563 // set up the fancy (=robust and error ignoring ) KDE xio error handlers (Matthias) 00564 oldXErrorHandler = XSetErrorHandler( kde_x_errhandler ); 00565 oldXIOErrorHandler = XSetIOErrorHandler( kde_xio_errhandler ); 00566 #endif 00567 00568 // Trigger initial settings 00569 KGlobalSettings::self()->activate(); 00570 00571 KMessage::setMessageHandler( new KMessageBoxMessageHandler(0) ); 00572 00573 KCheckAccelerators::initiateIfNeeded(q); 00574 KGestureMap::self()->installEventFilterOnMe( q ); 00575 00576 q->connect(KToolInvocation::self(), SIGNAL(kapplication_hook(QStringList&, QByteArray&)), 00577 q, SLOT(_k_slot_KToolInvocation_hook(QStringList&,QByteArray&))); 00578 } 00579 00580 #ifdef Q_WS_MAC 00581 if (q->type() == KApplication::GuiClient) { 00582 // This is a QSystemTrayIcon instead of K* because we can't be sure q is a QWidget 00583 QSystemTrayIcon *trayIcon; //krazy:exclude=qclasses 00584 if (QSystemTrayIcon::isSystemTrayAvailable()) //krazy:exclude=qclasses 00585 { 00586 trayIcon = new QSystemTrayIcon(q); //krazy:exclude=qclasses 00587 trayIcon->setIcon(q->windowIcon()); 00588 /* it's counter-intuitive, but once you do setIcon it's already set the 00589 dock icon... ->show actually shows an icon in the menu bar too :P */ 00590 // trayIcon->show(); 00591 } 00592 } 00593 #endif 00594 00595 qRegisterMetaType<KUrl>(); 00596 qRegisterMetaType<KUrl::List>(); 00597 00598 #ifdef Q_WS_WIN 00599 KApplication_init_windows(); 00600 #endif 00601 } 00602 00603 KApplication* KApplication::kApplication() 00604 { 00605 return KApp; 00606 } 00607 00608 KConfig* KApplication::sessionConfig() 00609 { 00610 if (!d->pSessionConfig) // create an instance specific config object 00611 d->pSessionConfig = new KConfig( d->sessionConfigName(), KConfig::SimpleConfig ); 00612 return d->pSessionConfig; 00613 } 00614 00615 void KApplication::reparseConfiguration() 00616 { 00617 KGlobal::config()->reparseConfiguration(); 00618 } 00619 00620 void KApplication::quit() 00621 { 00622 QApplication::quit(); 00623 } 00624 00625 void KApplication::disableSessionManagement() { 00626 d->bSessionManagement = false; 00627 } 00628 00629 void KApplication::enableSessionManagement() { 00630 d->bSessionManagement = true; 00631 #ifdef Q_WS_X11 00632 // Session management support in Qt/KDE is awfully broken. 00633 // If konqueror disables session management right after its startup, 00634 // and enables it later (preloading stuff), it won't be properly 00635 // saved on session shutdown. 00636 // I'm not actually sure why it doesn't work, but saveState() 00637 // doesn't seem to be called on session shutdown, possibly 00638 // because disabling session management after konqueror startup 00639 // disabled it somehow. Forcing saveState() here for this application 00640 // seems to fix it. 00641 if( mySmcConnection ) { 00642 SmcRequestSaveYourself( mySmcConnection, SmSaveLocal, False, 00643 SmInteractStyleAny, 00644 False, False ); 00645 00646 // flush the request 00647 IceFlush(SmcGetIceConnection(mySmcConnection)); 00648 } 00649 #endif 00650 } 00651 00652 void KApplication::commitData( QSessionManager& sm ) 00653 { 00654 d->session_save = true; 00655 bool canceled = false; 00656 00657 foreach (KSessionManager *it, KSessionManager::sessionClients()) { 00658 if ( ( canceled = !it->commitData( sm ) ) ) 00659 break; 00660 } 00661 00662 if ( canceled ) 00663 sm.cancel(); 00664 00665 if ( sm.allowsInteraction() ) { 00666 QWidgetList donelist, todolist; 00667 QWidget* w; 00668 00669 commitDataRestart: 00670 todolist = QApplication::topLevelWidgets(); 00671 00672 for ( int i = 0; i < todolist.size(); ++i ) { 00673 w = todolist.at( i ); 00674 if( !w ) 00675 break; 00676 00677 if ( donelist.contains( w ) ) 00678 continue; 00679 00680 if ( !w->isHidden() && !w->inherits( "KMainWindow" ) ) { 00681 QCloseEvent e; 00682 sendEvent( w, &e ); 00683 if ( !e.isAccepted() ) 00684 break; //canceled 00685 00686 donelist.append( w ); 00687 00688 //grab the new list that was just modified by our closeevent 00689 goto commitDataRestart; 00690 } 00691 } 00692 } 00693 00694 if ( !d->bSessionManagement ) 00695 sm.setRestartHint( QSessionManager::RestartNever ); 00696 else 00697 sm.setRestartHint( QSessionManager::RestartIfRunning ); 00698 d->session_save = false; 00699 } 00700 00701 #ifdef Q_WS_X11 00702 static void checkRestartVersion( QSessionManager& sm ) 00703 { 00704 Display* dpy = QX11Info::display(); 00705 Atom type; 00706 int format; 00707 unsigned long nitems, after; 00708 unsigned char* data; 00709 if( dpy != NULL && XGetWindowProperty( dpy, RootWindow( dpy, 0 ), XInternAtom( dpy, "KDE_SESSION_VERSION", False ), 00710 0, 1, False, AnyPropertyType, &type, &format, &nitems, &after, &data ) == Success ) { 00711 if( type == XA_CARDINAL && format == 32 ) { 00712 int version = *( long* ) data; 00713 if( version == KDE_VERSION_MAJOR ) { // we run in our native session 00714 XFree( data ); 00715 return; // no need to wrap 00716 } 00717 } 00718 XFree( data ); 00719 } 00720 if( getenv( "KDE_SESSION_VERSION" ) != NULL && atoi( getenv( "KDE_SESSION_VERSION" )) == KDE_VERSION_MAJOR ) 00721 return; // we run in our native session, no need to wrap 00722 #define NUM_TO_STRING2( num ) #num 00723 #define NUM_TO_STRING( num ) NUM_TO_STRING2( num ) 00724 QString wrapper = KStandardDirs::findExe( "kde" NUM_TO_STRING( KDE_VERSION_MAJOR ) ); // "kde4", etc. 00725 #undef NUM_TO_STRING 00726 #undef NUM_TO_STRING2 00727 if( !wrapper.isEmpty()) { 00728 QStringList restartCommand = sm.restartCommand(); 00729 restartCommand.prepend( wrapper ); 00730 sm.setRestartCommand( restartCommand ); 00731 } 00732 } 00733 #endif // Q_WS_X11 00734 00735 void KApplication::saveState( QSessionManager& sm ) 00736 { 00737 d->session_save = true; 00738 #ifdef Q_WS_X11 00739 static bool firstTime = true; 00740 mySmcConnection = (SmcConn) sm.handle(); 00741 00742 if ( !d->bSessionManagement ) { 00743 sm.setRestartHint( QSessionManager::RestartNever ); 00744 d->session_save = false; 00745 return; 00746 } 00747 else 00748 sm.setRestartHint( QSessionManager::RestartIfRunning ); 00749 00750 if ( firstTime ) { 00751 firstTime = false; 00752 d->session_save = false; 00753 return; // no need to save the state. 00754 } 00755 00756 // remove former session config if still existing, we want a new 00757 // and fresh one. Note that we do not delete the config file here, 00758 // this is done by the session manager when it executes the 00759 // discard commands. In fact it would be harmful to remove the 00760 // file here, as the session might be stored under a different 00761 // name, meaning the user still might need it eventually. 00762 delete d->pSessionConfig; 00763 d->pSessionConfig = 0; 00764 00765 // tell the session manager about our new lifecycle 00766 QStringList restartCommand = sm.restartCommand(); 00767 00768 QByteArray multiHead = qgetenv("KDE_MULTIHEAD"); 00769 if (multiHead.toLower() == "true") { 00770 // if multihead is enabled, we save our -display argument so that 00771 // we are restored onto the correct head... one problem with this 00772 // is that the display is hard coded, which means we cannot restore 00773 // to a different display (ie. if we are in a university lab and try, 00774 // try to restore a multihead session, our apps could be started on 00775 // someone else's display instead of our own) 00776 QByteArray displayname = qgetenv("DISPLAY"); 00777 if (! displayname.isNull()) { 00778 // only store the command if we actually have a DISPLAY 00779 // environment variable 00780 restartCommand.append(QLatin1String("-display")); 00781 restartCommand.append(QLatin1String(displayname)); 00782 } 00783 sm.setRestartCommand( restartCommand ); 00784 } 00785 00786 #ifdef Q_WS_X11 00787 checkRestartVersion( sm ); 00788 #endif 00789 00790 // finally: do session management 00791 emit saveYourself(); // for compatibility 00792 bool canceled = false; 00793 foreach(KSessionManager* it, KSessionManager::sessionClients()) { 00794 if(canceled) break; 00795 canceled = !it->saveState( sm ); 00796 } 00797 00798 // if we created a new session config object, register a proper discard command 00799 if ( d->pSessionConfig ) { 00800 d->pSessionConfig->sync(); 00801 QStringList discard; 00802 discard << QLatin1String("rm") << KStandardDirs::locateLocal("config", d->sessionConfigName()); 00803 sm.setDiscardCommand( discard ); 00804 } else { 00805 sm.setDiscardCommand( QStringList( QLatin1String("") ) ); 00806 } 00807 00808 if ( canceled ) 00809 sm.cancel(); 00810 #endif 00811 d->session_save = false; 00812 } 00813 00814 bool KApplication::sessionSaving() const 00815 { 00816 return d->session_save; 00817 } 00818 00819 void KApplicationPrivate::parseCommandLine( ) 00820 { 00821 KCmdLineArgs *args = KCmdLineArgs::parsedArgs("kde"); 00822 00823 if (args && args->isSet("style")) 00824 { 00825 extern QString kde_overrideStyle; // see KGlobalSettings. Should we have a static setter? 00826 QString reqStyle(args->getOption("style").toLower()); 00827 if (QStyleFactory::keys().contains(reqStyle, Qt::CaseInsensitive)) 00828 kde_overrideStyle = reqStyle; 00829 else 00830 qWarning() << i18n("The style '%1' was not found", reqStyle); 00831 } 00832 00833 if ( q->type() != KApplication::Tty ) { 00834 if (args && args->isSet("icon")) 00835 { 00836 q->setWindowIcon(KIcon(args->getOption("icon"))); 00837 } 00838 else { 00839 q->setWindowIcon(KIcon(componentData.aboutData()->programIconName())); 00840 } 00841 } 00842 00843 if (!args) 00844 return; 00845 00846 if (args->isSet("config")) 00847 { 00848 QString config = args->getOption("config"); 00849 componentData.setConfigName(config); 00850 } 00851 00852 bool nocrashhandler = (!qgetenv("KDE_DEBUG").isEmpty()); 00853 if (!nocrashhandler && args->isSet("crashhandler")) 00854 { 00855 // enable drkonqi 00856 KCrash::setDrKonqiEnabled(true); 00857 } 00858 // Always set the app name, can be usefuls for apps that call setEmergencySaveFunction or enable AutoRestart 00859 KCrash::setApplicationName(args->appName()); 00860 if (!QCoreApplication::applicationDirPath().isEmpty()) { 00861 KCrash::setApplicationPath(QCoreApplication::applicationDirPath()); 00862 } 00863 00864 #ifdef Q_WS_X11 00865 if ( args->isSet( "waitforwm" ) ) { 00866 Atom type; 00867 (void) q->desktop(); // trigger desktop creation, we need PropertyNotify events for the root window 00868 int format; 00869 unsigned long length, after; 00870 unsigned char *data; 00871 while ( XGetWindowProperty( QX11Info::display(), QX11Info::appRootWindow(), atom_NetSupported, 00872 0, 1, false, AnyPropertyType, &type, &format, 00873 &length, &after, &data ) != Success || !length ) { 00874 if ( data ) 00875 XFree( data ); 00876 XEvent event; 00877 XWindowEvent( QX11Info::display(), QX11Info::appRootWindow(), PropertyChangeMask, &event ); 00878 } 00879 if ( data ) 00880 XFree( data ); 00881 } 00882 #endif 00883 00884 #ifndef Q_WS_WIN 00885 if (args->isSet("smkey")) 00886 { 00887 sessionKey = args->getOption("smkey"); 00888 } 00889 #endif 00890 } 00891 00892 extern void kDebugCleanup(); 00893 00894 KApplication::~KApplication() 00895 { 00896 #ifdef Q_WS_X11 00897 if ( d->oldXErrorHandler != NULL ) 00898 XSetErrorHandler( d->oldXErrorHandler ); 00899 if ( d->oldXIOErrorHandler != NULL ) 00900 XSetIOErrorHandler( d->oldXIOErrorHandler ); 00901 if ( d->oldIceIOErrorHandler != NULL ) 00902 IceSetIOErrorHandler( d->oldIceIOErrorHandler ); 00903 #endif 00904 00905 delete d; 00906 KApp = 0; 00907 00908 #ifdef Q_WS_X11 00909 mySmcConnection = 0; 00910 #endif 00911 } 00912 00913 00914 #ifdef Q_WS_X11 00915 class KAppX11HackWidget: public QWidget 00916 { 00917 public: 00918 bool publicx11Event( XEvent * e) { return x11Event( e ); } 00919 }; 00920 #endif 00921 00922 00923 00924 #ifdef Q_WS_X11 00925 bool KApplication::x11EventFilter( XEvent *_event ) 00926 { 00927 if (x11Filter) { 00928 foreach (const QWeakPointer< QWidget >& wp, *x11Filter) { 00929 if( QWidget* w = wp.data()) 00930 if ( static_cast<KAppX11HackWidget*>( w )->publicx11Event(_event)) 00931 return true; 00932 } 00933 } 00934 00935 return false; 00936 } 00937 #endif // Q_WS_X11 00938 00939 void KApplication::updateUserTimestamp( int time ) 00940 { 00941 #if defined Q_WS_X11 00942 if( time == 0 ) 00943 { // get current X timestamp 00944 Window w = XCreateSimpleWindow( QX11Info::display(), QX11Info::appRootWindow(), 0, 0, 1, 1, 0, 0, 0 ); 00945 XSelectInput( QX11Info::display(), w, PropertyChangeMask ); 00946 unsigned char data[ 1 ]; 00947 XChangeProperty( QX11Info::display(), w, XA_ATOM, XA_ATOM, 8, PropModeAppend, data, 1 ); 00948 XEvent ev; 00949 XWindowEvent( QX11Info::display(), w, PropertyChangeMask, &ev ); 00950 time = ev.xproperty.time; 00951 XDestroyWindow( QX11Info::display(), w ); 00952 } 00953 if( QX11Info::appUserTime() == 0 00954 || NET::timestampCompare( time, QX11Info::appUserTime()) > 0 ) // time > appUserTime 00955 QX11Info::setAppUserTime(time); 00956 if( QX11Info::appTime() == 0 00957 || NET::timestampCompare( time, QX11Info::appTime()) > 0 ) // time > appTime 00958 QX11Info::setAppTime(time); 00959 #endif 00960 } 00961 00962 unsigned long KApplication::userTimestamp() const 00963 { 00964 #if defined Q_WS_X11 00965 return QX11Info::appUserTime(); 00966 #else 00967 return 0; 00968 #endif 00969 } 00970 00971 void KApplication::updateRemoteUserTimestamp( const QString& service, int time ) 00972 { 00973 #if defined Q_WS_X11 00974 Q_ASSERT(service.contains('.')); 00975 if( time == 0 ) 00976 time = QX11Info::appUserTime(); 00977 QDBusInterface(service, QLatin1String("/MainApplication"), 00978 QString(QLatin1String("org.kde.KApplication"))) 00979 .call(QLatin1String("updateUserTimestamp"), time); 00980 #endif 00981 } 00982 00983 00984 #ifndef KDE_NO_DEPRECATED 00985 QString KApplication::tempSaveName( const QString& pFilename ) 00986 { 00987 QString aFilename; 00988 00989 if( QDir::isRelativePath(pFilename) ) 00990 { 00991 kWarning(240) << "Relative filename passed to KApplication::tempSaveName"; 00992 aFilename = QFileInfo( QDir( QLatin1String(".") ), pFilename ).absoluteFilePath(); 00993 } 00994 else 00995 aFilename = pFilename; 00996 00997 QDir aAutosaveDir( QDir::homePath() + QLatin1String("/autosave/") ); 00998 if( !aAutosaveDir.exists() ) 00999 { 01000 if( !aAutosaveDir.mkdir( aAutosaveDir.absolutePath() ) ) 01001 { 01002 // Last chance: use temp dir 01003 aAutosaveDir.setPath( KGlobal::dirs()->saveLocation("tmp") ); 01004 } 01005 } 01006 01007 aFilename.replace( '/', QLatin1String("\\!") ) 01008 .prepend( QLatin1Char('#') ) 01009 .append( QLatin1Char('#') ) 01010 .prepend( QLatin1Char('/') ).prepend( aAutosaveDir.absolutePath() ); 01011 01012 return aFilename; 01013 } 01014 #endif 01015 01016 01017 QString KApplication::checkRecoverFile( const QString& pFilename, 01018 bool& bRecover ) 01019 { 01020 QString aFilename; 01021 01022 if( QDir::isRelativePath(pFilename) ) 01023 { 01024 kWarning(240) << "Relative filename passed to KApplication::tempSaveName"; 01025 aFilename = QFileInfo( QDir( QLatin1String(".") ), pFilename ).absoluteFilePath(); 01026 } 01027 else 01028 aFilename = pFilename; 01029 01030 QDir aAutosaveDir( QDir::homePath() + QLatin1String("/autosave/") ); 01031 if( !aAutosaveDir.exists() ) 01032 { 01033 if( !aAutosaveDir.mkdir( aAutosaveDir.absolutePath() ) ) 01034 { 01035 // Last chance: use temp dir 01036 aAutosaveDir.setPath( KGlobal::dirs()->saveLocation("tmp") ); 01037 } 01038 } 01039 01040 aFilename.replace( QLatin1String("/"), QLatin1String("\\!") ) 01041 .prepend( QLatin1Char('#') ) 01042 .append( QLatin1Char('#') ) 01043 .prepend( QLatin1Char('/') ) 01044 .prepend( aAutosaveDir.absolutePath() ); 01045 01046 if( QFile( aFilename ).exists() ) 01047 { 01048 bRecover = true; 01049 return aFilename; 01050 } 01051 else 01052 { 01053 bRecover = false; 01054 return pFilename; 01055 } 01056 } 01057 01058 01059 void KApplication::setTopWidget( QWidget *topWidget ) 01060 { 01061 if( !topWidget ) 01062 return; 01063 01064 // set the specified caption 01065 if ( !topWidget->inherits("KMainWindow") ) { // KMainWindow does this already for us 01066 topWidget->setWindowTitle(KGlobal::caption()); 01067 } 01068 01069 #ifdef Q_WS_X11 01070 // set the app startup notification window property 01071 KStartupInfo::setWindowStartupId(topWidget->winId(), startupId()); 01072 #endif 01073 } 01074 01075 QByteArray KApplication::startupId() const 01076 { 01077 return d->startup_id; 01078 } 01079 01080 void KApplication::setStartupId( const QByteArray& startup_id ) 01081 { 01082 if( startup_id == d->startup_id ) 01083 return; 01084 #if defined Q_WS_X11 01085 KStartupInfo::handleAutoAppStartedSending(); // finish old startup notification if needed 01086 #endif 01087 if( startup_id.isEmpty()) 01088 d->startup_id = "0"; 01089 else 01090 { 01091 d->startup_id = startup_id; 01092 #if defined Q_WS_X11 01093 KStartupInfoId id; 01094 id.initId( startup_id ); 01095 long timestamp = id.timestamp(); 01096 if( timestamp != 0 ) 01097 updateUserTimestamp( timestamp ); 01098 #endif 01099 } 01100 } 01101 01102 void KApplication::clearStartupId() 01103 { 01104 d->startup_id = "0"; 01105 } 01106 01107 // Qt reads and unsets the value and doesn't provide any way to reach the value, 01108 // so steal it from it beforehand. If Qt gets API for taking (reading and unsetting) 01109 // the startup id from it, this can be dumped. 01110 void KApplicationPrivate::preread_app_startup_id() 01111 { 01112 #if defined Q_WS_X11 01113 KStartupInfoId id = KStartupInfo::currentStartupIdEnv(); 01114 KStartupInfo::resetStartupEnv(); 01115 startup_id_tmp = new QByteArray( id.id()); 01116 #endif 01117 } 01118 01119 // read the startup notification env variable, save it and unset it in order 01120 // not to propagate it to processes started from this app 01121 void KApplicationPrivate::read_app_startup_id() 01122 { 01123 #if defined Q_WS_X11 01124 startup_id = *startup_id_tmp; 01125 delete startup_id_tmp; 01126 startup_id_tmp = NULL; 01127 #endif 01128 } 01129 01130 // Hook called by KToolInvocation 01131 void KApplicationPrivate::_k_slot_KToolInvocation_hook(QStringList& envs,QByteArray& startup_id) 01132 { 01133 #ifdef Q_WS_X11 01134 if (QX11Info::display()) { 01135 QByteArray dpystring(XDisplayString(QX11Info::display())); 01136 envs << QLatin1String("DISPLAY=") + dpystring; 01137 } else { 01138 const QByteArray dpystring( qgetenv( "DISPLAY" )); 01139 if(!dpystring.isEmpty()) 01140 envs << QLatin1String("DISPLAY=") + dpystring; 01141 } 01142 01143 if(startup_id.isEmpty()) 01144 startup_id = KStartupInfo::createNewStartupId(); 01145 #else 01146 Q_UNUSED(envs); 01147 Q_UNUSED(startup_id); 01148 #endif 01149 } 01150 01151 void KApplication::setSynchronizeClipboard(bool synchronize) 01152 { 01153 KClipboardSynchronizer::self()->setSynchronizing(synchronize); 01154 KClipboardSynchronizer::self()->setReverseSynchronizing(synchronize); 01155 } 01156 01157 #include "kapplication.moc" 01158
KDE 4.7 API Reference