# kdecore Library API Documentation

Overview | Class Hierarchy | Classes | Classes (annotated) | Members | Namespaces | Source Files

# kstartupinfo.cpp

    00001 /****************************************************************************
    00002
    00003  $Id: kstartupinfo.cpp 382228 2005-01-25 10:23:34Z dfaure $
    00004
    00005  Copyright (C) 2001-2003 Lubos Lunak        <l.lunak@kde.org>
    00006
    00007 Permission is hereby granted, free of charge, to any person obtaining a
    00008 copy of this software and associated documentation files (the "Software"),
    00009 to deal in the Software without restriction, including without limitation
    00010 the rights to use, copy, modify, merge, publish, distribute, sublicense,
    00011 and/or sell copies of the Software, and to permit persons to whom the
    00012 Software is furnished to do so, subject to the following conditions:
    00013
    00014 The above copyright notice and this permission notice shall be included in
    00015 all copies or substantial portions of the Software.
    00016
    00017 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    00018 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    00019 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
    00020 THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    00021 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
    00022 FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
    00023 DEALINGS IN THE SOFTWARE.
    00024
    00025 ****************************************************************************/
    00026
    00027 // kdDebug() can't be turned off in kdeinit
    00028 #if 0
    00029 #define KSTARTUPINFO_ALL_DEBUG
    00030 #warning Extra KStartupInfo debug messages enabled.
    00031 #endif
    00032
    00033 #include <qwidget.h>
    00034
    00035 #include "config.h"
    00036 #ifdef Q_WS_X11
    00037 //#ifdef Q_WS_X11 // FIXME(E): Re-implement in a less X11 specific way
    00038 #include <qglobal.h>
    00039 #ifdef HAVE_CONFIG_H
    00040 #include <config.h>
    00041 #endif
    00042
    00043 // need to resolve INT32(qglobal.h)<>INT32(Xlibint.h) conflict
    00044 #ifndef QT_CLEAN_NAMESPACE
    00045 #define QT_CLEAN_NAMESPACE
    00046 #endif
    00047
    00048 #include "kstartupinfo.h"
    00049
    00050 #include <unistd.h>
    00051 #include <sys/time.h>
    00052 #include <stdlib.h>
    00053 #include <qtimer.h>
    00054 #ifdef Q_WS_X11
    00055 #include <netwm.h>
    00056 #endif
    00057 #include <kdebug.h>
    00058 #include <kapplication.h>
    00059 #include <signal.h>
    00060 #ifdef Q_WS_X11
    00061 #include <kwinmodule.h>
    00062 #include <kxmessages.h>
    00063 #include <kwin.h>
    00064 #endif
    00065
    00066 static const char* const NET_STARTUP_MSG = "_NET_STARTUP_INFO";
    00067 static const char* const NET_STARTUP_WINDOW = "_NET_STARTUP_ID";
    00068 // DESKTOP_STARTUP_ID is used also in kinit/wrapper.c
    00069 static const char* const NET_STARTUP_ENV = "DESKTOP_STARTUP_ID";
    00070
    00071 static bool auto_app_started_sending = true;
    00072
    00073 static long get_num( const QString& item_P );
    00074 static unsigned long get_unum( const QString& item_P );
    00075 static QString get_str( const QString& item_P );
    00076 static QCString get_cstr( const QString& item_P );
    00077 static QStringList get_fields( const QString& txt_P );
    00078 static QString escape_str( const QString& str_P );
    00079
    00080 static Atom utf8_string_atom = None;
    00081
    00082 class KStartupInfo::Data
    00083     : public KStartupInfoData
    00084     {
    00085     public:
    00086         Data() {}; // just because it's in a QMap
    00087         Data( const QString& txt_P )
    00088             : KStartupInfoData( txt_P ), age( 0 ) {};
    00089         unsigned int age;
    00090     };
    00091
    00092 struct KStartupInfoPrivate
    00093     {
    00094     public:
    00095         QMap< KStartupInfoId, KStartupInfo::Data > startups;
    00096     // contains silenced ASN's only if !AnnounceSilencedChanges
    00097         QMap< KStartupInfoId, KStartupInfo::Data > silent_startups;
    00098         // contains ASN's that had change: but no new: yet
    00099         QMap< KStartupInfoId, KStartupInfo::Data > uninited_startups;
    00100 #ifdef Q_WS_X11
    00101         KWinModule* wm_module;
    00102         KXMessages msgs;
    00103 #endif
    00104     QTimer* cleanup;
    00105     int flags;
    00106     KStartupInfoPrivate( int flags_P )
    00107             :
    00108 #ifdef Q_WS_X11
    00109         msgs( NET_STARTUP_MSG, NULL, false ),
    00110 #endif
    00111           flags( flags_P ) {}
    00112     };
    00113
    00114 KStartupInfo::KStartupInfo( int flags_P, QObject* parent_P, const char* name_P )
    00115     : QObject( parent_P, name_P ),
    00116         timeout( 60 ), d( NULL )
    00117     {
    00118     init( flags_P );
    00119     }
    00120
    00121 KStartupInfo::KStartupInfo( bool clean_on_cantdetect_P, QObject* parent_P, const char* name_P )
    00122     : QObject( parent_P, name_P ),
    00123         timeout( 60 ), d( NULL )
    00124     {
    00125     init( clean_on_cantdetect_P ? CleanOnCantDetect : 0 );
    00126     }
    00127
    00128 void KStartupInfo::init( int flags_P )
    00129     {
    00130     // d == NULL means "disabled"
    00131     if( !KApplication::kApplication())
    00132         return;
    00133     if( !KApplication::kApplication()->getDisplay())
    00134         return;
    00135
    00136     d = new KStartupInfoPrivate( flags_P );
    00137 #ifdef Q_WS_X11
    00138     if( !( d->flags & DisableKWinModule ))
    00139         {
    00140         d->wm_module = new KWinModule( this );
    00141         connect( d->wm_module, SIGNAL( windowAdded( WId )), SLOT( slot_window_added( WId )));
    00142         connect( d->wm_module, SIGNAL( systemTrayWindowAdded( WId )), SLOT( slot_window_added( WId )));
    00143         }
    00144     else
    00145         d->wm_module = NULL;
    00146     connect( &d->msgs, SIGNAL( gotMessage( const QString& )), SLOT( got_message( const QString& )));
    00147 #endif
    00148     d->cleanup = new QTimer( this );
    00149     connect( d->cleanup, SIGNAL( timeout()), SLOT( startups_cleanup()));
    00150     }
    00151
    00152 KStartupInfo::~KStartupInfo()
    00153     {
    00154     delete d;
    00155     }
    00156
    00157 void KStartupInfo::got_message( const QString& msg_P )
    00158     {
    00159 // TODO do something with SCREEN= ?
    00160     kdDebug( 172 ) << "got:" << msg_P << endl;
    00161     QString msg = msg_P.stripWhiteSpace();
    00162     if( msg.startsWith( "new:" )) // must match length below
    00163         got_startup_info( msg.mid( 4 ), false );
    00164     else if( msg.startsWith( "change:" )) // must match length below
    00165         got_startup_info( msg.mid( 7 ), true );
    00166     else if( msg.startsWith( "remove:" )) // must match length below
    00167         got_remove_startup_info( msg.mid( 7 ));
    00168     }
    00169
    00170 // if the application stops responding for a while, KWinModule may get
    00171 // the information about the already mapped window before KXMessages
    00172 // actually gets the info about the started application (depends
    00173 // on their order in X11 event filter in KApplication)
    00174 // simply delay info from KWinModule a bit
    00175 // SELI???
    00176 namespace
    00177 {
    00178 class DelayedWindowEvent
    00179     : public QCustomEvent
    00180     {
    00181     public:
    00182     DelayedWindowEvent( WId w_P )
    00183         : QCustomEvent( QEvent::User + 15 ), w( w_P ) {}
    00184     Window w;
    00185     };
    00186 }
    00187
    00188 void KStartupInfo::slot_window_added( WId w_P )
    00189     {
    00190     kapp->postEvent( this, new DelayedWindowEvent( w_P ));
    00191     }
    00192
    00193 void KStartupInfo::customEvent( QCustomEvent* e_P )
    00194     {
    00195     if( e_P->type() == QEvent::User + 15 )
    00196     window_added( static_cast< DelayedWindowEvent* >( e_P )->w );
    00197     else
    00198     QObject::customEvent( e_P );
    00199     }
    00200
    00201 void KStartupInfo::window_added( WId w_P )
    00202     {
    00203     KStartupInfoId id;
    00204     KStartupInfoData data;
    00205     startup_t ret = check_startup_internal( w_P, &id, &data );
    00206     switch( ret )
    00207         {
    00208         case Match:
    00209             kdDebug( 172 ) << "new window match" << endl;
    00210           break;
    00211         case NoMatch:
    00212           break; // nothing
    00213         case CantDetect:
    00214             if( d->flags & CleanOnCantDetect )
    00215                 clean_all_noncompliant();
    00216           break;
    00217         }
    00218     }
    00219
    00220 void KStartupInfo::got_startup_info( const QString& msg_P, bool update_P )
    00221     {
    00222     KStartupInfoId id( msg_P );
    00223     if( id.none())
    00224         return;
    00225     KStartupInfo::Data data( msg_P );
    00226     new_startup_info_internal( id, data, update_P );
    00227     }
    00228
    00229 void KStartupInfo::new_startup_info_internal( const KStartupInfoId& id_P,
    00230     Data& data_P, bool update_P )
    00231     {
    00232     if( d == NULL )
    00233         return;
    00234     if( id_P.none())
    00235         return;
    00236     if( d->startups.contains( id_P ))
    00237         { // already reported, update
    00238         d->startups[ id_P ].update( data_P );
    00239         d->startups[ id_P ].age = 0; // CHECKME
    00240         kdDebug( 172 ) << "updating" << endl;
    00241     if( d->startups[ id_P ].silent() == Data::Yes
    00242         && !( d->flags & AnnounceSilenceChanges ))
    00243         {
    00244         d->silent_startups[ id_P ] = d->startups[ id_P ];
    00245         d->startups.remove( id_P );
    00246         emit gotRemoveStartup( id_P, d->silent_startups[ id_P ] );
    00247         return;
    00248         }
    00249         emit gotStartupChange( id_P, d->startups[ id_P ] );
    00250         return;
    00251         }
    00252     if( d->silent_startups.contains( id_P ))
    00253         { // already reported, update
    00254         d->silent_startups[ id_P ].update( data_P );
    00255         d->silent_startups[ id_P ].age = 0; // CHECKME
    00256         kdDebug( 172 ) << "updating silenced" << endl;
    00257     if( d->silent_startups[ id_P ].silent() != Data::Yes )
    00258         {
    00259         d->startups[ id_P ] = d->silent_startups[ id_P ];
    00260         d->silent_startups.remove( id_P );
    00261         emit gotNewStartup( id_P, d->startups[ id_P ] );
    00262         return;
    00263         }
    00264         emit gotStartupChange( id_P, d->startups[ id_P ] );
    00265         return;
    00266         }
    00267     if( d->uninited_startups.contains( id_P ))
    00268         {
    00269         d->uninited_startups[ id_P ].update( data_P );
    00270         kdDebug( 172 ) << "updating uninited" << endl;
    00271         if( !update_P ) // uninited finally got new:
    00272             {
    00273             d->startups[ id_P ] = d->uninited_startups[ id_P ];
    00274             d->uninited_startups.remove( id_P );
    00275             emit gotNewStartup( id_P, d->startups[ id_P ] );
    00276             return;
    00277             }
    00278         // no change announce, it's still uninited
    00279         return;
    00280         }
    00281     if( update_P ) // change: without any new: first
    00282         {
    00283         kdDebug( 172 ) << "adding uninited" << endl;
    00284     d->uninited_startups.insert( id_P, data_P );
    00285         }
    00286     else if( data_P.silent() != Data::Yes || d->flags & AnnounceSilenceChanges )
    00287     {
    00288         kdDebug( 172 ) << "adding" << endl;
    00289         d->startups.insert( id_P, data_P );
    00290     emit gotNewStartup( id_P, data_P );
    00291     }
    00292     else // new silenced, and silent shouldn't be announced
    00293     {
    00294         kdDebug( 172 ) << "adding silent" << endl;
    00295     d->silent_startups.insert( id_P, data_P );
    00296     }
    00297     d->cleanup->start( 1000 ); // 1 sec
    00298     }
    00299
    00300 void KStartupInfo::got_remove_startup_info( const QString& msg_P )
    00301     {
    00302     KStartupInfoId id( msg_P );
    00303     KStartupInfoData data( msg_P );
    00304     if( data.pids().count() > 0 )
    00305         {
    00306         if( !id.none())
    00307             remove_startup_pids( id, data );
    00308         else
    00309             remove_startup_pids( data );
    00310         return;
    00311         }
    00312     remove_startup_info_internal( id );
    00313     }
    00314
    00315 void KStartupInfo::remove_startup_info_internal( const KStartupInfoId& id_P )
    00316     {
    00317     if( d == NULL )
    00318         return;
    00319     if( d->startups.contains( id_P ))
    00320         {
    00321     kdDebug( 172 ) << "removing" << endl;
    00322     emit gotRemoveStartup( id_P, d->startups[ id_P ]);
    00323     d->startups.remove( id_P );
    00324     }
    00325     else if( d->silent_startups.contains( id_P ))
    00326     {
    00327     kdDebug( 172 ) << "removing silent" << endl;
    00328     d->silent_startups.remove( id_P );
    00329     }
    00330     else if( d->uninited_startups.contains( id_P ))
    00331     {
    00332     kdDebug( 172 ) << "removing uninited" << endl;
    00333     d->uninited_startups.remove( id_P );
    00334     }
    00335     return;
    00336     }
    00337
    00338 void KStartupInfo::remove_startup_pids( const KStartupInfoData& data_P )
    00339     { // first find the matching info
    00340     if( d == NULL )
    00341         return;
    00342     for( QMap< KStartupInfoId, Data >::Iterator it = d->startups.begin();
    00343          it != d->startups.end();
    00344          ++it )
    00345         {
    00346         if( ( *it ).hostname() != data_P.hostname())
    00347             continue;
    00348         if( !( *it ).is_pid( data_P.pids().first()))
    00349             continue; // not the matching info
    00350         remove_startup_pids( it.key(), data_P );
    00351         break;
    00352         }
    00353     }
    00354
    00355 void KStartupInfo::remove_startup_pids( const KStartupInfoId& id_P,
    00356     const KStartupInfoData& data_P )
    00357     {
    00358     if( d == NULL )
    00359         return;
    00360     kdFatal( data_P.pids().count() == 0, 172 );
    00361     Data* data = NULL;
    00362     if( d->startups.contains( id_P ))
    00363     data = &d->startups[ id_P ];
    00364     else if( d->silent_startups.contains( id_P ))
    00365     data = &d->silent_startups[ id_P ];
    00366     else if( d->uninited_startups.contains( id_P ))
    00367         data = &d->uninited_startups[ id_P ];
    00368     else
    00369     return;
    00370     for( QValueList< pid_t >::ConstIterator it2 = data_P.pids().begin();
    00371          it2 != data_P.pids().end();
    00372          ++it2 )
    00373     data->remove_pid( *it2 ); // remove all pids from the info
    00374     if( data->pids().count() == 0 ) // all pids removed -> remove info
    00375         remove_startup_info_internal( id_P );
    00376     }
    00377
    00378 bool KStartupInfo::sendStartup( const KStartupInfoId& id_P, const KStartupInfoData& data_P )
    00379     {
    00380     if( id_P.none())
    00381         return false;
    00382     KXMessages msgs;
    00383     QString msg = QString::fromLatin1( "new: %1 %2" )
    00384         .arg( id_P.to_text()).arg( data_P.to_text());
    00385     msg = check_required_startup_fields( msg, data_P, qt_xscreen());
    00386     kdDebug( 172 ) << "sending " << msg << endl;
    00387     msgs.broadcastMessage( NET_STARTUP_MSG, msg, -1, false );
    00388     return true;
    00389     }
    00390
    00391 bool KStartupInfo::sendStartupX( Display* disp_P, const KStartupInfoId& id_P,
    00392     const KStartupInfoData& data_P )
    00393     {
    00394     if( id_P.none())
    00395         return false;
    00396     QString msg = QString::fromLatin1( "new: %1 %2" )
    00397         .arg( id_P.to_text()).arg( data_P.to_text());
    00398     msg = check_required_startup_fields( msg, data_P, DefaultScreen( disp_P ));
    00399 #ifdef KSTARTUPINFO_ALL_DEBUG
    00400     kdDebug( 172 ) << "sending " << msg << endl;
    00401 #endif
    00402     return KXMessages::broadcastMessageX( disp_P, NET_STARTUP_MSG, msg, -1, false );
    00403     }
    00404
    00405 QString KStartupInfo::check_required_startup_fields( const QString& msg, const KStartupInfoData& data_P,
    00406     int screen )
    00407     {
    00408     QString ret = msg;
    00409     if( data_P.name().isEmpty())
    00410         {
    00411 //        kdWarning( 172 ) << "NAME not specified in initial startup message" << endl;
    00412         QString name = data_P.bin();
    00413         if( name.isEmpty())
    00414             name = "UNKNOWN";
    00415         ret += QString( " NAME=\"%1\"" ).arg( escape_str( name ));
    00416         }
    00417     if( data_P.screen() == -1 ) // add automatically if needed
    00418         ret += QString( " SCREEN=%1" ).arg( screen );
    00419     return ret;
    00420     }
    00421
    00422 bool KStartupInfo::sendChange( const KStartupInfoId& id_P, const KStartupInfoData& data_P )
    00423     {
    00424     if( id_P.none())
    00425         return false;
    00426     KXMessages msgs;
    00427     QString msg = QString::fromLatin1( "change: %1 %2" )
    00428         .arg( id_P.to_text()).arg( data_P.to_text());
    00429     kdDebug( 172 ) << "sending " << msg << endl;
    00430     msgs.broadcastMessage( NET_STARTUP_MSG, msg, -1, false );
    00431     return true;
    00432     }
    00433
    00434 bool KStartupInfo::sendChangeX( Display* disp_P, const KStartupInfoId& id_P,
    00435     const KStartupInfoData& data_P )
    00436     {
    00437     if( id_P.none())
    00438         return false;
    00439     QString msg = QString::fromLatin1( "change: %1 %2" )
    00440         .arg( id_P.to_text()).arg( data_P.to_text());
    00441 #ifdef KSTARTUPINFO_ALL_DEBUG
    00442     kdDebug( 172 ) << "sending " << msg << endl;
    00443 #endif
    00444     return KXMessages::broadcastMessageX( disp_P, NET_STARTUP_MSG, msg, -1, false );
    00445     }
    00446
    00447 bool KStartupInfo::sendFinish( const KStartupInfoId& id_P )
    00448     {
    00449     if( id_P.none())
    00450         return false;
    00451     KXMessages msgs;
    00452     QString msg = QString::fromLatin1( "remove: %1" ).arg( id_P.to_text());
    00453     kdDebug( 172 ) << "sending " << msg << endl;
    00454     msgs.broadcastMessage( NET_STARTUP_MSG, msg, -1, false );
    00455     return true;
    00456     }
    00457
    00458 bool KStartupInfo::sendFinishX( Display* disp_P, const KStartupInfoId& id_P )
    00459     {
    00460     if( id_P.none())
    00461         return false;
    00462     QString msg = QString::fromLatin1( "remove: %1" ).arg( id_P.to_text());
    00463 #ifdef KSTARTUPINFO_ALL_DEBUG
    00464     kdDebug( 172 ) << "sending " << msg << endl;
    00465 #endif
    00466     return KXMessages::broadcastMessageX( disp_P, NET_STARTUP_MSG, msg, -1, false );
    00467     }
    00468
    00469 bool KStartupInfo::sendFinish( const KStartupInfoId& id_P, const KStartupInfoData& data_P )
    00470     {
    00471 //    if( id_P.none()) // id may be none, the pids and hostname matter then
    00472 //        return false;
    00473     KXMessages msgs;
    00474     QString msg = QString::fromLatin1( "remove: %1 %2" )
    00475         .arg( id_P.to_text()).arg( data_P.to_text());
    00476     kdDebug( 172 ) << "sending " << msg << endl;
    00477     msgs.broadcastMessage( NET_STARTUP_MSG, msg, -1, false );
    00478     return true;
    00479     }
    00480
    00481 bool KStartupInfo::sendFinishX( Display* disp_P, const KStartupInfoId& id_P,
    00482     const KStartupInfoData& data_P )
    00483     {
    00484 //    if( id_P.none()) // id may be none, the pids and hostname matter then
    00485 //        return false;
    00486     QString msg = QString::fromLatin1( "remove: %1 %2" )
    00487         .arg( id_P.to_text()).arg( data_P.to_text());
    00488 #ifdef KSTARTUPINFO_ALL_DEBUG
    00489     kdDebug( 172 ) << "sending " << msg << endl;
    00490 #endif
    00491     return KXMessages::broadcastMessageX( disp_P, NET_STARTUP_MSG, msg, -1, false );
    00492     }
    00493
    00494 void KStartupInfo::appStarted()
    00495     {
    00496     if( kapp != NULL )  // KApplication constructor unsets the env. variable
    00497         appStarted( kapp->startupId());
    00498     else
    00499         appStarted( KStartupInfo::currentStartupIdEnv().id());
    00500     }
    00501
    00502 void KStartupInfo::appStarted( const QCString& startup_id )
    00503     {
    00504     KStartupInfoId id;
    00505     id.initId( startup_id );
    00506     if( id.none())
    00507         return;
    00508     if( kapp != NULL )
    00509         KStartupInfo::sendFinish( id );
    00510     else if( getenv( "DISPLAY" ) != NULL ) // don't rely on qt_xdisplay()
    00511         {
    00512 #ifdef Q_WS_X11
    00513         Display* disp = XOpenDisplay( NULL );
    00514         if( disp != NULL )
    00515             {
    00516             KStartupInfo::sendFinishX( disp, id );
    00517             XCloseDisplay( disp );
    00518             }
    00519 #endif
    00520         }
    00521     }
    00522
    00523 void KStartupInfo::disableAutoAppStartedSending( bool disable )
    00524     {
    00525     auto_app_started_sending = !disable;
    00526     }
    00527
    00528 void KStartupInfo::silenceStartup( bool silence )
    00529     {
    00530     KStartupInfoId id;
    00531     id.initId( kapp->startupId());
    00532     if( id.none())
    00533         return;
    00534     KStartupInfoData data;
    00535     data.setSilent( silence ? KStartupInfoData::Yes : KStartupInfoData::No );
    00536     sendChange( id, data );
    00537     }
    00538
    00539 void KStartupInfo::handleAutoAppStartedSending()
    00540     {
    00541     if( auto_app_started_sending )
    00542         appStarted();
    00543     }
    00544
    00545 void KStartupInfo::setNewStartupId( QWidget* window, const QCString& startup_id )
    00546     {
    00547     long activate = true;
    00548     kapp->setStartupId( startup_id );
    00549     if( window != NULL )
    00550         {
    00551         if( !startup_id.isEmpty() && startup_id != "0" )
    00552             {
    00553             NETRootInfo i( qt_xdisplay(), NET::Supported );
    00554             if( i.isSupported( NET::WM2StartupId ))
    00555                 {
    00556                 KStartupInfo::setWindowStartupId( window->winId(), startup_id );
    00557                 activate = false; // WM will take care of it
    00558                 }
    00559             }
    00560         if( activate )
    00561         // This is not very nice, but there's no way how to get any
    00562         // usable timestamp without ASN, so force activating the window.
    00563         // And even with ASN, it's not possible to get the timestamp here,
    00564         // so if the WM doesn't have support for ASN, it can't be used either.
    00565             KWin::forceActiveWindow( window->winId());
    00566         }
    00567     KStartupInfo::handleAutoAppStartedSending();
    00568     }
    00569
    00570 KStartupInfo::startup_t KStartupInfo::checkStartup( WId w_P, KStartupInfoId& id_O,
    00571     KStartupInfoData& data_O )
    00572     {
    00573     return check_startup_internal( w_P, &id_O, &data_O );
    00574     }
    00575
    00576 KStartupInfo::startup_t KStartupInfo::checkStartup( WId w_P, KStartupInfoId& id_O )
    00577     {
    00578     return check_startup_internal( w_P, &id_O, NULL );
    00579     }
    00580
    00581 KStartupInfo::startup_t KStartupInfo::checkStartup( WId w_P, KStartupInfoData& data_O )
    00582     {
    00583     return check_startup_internal( w_P, NULL, &data_O );
    00584     }
    00585
    00586 KStartupInfo::startup_t KStartupInfo::checkStartup( WId w_P )
    00587     {
    00588     return check_startup_internal( w_P, NULL, NULL );
    00589     }
    00590
    00591 KStartupInfo::startup_t KStartupInfo::check_startup_internal( WId w_P, KStartupInfoId* id_O,
    00592     KStartupInfoData* data_O )
    00593     {
    00594     if( d == NULL )
    00595         return NoMatch;
    00596     if( d->startups.count() == 0 )
    00597         return NoMatch; // no startups
    00598     // Strategy:
    00599     //
    00600     // Is this a compliant app ?
    00601     //  - Yes - test for match
    00602     //  - No - Is this a NET_WM compliant app ?
    00603     //           - Yes - test for pid match
    00604     //           - No - test for WM_CLASS match
    00605     kdDebug( 172 ) << "check_startup" << endl;
    00606     QCString id = windowStartupId( w_P );
    00607     if( !id.isNull())
    00608         {
    00609         if( id.isEmpty() || id == "0" ) // means ignore this window
    00610             {
    00611             kdDebug( 172 ) << "ignore" << endl;
    00612             return NoMatch;
    00613             }
    00614         return find_id( id, id_O, data_O ) ? Match : NoMatch;
    00615         }
    00616 #ifdef Q_WS_X11
    00617     NETWinInfo info( qt_xdisplay(),  w_P, qt_xrootwin(),
    00618         NET::WMWindowType | NET::WMPid | NET::WMState );
    00619     pid_t pid = info.pid();
    00620     if( pid > 0 )
    00621         {
    00622         QCString hostname = get_window_hostname( w_P );
    00623         if( !hostname.isEmpty()
    00624             && find_pid( pid, hostname, id_O, data_O ))
    00625             return Match;
    00626         // try XClass matching , this PID stuff sucks :(
    00627         }
    00628     XClassHint hint;
    00629     if( XGetClassHint( qt_xdisplay(), w_P, &hint ) != 0 )
    00630         { // We managed to read the class hint
    00631         QCString res_name = hint.res_name;
    00632         QCString res_class = hint.res_class;
    00633         XFree( hint.res_name );
    00634         XFree( hint.res_class );
    00635         if( find_wclass( res_name, res_class, id_O, data_O ))
    00636             return Match;
    00637         }
    00638     // ignore NET::Tool and other special window types, if they can't be matched
    00639     NET::WindowType type = info.windowType( NET::NormalMask | NET::DesktopMask
    00640         | NET::DockMask | NET::ToolbarMask | NET::MenuMask | NET::DialogMask
    00641         | NET::OverrideMask | NET::TopMenuMask | NET::UtilityMask | NET::SplashMask );
    00642     if( type != NET::Normal
    00643         && type != NET::Override
    00644         && type != NET::Unknown
    00645         && type != NET::Dialog
    00646         && type != NET::Utility )
    00647 //        && type != NET::Dock ) why did I put this here?
    00648     return NoMatch;
    00649     // lets see if this is a transient
    00650     Window transient_for;
    00651     if( XGetTransientForHint( qt_xdisplay(), static_cast< Window >( w_P ), &transient_for )
    00652         && static_cast< WId >( transient_for ) != qt_xrootwin()
    00653         && transient_for != None )
    00654     return NoMatch;
    00655 #endif
    00656     kdDebug( 172 ) << "check_startup:cantdetect" << endl;
    00657     return CantDetect;
    00658     }
    00659
    00660 bool KStartupInfo::find_id( const QCString& id_P, KStartupInfoId* id_O,
    00661     KStartupInfoData* data_O )
    00662     {
    00663     if( d == NULL )
    00664         return false;
    00665     kdDebug( 172 ) << "find_id:" << id_P << endl;
    00666     KStartupInfoId id;
    00667     id.initId( id_P );
    00668     if( d->startups.contains( id ))
    00669         {
    00670         if( id_O != NULL )
    00671             *id_O = id;
    00672         if( data_O != NULL )
    00673             *data_O = d->startups[ id ];
    00674         kdDebug( 172 ) << "check_startup_id:match" << endl;
    00675         return true;
    00676         }
    00677     return false;
    00678     }
    00679
    00680 bool KStartupInfo::find_pid( pid_t pid_P, const QCString& hostname_P,
    00681     KStartupInfoId* id_O, KStartupInfoData* data_O )
    00682     {
    00683     if( d == NULL )
    00684         return false;
    00685     kdDebug( 172 ) << "find_pid:" << pid_P << endl;
    00686     for( QMap< KStartupInfoId, Data >::Iterator it = d->startups.begin();
    00687          it != d->startups.end();
    00688          ++it )
    00689         {
    00690         if( ( *it ).is_pid( pid_P ) && ( *it ).hostname() == hostname_P )
    00691             { // Found it !
    00692             if( id_O != NULL )
    00693                 *id_O = it.key();
    00694             if( data_O != NULL )
    00695                 *data_O = *it;
    00696             // non-compliant, remove on first match
    00697             remove_startup_info_internal( it.key());
    00698             kdDebug( 172 ) << "check_startup_pid:match" << endl;
    00699             return true;
    00700             }
    00701         }
    00702     return false;
    00703     }
    00704
    00705 bool KStartupInfo::find_wclass( QCString res_name, QCString res_class,
    00706     KStartupInfoId* id_O, KStartupInfoData* data_O )
    00707     {
    00708     if( d == NULL )
    00709         return false;
    00710     res_name = res_name.lower();
    00711     res_class = res_class.lower();
    00712     kdDebug( 172 ) << "find_wclass:" << res_name << ":" << res_class << endl;
    00713     for( QMap< KStartupInfoId, Data >::Iterator it = d->startups.begin();
    00714          it != d->startups.end();
    00715          ++it )
    00716         {
    00717         const QCString wmclass = ( *it ).findWMClass();
    00718         if( wmclass.lower() == res_name || wmclass.lower() == res_class )
    00719             { // Found it !
    00720             if( id_O != NULL )
    00721                 *id_O = it.key();
    00722             if( data_O != NULL )
    00723                 *data_O = *it;
    00724             // non-compliant, remove on first match
    00725             remove_startup_info_internal( it.key());
    00726             kdDebug( 172 ) << "check_startup_wclass:match" << endl;
    00727             return true;
    00728             }
    00729         }
    00730     return false;
    00731     }
    00732
    00733 #ifdef Q_WS_X11
    00734 static Atom net_startup_atom = None;
    00735
    00736 static QCString read_startup_id_property( WId w_P )
    00737     {
    00738     QCString ret;
    00739     unsigned char *name_ret;
    00740     Atom type_ret;
    00741     int format_ret;
    00742     unsigned long nitems_ret = 0, after_ret = 0;
    00743     if( XGetWindowProperty( qt_xdisplay(), w_P, net_startup_atom, 0l, 4096,
    00744             False, utf8_string_atom, &type_ret, &format_ret, &nitems_ret, &after_ret, &name_ret )
    00745         == Success )
    00746         {
    00747     if( type_ret == utf8_string_atom && format_ret == 8 && name_ret != NULL )
    00748         ret = reinterpret_cast< char* >( name_ret );
    00749         if ( name_ret != NULL )
    00750             XFree( name_ret );
    00751         }
    00752     return ret;
    00753     }
    00754
    00755 #endif
    00756
    00757 QCString KStartupInfo::windowStartupId( WId w_P )
    00758     {
    00759 #ifdef Q_WS_X11
    00760     if( net_startup_atom == None )
    00761         net_startup_atom = XInternAtom( qt_xdisplay(), NET_STARTUP_WINDOW, False );
    00762     if( utf8_string_atom == None )
    00763         utf8_string_atom = XInternAtom( qt_xdisplay(), "UTF8_STRING", False );
    00764     QCString ret = read_startup_id_property( w_P );
    00765     if( ret.isEmpty())
    00766         { // retry with window group leader, as the spec says
    00767         XWMHints* hints = XGetWMHints( qt_xdisplay(), w_P );
    00768         if( hints && ( hints->flags & WindowGroupHint ) != 0 )
    00769             ret = read_startup_id_property( hints->window_group );
    00770         if( hints )
    00771             XFree( hints );
    00772         }
    00773     return ret;
    00774 #else
    00775     return QCString();
    00776 #endif
    00777     }
    00778
    00779 void KStartupInfo::setWindowStartupId( WId w_P, const QCString& id_P )
    00780     {
    00781 #ifdef Q_WS_X11
    00782     if( id_P.isNull())
    00783         return;
    00784     if( net_startup_atom == None )
    00785         net_startup_atom = XInternAtom( qt_xdisplay(), NET_STARTUP_WINDOW, False );
    00786     if( utf8_string_atom == None )
    00787         utf8_string_atom = XInternAtom( qt_xdisplay(), "UTF8_STRING", False );
    00788     XChangeProperty( qt_xdisplay(), w_P, net_startup_atom, utf8_string_atom, 8,
    00789         PropModeReplace, reinterpret_cast< unsigned char* >( id_P.data()), id_P.length());
    00790 #endif
    00791     }
    00792
    00793 QCString KStartupInfo::get_window_hostname( WId w_P )
    00794     {
    00795 #ifdef Q_WS_X11
    00796     XTextProperty tp;
    00797     char** hh;
    00798     int cnt;
    00799     if( XGetWMClientMachine( qt_xdisplay(), w_P, &tp ) != 0
    00800         && XTextPropertyToStringList( &tp, &hh, &cnt ) != 0 )
    00801         {
    00802         if( cnt == 1 )
    00803             {
    00804             QCString hostname = hh[ 0 ];
    00805             XFreeStringList( hh );
    00806             return hostname;
    00807             }
    00808         XFreeStringList( hh );
    00809         }
    00810 #endif
    00811     // no hostname
    00812     return QCString();
    00813     }
    00814
    00815 void KStartupInfo::setTimeout( unsigned int secs_P )
    00816     {
    00817     timeout = secs_P;
    00818  // schedule removing entries that are older than the new timeout
    00819     QTimer::singleShot( 0, this, SLOT( startups_cleanup_no_age()));
    00820     }
    00821
    00822 void KStartupInfo::startups_cleanup_no_age()
    00823     {
    00824     startups_cleanup_internal( false );
    00825     }
    00826
    00827 void KStartupInfo::startups_cleanup()
    00828     {
    00829     if( d == NULL )
    00830         return;
    00831     if( d->startups.count() == 0 && d->silent_startups.count() == 0
    00832         && d->uninited_startups.count() == 0 )
    00833         {
    00834         d->cleanup->stop();
    00835         return;
    00836         }
    00837     startups_cleanup_internal( true );
    00838     }
    00839
    00840 void KStartupInfo::startups_cleanup_internal( bool age_P )
    00841     {
    00842     if( d == NULL )
    00843         return;
    00844     for( QMap< KStartupInfoId, Data >::Iterator it = d->startups.begin();
    00845          it != d->startups.end();
    00846          )
    00847         {
    00848         if( age_P )
    00849             ( *it ).age++;
    00850     unsigned int tout = timeout;
    00851     if( ( *it ).silent() == Data::Yes ) // TODO
    00852         tout *= 20;
    00853         if( ( *it ).age >= tout )
    00854             {
    00855             const KStartupInfoId& key = it.key();
    00856             ++it;
    00857             kdDebug( 172 ) << "entry timeout:" << key.id() << endl;
    00858             remove_startup_info_internal( key );
    00859             }
    00860         else
    00861             ++it;
    00862         }
    00863     for( QMap< KStartupInfoId, Data >::Iterator it = d->silent_startups.begin();
    00864          it != d->silent_startups.end();
    00865          )
    00866         {
    00867         if( age_P )
    00868             ( *it ).age++;
    00869     unsigned int tout = timeout;
    00870     if( ( *it ).silent() == Data::Yes ) // TODO
    00871         tout *= 20;
    00872         if( ( *it ).age >= tout )
    00873             {
    00874             const KStartupInfoId& key = it.key();
    00875             ++it;
    00876             kdDebug( 172 ) << "entry timeout:" << key.id() << endl;
    00877             remove_startup_info_internal( key );
    00878             }
    00879         else
    00880             ++it;
    00881         }
    00882     for( QMap< KStartupInfoId, Data >::Iterator it = d->uninited_startups.begin();
    00883          it != d->uninited_startups.end();
    00884          )
    00885         {
    00886         if( age_P )
    00887             ( *it ).age++;
    00888     unsigned int tout = timeout;
    00889     if( ( *it ).silent() == Data::Yes ) // TODO
    00890         tout *= 20;
    00891         if( ( *it ).age >= tout )
    00892             {
    00893             const KStartupInfoId& key = it.key();
    00894             ++it;
    00895             kdDebug( 172 ) << "entry timeout:" << key.id() << endl;
    00896             remove_startup_info_internal( key );
    00897             }
    00898         else
    00899             ++it;
    00900         }
    00901     }
    00902
    00903 void KStartupInfo::clean_all_noncompliant()
    00904     {
    00905     if( d == NULL )
    00906         return;
    00907     for( QMap< KStartupInfoId, Data >::Iterator it = d->startups.begin();
    00908          it != d->startups.end();
    00909          )
    00910         {
    00911         if( ( *it ).WMClass() != "0" )
    00912             {
    00913             ++it;
    00914             continue;
    00915             }
    00916         const KStartupInfoId& key = it.key();
    00917         ++it;
    00918         kdDebug( 172 ) << "entry cleaning:" << key.id() << endl;
    00919         remove_startup_info_internal( key );
    00920         }
    00921     }
    00922
    00923 QCString KStartupInfo::createNewStartupId()
    00924     {
    00925     // Assign a unique id, use hostname+time+pid, that should be 200% unique.
    00926     // Also append the user timestamp (for focus stealing prevention).
    00927     struct timeval tm;
    00928     gettimeofday( &tm, NULL );
    00929     char hostname[ 256 ];
    00930     hostname[ 0 ] = '\0';
    00931     if (!gethostname( hostname, 255 ))
    00932     hostname[sizeof(hostname)-1] = '\0';
    00933 #ifdef Q_WS_X11
    00934     extern Time qt_x_user_time;
    00935 #else
    00936     long qt_x_user_time = 0;
    00937 #endif
    00938     QCString id = QString( "%1;%2;%3;%4_TIME%5" ).arg( hostname ).arg( tm.tv_sec )
    00939         .arg( tm.tv_usec ).arg( getpid()).arg( qt_x_user_time ).utf8();
    00940     kdDebug( 172 ) << "creating: " << id << ":" << qAppName() << endl;
    00941     return id;
    00942     }
    00943
    00944
    00945 struct KStartupInfoIdPrivate
    00946     {
    00947     KStartupInfoIdPrivate() : id( "" ) {};
    00948     QCString id; // id
    00949     };
    00950
    00951 const QCString& KStartupInfoId::id() const
    00952     {
    00953     return d->id;
    00954     }
    00955
    00956
    00957 QString KStartupInfoId::to_text() const
    00958     {
    00959     return QString::fromLatin1( " ID=\"%1\" " ).arg( escape_str( id()));
    00960     }
    00961
    00962 KStartupInfoId::KStartupInfoId( const QString& txt_P )
    00963     {
    00964     d = new KStartupInfoIdPrivate;
    00965     QStringList items = get_fields( txt_P );
    00966     const QString id_str = QString::fromLatin1( "ID=" );
    00967     for( QStringList::Iterator it = items.begin();
    00968          it != items.end();
    00969          ++it )
    00970         {
    00971         if( ( *it ).startsWith( id_str ))
    00972             d->id = get_cstr( *it );
    00973         }
    00974     }
    00975
    00976 void KStartupInfoId::initId( const QCString& id_P )
    00977     {
    00978     if( !id_P.isEmpty())
    00979         {
    00980         d->id = id_P;
    00981 #ifdef KSTARTUPINFO_ALL_DEBUG
    00982         kdDebug( 172 ) << "using: " << d->id << endl;
    00983 #endif
    00984         return;
    00985         }
    00986     const char* startup_env = getenv( NET_STARTUP_ENV );
    00987     if( startup_env != NULL && *startup_env != '\0' )
    00988         { // already has id
    00989         d->id = startup_env;
    00990 #ifdef KSTARTUPINFO_ALL_DEBUG
    00991         kdDebug( 172 ) << "reusing: " << d->id << endl;
    00992 #endif
    00993         return;
    00994         }
    00995     d->id = KStartupInfo::createNewStartupId();
    00996     }
    00997
    00998 bool KStartupInfoId::setupStartupEnv() const
    00999     {
    01000     if( id().isEmpty())
    01001         {
    01002         unsetenv( NET_STARTUP_ENV );
    01003         return false;
    01004         }
    01005     return setenv( NET_STARTUP_ENV, id(), true ) == 0;
    01006     }
    01007
    01008 KStartupInfoId KStartupInfo::currentStartupIdEnv()
    01009     {
    01010     const char* startup_env = getenv( NET_STARTUP_ENV );
    01011     KStartupInfoId id;
    01012     if( startup_env != NULL && *startup_env != '\0' )
    01013         id.d->id = startup_env;
    01014     else
    01015         id.d->id = "0";
    01016     return id;
    01017     }
    01018
    01019 void KStartupInfo::resetStartupEnv()
    01020     {
    01021     unsetenv( NET_STARTUP_ENV );
    01022     }
    01023
    01024 KStartupInfoId::KStartupInfoId()
    01025     {
    01026     d = new KStartupInfoIdPrivate;
    01027     }
    01028
    01029 KStartupInfoId::~KStartupInfoId()
    01030     {
    01031     delete d;
    01032     }
    01033
    01034 KStartupInfoId::KStartupInfoId( const KStartupInfoId& id_P )
    01035     {
    01036     d = new KStartupInfoIdPrivate( *id_P.d );
    01037     }
    01038
    01039 KStartupInfoId& KStartupInfoId::operator=( const KStartupInfoId& id_P )
    01040     {
    01041     if( &id_P == this )
    01042         return *this;
    01043     delete d;
    01044     d = new KStartupInfoIdPrivate( *id_P.d );
    01045     return *this;
    01046     }
    01047
    01048 bool KStartupInfoId::operator==( const KStartupInfoId& id_P ) const
    01049     {
    01050     return id() == id_P.id();
    01051     }
    01052
    01053 bool KStartupInfoId::operator!=( const KStartupInfoId& id_P ) const
    01054     {
    01055     return !(*this == id_P );
    01056     }
    01057
    01058 // needed for QMap
    01059 bool KStartupInfoId::operator<( const KStartupInfoId& id_P ) const
    01060     {
    01061     return id() < id_P.id();
    01062     }
    01063
    01064 bool KStartupInfoId::none() const
    01065     {
    01066     return d->id.isEmpty() || d->id == "0";
    01067     }
    01068
    01069 unsigned long KStartupInfoId::timestamp() const
    01070     {
    01071     if( none())
    01072         return 0;
    01073     int pos = d->id.findRev( "_TIME" );
    01074     if( pos >= 0 )
    01075         {
    01076         bool ok;
    01077         long time = d->id.mid( pos + 5 ).toLong( &ok );
    01078         if( ok )
    01079             return time;
    01080         }
    01081     // libstartup-notification style :
    01082     // snprintf (s, len, "%s/%s/%lu/%d-%d-%s",
    01083     //   canonicalized_launcher, canonicalized_launchee, (unsigned long) timestamp,
    01084     //  (int) getpid (), (int) sequence_number, hostbuf);
    01085     int pos1 = d->id.findRev( '/' );
    01086     if( pos1 > 0 )
    01087         {
    01088         int pos2 = d->id.findRev( '/', pos1 - 1 );
    01089         if( pos2 >= 0 )
    01090             {
    01091             bool ok;
    01092             long time = d->id.mid( pos2 + 1, pos1 - pos2 - 1 ).toLong( &ok );
    01093             if( ok )
    01094                 return time;
    01095             }
    01096         }
    01097     // bah ... old KStartupInfo or a problem
    01098     return 0;
    01099     }
    01100
    01101 struct KStartupInfoDataPrivate
    01102     {
    01103     KStartupInfoDataPrivate() : desktop( 0 ), wmclass( "" ), hostname( "" ),
    01104     silent( KStartupInfoData::Unknown ), timestamp( -1U ), screen( -1 ) {};
    01105     QString bin;
    01106     QString name;
    01107     QString description;
    01108     QString icon;
    01109     int desktop;
    01110     QValueList< pid_t > pids;
    01111     QCString wmclass;
    01112     QCString hostname;
    01113     KStartupInfoData::TriState silent;
    01114     unsigned long timestamp;
    01115     int screen;
    01116     };
    01117
    01118 QString KStartupInfoData::to_text() const
    01119     {
    01120     QString ret = "";
    01121     if( !d->bin.isEmpty())
    01122         ret += QString::fromLatin1( " BIN=\"%1\"" ).arg( escape_str( d->bin ));
    01123     if( !d->name.isEmpty())
    01124         ret += QString::fromLatin1( " NAME=\"%1\"" ).arg( escape_str( d->name ));
    01125     if( !d->description.isEmpty())
    01126         ret += QString::fromLatin1( " DESCRIPTION=\"%1\"" ).arg( escape_str( d->description ));
    01127     if( !d->icon.isEmpty())
    01128         ret += QString::fromLatin1( " ICON=%1" ).arg( d->icon );
    01129     if( d->desktop != 0 )
    01130         ret += QString::fromLatin1( " DESKTOP=%1" )
    01131             .arg( d->desktop == NET::OnAllDesktops ? NET::OnAllDesktops : d->desktop - 1 ); // spec counts from 0
    01132     if( !d->wmclass.isEmpty())
    01133         ret += QString::fromLatin1( " WMCLASS=%1" ).arg( d->wmclass );
    01134     if( !d->hostname.isEmpty())
    01135         ret += QString::fromLatin1( " HOSTNAME=%1" ).arg( d->hostname );
    01136     for( QValueList< pid_t >::ConstIterator it = d->pids.begin();
    01137          it != d->pids.end();
    01138          ++it )
    01139         ret += QString::fromLatin1( " PID=%1" ).arg( *it );
    01140     if( d->silent != Unknown )
    01141     ret += QString::fromLatin1( " SILENT=%1" ).arg( d->silent == Yes ? 1 : 0 );
    01142     if( d->timestamp != -1U )
    01143         ret += QString::fromLatin1( " TIMESTAMP=%1" ).arg( d->timestamp );
    01144     if( d->screen != -1 )
    01145         ret += QString::fromLatin1( " SCREEN=%1" ).arg( d->screen );
    01146     return ret;
    01147     }
    01148
    01149 KStartupInfoData::KStartupInfoData( const QString& txt_P )
    01150     {
    01151     d = new KStartupInfoDataPrivate;
    01152     QStringList items = get_fields( txt_P );
    01153     const QString bin_str = QString::fromLatin1( "BIN=" );
    01154     const QString name_str = QString::fromLatin1( "NAME=" );
    01155     const QString description_str = QString::fromLatin1( "DESCRIPTION=" );
    01156     const QString icon_str = QString::fromLatin1( "ICON=" );
    01157     const QString desktop_str = QString::fromLatin1( "DESKTOP=" );
    01158     const QString wmclass_str = QString::fromLatin1( "WMCLASS=" );
    01159     const QString hostname_str = QString::fromLatin1( "HOSTNAME=" ); // SELI nonstd
    01160     const QString pid_str = QString::fromLatin1( "PID=" );  // SELI nonstd
    01161     const QString silent_str = QString::fromLatin1( "SILENT=" );
    01162     const QString timestamp_str = QString::fromLatin1( "TIMESTAMP=" );
    01163     const QString screen_str = QString::fromLatin1( "SCREEN=" );
    01164     for( QStringList::Iterator it = items.begin();
    01165          it != items.end();
    01166          ++it )
    01167         {
    01168         if( ( *it ).startsWith( bin_str ))
    01169             d->bin = get_str( *it );
    01170         else if( ( *it ).startsWith( name_str ))
    01171             d->name = get_str( *it );
    01172         else if( ( *it ).startsWith( description_str ))
    01173             d->description = get_str( *it );
    01174         else if( ( *it ).startsWith( icon_str ))
    01175             d->icon = get_str( *it );
    01176         else if( ( *it ).startsWith( desktop_str ))
    01177             {
    01178             d->desktop = get_num( *it );
    01179             if( d->desktop != NET::OnAllDesktops )
    01180                 ++d->desktop; // spec counts from 0
    01181             }
    01182         else if( ( *it ).startsWith( wmclass_str ))
    01183             d->wmclass = get_cstr( *it );
    01184         else if( ( *it ).startsWith( hostname_str ))
    01185             d->hostname = get_cstr( *it );
    01186         else if( ( *it ).startsWith( pid_str ))
    01187             addPid( get_num( *it ));
    01188         else if( ( *it ).startsWith( silent_str ))
    01189             d->silent = get_num( *it ) != 0 ? Yes : No;
    01190         else if( ( *it ).startsWith( timestamp_str ))
    01191             d->timestamp = get_unum( *it );
    01192         else if( ( *it ).startsWith( screen_str ))
    01193             d->screen = get_num( *it );
    01194         }
    01195     }
    01196
    01197 KStartupInfoData::KStartupInfoData( const KStartupInfoData& data )
    01198 {
    01199     d = new KStartupInfoDataPrivate( *data.d );
    01200 }
    01201
    01202 KStartupInfoData& KStartupInfoData::operator=( const KStartupInfoData& data )
    01203 {
    01204     if( &data == this )
    01205         return *this;
    01206     delete d;
    01207     d = new KStartupInfoDataPrivate( *data.d );
    01208     return *this;
    01209 }
    01210
    01211 void KStartupInfoData::update( const KStartupInfoData& data_P )
    01212     {
    01213     if( !data_P.bin().isEmpty())
    01214         d->bin = data_P.bin();
    01215     if( !data_P.name().isEmpty() && name().isEmpty()) // don't overwrite
    01216         d->name = data_P.name();
    01217     if( !data_P.description().isEmpty() && description().isEmpty()) // don't overwrite
    01218         d->description = data_P.description();
    01219     if( !data_P.icon().isEmpty() && icon().isEmpty()) // don't overwrite
    01220         d->icon = data_P.icon();
    01221     if( data_P.desktop() != 0 && desktop() == 0 ) // don't overwrite
    01222         d->desktop = data_P.desktop();
    01223     if( !data_P.d->wmclass.isEmpty())
    01224         d->wmclass = data_P.d->wmclass;
    01225     if( !data_P.d->hostname.isEmpty())
    01226         d->hostname = data_P.d->hostname;
    01227     for( QValueList< pid_t >::ConstIterator it = data_P.d->pids.begin();
    01228          it != data_P.d->pids.end();
    01229          ++it )
    01230         addPid( *it );
    01231     if( data_P.silent() != Unknown )
    01232     d->silent = data_P.silent();
    01233     if( data_P.timestamp() != -1U && timestamp() == -1U ) // don't overwrite
    01234         d->timestamp = data_P.timestamp();
    01235     if( data_P.screen() != -1 )
    01236         d->screen = data_P.screen();
    01237     }
    01238
    01239 KStartupInfoData::KStartupInfoData()
    01240 {
    01241     d = new KStartupInfoDataPrivate;
    01242 }
    01243
    01244 KStartupInfoData::~KStartupInfoData()
    01245 {
    01246     delete d;
    01247 }
    01248
    01249 void KStartupInfoData::setBin( const QString& bin_P )
    01250     {
    01251     d->bin = bin_P;
    01252     }
    01253
    01254 const QString& KStartupInfoData::bin() const
    01255     {
    01256     return d->bin;
    01257     }
    01258
    01259 void KStartupInfoData::setName( const QString& name_P )
    01260     {
    01261     d->name = name_P;
    01262     }
    01263
    01264 const QString& KStartupInfoData::name() const
    01265     {
    01266     return d->name;
    01267     }
    01268
    01269 const QString& KStartupInfoData::findName() const
    01270     {
    01271     if( !name().isEmpty())
    01272         return name();
    01273     return bin();
    01274     }
    01275
    01276 void KStartupInfoData::setDescription( const QString& desc_P )
    01277     {
    01278     d->description = desc_P;
    01279     }
    01280
    01281 const QString& KStartupInfoData::description() const
    01282     {
    01283     return d->description;
    01284     }
    01285
    01286 const QString& KStartupInfoData::findDescription() const
    01287     {
    01288     if( !description().isEmpty())
    01289         return description();
    01290     return name();
    01291     }
    01292
    01293 void KStartupInfoData::setIcon( const QString& icon_P )
    01294     {
    01295     d->icon = icon_P;
    01296     }
    01297
    01298 const QString& KStartupInfoData::findIcon() const
    01299     {
    01300     if( !icon().isEmpty())
    01301         return icon();
    01302     return bin();
    01303     }
    01304
    01305 const QString& KStartupInfoData::icon() const
    01306     {
    01307     return d->icon;
    01308     }
    01309
    01310 void KStartupInfoData::setDesktop( int desktop_P )
    01311     {
    01312     d->desktop = desktop_P;
    01313     }
    01314
    01315 int KStartupInfoData::desktop() const
    01316     {
    01317     return d->desktop;
    01318     }
    01319
    01320 void KStartupInfoData::setWMClass( const QCString& wmclass_P )
    01321     {
    01322     d->wmclass = wmclass_P;
    01323     }
    01324
    01325 const QCString KStartupInfoData::findWMClass() const
    01326     {
    01327     if( !WMClass().isEmpty() && WMClass() != "0" )
    01328         return WMClass();
    01329     return bin().utf8();
    01330     }
    01331
    01332 const QCString& KStartupInfoData::WMClass() const
    01333     {
    01334     return d->wmclass;
    01335     }
    01336
    01337 void KStartupInfoData::setHostname( const QCString& hostname_P )
    01338     {
    01339     if( !hostname_P.isNull())
    01340         d->hostname = hostname_P;
    01341     else
    01342         {
    01343         char tmp[ 256 ];
    01344         tmp[ 0 ] = '\0';
    01345         if (!gethostname( tmp, 255 ))
    01346         tmp[sizeof(tmp)-1] = '\0';
    01347         d->hostname = tmp;
    01348         }
    01349     }
    01350
    01351 const QCString& KStartupInfoData::hostname() const
    01352     {
    01353     return d->hostname;
    01354     }
    01355
    01356 void KStartupInfoData::addPid( pid_t pid_P )
    01357     {
    01358     if( !d->pids.contains( pid_P ))
    01359         d->pids.append( pid_P );
    01360     }
    01361
    01362 void KStartupInfoData::remove_pid( pid_t pid_P )
    01363     {
    01364     d->pids.remove( pid_P );
    01365     }
    01366
    01367 const QValueList< pid_t >& KStartupInfoData::pids() const
    01368     {
    01369     return d->pids;
    01370     }
    01371
    01372 bool KStartupInfoData::is_pid( pid_t pid_P ) const
    01373     {
    01374     return d->pids.contains( pid_P );
    01375     }
    01376
    01377 void KStartupInfoData::setSilent( TriState state_P )
    01378     {
    01379     d->silent = state_P;
    01380     }
    01381
    01382 KStartupInfoData::TriState KStartupInfoData::silent() const
    01383     {
    01384     return d->silent;
    01385     }
    01386
    01387 void KStartupInfoData::setTimestamp( unsigned long time )
    01388     {
    01389     d->timestamp = time;
    01390     }
    01391
    01392 unsigned long KStartupInfoData::timestamp() const
    01393     {
    01394     return d->timestamp;
    01395     }
    01396
    01397 void KStartupInfoData::setScreen( int screen )
    01398     {
    01399     d->screen = screen;
    01400     }
    01401
    01402 int KStartupInfoData::screen() const
    01403     {
    01404     return d->screen;
    01405     }
    01406
    01407 static
    01408 long get_num( const QString& item_P )
    01409     {
    01410     unsigned int pos = item_P.find( '=' );
    01411     return item_P.mid( pos + 1 ).toLong();
    01412     }
    01413
    01414 static
    01415 unsigned long get_unum( const QString& item_P )
    01416     {
    01417     unsigned int pos = item_P.find( '=' );
    01418     return item_P.mid( pos + 1 ).toULong();
    01419     }
    01420
    01421 static
    01422 QString get_str( const QString& item_P )
    01423     {
    01424     unsigned int pos = item_P.find( '=' );
    01425     if( item_P.length() > pos + 2 && item_P[ pos + 1 ] == '\"' )
    01426         {
    01427         int pos2 = item_P.left( pos + 2 ).find( '\"' );
    01428         if( pos2 < 0 )
    01429             return QString::null;                      // 01234
    01430         return item_P.mid( pos + 2, pos2 - 2 - pos );  // A="C"
    01431         }
    01432     return item_P.mid( pos + 1 );
    01433     }
    01434
    01435 static
    01436 QCString get_cstr( const QString& item_P )
    01437     {
    01438     return get_str( item_P ).utf8();
    01439     }
    01440
    01441 static
    01442 QStringList get_fields( const QString& txt_P )
    01443     {
    01444     QString txt = txt_P.simplifyWhiteSpace();
    01445     QStringList ret;
    01446     QString item = "";
    01447     bool in = false;
    01448     bool escape = false;
    01449     for( unsigned int pos = 0;
    01450          pos < txt.length();
    01451          ++pos )
    01452         {
    01453         if( escape )
    01454             {
    01455             item += txt[ pos ];
    01456             escape = false;
    01457             }
    01458         else if( txt[ pos ] == '\\' )
    01459             escape = true;
    01460         else if( txt[ pos ] == '\"' )
    01461             in = !in;
    01462         else if( txt[ pos ] == ' ' && !in )
    01463             {
    01464             ret.append( item );
    01465             item = "";
    01466             }
    01467         else
    01468             item += txt[ pos ];
    01469         }
    01470     ret.append( item );
    01471     return ret;
    01472     }
    01473
    01474 static QString escape_str( const QString& str_P )
    01475     {
    01476     QString ret = "";
    01477     for( unsigned int pos = 0;
    01478      pos < str_P.length();
    01479      ++pos )
    01480     {
    01481     if( str_P[ pos ] == '\\'
    01482         || str_P[ pos ] == '"' )
    01483         ret += '\\';
    01484     ret += str_P[ pos ];
    01485     }
    01486     return ret;
    01487     }
    01488
    01489 #include "kstartupinfo.moc"
    01490 #endif

This file is part of the documentation for kdecore Library Version 3.4.1.
Documentation copyright © 1996-2004 the KDE developers.
Generated on Sat Jun 11 20:18:11 2005 by doxygen 1.4.1 written by Dimitri van Heesch, © 1997-2003