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 parseCommandLine(); 00476 00477 if(GUIenabled) 00478 (void) KClipboardSynchronizer::self(); 00479 00480 extern KDECORE_EXPORT bool kde_kdebug_enable_dbus_interface; 00481 kde_kdebug_enable_dbus_interface = true; 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(101) << "Session bus not found" << endl << 00517 "To circumvent this problem try the following command (with Linux and bash)" << endl << 00518 "export $(dbus-launch)" << endl; 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(101) << "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 switch ( _event->type ) { 00928 case ClientMessage: 00929 { 00930 #if KDE_IS_VERSION( 3, 90, 90 ) 00931 #ifdef __GNUC__ 00932 #warning This should be already in Qt, check. 00933 #endif 00934 #endif 00935 // Workaround for focus stealing prevention not working when dragging e.g. text from KWrite 00936 // to KDesktop -> the dialog asking for filename doesn't get activated. This is because 00937 // Qt-3.2.x doesn't have concept of qt_x_user_time at all, and Qt-3.3.0b1 passes the timestamp 00938 // in the XdndDrop message in incorrect field (and doesn't update qt_x_user_time either). 00939 // Patch already sent, future Qt version should have this fixed. 00940 if( _event->xclient.message_type == kde_xdnd_drop ) 00941 { // if the message is XdndDrop 00942 if( _event->xclient.data.l[ 1 ] == 1 << 24 // and it's broken the way it's in Qt-3.2.x 00943 && _event->xclient.data.l[ 2 ] == 0 00944 && _event->xclient.data.l[ 4 ] == 0 00945 && _event->xclient.data.l[ 3 ] != 0 ) 00946 { 00947 if( QX11Info::appUserTime() == 0 00948 || NET::timestampCompare( _event->xclient.data.l[ 3 ], QX11Info::appUserTime() ) > 0 ) 00949 { // and the timestamp looks reasonable 00950 QX11Info::setAppUserTime(_event->xclient.data.l[ 3 ]); // update our qt_x_user_time from it 00951 } 00952 } 00953 else // normal DND, only needed until Qt updates qt_x_user_time from XdndDrop 00954 { 00955 if( QX11Info::appUserTime() == 0 00956 || NET::timestampCompare( _event->xclient.data.l[ 2 ], QX11Info::appUserTime() ) > 0 ) 00957 { // the timestamp looks reasonable 00958 QX11Info::setAppUserTime(_event->xclient.data.l[ 2 ]); // update our qt_x_user_time from it 00959 } 00960 } 00961 } 00962 } 00963 default: break; 00964 } 00965 00966 if (x11Filter) { 00967 foreach (const QWeakPointer< QWidget >& wp, *x11Filter) { 00968 if( QWidget* w = wp.data()) 00969 if ( static_cast<KAppX11HackWidget*>( w )->publicx11Event(_event)) 00970 return true; 00971 } 00972 } 00973 00974 return false; 00975 } 00976 #endif // Q_WS_X11 00977 00978 void KApplication::updateUserTimestamp( int time ) 00979 { 00980 #if defined Q_WS_X11 00981 if( time == 0 ) 00982 { // get current X timestamp 00983 Window w = XCreateSimpleWindow( QX11Info::display(), QX11Info::appRootWindow(), 0, 0, 1, 1, 0, 0, 0 ); 00984 XSelectInput( QX11Info::display(), w, PropertyChangeMask ); 00985 unsigned char data[ 1 ]; 00986 XChangeProperty( QX11Info::display(), w, XA_ATOM, XA_ATOM, 8, PropModeAppend, data, 1 ); 00987 XEvent ev; 00988 XWindowEvent( QX11Info::display(), w, PropertyChangeMask, &ev ); 00989 time = ev.xproperty.time; 00990 XDestroyWindow( QX11Info::display(), w ); 00991 } 00992 if( QX11Info::appUserTime() == 0 00993 || NET::timestampCompare( time, QX11Info::appUserTime()) > 0 ) // time > appUserTime 00994 QX11Info::setAppUserTime(time); 00995 if( QX11Info::appTime() == 0 00996 || NET::timestampCompare( time, QX11Info::appTime()) > 0 ) // time > appTime 00997 QX11Info::setAppTime(time); 00998 #endif 00999 } 01000 01001 unsigned long KApplication::userTimestamp() const 01002 { 01003 #if defined Q_WS_X11 01004 return QX11Info::appUserTime(); 01005 #else 01006 return 0; 01007 #endif 01008 } 01009 01010 void KApplication::updateRemoteUserTimestamp( const QString& service, int time ) 01011 { 01012 #if defined Q_WS_X11 01013 Q_ASSERT(service.contains('.')); 01014 if( time == 0 ) 01015 time = QX11Info::appUserTime(); 01016 QDBusInterface(service, QLatin1String("/MainApplication"), 01017 QString(QLatin1String("org.kde.KApplication"))) 01018 .call(QLatin1String("updateUserTimestamp"), time); 01019 #endif 01020 } 01021 01022 01023 #ifndef KDE_NO_DEPRECATED 01024 QString KApplication::tempSaveName( const QString& pFilename ) 01025 { 01026 QString aFilename; 01027 01028 if( QDir::isRelativePath(pFilename) ) 01029 { 01030 kWarning(101) << "Relative filename passed to KApplication::tempSaveName"; 01031 aFilename = QFileInfo( QDir( QLatin1String(".") ), pFilename ).absoluteFilePath(); 01032 } 01033 else 01034 aFilename = pFilename; 01035 01036 QDir aAutosaveDir( QDir::homePath() + QLatin1String("/autosave/") ); 01037 if( !aAutosaveDir.exists() ) 01038 { 01039 if( !aAutosaveDir.mkdir( aAutosaveDir.absolutePath() ) ) 01040 { 01041 // Last chance: use temp dir 01042 aAutosaveDir.setPath( KGlobal::dirs()->saveLocation("tmp") ); 01043 } 01044 } 01045 01046 aFilename.replace( '/', QLatin1String("\\!") ) 01047 .prepend( QLatin1Char('#') ) 01048 .append( QLatin1Char('#') ) 01049 .prepend( QLatin1Char('/') ).prepend( aAutosaveDir.absolutePath() ); 01050 01051 return aFilename; 01052 } 01053 #endif 01054 01055 01056 QString KApplication::checkRecoverFile( const QString& pFilename, 01057 bool& bRecover ) 01058 { 01059 QString aFilename; 01060 01061 if( QDir::isRelativePath(pFilename) ) 01062 { 01063 kWarning(101) << "Relative filename passed to KApplication::tempSaveName"; 01064 aFilename = QFileInfo( QDir( QLatin1String(".") ), pFilename ).absoluteFilePath(); 01065 } 01066 else 01067 aFilename = pFilename; 01068 01069 QDir aAutosaveDir( QDir::homePath() + QLatin1String("/autosave/") ); 01070 if( !aAutosaveDir.exists() ) 01071 { 01072 if( !aAutosaveDir.mkdir( aAutosaveDir.absolutePath() ) ) 01073 { 01074 // Last chance: use temp dir 01075 aAutosaveDir.setPath( KGlobal::dirs()->saveLocation("tmp") ); 01076 } 01077 } 01078 01079 aFilename.replace( QLatin1String("/"), QLatin1String("\\!") ) 01080 .prepend( QLatin1Char('#') ) 01081 .append( QLatin1Char('#') ) 01082 .prepend( QLatin1Char('/') ) 01083 .prepend( aAutosaveDir.absolutePath() ); 01084 01085 if( QFile( aFilename ).exists() ) 01086 { 01087 bRecover = true; 01088 return aFilename; 01089 } 01090 else 01091 { 01092 bRecover = false; 01093 return pFilename; 01094 } 01095 } 01096 01097 01098 void KApplication::setTopWidget( QWidget *topWidget ) 01099 { 01100 if( !topWidget ) 01101 return; 01102 01103 // set the specified caption 01104 if ( !topWidget->inherits("KMainWindow") ) { // KMainWindow does this already for us 01105 topWidget->setWindowTitle(KGlobal::caption()); 01106 } 01107 01108 #ifdef Q_WS_X11 01109 // set the app startup notification window property 01110 KStartupInfo::setWindowStartupId(topWidget->winId(), startupId()); 01111 #endif 01112 } 01113 01114 QByteArray KApplication::startupId() const 01115 { 01116 return d->startup_id; 01117 } 01118 01119 void KApplication::setStartupId( const QByteArray& startup_id ) 01120 { 01121 if( startup_id == d->startup_id ) 01122 return; 01123 #if defined Q_WS_X11 01124 KStartupInfo::handleAutoAppStartedSending(); // finish old startup notification if needed 01125 #endif 01126 if( startup_id.isEmpty()) 01127 d->startup_id = "0"; 01128 else 01129 { 01130 d->startup_id = startup_id; 01131 #if defined Q_WS_X11 01132 KStartupInfoId id; 01133 id.initId( startup_id ); 01134 long timestamp = id.timestamp(); 01135 if( timestamp != 0 ) 01136 updateUserTimestamp( timestamp ); 01137 #endif 01138 } 01139 } 01140 01141 void KApplication::clearStartupId() 01142 { 01143 d->startup_id = "0"; 01144 } 01145 01146 // Qt reads and unsets the value and doesn't provide any way to reach the value, 01147 // so steal it from it beforehand. If Qt gets API for taking (reading and unsetting) 01148 // the startup id from it, this can be dumped. 01149 void KApplicationPrivate::preread_app_startup_id() 01150 { 01151 #if defined Q_WS_X11 01152 KStartupInfoId id = KStartupInfo::currentStartupIdEnv(); 01153 KStartupInfo::resetStartupEnv(); 01154 startup_id_tmp = new QByteArray( id.id()); 01155 #endif 01156 } 01157 01158 // read the startup notification env variable, save it and unset it in order 01159 // not to propagate it to processes started from this app 01160 void KApplicationPrivate::read_app_startup_id() 01161 { 01162 #if defined Q_WS_X11 01163 startup_id = *startup_id_tmp; 01164 delete startup_id_tmp; 01165 startup_id_tmp = NULL; 01166 #endif 01167 } 01168 01169 // Hook called by KToolInvocation 01170 void KApplicationPrivate::_k_slot_KToolInvocation_hook(QStringList& envs,QByteArray& startup_id) 01171 { 01172 #ifdef Q_WS_X11 01173 if (QX11Info::display()) { 01174 QByteArray dpystring(XDisplayString(QX11Info::display())); 01175 envs << QString::fromLatin1( QByteArray("DISPLAY=") + dpystring ); 01176 } else { 01177 const QByteArray dpystring( qgetenv( "DISPLAY" )); 01178 if(!dpystring.isEmpty()) 01179 envs << QString::fromLatin1( QByteArray("DISPLAY=") + dpystring ); 01180 } 01181 01182 if(startup_id.isEmpty()) 01183 startup_id = KStartupInfo::createNewStartupId(); 01184 #else 01185 Q_UNUSED(envs); 01186 Q_UNUSED(startup_id); 01187 #endif 01188 } 01189 01190 void KApplication::setSynchronizeClipboard(bool synchronize) 01191 { 01192 KClipboardSynchronizer::self()->setSynchronizing(synchronize); 01193 KClipboardSynchronizer::self()->setReverseSynchronizing(synchronize); 01194 } 01195 01196 #include "kapplication.moc" 01197
KDE 4.6 API Reference