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 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:" ))
00129 got_startup_info( msg.mid( 4 ), false );
00130 else if( msg.startsWith( "change:" ))
00131 got_startup_info( msg.mid( 7 ), true );
00132 else if( msg.startsWith( "remove:" ))
00133 got_remove_startup_info( msg.mid( 7 ));
00134 }
00135
00136
00137
00138
00139
00140
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;
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 {
00202 d->startups[ id_P ].update( data_P );
00203 d->startups[ id_P ].age = 0;
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 );
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 {
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;
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 );
00270 if( data.pids().count() == 0 )
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
00349
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
00362
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 )
00374 {
00375 KStartupInfoId id;
00376 id.initId( kapp->startupId());
00377 if( !id.none())
00378 KStartupInfo::sendFinish( id );
00379 }
00380 else if( getenv( "DISPLAY" ) != NULL )
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;
00422 NETWinInfo info( qt_xdisplay(), w_P, qt_xrootwin(),
00423 NET::WMWindowType | NET::WMPid | NET::WMState );
00424
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
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
00438
00439
00440
00441
00442
00443
00444 kdDebug( 172 ) << "check_startup" << endl;
00445 QCString id = windowStartupId( w_P );
00446 if( !id.isNull())
00447 {
00448 if( id.isEmpty() || id == "0" )
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
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
00464 }
00465
00466 XClassHint hint;
00467 if( XGetClassHint( qt_xdisplay(), w_P, &hint ) != 0 )
00468 {
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 {
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 {
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
00610 return QCString();
00611 }
00612
00613 void KStartupInfo::setTimeout( unsigned int secs_P )
00614 {
00615 timeout = secs_P;
00616
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;
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 {
00720 d->id = startup_env;
00721 #ifdef KSTARTUPINFO_ALL_DEBUG
00722 kdDebug( 172 ) << "reusing: " << d->id << endl;
00723 #endif
00724 return;
00725 }
00726
00727
00728
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
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())
00897 d->name = data_P.name();
00898 if( !data_P.icon().isEmpty() && icon().isEmpty())
00899 d->icon = data_P.icon();
00900 if( data_P.desktop() != 0 && desktop() == 0 )
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();
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;
01048 return item_P.mid( pos + 2, pos2 - 2 - pos );
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();
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