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