kdecore Library API Documentation

kstartupinfo.cpp

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