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