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
KDE 4.6 API Reference