kdecore Library API Documentation

kstartupinfo.cpp

00001 /****************************************************************************
00002 
00003  $Id: kstartupinfo.cpp,v 1.59 2004/07/15 14:07:27 lunakl Exp $
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 #if defined Q_WS_X11 && ! defined K_WS_QTONLY
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 #if defined Q_WS_X11 && ! defined K_WS_QTONLY
00055 #include <netwm.h> 
00056 #endif
00057 #include <kdebug.h>
00058 #include <kapplication.h>
00059 #include <signal.h>
00060 #if defined Q_WS_X11 && ! defined K_WS_QTONLY
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 #if defined Q_WS_X11 && ! defined K_WS_QTONLY
00101         KWinModule* wm_module;
00102         KXMessages msgs;
00103 #endif
00104     QTimer* cleanup;
00105     int flags;
00106     KStartupInfoPrivate( int flags_P )
00107             :
00108 #if defined Q_WS_X11 && ! defined K_WS_QTONLY
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 #if defined Q_WS_X11 && ! defined K_WS_QTONLY
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 #if defined Q_WS_X11 && ! defined K_WS_QTONLY
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 #if defined Q_WS_X11 && ! defined K_WS_QTONLY
00599 // SELI ???
00600     NETWinInfo info( qt_xdisplay(),  w_P, qt_xrootwin(),
00601         NET::WMWindowType | NET::WMPid | NET::WMState );
00602     // ignore NET::Tool and other special window types
00603     NET::WindowType type = info.windowType( NET::NormalMask | NET::DesktopMask
00604         | NET::DockMask | NET::ToolbarMask | NET::MenuMask | NET::DialogMask
00605         | NET::OverrideMask | NET::TopMenuMask | NET::UtilityMask | NET::SplashMask );
00606     if( type != NET::Normal
00607         && type != NET::Override
00608         && type != NET::Unknown
00609         && type != NET::Dialog
00610         && type != NET::Utility )
00611 //        && type != NET::Dock ) why did I put this here?
00612     return NoMatch;
00613     // lets see if this is a transient
00614     Window transient_for;
00615     if( XGetTransientForHint( qt_xdisplay(), static_cast< Window >( w_P ), &transient_for )
00616         && static_cast< WId >( transient_for ) != qt_xrootwin()
00617         && transient_for != None )
00618     return NoMatch;
00619 #endif
00620     // Strategy:
00621     //
00622     // Is this a compliant app ?
00623     //  - Yes - test for match
00624     //  - No - Is this a NET_WM compliant app ?
00625     //           - Yes - test for pid match
00626     //           - No - test for WM_CLASS match
00627     kdDebug( 172 ) << "check_startup" << endl;
00628     QCString id = windowStartupId( w_P );
00629     if( !id.isNull())
00630         {
00631         if( id.isEmpty() || id == "0" ) // means ignore this window
00632             {
00633             kdDebug( 172 ) << "ignore" << endl;
00634             return NoMatch;
00635             }
00636         return find_id( id, id_O, data_O ) ? Match : NoMatch;
00637         }
00638 #if defined Q_WS_X11 && ! defined K_WS_QTONLY
00639     pid_t pid = info.pid();
00640     if( pid > 0 )
00641         {
00642         QCString hostname = get_window_hostname( w_P );
00643         if( !hostname.isEmpty()
00644             && find_pid( pid, hostname, id_O, data_O ))
00645             return Match;
00646         // try XClass matching , this PID stuff sucks :(
00647         }
00648     XClassHint hint;
00649     if( XGetClassHint( qt_xdisplay(), w_P, &hint ) != 0 )
00650         { // We managed to read the class hint
00651         QCString res_name = hint.res_name;
00652         QCString res_class = hint.res_class;
00653         XFree( hint.res_name );
00654         XFree( hint.res_class );
00655         if( find_wclass( res_name, res_class, id_O, data_O ))
00656             return Match;
00657         }
00658 #endif
00659     kdDebug( 172 ) << "check_startup:cantdetect" << endl;
00660     return CantDetect;
00661     }
00662 
00663 bool KStartupInfo::find_id( const QCString& id_P, KStartupInfoId* id_O,
00664     KStartupInfoData* data_O )
00665     {
00666     if( d == NULL )
00667         return false;
00668     kdDebug( 172 ) << "find_id:" << id_P << endl;
00669     KStartupInfoId id;
00670     id.initId( id_P );
00671     if( d->startups.contains( id ))
00672         {
00673         if( id_O != NULL )
00674             *id_O = id;
00675         if( data_O != NULL )
00676             *data_O = d->startups[ id ];
00677         kdDebug( 172 ) << "check_startup_id:match" << endl;
00678         return true;
00679         }
00680     return false;
00681     }
00682 
00683 bool KStartupInfo::find_pid( pid_t pid_P, const QCString& hostname_P,
00684     KStartupInfoId* id_O, KStartupInfoData* data_O )
00685     {
00686     if( d == NULL )
00687         return false;
00688     kdDebug( 172 ) << "find_pid:" << pid_P << endl;
00689     for( QMap< KStartupInfoId, Data >::Iterator it = d->startups.begin();
00690          it != d->startups.end();
00691          ++it )
00692         {
00693         if( ( *it ).is_pid( pid_P ) && ( *it ).hostname() == hostname_P )
00694             { // Found it !
00695             if( id_O != NULL )
00696                 *id_O = it.key();
00697             if( data_O != NULL )
00698                 *data_O = *it;
00699             // non-compliant, remove on first match
00700             remove_startup_info_internal( it.key());
00701             kdDebug( 172 ) << "check_startup_pid:match" << endl;
00702             return true;
00703             }
00704         }
00705     return false;
00706     }
00707 
00708 bool KStartupInfo::find_wclass( QCString res_name, QCString res_class,
00709     KStartupInfoId* id_O, KStartupInfoData* data_O )
00710     {
00711     if( d == NULL )
00712         return false;
00713     res_name = res_name.lower();
00714     res_class = res_class.lower();
00715     kdDebug( 172 ) << "find_wclass:" << res_name << ":" << res_class << endl;
00716     for( QMap< KStartupInfoId, Data >::Iterator it = d->startups.begin();
00717          it != d->startups.end();
00718          ++it )
00719         {
00720         const QCString wmclass = ( *it ).findWMClass();
00721         if( wmclass.lower() == res_name || wmclass.lower() == res_class )
00722             { // Found it !
00723             if( id_O != NULL )
00724                 *id_O = it.key();
00725             if( data_O != NULL )
00726                 *data_O = *it;
00727             // non-compliant, remove on first match
00728             remove_startup_info_internal( it.key());
00729             kdDebug( 172 ) << "check_startup_wclass:match" << endl;
00730             return true;
00731             }
00732         }
00733     return false;
00734     }
00735 
00736 #if defined Q_WS_X11 && ! defined K_WS_QTONLY
00737 static Atom net_startup_atom = None;
00738 
00739 static QCString read_startup_id_property( WId w_P )
00740     {
00741     QCString ret;
00742     unsigned char *name_ret;
00743     Atom type_ret;
00744     int format_ret;
00745     unsigned long nitems_ret = 0, after_ret = 0;
00746     if( XGetWindowProperty( qt_xdisplay(), w_P, net_startup_atom, 0l, 4096,
00747             False, utf8_string_atom, &type_ret, &format_ret, &nitems_ret, &after_ret, &name_ret )
00748         == Success )
00749         {
00750     if( type_ret == utf8_string_atom && format_ret == 8 && name_ret != NULL )
00751         ret = reinterpret_cast< char* >( name_ret );
00752         if ( name_ret != NULL )
00753             XFree( name_ret );
00754         }
00755     return ret;
00756     }
00757 
00758 #endif
00759 
00760 QCString KStartupInfo::windowStartupId( WId w_P )
00761     {
00762 #if defined Q_WS_X11 && ! defined K_WS_QTONLY
00763     if( net_startup_atom == None )
00764         net_startup_atom = XInternAtom( qt_xdisplay(), NET_STARTUP_WINDOW, False );
00765     if( utf8_string_atom == None )
00766         utf8_string_atom = XInternAtom( qt_xdisplay(), "UTF8_STRING", False );
00767     QCString ret = read_startup_id_property( w_P );
00768     if( ret.isEmpty())
00769         { // retry with window group leader, as the spec says
00770         XWMHints* hints = XGetWMHints( qt_xdisplay(), w_P );
00771         if( hints && ( hints->flags & WindowGroupHint ) != 0 )
00772             ret = read_startup_id_property( hints->window_group );
00773         if( hints )
00774             XFree( hints );
00775         }
00776     return ret;
00777 #else
00778     return QCString();
00779 #endif
00780     }
00781 
00782 void KStartupInfo::setWindowStartupId( WId w_P, const QCString& id_P )
00783     {
00784 #if defined Q_WS_X11 && ! defined K_WS_QTONLY
00785     if( id_P.isNull())
00786         return;
00787     if( net_startup_atom == None )
00788         net_startup_atom = XInternAtom( qt_xdisplay(), NET_STARTUP_WINDOW, False );
00789     if( utf8_string_atom == None )
00790         utf8_string_atom = XInternAtom( qt_xdisplay(), "UTF8_STRING", False );
00791     XChangeProperty( qt_xdisplay(), w_P, net_startup_atom, utf8_string_atom, 8,
00792         PropModeReplace, reinterpret_cast< unsigned char* >( id_P.data()), id_P.length());
00793 #endif
00794     }
00795 
00796 QCString KStartupInfo::get_window_hostname( WId w_P )
00797     {
00798 #if defined Q_WS_X11 && ! defined K_WS_QTONLY
00799     XTextProperty tp;
00800     char** hh;
00801     int cnt;
00802     if( XGetWMClientMachine( qt_xdisplay(), w_P, &tp ) != 0
00803         && XTextPropertyToStringList( &tp, &hh, &cnt ) != 0 )
00804         {
00805         if( cnt == 1 )
00806             {
00807             QCString hostname = hh[ 0 ];
00808             XFreeStringList( hh );
00809             return hostname;
00810             }
00811         XFreeStringList( hh );
00812         }
00813 #endif
00814     // no hostname
00815     return QCString();
00816     }
00817 
00818 void KStartupInfo::setTimeout( unsigned int secs_P )
00819     {
00820     timeout = secs_P;
00821  // schedule removing entries that are older than the new timeout
00822     QTimer::singleShot( 0, this, SLOT( startups_cleanup_no_age()));
00823     }
00824 
00825 void KStartupInfo::startups_cleanup_no_age()
00826     {
00827     startups_cleanup_internal( false );
00828     }
00829 
00830 void KStartupInfo::startups_cleanup()
00831     {
00832     if( d == NULL )
00833         return;
00834     if( d->startups.count() == 0 && d->silent_startups.count() == 0
00835         && d->uninited_startups.count() == 0 )
00836         {
00837         d->cleanup->stop();
00838         return;
00839         }
00840     startups_cleanup_internal( true );
00841     }
00842 
00843 void KStartupInfo::startups_cleanup_internal( bool age_P )
00844     {
00845     if( d == NULL )
00846         return;
00847     for( QMap< KStartupInfoId, Data >::Iterator it = d->startups.begin();
00848          it != d->startups.end();
00849          )
00850         {
00851         if( age_P )
00852             ( *it ).age++;
00853     unsigned int tout = timeout;
00854     if( ( *it ).silent() == Data::Yes ) // TODO
00855         tout *= 20;
00856         if( ( *it ).age >= tout )
00857             {
00858             const KStartupInfoId& key = it.key();
00859             ++it;
00860             kdDebug( 172 ) << "entry timeout:" << key.id() << endl;
00861             remove_startup_info_internal( key );
00862             }
00863         else
00864             ++it;
00865         }
00866     for( QMap< KStartupInfoId, Data >::Iterator it = d->silent_startups.begin();
00867          it != d->silent_startups.end();
00868          )
00869         {
00870         if( age_P )
00871             ( *it ).age++;
00872     unsigned int tout = timeout;
00873     if( ( *it ).silent() == Data::Yes ) // TODO
00874         tout *= 20;
00875         if( ( *it ).age >= tout )
00876             {
00877             const KStartupInfoId& key = it.key();
00878             ++it;
00879             kdDebug( 172 ) << "entry timeout:" << key.id() << endl;
00880             remove_startup_info_internal( key );
00881             }
00882         else
00883             ++it;
00884         }
00885     for( QMap< KStartupInfoId, Data >::Iterator it = d->uninited_startups.begin();
00886          it != d->uninited_startups.end();
00887          )
00888         {
00889         if( age_P )
00890             ( *it ).age++;
00891     unsigned int tout = timeout;
00892     if( ( *it ).silent() == Data::Yes ) // TODO
00893         tout *= 20;
00894         if( ( *it ).age >= tout )
00895             {
00896             const KStartupInfoId& key = it.key();
00897             ++it;
00898             kdDebug( 172 ) << "entry timeout:" << key.id() << endl;
00899             remove_startup_info_internal( key );
00900             }
00901         else
00902             ++it;
00903         }
00904     }
00905 
00906 void KStartupInfo::clean_all_noncompliant()
00907     {
00908     if( d == NULL )
00909         return;
00910     for( QMap< KStartupInfoId, Data >::Iterator it = d->startups.begin();
00911          it != d->startups.end();
00912          )
00913         {
00914         if( ( *it ).WMClass() != "0" )
00915             {
00916             ++it;
00917             continue;
00918             }
00919         const KStartupInfoId& key = it.key();
00920         ++it;
00921         kdDebug( 172 ) << "entry cleaning:" << key.id() << endl;
00922         remove_startup_info_internal( key );
00923         }
00924     }
00925 
00926 QCString KStartupInfo::createNewStartupId()
00927     {
00928     // Assign a unique id, use hostname+time+pid, that should be 200% unique.
00929     // Also append the user timestamp (for focus stealing prevention).
00930     struct timeval tm;
00931     gettimeofday( &tm, NULL );
00932     char hostname[ 256 ];
00933     hostname[ 0 ] = '\0';
00934     if (!gethostname( hostname, 255 ))
00935     hostname[sizeof(hostname)-1] = '\0';
00936 #if defined Q_WS_X11 && ! defined K_WS_QTONLY
00937     extern Time qt_x_user_time;
00938 #else
00939     long qt_x_user_time = 0;
00940 #endif
00941     QCString id = QString( "%1;%2;%3;%4_TIME%5" ).arg( hostname ).arg( tm.tv_sec )
00942         .arg( tm.tv_usec ).arg( getpid()).arg( qt_x_user_time ).utf8();
00943     kdDebug( 172 ) << "creating: " << id << ":" << qAppName() << endl;
00944     return id;
00945     }
00946 
00947 
00948 struct KStartupInfoIdPrivate
00949     {
00950     KStartupInfoIdPrivate() : id( "" ) {};
00951     QCString id; // id
00952     };
00953 
00954 const QCString& KStartupInfoId::id() const
00955     {
00956     return d->id;
00957     }
00958 
00959 
00960 QString KStartupInfoId::to_text() const
00961     {
00962     return QString::fromLatin1( " ID=\"%1\" " ).arg( escape_str( id()));
00963     }
00964 
00965 KStartupInfoId::KStartupInfoId( const QString& txt_P )
00966     {
00967     d = new KStartupInfoIdPrivate;
00968     QStringList items = get_fields( txt_P );
00969     const QString id_str = QString::fromLatin1( "ID=" );
00970     for( QStringList::Iterator it = items.begin();
00971          it != items.end();
00972          ++it )
00973         {
00974         if( ( *it ).startsWith( id_str ))
00975             d->id = get_cstr( *it );
00976         }
00977     }
00978 
00979 void KStartupInfoId::initId( const QCString& id_P )
00980     {
00981     if( !id_P.isEmpty())
00982         {
00983         d->id = id_P;
00984 #ifdef KSTARTUPINFO_ALL_DEBUG
00985         kdDebug( 172 ) << "using: " << d->id << endl;
00986 #endif
00987         return;
00988         }
00989     const char* startup_env = getenv( NET_STARTUP_ENV );
00990     if( startup_env != NULL && *startup_env != '\0' )
00991         { // already has id
00992         d->id = startup_env;
00993 #ifdef KSTARTUPINFO_ALL_DEBUG
00994         kdDebug( 172 ) << "reusing: " << d->id << endl;
00995 #endif
00996         return;
00997         }
00998     d->id = KStartupInfo::createNewStartupId();
00999     }
01000     
01001 bool KStartupInfoId::setupStartupEnv() const
01002     {
01003     if( id().isEmpty())
01004         {
01005         unsetenv( NET_STARTUP_ENV );
01006         return false;
01007         }
01008     return setenv( NET_STARTUP_ENV, id(), true ) == 0;
01009     }
01010 
01011 KStartupInfoId KStartupInfo::currentStartupIdEnv()
01012     {
01013     const char* startup_env = getenv( NET_STARTUP_ENV );
01014     KStartupInfoId id;
01015     if( startup_env != NULL && *startup_env != '\0' )
01016         id.d->id = startup_env;
01017     else
01018         id.d->id = "0";
01019     return id;
01020     }
01021 
01022 void KStartupInfo::resetStartupEnv()
01023     {
01024     unsetenv( NET_STARTUP_ENV );
01025     }
01026 
01027 KStartupInfoId::KStartupInfoId()
01028     {
01029     d = new KStartupInfoIdPrivate;
01030     }
01031 
01032 KStartupInfoId::~KStartupInfoId()
01033     {
01034     delete d;
01035     }
01036 
01037 KStartupInfoId::KStartupInfoId( const KStartupInfoId& id_P )
01038     {
01039     d = new KStartupInfoIdPrivate( *id_P.d );
01040     }
01041 
01042 KStartupInfoId& KStartupInfoId::operator=( const KStartupInfoId& id_P )
01043     {
01044     if( &id_P == this )
01045         return *this;
01046     delete d;
01047     d = new KStartupInfoIdPrivate( *id_P.d );
01048     return *this;
01049     }
01050 
01051 bool KStartupInfoId::operator==( const KStartupInfoId& id_P ) const
01052     {
01053     return id() == id_P.id();
01054     }
01055 
01056 bool KStartupInfoId::operator!=( const KStartupInfoId& id_P ) const
01057     {
01058     return !(*this == id_P );
01059     }
01060 
01061 // needed for QMap
01062 bool KStartupInfoId::operator<( const KStartupInfoId& id_P ) const
01063     {
01064     return id() < id_P.id();
01065     }
01066 
01067 bool KStartupInfoId::none() const
01068     {
01069     return d->id.isEmpty() || d->id == "0";
01070     }
01071 
01072 unsigned long KStartupInfoId::timestamp() const
01073     {
01074     if( none())
01075         return 0;
01076     int pos = d->id.findRev( "_TIME" );
01077     if( pos >= 0 )
01078         {
01079         bool ok;
01080         long time = d->id.mid( pos + 5 ).toLong( &ok );
01081         if( ok )
01082             return time;
01083         }
01084     // libstartup-notification style :
01085     // snprintf (s, len, "%s/%s/%lu/%d-%d-%s",
01086     //   canonicalized_launcher, canonicalized_launchee, (unsigned long) timestamp,
01087     //  (int) getpid (), (int) sequence_number, hostbuf);
01088     int pos1 = d->id.findRev( '/' );
01089     if( pos1 > 0 )
01090         {
01091         int pos2 = d->id.findRev( '/', pos1 - 1 );
01092         if( pos2 >= 0 )
01093             {
01094             bool ok;
01095             long time = d->id.mid( pos2 + 1, pos1 - pos2 - 1 ).toLong( &ok );
01096             if( ok )
01097                 return time;
01098             }
01099         }
01100     // bah ... old KStartupInfo or a problem
01101     return 0;
01102     }
01103 
01104 struct KStartupInfoDataPrivate
01105     {
01106     KStartupInfoDataPrivate() : desktop( 0 ), wmclass( "" ), hostname( "" ),
01107     silent( KStartupInfoData::Unknown ), timestamp( -1U ), screen( -1 ) {};
01108     QString bin;
01109     QString name;
01110     QString description;
01111     QString icon;
01112     int desktop;
01113     QValueList< pid_t > pids;
01114     QCString wmclass;
01115     QCString hostname;
01116     KStartupInfoData::TriState silent;
01117     unsigned long timestamp;
01118     int screen;
01119     };
01120 
01121 QString KStartupInfoData::to_text() const
01122     {
01123     QString ret = "";
01124     if( !d->bin.isEmpty())
01125         ret += QString::fromLatin1( " BIN=\"%1\"" ).arg( escape_str( d->bin ));
01126     if( !d->name.isEmpty())
01127         ret += QString::fromLatin1( " NAME=\"%1\"" ).arg( escape_str( d->name ));
01128     if( !d->description.isEmpty())
01129         ret += QString::fromLatin1( " DESCRIPTION=\"%1\"" ).arg( escape_str( d->description ));
01130     if( !d->icon.isEmpty())
01131         ret += QString::fromLatin1( " ICON=%1" ).arg( d->icon );
01132     if( d->desktop != 0 )
01133         ret += QString::fromLatin1( " DESKTOP=%1" )
01134             .arg( d->desktop == NET::OnAllDesktops ? NET::OnAllDesktops : d->desktop - 1 ); // spec counts from 0
01135     if( !d->wmclass.isEmpty())
01136         ret += QString::fromLatin1( " WMCLASS=%1" ).arg( d->wmclass );
01137     if( !d->hostname.isEmpty())
01138         ret += QString::fromLatin1( " HOSTNAME=%1" ).arg( d->hostname );
01139     for( QValueList< pid_t >::ConstIterator it = d->pids.begin();
01140          it != d->pids.end();
01141          ++it )
01142         ret += QString::fromLatin1( " PID=%1" ).arg( *it );
01143     if( d->silent != Unknown )
01144     ret += QString::fromLatin1( " SILENT=%1" ).arg( d->silent == Yes ? 1 : 0 );
01145     if( d->timestamp != -1U )
01146         ret += QString::fromLatin1( " TIMESTAMP=%1" ).arg( d->timestamp );
01147     if( d->screen != -1 )
01148         ret += QString::fromLatin1( " SCREEN=%1" ).arg( d->screen );
01149     return ret;
01150     }
01151 
01152 KStartupInfoData::KStartupInfoData( const QString& txt_P )
01153     {
01154     d = new KStartupInfoDataPrivate;
01155     QStringList items = get_fields( txt_P );
01156     const QString bin_str = QString::fromLatin1( "BIN=" );
01157     const QString name_str = QString::fromLatin1( "NAME=" );
01158     const QString description_str = QString::fromLatin1( "DESCRIPTION=" );
01159     const QString icon_str = QString::fromLatin1( "ICON=" );
01160     const QString desktop_str = QString::fromLatin1( "DESKTOP=" );
01161     const QString wmclass_str = QString::fromLatin1( "WMCLASS=" );
01162     const QString hostname_str = QString::fromLatin1( "HOSTNAME=" ); // SELI nonstd
01163     const QString pid_str = QString::fromLatin1( "PID=" );  // SELI nonstd
01164     const QString silent_str = QString::fromLatin1( "SILENT=" );
01165     const QString timestamp_str = QString::fromLatin1( "TIMESTAMP=" );
01166     const QString screen_str = QString::fromLatin1( "SCREEN=" );
01167     for( QStringList::Iterator it = items.begin();
01168          it != items.end();
01169          ++it )
01170         {
01171         if( ( *it ).startsWith( bin_str ))
01172             d->bin = get_str( *it );
01173         else if( ( *it ).startsWith( name_str ))
01174             d->name = get_str( *it );
01175         else if( ( *it ).startsWith( description_str ))
01176             d->description = get_str( *it );
01177         else if( ( *it ).startsWith( icon_str ))
01178             d->icon = get_str( *it );
01179         else if( ( *it ).startsWith( desktop_str ))
01180             {
01181             d->desktop = get_num( *it );
01182             if( d->desktop != NET::OnAllDesktops )
01183                 ++d->desktop; // spec counts from 0
01184             }
01185         else if( ( *it ).startsWith( wmclass_str ))
01186             d->wmclass = get_cstr( *it );
01187         else if( ( *it ).startsWith( hostname_str ))
01188             d->hostname = get_cstr( *it );
01189         else if( ( *it ).startsWith( pid_str ))
01190             addPid( get_num( *it ));
01191         else if( ( *it ).startsWith( silent_str ))
01192             d->silent = get_num( *it ) != 0 ? Yes : No;
01193         else if( ( *it ).startsWith( timestamp_str ))
01194             d->timestamp = get_unum( *it );
01195         else if( ( *it ).startsWith( screen_str ))
01196             d->screen = get_num( *it );
01197         }
01198     }
01199 
01200 KStartupInfoData::KStartupInfoData( const KStartupInfoData& data )
01201 {
01202     d = new KStartupInfoDataPrivate( *data.d );
01203 }
01204 
01205 KStartupInfoData& KStartupInfoData::operator=( const KStartupInfoData& data )
01206 {
01207     if( &data == this )
01208         return *this;
01209     delete d;
01210     d = new KStartupInfoDataPrivate( *data.d );
01211     return *this;
01212 }
01213 
01214 void KStartupInfoData::update( const KStartupInfoData& data_P )
01215     {
01216     if( !data_P.bin().isEmpty())
01217         d->bin = data_P.bin();
01218     if( !data_P.name().isEmpty() && name().isEmpty()) // don't overwrite
01219         d->name = data_P.name();
01220     if( !data_P.description().isEmpty() && description().isEmpty()) // don't overwrite
01221         d->description = data_P.description();
01222     if( !data_P.icon().isEmpty() && icon().isEmpty()) // don't overwrite
01223         d->icon = data_P.icon();
01224     if( data_P.desktop() != 0 && desktop() == 0 ) // don't overwrite
01225         d->desktop = data_P.desktop();
01226     if( !data_P.d->wmclass.isEmpty())
01227         d->wmclass = data_P.d->wmclass;
01228     if( !data_P.d->hostname.isEmpty())
01229         d->hostname = data_P.d->hostname;
01230     for( QValueList< pid_t >::ConstIterator it = data_P.d->pids.begin();
01231          it != data_P.d->pids.end();
01232          ++it )
01233         addPid( *it );
01234     if( data_P.silent() != Unknown )
01235     d->silent = data_P.silent();
01236     if( data_P.timestamp() != -1U && timestamp() == -1U ) // don't overwrite
01237         d->timestamp = data_P.timestamp();
01238     if( data_P.screen() != -1 )
01239         d->screen = data_P.screen();
01240     }
01241 
01242 KStartupInfoData::KStartupInfoData()
01243 {
01244     d = new KStartupInfoDataPrivate;
01245 }
01246 
01247 KStartupInfoData::~KStartupInfoData()
01248 {
01249     delete d;
01250 }
01251 
01252 void KStartupInfoData::setBin( const QString& bin_P )
01253     {
01254     d->bin = bin_P;
01255     }
01256 
01257 const QString& KStartupInfoData::bin() const
01258     {
01259     return d->bin;
01260     }
01261 
01262 void KStartupInfoData::setName( const QString& name_P )
01263     {
01264     d->name = name_P;
01265     }
01266 
01267 const QString& KStartupInfoData::name() const
01268     {
01269     return d->name;
01270     }
01271 
01272 const QString& KStartupInfoData::findName() const
01273     {
01274     if( !name().isEmpty())
01275         return name();
01276     return bin();
01277     }
01278 
01279 void KStartupInfoData::setDescription( const QString& desc_P )
01280     {
01281     d->description = desc_P;
01282     }
01283 
01284 const QString& KStartupInfoData::description() const
01285     {
01286     return d->description;
01287     }
01288 
01289 const QString& KStartupInfoData::findDescription() const
01290     {
01291     if( !description().isEmpty())
01292         return description();
01293     return name();
01294     }
01295 
01296 void KStartupInfoData::setIcon( const QString& icon_P )
01297     {
01298     d->icon = icon_P;
01299     }
01300 
01301 const QString& KStartupInfoData::findIcon() const
01302     {
01303     if( !icon().isEmpty())
01304         return icon();
01305     return bin();
01306     }
01307 
01308 const QString& KStartupInfoData::icon() const
01309     {
01310     return d->icon;
01311     }
01312 
01313 void KStartupInfoData::setDesktop( int desktop_P )
01314     {
01315     d->desktop = desktop_P;
01316     }
01317 
01318 int KStartupInfoData::desktop() const
01319     {
01320     return d->desktop;
01321     }
01322 
01323 void KStartupInfoData::setWMClass( const QCString& wmclass_P )
01324     {
01325     d->wmclass = wmclass_P;
01326     }
01327 
01328 const QCString KStartupInfoData::findWMClass() const
01329     {
01330     if( !WMClass().isEmpty() && WMClass() != "0" )
01331         return WMClass();
01332     return bin().utf8();
01333     }
01334 
01335 const QCString& KStartupInfoData::WMClass() const
01336     {
01337     return d->wmclass;
01338     }
01339 
01340 void KStartupInfoData::setHostname( const QCString& hostname_P )
01341     {
01342     if( !hostname_P.isNull())
01343         d->hostname = hostname_P;
01344     else
01345         {
01346         char tmp[ 256 ];
01347         tmp[ 0 ] = '\0';
01348         if (!gethostname( tmp, 255 ))
01349         tmp[sizeof(tmp)-1] = '\0';
01350         d->hostname = tmp;
01351         }
01352     }
01353 
01354 const QCString& KStartupInfoData::hostname() const
01355     {
01356     return d->hostname;
01357     }
01358 
01359 void KStartupInfoData::addPid( pid_t pid_P )
01360     {
01361     if( !d->pids.contains( pid_P ))
01362         d->pids.append( pid_P );
01363     }
01364 
01365 void KStartupInfoData::remove_pid( pid_t pid_P )
01366     {
01367     d->pids.remove( pid_P );
01368     }
01369 
01370 const QValueList< pid_t >& KStartupInfoData::pids() const
01371     {
01372     return d->pids;
01373     }
01374 
01375 bool KStartupInfoData::is_pid( pid_t pid_P ) const
01376     {
01377     return d->pids.contains( pid_P );
01378     }
01379 
01380 void KStartupInfoData::setSilent( TriState state_P )
01381     {
01382     d->silent = state_P;
01383     }
01384 
01385 KStartupInfoData::TriState KStartupInfoData::silent() const
01386     {
01387     return d->silent;
01388     }
01389 
01390 void KStartupInfoData::setTimestamp( unsigned long time )
01391     {
01392     d->timestamp = time;
01393     }
01394 
01395 unsigned long KStartupInfoData::timestamp() const
01396     {
01397     return d->timestamp;
01398     }
01399 
01400 void KStartupInfoData::setScreen( int screen )
01401     {
01402     d->screen = screen;
01403     }
01404 
01405 int KStartupInfoData::screen() const
01406     {
01407     return d->screen;
01408     }
01409 
01410 static
01411 long get_num( const QString& item_P )
01412     {
01413     unsigned int pos = item_P.find( '=' );
01414     return item_P.mid( pos + 1 ).toLong();
01415     }
01416 
01417 static
01418 unsigned long get_unum( const QString& item_P )
01419     {
01420     unsigned int pos = item_P.find( '=' );
01421     return item_P.mid( pos + 1 ).toULong();
01422     }
01423 
01424 static
01425 QString get_str( const QString& item_P )
01426     {
01427     unsigned int pos = item_P.find( '=' );
01428     if( item_P.length() > pos + 2 && item_P[ pos + 1 ] == '\"' )
01429         {
01430         int pos2 = item_P.left( pos + 2 ).find( '\"' );
01431         if( pos2 < 0 )
01432             return QString::null;                      // 01234
01433         return item_P.mid( pos + 2, pos2 - 2 - pos );  // A="C"
01434         }
01435     return item_P.mid( pos + 1 );
01436     }
01437 
01438 static
01439 QCString get_cstr( const QString& item_P )
01440     {
01441     return get_str( item_P ).utf8();
01442     }
01443 
01444 static
01445 QStringList get_fields( const QString& txt_P )
01446     {
01447     QString txt = txt_P.simplifyWhiteSpace();
01448     QStringList ret;
01449     QString item = "";
01450     bool in = false;
01451     bool escape = false;
01452     for( unsigned int pos = 0;
01453          pos < txt.length();
01454          ++pos )
01455         {
01456         if( escape )
01457             {
01458             item += txt[ pos ];
01459             escape = false;
01460             }
01461         else if( txt[ pos ] == '\\' )
01462             escape = true;
01463         else if( txt[ pos ] == '\"' )
01464             in = !in;
01465         else if( txt[ pos ] == ' ' && !in )
01466             {
01467             ret.append( item );
01468             item = "";
01469             }
01470         else
01471             item += txt[ pos ];
01472         }
01473     ret.append( item );
01474     return ret;
01475     }
01476 
01477 static QString escape_str( const QString& str_P )
01478     {
01479     QString ret = "";
01480     for( unsigned int pos = 0;
01481      pos < str_P.length();
01482      ++pos )
01483     {
01484     if( str_P[ pos ] == '\\'
01485         || str_P[ pos ] == '"' )
01486         ret += '\\';
01487     ret += str_P[ pos ];
01488     }
01489     return ret;
01490     }
01491 
01492 #include "kstartupinfo.moc"
01493 #endif
KDE Logo
This file is part of the documentation for kdecore Library Version 3.3.0.
Documentation copyright © 1996-2004 the KDE developers.
Generated on Thu Sep 23 17:11:38 2004 by doxygen 1.3.8-20040913 written by Dimitri van Heesch, © 1997-2003