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 #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
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>
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
00069 static const char* const KDE_STARTUP_ENV = "KDE_STARTUP_ENV";
00070
00071
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() {};
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
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
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:" ))
00152 got_startup_info( msg.mid( 4 ), false );
00153 else if( msg.startsWith( "change:" ))
00154 got_startup_info( msg.mid( 7 ), true );
00155 else if( msg.startsWith( "remove:" ))
00156 got_remove_startup_info( msg.mid( 7 ));
00157 }
00158
00159
00160
00161
00162
00163
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
00199
00200
00201
00202 if( data.silent() != KStartupInfoData::Yes )
00203 remove_startup_info_internal( id );
00204 break;
00205 case NoMatch:
00206 break;
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 {
00231 d->startups[ id_P ].update( data_P );
00232 d->startups[ id_P ].age = 0;
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 {
00247 d->silent_startups[ id_P ].update( data_P );
00248 d->silent_startups[ id_P ].age = 0;
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
00269 {
00270 kdDebug( 172 ) << "adding silent" << endl;
00271 d->silent_startups.insert( id_P, data_P );
00272 }
00273 d->cleanup->start( 1000 );
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 {
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;
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 );
00340 if( data->pids().count() == 0 )
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
00419
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
00432
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 )
00444 {
00445 KStartupInfoId id;
00446 id.initId( kapp->startupId());
00447 if( !id.none())
00448 KStartupInfo::sendFinish( id );
00449 }
00450 else if( getenv( "DISPLAY" ) != NULL )
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;
00492 NETWinInfo info( qt_xdisplay(), w_P, qt_xrootwin(),
00493 NET::WMWindowType | NET::WMPid | NET::WMState );
00494
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
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
00508
00509
00510
00511
00512
00513
00514 kdDebug( 172 ) << "check_startup" << endl;
00515 QCString id = windowStartupId( w_P );
00516 if( !id.isNull())
00517 {
00518 if( id.isEmpty() || id == "0" )
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
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
00534 }
00535
00536 XClassHint hint;
00537 if( XGetClassHint( qt_xdisplay(), w_P, &hint ) != 0 )
00538 {
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 )
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 {
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 )
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 {
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 )
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
00683 return QCString();
00684 }
00685
00686 void KStartupInfo::setTimeout( unsigned int secs_P )
00687 {
00688 timeout = secs_P;
00689
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 )
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 )
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;
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 {
00815 d->id = startup_env;
00816 #ifdef KSTARTUPINFO_ALL_DEBUG
00817 kdDebug( 172 ) << "reusing: " << d->id << endl;
00818 #endif
00819 return;
00820 }
00821
00822
00823
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
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())
00999 d->name = data_P.name();
01000 if( !data_P.icon().isEmpty() && icon().isEmpty())
01001 d->icon = data_P.icon();
01002 if( data_P.desktop() != 0 && desktop() == 0 )
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();
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;
01162 return item_P.mid( pos + 2, pos2 - 2 - pos );
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();
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