kdecore Library API Documentation

kstartupinfo.cpp

00001 /****************************************************************************
00002 
00003  $Id: kstartupinfo.cpp,v 1.32.2.5 2003/02/25 08:05:12 lunakl Exp $
00004 
00005  Copyright (C) 2001 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 #endif
00031 
00032 #include <qwidget.h>
00033 #ifdef Q_WS_X11 // FIXME(E): Re-implement in a less X11 specific way
00034 #include <qglobal.h>
00035 #ifdef HAVE_CONFIG_H
00036 #include <config.h>
00037 #endif
00038 
00039 // need to resolve INT32(qglobal.h)<>INT32(Xlibint.h) conflict
00040 #ifndef QT_CLEAN_NAMESPACE
00041 #define QT_CLEAN_NAMESPACE
00042 #endif
00043 
00044 #include "kstartupinfo.h"
00045 
00046 #include <unistd.h>
00047 #include <sys/time.h>
00048 #include <stdlib.h>
00049 #include <qtimer.h>
00050 #include <netwm.h>
00051 #include <kdebug.h>
00052 #include <kapplication.h>
00053 #include <signal.h>
00054 #include <kwinmodule.h>
00055 #include <kxmessages.h>
00056 #ifndef KDE_USE_FINAL
00057 #include <X11/Xlibint.h> // cannot be included multiple times
00058 #endif
00059 
00060 #undef Data // crappy X11
00061 
00062 #ifndef None  // CHECKME
00063 #define None 0
00064 #endif
00065 
00066 static const char* const KDE_STARTUP_INFO = "_KDE_STARTUP_INFO";
00067 static const char* const KDE_STARTUP_ID = "_KDE_STARTUP_ID";
00068 // KDE_STARTUP_ENV is used also in kinit/wrapper.c
00069 static const char* const KDE_STARTUP_ENV = "KDE_STARTUP_ENV";
00070 
00071 // TODO these two are for backward compatibility with KDE2.x
00072 static const char* const KDE_STARTUP_INFO_2 = "KDE_STARTUP_INFO";
00073 static const char* const KDE_STARTUP_ID_2 = "KDE_STARTUP_ID";
00074 
00075 static int get_num( 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 class KStartupInfo::Data
00082     : public KStartupInfoData
00083     {
00084     public:
00085         Data() {}; // just because it's in a QMap
00086         Data( const QString& txt_P )
00087             : KStartupInfoData( txt_P ), age( 0 ) {};
00088         unsigned int age;
00089     };
00090 
00091 struct KStartupInfoPrivate
00092     {
00093     public:
00094         QMap< KStartupInfoId, KStartupInfo::Data > startups;
00095         // contains silenced ASN's only if !AnnounceSilencedChanges
00096         QMap< KStartupInfoId, KStartupInfo::Data > silent_startups;
00097         KWinModule* wm_module;
00098         KXMessages msgs;
00099         KXMessages msgs_2;
00100         QTimer* cleanup;
00101         int flags;
00102         KStartupInfoPrivate( int flags_P )
00103             : msgs( KDE_STARTUP_INFO ), msgs_2( KDE_STARTUP_INFO_2 ),
00104               flags( flags_P ) {}
00105     };
00106 
00107 KStartupInfo::KStartupInfo( int flags_P, QObject* parent_P, const char* name_P )
00108     : QObject( parent_P, name_P ),
00109         timeout( 60 ), d( NULL )
00110     {
00111     init( flags_P );
00112     }
00113 
00114 KStartupInfo::KStartupInfo( bool clean_on_cantdetect_P, QObject* parent_P, const char* name_P )
00115     : QObject( parent_P, name_P ),
00116         timeout( 60 ), d( NULL )
00117     {
00118     init( clean_on_cantdetect_P ? CleanOnCantDetect : 0 );
00119     }
00120 
00121 void KStartupInfo::init( int flags_P )
00122     {
00123     // d == NULL means "disabled"
00124     if (!KApplication::kApplication()) return;
00125     if (!KApplication::kApplication()->getDisplay()) return;
00126 
00127     d = new KStartupInfoPrivate( flags_P );
00128     if( !( d->flags & DisableKWinModule ))
00129         {
00130         d->wm_module = new KWinModule( this );
00131         connect( d->wm_module, SIGNAL( windowAdded( WId )), SLOT( slot_window_added( WId )));
00132         connect( d->wm_module, SIGNAL( systemTrayWindowAdded( WId )), SLOT( slot_window_added( WId )));
00133         }
00134     else
00135         d->wm_module = NULL;
00136     connect( &d->msgs, SIGNAL( gotMessage( const QString& )), SLOT( got_message( const QString& )));
00137     connect( &d->msgs_2, SIGNAL( gotMessage( const QString& )), SLOT( got_message( const QString& )));
00138     d->cleanup = new QTimer( this );
00139     connect( d->cleanup, SIGNAL( timeout()), SLOT( startups_cleanup()));
00140     }
00141 
00142 KStartupInfo::~KStartupInfo()
00143     {
00144     delete d;
00145     }
00146 
00147 void KStartupInfo::got_message( const QString& msg_P )
00148     {
00149     kdDebug( 172 ) << "got:" << msg_P << endl;
00150     QString msg = msg_P.stripWhiteSpace();
00151     if( msg.startsWith( "new:" )) // must match length below
00152         got_startup_info( msg.mid( 4 ), false );
00153     else if( msg.startsWith( "change:" )) // must match length below
00154         got_startup_info( msg.mid( 7 ), true );
00155     else if( msg.startsWith( "remove:" )) // must match length below
00156         got_remove_startup_info( msg.mid( 7 ));
00157     }
00158 
00159 // if the application stops responding for a while, KWinModule may get
00160 // the information about the already mapped window before KXMessages
00161 // actually gets the info about the started application (depends
00162 // on their order in X11 event filter in KApplication)
00163 // simply delay info from KWinModule a bit
00164 namespace
00165 {
00166 class DelayedWindowEvent
00167     : public QCustomEvent
00168     {
00169     public:
00170         DelayedWindowEvent( WId w_P )
00171             : QCustomEvent( QEvent::User + 15 ), w( w_P ) {}
00172         Window w;
00173     };
00174 }
00175 
00176 void KStartupInfo::slot_window_added( WId w_P )
00177     {
00178     kapp->postEvent( this, new DelayedWindowEvent( w_P ));
00179     }
00180 
00181 void KStartupInfo::customEvent( QCustomEvent* e_P )
00182     {
00183     if( e_P->type() == QEvent::User + 15 )
00184         window_added( static_cast< DelayedWindowEvent* >( e_P )->w );
00185     else
00186         QObject::customEvent( e_P );
00187     }
00188 
00189 void KStartupInfo::window_added( WId w_P )
00190     {
00191     KStartupInfoId id;
00192     KStartupInfoData data;
00193     startup_t ret = check_startup_internal( w_P, &id, &data, false );
00194     switch( ret )
00195         {
00196         case Match:
00197             kdDebug( 172 ) << "new window match" << endl;
00198             // HACK
00199             // with KDE's spec, first matching window ends ASN
00200             // but when silenced, ASN shouldn't be ended
00201             // TODO this shouldn't be needed with the new spec?
00202             if( data.silent() != KStartupInfoData::Yes )
00203                 remove_startup_info_internal( id );
00204           break;
00205         case NoMatch:
00206           break; // nothing
00207         case CantDetect:
00208             if( d->flags & CleanOnCantDetect )
00209                 clean_all_noncompliant();
00210           break;
00211         }
00212     }
00213 
00214 void KStartupInfo::got_startup_info( const QString& msg_P, bool update_only_P )
00215     {
00216     KStartupInfoId id( msg_P );
00217     if( id.none())
00218         return;
00219     KStartupInfo::Data data( msg_P );
00220     new_startup_info_internal( id, data, update_only_P );
00221     }
00222 
00223 void KStartupInfo::new_startup_info_internal( const KStartupInfoId& id_P,
00224     Data& data_P, bool update_only_P )
00225     {
00226     if (!d) return;
00227     if( id_P.none())
00228         return;
00229     if( d->startups.contains( id_P ))
00230         { // already reported, update
00231         d->startups[ id_P ].update( data_P );
00232         d->startups[ id_P ].age = 0; // CHECKME
00233         kdDebug( 172 ) << "updating" << endl;
00234         if( d->startups[ id_P ].silent() == Data::Yes
00235             && !( d->flags & AnnounceSilenceChanges ))
00236             {
00237             d->silent_startups[ id_P ] = d->startups[ id_P ];
00238             d->startups.remove( id_P );
00239             emit gotRemoveStartup( id_P, d->silent_startups[ id_P ] );
00240             return;
00241             }
00242         emit gotStartupChange( id_P, d->startups[ id_P ] );
00243         return;
00244         }
00245     if( d->silent_startups.contains( id_P ))
00246         { // already reported, update
00247         d->silent_startups[ id_P ].update( data_P );
00248         d->silent_startups[ id_P ].age = 0; // CHECKME
00249         kdDebug( 172 ) << "updating silenced" << endl;
00250         if( d->silent_startups[ id_P ].silent() != Data::Yes )
00251             {
00252             d->startups[ id_P ] = d->silent_startups[ id_P ];
00253             d->silent_startups.remove( id_P );
00254             emit gotNewStartup( id_P, d->startups[ id_P ] );
00255             return;
00256             }
00257         emit gotStartupChange( id_P, d->startups[ id_P ] );
00258         return;
00259         }
00260     if( update_only_P )
00261         return;
00262     if( data_P.silent() != Data::Yes || d->flags & AnnounceSilenceChanges )
00263         {
00264         kdDebug( 172 ) << "adding" << endl;
00265         d->startups.insert( id_P, data_P );
00266         emit gotNewStartup( id_P, data_P );
00267         }
00268     else // new silenced, and silent shouldn't be announced
00269         {
00270         kdDebug( 172 ) << "adding silent" << endl;
00271         d->silent_startups.insert( id_P, data_P );
00272         }
00273     d->cleanup->start( 1000 ); // 1 sec
00274     }
00275 
00276 void KStartupInfo::got_remove_startup_info( const QString& msg_P )
00277     {
00278     KStartupInfoId id( msg_P );
00279     KStartupInfoData data( msg_P );
00280     if( data.pids().count() > 0 )
00281         {
00282         if( !id.none())
00283             remove_startup_pids( id, data );
00284         else
00285             remove_startup_pids( data );
00286         return;
00287         }
00288     remove_startup_info_internal( id );
00289     }
00290 
00291 void KStartupInfo::remove_startup_info_internal( const KStartupInfoId& id_P )
00292     {
00293     if (!d) return;
00294     if( d->startups.contains( id_P ))
00295         {
00296         kdDebug( 172 ) << "removing" << endl;
00297         emit gotRemoveStartup( id_P, d->startups[ id_P ]);
00298         d->startups.remove( id_P );
00299         }
00300     else if( d->silent_startups.contains( id_P ))
00301         {
00302         kdDebug( 172 ) << "removing silent" << endl;
00303         d->silent_startups.remove( id_P );
00304         }
00305     return;
00306     }
00307 
00308 void KStartupInfo::remove_startup_pids( const KStartupInfoData& data_P )
00309     { // first find the matching info
00310     if (!d) return;
00311     for( QMap< KStartupInfoId, Data >::Iterator it = d->startups.begin();
00312          it != d->startups.end();
00313          ++it )
00314         {
00315         if( ( *it ).hostname() != data_P.hostname())
00316             continue;
00317         if( !( *it ).is_pid( data_P.pids().first()))
00318             continue; // not the matching info
00319         remove_startup_pids( it.key(), data_P );
00320         break;
00321         }
00322     }
00323 
00324 void KStartupInfo::remove_startup_pids( const KStartupInfoId& id_P,
00325     const KStartupInfoData& data_P )
00326     {
00327     if (!d) return;
00328     kdFatal( data_P.pids().count() == 0, 172 );
00329     Data* data = NULL;
00330     if( d->startups.contains( id_P ))
00331         data = &d->startups[ id_P ];
00332     else if( d->silent_startups.contains( id_P ))
00333         data = &d->silent_startups[ id_P ];
00334     else
00335         return;
00336     for( QValueList< pid_t >::ConstIterator it2 = data_P.pids().begin();
00337          it2 != data_P.pids().end();
00338          ++it2 )
00339         data->remove_pid( *it2 ); // remove all pids from the info
00340     if( data->pids().count() == 0 ) // all pids removed -> remove info
00341         remove_startup_info_internal( id_P );
00342     }
00343 
00344 bool KStartupInfo::sendStartup( const KStartupInfoId& id_P, const KStartupInfoData& data_P )
00345     {
00346     if( id_P.none())
00347         return false;
00348     KXMessages msgs;
00349     QString msg = QString::fromLatin1( "new: %1 %2" )
00350         .arg( id_P.to_text()).arg( data_P.to_text());
00351     kdDebug( 172 ) << "sending " << msg << endl;
00352     msgs.broadcastMessage( KDE_STARTUP_INFO, msg );
00353     return true;
00354     }
00355 
00356 bool KStartupInfo::sendStartupX( Display* disp_P, const KStartupInfoId& id_P,
00357     const KStartupInfoData& data_P )
00358     {
00359     if( id_P.none())
00360         return false;
00361     QString msg = QString::fromLatin1( "new: %1 %2" )
00362         .arg( id_P.to_text()).arg( data_P.to_text());
00363 #ifdef KSTARTUPINFO_ALL_DEBUG
00364     kdDebug( 172 ) << "sending " << msg << endl;
00365 #endif
00366     return KXMessages::broadcastMessageX( disp_P, KDE_STARTUP_INFO, msg );
00367     }
00368 
00369 bool KStartupInfo::sendChange( const KStartupInfoId& id_P, const KStartupInfoData& data_P )
00370     {
00371     if( id_P.none())
00372         return false;
00373     KXMessages msgs;
00374     QString msg = QString::fromLatin1( "change: %1 %2" )
00375         .arg( id_P.to_text()).arg( data_P.to_text());
00376     kdDebug( 172 ) << "sending " << msg << endl;
00377     msgs.broadcastMessage( KDE_STARTUP_INFO, msg );
00378     return true;
00379     }
00380 
00381 bool KStartupInfo::sendChangeX( Display* disp_P, const KStartupInfoId& id_P,
00382     const KStartupInfoData& data_P )
00383     {
00384     if( id_P.none())
00385         return false;
00386     QString msg = QString::fromLatin1( "change: %1 %2" )
00387         .arg( id_P.to_text()).arg( data_P.to_text());
00388 #ifdef KSTARTUPINFO_ALL_DEBUG
00389     kdDebug( 172 ) << "sending " << msg << endl;
00390 #endif
00391     return KXMessages::broadcastMessageX( disp_P, KDE_STARTUP_INFO, msg );
00392     }
00393 
00394 bool KStartupInfo::sendFinish( const KStartupInfoId& id_P )
00395     {
00396     if( id_P.none())
00397         return false;
00398     KXMessages msgs;
00399     QString msg = QString::fromLatin1( "remove: %1" ).arg( id_P.to_text());
00400     kdDebug( 172 ) << "sending " << msg << endl;
00401     msgs.broadcastMessage( KDE_STARTUP_INFO, msg );
00402     return true;
00403     }
00404 
00405 bool KStartupInfo::sendFinishX( Display* disp_P, const KStartupInfoId& id_P )
00406     {
00407     if( id_P.none())
00408         return false;
00409     QString msg = QString::fromLatin1( "remove: %1" ).arg( id_P.to_text());
00410 #ifdef KSTARTUPINFO_ALL_DEBUG
00411     kdDebug( 172 ) << "sending " << msg << endl;
00412 #endif
00413     return KXMessages::broadcastMessageX( disp_P, KDE_STARTUP_INFO, msg );
00414     }
00415 
00416 bool KStartupInfo::sendFinish( const KStartupInfoId& id_P, const KStartupInfoData& data_P )
00417     {
00418 //    if( id_P.none()) // id may be none, the pids and hostname matter then
00419 //        return false;
00420     KXMessages msgs;
00421     QString msg = QString::fromLatin1( "remove: %1 %2" )
00422         .arg( id_P.to_text()).arg( data_P.to_text());
00423     kdDebug( 172 ) << "sending " << msg << endl;
00424     msgs.broadcastMessage( KDE_STARTUP_INFO, msg );
00425     return true;
00426     }
00427 
00428 bool KStartupInfo::sendFinishX( Display* disp_P, const KStartupInfoId& id_P,
00429     const KStartupInfoData& data_P )
00430     {
00431 //    if( id_P.none()) // id may be none, the pids and hostname matter then
00432 //        return false;
00433     QString msg = QString::fromLatin1( "remove: %1 %2" )
00434         .arg( id_P.to_text()).arg( data_P.to_text());
00435 #ifdef KSTARTUPINFO_ALL_DEBUG
00436     kdDebug( 172 ) << "sending " << msg << endl;
00437 #endif
00438     return KXMessages::broadcastMessageX( disp_P, KDE_STARTUP_INFO, msg );
00439     }
00440 
00441 void KStartupInfo::appStarted()
00442     {
00443     if( kapp != NULL ) // KApplication constructor unsets the env. variable
00444         {
00445         KStartupInfoId id;
00446         id.initId( kapp->startupId());
00447         if( !id.none())
00448             KStartupInfo::sendFinish( id );
00449         }
00450     else if( getenv( "DISPLAY" ) != NULL ) // don't rely on qt_xdisplay()
00451         {
00452         KStartupInfoId id = KStartupInfo::currentStartupIdEnv();
00453         if( !id.none())
00454             {
00455             Display* disp = XOpenDisplay( NULL );
00456             if( disp != NULL )
00457                 {
00458                 KStartupInfo::sendFinishX( disp, id );
00459                 XCloseDisplay( disp );
00460                 }
00461             }
00462         }
00463     }
00464 
00465 KStartupInfo::startup_t KStartupInfo::checkStartup( WId w_P, KStartupInfoId& id_O,
00466     KStartupInfoData& data_O )
00467     {
00468     return check_startup_internal( w_P, &id_O, &data_O, true );
00469     }
00470 
00471 KStartupInfo::startup_t KStartupInfo::checkStartup( WId w_P, KStartupInfoId& id_O )
00472     {
00473     return check_startup_internal( w_P, &id_O, NULL, true );
00474     }
00475 
00476 KStartupInfo::startup_t KStartupInfo::checkStartup( WId w_P, KStartupInfoData& data_O )
00477     {
00478     return check_startup_internal( w_P, NULL, &data_O, true );
00479     }
00480 
00481 KStartupInfo::startup_t KStartupInfo::checkStartup( WId w_P )
00482     {
00483     return check_startup_internal( w_P, NULL, NULL, true );
00484     }
00485 
00486 KStartupInfo::startup_t KStartupInfo::check_startup_internal( WId w_P, KStartupInfoId* id_O,
00487     KStartupInfoData* data_O, bool remove_P )
00488     {
00489     if (!d) return NoMatch;
00490     if( d->startups.count() == 0 )
00491         return NoMatch; // no startups
00492     NETWinInfo info( qt_xdisplay(),  w_P, qt_xrootwin(),
00493         NET::WMWindowType | NET::WMPid | NET::WMState );
00494     // ignore NET::Tool and other special window types
00495     if( info.windowType() != NET::Normal
00496         && info.windowType() != NET::Override
00497         && info.windowType() != NET::Unknown
00498         && info.windowType() != NET::Dialog
00499         && info.windowType() != NET::Dock )
00500         return NoMatch;
00501     // lets see if this is a transient
00502     Window transient_for;
00503     if( XGetTransientForHint( qt_xdisplay(), static_cast< Window >( w_P ), &transient_for )
00504         && static_cast< WId >( transient_for ) != qt_xrootwin()
00505         && transient_for != None )
00506         return NoMatch;
00507     // Strategy:
00508     //
00509     // Is this a compliant app ?
00510     //  - Yes - test for match
00511     //  - No - Is this a NET_WM compliant app ?
00512     //           - Yes - test for pid match
00513     //           - No - test for WM_CLASS match
00514     kdDebug( 172 ) << "check_startup" << endl;
00515     QCString id = windowStartupId( w_P );
00516     if( !id.isNull())
00517         {
00518         if( id.isEmpty() || id == "0" ) // means ignore this window
00519             {
00520             kdDebug( 172 ) << "ignore" << endl;
00521             return NoMatch;
00522             }
00523         return find_id( id, id_O, data_O, remove_P ) ? Match : NoMatch;
00524         }
00525     // _NET_WM_PID apps are also considered compliant
00526     pid_t pid = info.pid();
00527     if( pid > 0 )
00528         {
00529         QCString hostname = get_window_hostname( w_P );
00530         if( !hostname.isEmpty()
00531             && find_pid( pid, hostname, id_O, data_O, remove_P ))
00532             return Match;
00533         // try XClass matching , this PID stuff sucks :(
00534         }
00535     // Hard - this app is not even NET_WM compliant
00536     XClassHint hint;
00537     if( XGetClassHint( qt_xdisplay(), w_P, &hint ) != 0 )
00538         { // We managed to read the class hint
00539         if( find_wclass( hint.res_name, hint.res_class, id_O, data_O, remove_P ))
00540             return Match;
00541         }
00542     kdDebug( 172 ) << "check_startup:cantdetect" << endl;
00543     return CantDetect;
00544     }
00545 
00546 bool KStartupInfo::find_id( const QCString& id_P, KStartupInfoId* id_O,
00547     KStartupInfoData* data_O, bool remove_P )
00548     {
00549     if (!d) return false;
00550     kdDebug( 172 ) << "find_id:" << id_P << endl;
00551     KStartupInfoId id;
00552     id.initId( id_P );
00553     if( d->startups.contains( id ))
00554         {
00555         if( id_O != NULL )
00556             *id_O = id;
00557         if( data_O != NULL )
00558             *data_O = d->startups[ id ];
00559         if( remove_P
00560             && d->startups[ id ].silent() != Data::Yes ) // HACK
00561             d->startups.remove( id );
00562         kdDebug( 172 ) << "check_startup_id:match" << endl;
00563         return true;
00564         }
00565     return false;
00566     }
00567 
00568 bool KStartupInfo::find_pid( pid_t pid_P, const QCString& hostname_P,
00569     KStartupInfoId* id_O, KStartupInfoData* data_O, bool remove_P )
00570     {
00571     if (!d) return false;
00572     kdDebug( 172 ) << "find_pid:" << pid_P << endl;
00573     for( QMap< KStartupInfoId, Data >::Iterator it = d->startups.begin();
00574          it != d->startups.end();
00575          ++it )
00576         {
00577         if( ( *it ).is_pid( pid_P ) && ( *it ).hostname() == hostname_P )
00578             { // Found it !
00579             if( id_O != NULL )
00580                 *id_O = it.key();
00581             if( data_O != NULL )
00582                 *data_O = *it;
00583             if( remove_P
00584                 && ( *it ).silent() != Data::Yes ) // HACK
00585                 d->startups.remove( it );
00586             kdDebug( 172 ) << "check_startup_pid:match" << endl;
00587             return true;
00588             }
00589         }
00590     return false;
00591     }
00592 
00593 bool KStartupInfo::find_wclass( QCString res_name, QCString res_class,
00594     KStartupInfoId* id_O, KStartupInfoData* data_O, bool remove_P )
00595     {
00596     if (!d) return false;
00597     res_name = res_name.lower();
00598     res_class = res_class.lower();
00599     kdDebug( 172 ) << "find_wclass:" << res_name << ":" << res_class << endl;
00600     for( QMap< KStartupInfoId, Data >::Iterator it = d->startups.begin();
00601          it != d->startups.end();
00602          ++it )
00603         {
00604         const QCString wmclass = ( *it ).findWMClass();
00605         if( wmclass.lower() == res_name || wmclass.lower() == res_class )
00606             { // Found it !
00607             if( id_O != NULL )
00608                 *id_O = it.key();
00609             if( data_O != NULL )
00610                 *data_O = *it;
00611             if( remove_P
00612                 && ( *it ).silent() != Data::Yes ) // HACK
00613                 d->startups.remove( it );
00614             kdDebug( 172 ) << "check_startup_wclass:match" << endl;
00615             return true;
00616             }
00617         }
00618     return false;
00619     }
00620 
00621 static Atom kde_startup_atom = None;
00622 static Atom kde_startup_atom_2 = None;
00623 
00624 QCString KStartupInfo::windowStartupId( WId w_P )
00625     {
00626     if( kde_startup_atom == None )
00627         kde_startup_atom = XInternAtom( qt_xdisplay(), KDE_STARTUP_ID, False );
00628     if( kde_startup_atom_2 == None )
00629         kde_startup_atom_2 = XInternAtom( qt_xdisplay(), KDE_STARTUP_ID_2, False );
00630     unsigned char *name_ret;
00631     QCString ret;
00632     Atom type_ret;
00633     int format_ret;
00634     unsigned long nitems_ret = 0, after_ret = 0;
00635     if( XGetWindowProperty( qt_xdisplay(), w_P, kde_startup_atom, 0l, (long) BUFSIZE,
00636             False, XA_STRING, &type_ret, &format_ret, &nitems_ret, &after_ret, &name_ret )
00637             == Success )
00638         {
00639         if( type_ret == XA_STRING && format_ret == 8 && name_ret != NULL )
00640             ret = reinterpret_cast< char* >( name_ret );
00641         if ( name_ret != NULL )
00642             XFree( name_ret );
00643         }
00644     if( ret.isNull() && XGetWindowProperty( qt_xdisplay(), w_P, kde_startup_atom_2, 0l, (long) BUFSIZE,
00645             False, XA_STRING, &type_ret, &format_ret, &nitems_ret, &after_ret, &name_ret )
00646             == Success )
00647         {
00648         if( type_ret == XA_STRING && format_ret == 8 && name_ret != NULL )
00649             ret = reinterpret_cast< char* >( name_ret );
00650         if ( name_ret != NULL )
00651             XFree( name_ret );
00652         }
00653     return ret;
00654     }
00655 
00656 void KStartupInfo::setWindowStartupId( WId w_P, const QCString& id_P )
00657     {
00658     if( id_P.isNull())
00659         return;
00660     if( kde_startup_atom == None )
00661         kde_startup_atom = XInternAtom( qt_xdisplay(), KDE_STARTUP_ID, False );
00662     XChangeProperty( qt_xdisplay(), w_P, kde_startup_atom, XA_STRING, 8,
00663         PropModeReplace, reinterpret_cast< unsigned char* >( id_P.data()), id_P.length());
00664     }
00665 
00666 QCString KStartupInfo::get_window_hostname( WId w_P )
00667     {
00668     XTextProperty tp;
00669     char** hh;
00670     int cnt;
00671     if( XGetWMClientMachine( qt_xdisplay(), w_P, &tp ) != 0
00672         && XTextPropertyToStringList( &tp, &hh, &cnt ) != 0 )
00673         {
00674         if( cnt == 1 )
00675             {
00676             QCString hostname = hh[ 0 ];
00677             XFreeStringList( hh );
00678             return hostname;
00679             }
00680         XFreeStringList( hh );
00681         }
00682     // no hostname
00683     return QCString();
00684     }
00685 
00686 void KStartupInfo::setTimeout( unsigned int secs_P )
00687     {
00688     timeout = secs_P;
00689  // schedule removing entries that are older than the new timeout
00690     QTimer::singleShot( 0, this, SLOT( startups_cleanup_no_age()));
00691     }
00692 
00693 void KStartupInfo::startups_cleanup_no_age()
00694     {
00695     startups_cleanup_internal( false );
00696     }
00697 
00698 void KStartupInfo::startups_cleanup()
00699     {
00700     if (!d) return;
00701     if( d->startups.count() == 0 && d->silent_startups.count() == 0 )
00702         {
00703         d->cleanup->stop();
00704         return;
00705         }
00706     startups_cleanup_internal( true );
00707     }
00708 
00709 void KStartupInfo::startups_cleanup_internal( bool age_P )
00710     {
00711     if (!d) return;
00712     for( QMap< KStartupInfoId, Data >::Iterator it = d->startups.begin();
00713          it != d->startups.end();
00714          )
00715         {
00716         if( age_P )
00717             ( *it ).age++;
00718         int tout = timeout;
00719         if( ( *it ).silent() == Data::Yes ) // TODO 
00720             tout *= 20;
00721         if( ( *it ).age >= timeout )
00722             {
00723             const KStartupInfoId& key = it.key();
00724             ++it;
00725             kdDebug( 172 ) << "entry timeout:" << key.id() << endl;
00726             remove_startup_info_internal( key );
00727             }
00728         else
00729             ++it;
00730         }
00731     for( QMap< KStartupInfoId, Data >::Iterator it = d->silent_startups.begin();
00732          it != d->silent_startups.end();
00733          )
00734         {
00735         if( age_P )
00736             ( *it ).age++;
00737         int tout = timeout;
00738         if( ( *it ).silent() == Data::Yes ) // TODO 
00739             tout *= 20;
00740         if( ( *it ).age >= timeout )
00741             {
00742             const KStartupInfoId& key = it.key();
00743             ++it;
00744             kdDebug( 172 ) << "entry timeout:" << key.id() << endl;
00745             remove_startup_info_internal( key );
00746             }
00747         else
00748             ++it;
00749         }
00750     }
00751 
00752 void KStartupInfo::clean_all_noncompliant()
00753     {
00754     if (!d) return;
00755     for( QMap< KStartupInfoId, Data >::Iterator it = d->startups.begin();
00756          it != d->startups.end();
00757          )
00758         {
00759         if( ( *it ).WMClass() != "0" )
00760             {
00761             ++it;
00762             continue;
00763             }
00764         const KStartupInfoId& key = it.key();
00765         ++it;
00766         kdDebug( 172 ) << "entry cleaning:" << key.id() << endl;
00767         remove_startup_info_internal( key );
00768         }
00769     }
00770 
00771 struct KStartupInfoIdPrivate
00772     {
00773     KStartupInfoIdPrivate() : id( "" ) {};
00774     QCString id; // id
00775     };
00776 
00777 const QCString& KStartupInfoId::id() const
00778     {
00779     return d->id;
00780     }
00781 
00782 
00783 QString KStartupInfoId::to_text() const
00784     {
00785     return QString::fromLatin1( " ID=\"%1\" " ).arg( escape_str( id()));
00786     }
00787 
00788 KStartupInfoId::KStartupInfoId( const QString& txt_P )
00789     {
00790     d = new KStartupInfoIdPrivate;
00791     QStringList items = get_fields( txt_P );
00792     const QString id_str = QString::fromLatin1( "ID=" );
00793     for( QStringList::Iterator it = items.begin();
00794          it != items.end();
00795          ++it )
00796         {
00797         if( ( *it ).startsWith( id_str ))
00798             d->id = get_cstr( *it );
00799         }
00800     }
00801 
00802 void KStartupInfoId::initId( const QCString& id_P )
00803     {
00804     if( !id_P.isEmpty())
00805         {
00806         d->id = id_P;
00807 #ifdef KSTARTUPINFO_ALL_DEBUG
00808         kdDebug( 172 ) << "using: " << d->id << endl;
00809 #endif
00810         return;
00811         }
00812     const char* startup_env = getenv( KDE_STARTUP_ENV );
00813     if( startup_env != NULL && *startup_env != '\0' )
00814         { // already has id
00815         d->id = startup_env;
00816 #ifdef KSTARTUPINFO_ALL_DEBUG
00817         kdDebug( 172 ) << "reusing: " << d->id << endl;
00818 #endif
00819         return;
00820         }
00821     // assign a unique id  CHECKME
00822     // use hostname+time+pid, that should be 200% unique
00823     // hmm, probably something 99.9% unique and much shorter would be enough
00824     struct timeval tm;
00825     gettimeofday( &tm, NULL );
00826     char hostname[ 256 ];
00827     hostname[ 0 ] = '\0';
00828     gethostname( hostname, 255 );
00829     d->id = QString( "%1;%2;%3;%4" ).arg( hostname ).arg( tm.tv_sec )
00830         .arg( tm.tv_usec ).arg( getpid()).latin1();
00831 #ifdef KSTARTUPINFO_ALL_DEBUG
00832     kdDebug( 172 ) << "creating: " << d->id << endl;
00833 #endif
00834     }
00835 
00836 bool KStartupInfoId::setupStartupEnv() const
00837     {
00838     if( id().isEmpty())
00839         {
00840         unsetenv( KDE_STARTUP_ENV );
00841         return false;
00842         }
00843     return setenv( KDE_STARTUP_ENV, id(), true ) == 0;
00844     }
00845 
00846 KStartupInfoId KStartupInfo::currentStartupIdEnv()
00847     {
00848     const char* startup_env = getenv( KDE_STARTUP_ENV );
00849     KStartupInfoId id;
00850     if( startup_env != NULL && *startup_env != '\0' )
00851         id.d->id = startup_env;
00852     else
00853         id.d->id = "0";
00854     return id;
00855     }
00856 
00857 void KStartupInfo::resetStartupEnv()
00858     {
00859     unsetenv( KDE_STARTUP_ENV );
00860     }
00861 
00862 KStartupInfoId::KStartupInfoId()
00863     {
00864     d = new KStartupInfoIdPrivate;
00865     }
00866 
00867 KStartupInfoId::~KStartupInfoId()
00868     {
00869     delete d;
00870     }
00871 
00872 KStartupInfoId::KStartupInfoId( const KStartupInfoId& id_P )
00873     {
00874     d = new KStartupInfoIdPrivate( *id_P.d );
00875     }
00876 
00877 KStartupInfoId& KStartupInfoId::operator=( const KStartupInfoId& id_P )
00878     {
00879     if( &id_P == this )
00880         return *this;
00881     delete d;
00882     d = new KStartupInfoIdPrivate( *id_P.d );
00883     return *this;
00884     }
00885 
00886 bool KStartupInfoId::operator==( const KStartupInfoId& id_P ) const
00887     {
00888     return id() == id_P.id();
00889     }
00890 
00891 bool KStartupInfoId::operator!=( const KStartupInfoId& id_P ) const
00892     {
00893     return !(*this == id_P );
00894     }
00895 
00896 // needed for QMap
00897 bool KStartupInfoId::operator<( const KStartupInfoId& id_P ) const
00898     {
00899     return id() < id_P.id();
00900     }
00901 
00902 bool KStartupInfoId::none() const
00903     {
00904     return d->id.isEmpty() || d->id == "0";
00905     }
00906 
00907 struct KStartupInfoDataPrivate
00908     {
00909     KStartupInfoDataPrivate() : desktop( 0 ), wmclass( "" ), hostname( "" ),
00910         silent( KStartupInfoData::Unknown ) {};
00911     QString bin;
00912     QString name;
00913     QString icon;
00914     int desktop;
00915     QValueList< pid_t > pids;
00916     QCString wmclass;
00917     QCString hostname;
00918     KStartupInfoData::TriState silent;
00919     };
00920 
00921 QString KStartupInfoData::to_text() const
00922     {
00923     QString ret = "";
00924     if( !d->bin.isEmpty())
00925         ret += QString::fromLatin1( " BIN=\"%1\"" ).arg( escape_str( d->bin ));
00926     if( !d->name.isEmpty())
00927         ret += QString::fromLatin1( " NAME=\"%1\"" ).arg( escape_str( d->name ));
00928     if( !d->icon.isEmpty())
00929         ret += QString::fromLatin1( " ICON=%1" ).arg( d->icon );
00930     if( d->desktop != 0 )
00931         ret += QString::fromLatin1( " DESKTOP=%1" ).arg( d->desktop );
00932     if( !d->wmclass.isEmpty())
00933         ret += QString::fromLatin1( " WMCLASS=%1" ).arg( d->wmclass );
00934     if( !d->hostname.isEmpty())
00935         ret += QString::fromLatin1( " HOSTNAME=%1" ).arg( d->hostname );
00936     for( QValueList< pid_t >::ConstIterator it = d->pids.begin();
00937          it != d->pids.end();
00938          ++it )
00939         ret += QString::fromLatin1( " PID=%1" ).arg( *it );
00940     if( d->silent != Unknown )
00941         ret += QString::fromLatin1( " SILENT=%1" ).arg( d->silent == Yes ? 1 : 0 );
00942     return ret;
00943     }
00944 
00945 KStartupInfoData::KStartupInfoData( const QString& txt_P )
00946     {
00947     d = new KStartupInfoDataPrivate;
00948     QStringList items = get_fields( txt_P );
00949     const QString bin_str = QString::fromLatin1( "BIN=" );
00950     const QString name_str = QString::fromLatin1( "NAME=" );
00951     const QString icon_str = QString::fromLatin1( "ICON=" );
00952     const QString desktop_str = QString::fromLatin1( "DESKTOP=" );
00953     const QString wmclass_str = QString::fromLatin1( "WMCLASS=" );
00954     const QString hostname_str = QString::fromLatin1( "HOSTNAME=" );
00955     const QString pid_str = QString::fromLatin1( "PID=" );
00956     const QString silent_str = QString::fromLatin1( "SILENT=" );
00957     for( QStringList::Iterator it = items.begin();
00958          it != items.end();
00959          ++it )
00960         {
00961         if( ( *it ).startsWith( bin_str ))
00962             d->bin = get_str( *it );
00963         else if( ( *it ).startsWith( name_str ))
00964             d->name = get_str( *it );
00965         else if( ( *it ).startsWith( icon_str ))
00966             d->icon = get_str( *it );
00967         else if( ( *it ).startsWith( desktop_str ))
00968             d->desktop = get_num( *it );
00969         else if( ( *it ).startsWith( wmclass_str ))
00970             d->wmclass = get_cstr( *it );
00971         else if( ( *it ).startsWith( hostname_str ))
00972             d->hostname = get_cstr( *it );
00973         else if( ( *it ).startsWith( pid_str ))
00974             addPid( get_num( *it ));
00975         else if( ( *it ).startsWith( silent_str ))
00976             d->silent = get_num( *it ) != 0 ? Yes : No;
00977         }
00978     }
00979 
00980 KStartupInfoData::KStartupInfoData( const KStartupInfoData& data )
00981 {
00982     d = new KStartupInfoDataPrivate( *data.d );
00983 }
00984 
00985 KStartupInfoData& KStartupInfoData::operator=( const KStartupInfoData& data )
00986 {
00987     if( &data == this )
00988         return *this;
00989     delete d;
00990     d = new KStartupInfoDataPrivate( *data.d );
00991     return *this;
00992 }
00993 
00994 void KStartupInfoData::update( const KStartupInfoData& data_P )
00995     {
00996     if( !data_P.bin().isEmpty())
00997         d->bin = data_P.bin();
00998     if( !data_P.name().isEmpty() && name().isEmpty()) // don't overwrite
00999         d->name = data_P.name();
01000     if( !data_P.icon().isEmpty() && icon().isEmpty()) // don't overwrite
01001         d->icon = data_P.icon();
01002     if( data_P.desktop() != 0 && desktop() == 0 ) // don't overwrite
01003         d->desktop = data_P.desktop();
01004     if( !data_P.d->wmclass.isEmpty())
01005         d->wmclass = data_P.d->wmclass;
01006     if( !data_P.d->hostname.isEmpty())
01007         d->hostname = data_P.d->hostname;
01008     for( QValueList< pid_t >::ConstIterator it = data_P.d->pids.begin();
01009          it != data_P.d->pids.end();
01010          ++it )
01011         addPid( *it );
01012     if( data_P.silent() != Unknown )
01013         d->silent = data_P.silent();
01014     }
01015 
01016 KStartupInfoData::KStartupInfoData()
01017 {
01018     d = new KStartupInfoDataPrivate;
01019 }
01020 
01021 KStartupInfoData::~KStartupInfoData()
01022 {
01023     delete d;
01024 }
01025 
01026 void KStartupInfoData::setBin( const QString& bin_P )
01027     {
01028     d->bin = bin_P;
01029     }
01030 
01031 const QString& KStartupInfoData::bin() const
01032     {
01033     return d->bin;
01034     }
01035 
01036 void KStartupInfoData::setName( const QString& name_P )
01037     {
01038     d->name = name_P;
01039     }
01040 
01041 const QString& KStartupInfoData::findName() const
01042     {
01043     if( !name().isEmpty())
01044         return name();
01045     return bin();
01046     }
01047 
01048 const QString& KStartupInfoData::name() const
01049     {
01050     return d->name;
01051     }
01052 
01053 void KStartupInfoData::setIcon( const QString& icon_P )
01054     {
01055     d->icon = icon_P;
01056     }
01057 
01058 const QString& KStartupInfoData::findIcon() const
01059     {
01060     if( !icon().isEmpty())
01061         return icon();
01062     return bin();
01063     }
01064 
01065 const QString& KStartupInfoData::icon() const
01066     {
01067     return d->icon;
01068     }
01069 
01070 void KStartupInfoData::setDesktop( int desktop_P )
01071     {
01072     d->desktop = desktop_P;
01073     }
01074 
01075 int KStartupInfoData::desktop() const
01076     {
01077     return d->desktop;
01078     }
01079 
01080 void KStartupInfoData::setWMClass( const QCString& wmclass_P )
01081     {
01082     d->wmclass = wmclass_P;
01083     }
01084 
01085 const QCString KStartupInfoData::findWMClass() const
01086     {
01087     if( !WMClass().isEmpty() && WMClass() != "0" )
01088         return WMClass();
01089     return bin().latin1(); // CHECKME
01090     }
01091 
01092 const QCString& KStartupInfoData::WMClass() const
01093     {
01094     return d->wmclass;
01095     }
01096 
01097 void KStartupInfoData::setHostname( const QCString& hostname_P )
01098     {
01099     if( !hostname_P.isNull())
01100         d->hostname = hostname_P;
01101     else
01102         {
01103         char tmp[ 256 ];
01104         tmp[ 0 ] = '\0';
01105         gethostname( tmp, 255 );
01106         d->hostname = tmp;
01107         }
01108     }
01109 
01110 const QCString& KStartupInfoData::hostname() const
01111     {
01112     return d->hostname;
01113     }
01114 
01115 void KStartupInfoData::addPid( pid_t pid_P )
01116     {
01117     if( !d->pids.contains( pid_P ))
01118         d->pids.append( pid_P );
01119     }
01120 
01121 void KStartupInfoData::remove_pid( pid_t pid_P )
01122     {
01123     d->pids.remove( pid_P );
01124     }
01125 
01126 const QValueList< pid_t >& KStartupInfoData::pids() const
01127     {
01128     return d->pids;
01129     }
01130 
01131 bool KStartupInfoData::is_pid( pid_t pid_P ) const
01132     {
01133     return d->pids.contains( pid_P );
01134     }
01135 
01136 void KStartupInfoData::setSilent( TriState state_P )
01137     {
01138     d->silent = state_P;
01139     }
01140     
01141 KStartupInfoData::TriState KStartupInfoData::silent() const
01142     {
01143     return d->silent;
01144     }
01145 
01146 static
01147 int get_num( const QString& item_P )
01148     {
01149     unsigned int pos = item_P.find( '=' );
01150     return item_P.mid( pos + 1 ).toInt();
01151     }
01152 
01153 static
01154 QString get_str( const QString& item_P )
01155     {
01156     unsigned int pos = item_P.find( '=' );
01157     if( item_P.length() > pos + 2 && item_P[ pos + 1 ] == '\"' )
01158         {
01159         int pos2 = item_P.left( pos + 2 ).find( '\"' );
01160         if( pos2 < 0 )
01161             return QString::null;                      // 01234
01162         return item_P.mid( pos + 2, pos2 - 2 - pos );  // A="C"
01163         }
01164     return item_P.mid( pos + 1 );
01165     }
01166 
01167 static
01168 QCString get_cstr( const QString& item_P )
01169     {
01170     return get_str( item_P ).latin1(); // CHECKME
01171     }
01172 
01173 static
01174 QStringList get_fields( const QString& txt_P )
01175     {
01176     QString txt = txt_P.simplifyWhiteSpace();
01177     QStringList ret;
01178     QString item = "";
01179     bool in = false;
01180     bool escape = false;
01181     for( unsigned int pos = 0;
01182          pos < txt.length();
01183          ++pos )
01184         {
01185         if( escape )
01186             {
01187             item += txt[ pos ];
01188             escape = false;
01189             }
01190         else if( txt[ pos ] == '\\' )
01191             escape = true;
01192         else if( txt[ pos ] == '\"' )
01193             in = !in;
01194         else if( txt[ pos ] == ' ' && !in )
01195             {
01196             ret.append( item );
01197             item = "";
01198             }
01199         else
01200             item += txt[ pos ];
01201         }
01202     ret.append( item );
01203     return ret;
01204     }
01205 
01206 static QString escape_str( const QString& str_P )
01207     {
01208     QString ret = "";
01209     for( unsigned int pos = 0;
01210          pos < str_P.length();
01211          ++pos )
01212         {
01213         if( str_P[ pos ] == '\\'
01214             || str_P[ pos ] == '"' )
01215             ret += '\\';
01216         ret += str_P[ pos ];
01217         }
01218     return ret;
01219     }
01220 
01221 #undef None
01222 
01223 #include "kstartupinfo.moc"
01224 #endif
KDE Logo
This file is part of the documentation for kdelibs Version 3.1.5.
Documentation copyright © 1996-2002 the KDE developers.
Generated on Wed Jan 28 12:46:57 2004 by doxygen 1.3.4 written by Dimitri van Heesch, © 1997-2001