kstartupinfo.cpp

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