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
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( window != NULL )
00525 {
00526 if( !startup_id.isEmpty() && startup_id != "0" )
00527 {
00528 NETRootInfo i( qt_xdisplay(), NET::Supported );
00529 if( i.isSupported( NET::WM2StartupId ))
00530 {
00531 KStartupInfo::setWindowStartupId( window->winId(), startup_id );
00532 activate = false;
00533 }
00534 }
00535 if( activate )
00536
00537
00538
00539
00540 KWin::forceActiveWindow( window->winId());
00541 }
00542 KStartupInfo::handleAutoAppStartedSending();
00543 }
00544
00545 KStartupInfo::startup_t KStartupInfo::checkStartup( WId w_P, KStartupInfoId& id_O,
00546 KStartupInfoData& data_O )
00547 {
00548 return check_startup_internal( w_P, &id_O, &data_O );
00549 }
00550
00551 KStartupInfo::startup_t KStartupInfo::checkStartup( WId w_P, KStartupInfoId& id_O )
00552 {
00553 return check_startup_internal( w_P, &id_O, NULL );
00554 }
00555
00556 KStartupInfo::startup_t KStartupInfo::checkStartup( WId w_P, KStartupInfoData& data_O )
00557 {
00558 return check_startup_internal( w_P, NULL, &data_O );
00559 }
00560
00561 KStartupInfo::startup_t KStartupInfo::checkStartup( WId w_P )
00562 {
00563 return check_startup_internal( w_P, NULL, NULL );
00564 }
00565
00566 KStartupInfo::startup_t KStartupInfo::check_startup_internal( WId w_P, KStartupInfoId* id_O,
00567 KStartupInfoData* data_O )
00568 {
00569 if( d == NULL )
00570 return NoMatch;
00571 if( d->startups.count() == 0 )
00572 return NoMatch;
00573 #if defined Q_WS_X11 && ! defined K_WS_QTONLY
00574
00575 NETWinInfo info( qt_xdisplay(), w_P, qt_xrootwin(),
00576 NET::WMWindowType | NET::WMPid | NET::WMState );
00577
00578 NET::WindowType type = info.windowType( NET::NormalMask | NET::DesktopMask
00579 | NET::DockMask | NET::ToolbarMask | NET::MenuMask | NET::DialogMask
00580 | NET::OverrideMask | NET::TopMenuMask | NET::UtilityMask | NET::SplashMask );
00581 if( type != NET::Normal
00582 && type != NET::Override
00583 && type != NET::Unknown
00584 && type != NET::Dialog
00585 && type != NET::Utility )
00586
00587 return NoMatch;
00588
00589 Window transient_for;
00590 if( XGetTransientForHint( qt_xdisplay(), static_cast< Window >( w_P ), &transient_for )
00591 && static_cast< WId >( transient_for ) != qt_xrootwin()
00592 && transient_for != None )
00593 return NoMatch;
00594 #endif
00595
00596
00597
00598
00599
00600
00601
00602 kdDebug( 172 ) << "check_startup" << endl;
00603 QCString id = windowStartupId( w_P );
00604 if( !id.isNull())
00605 {
00606 if( id.isEmpty() || id == "0" )
00607 {
00608 kdDebug( 172 ) << "ignore" << endl;
00609 return NoMatch;
00610 }
00611 return find_id( id, id_O, data_O ) ? Match : NoMatch;
00612 }
00613 #if defined Q_WS_X11 && ! defined K_WS_QTONLY
00614 pid_t pid = info.pid();
00615 if( pid > 0 )
00616 {
00617 QCString hostname = get_window_hostname( w_P );
00618 if( !hostname.isEmpty()
00619 && find_pid( pid, hostname, id_O, data_O ))
00620 return Match;
00621
00622 }
00623 XClassHint hint;
00624 if( XGetClassHint( qt_xdisplay(), w_P, &hint ) != 0 )
00625 {
00626 QCString res_name = hint.res_name;
00627 QCString res_class = hint.res_class;
00628 XFree( hint.res_name );
00629 XFree( hint.res_class );
00630 if( find_wclass( res_name, res_class, id_O, data_O ))
00631 return Match;
00632 }
00633 #endif
00634 kdDebug( 172 ) << "check_startup:cantdetect" << endl;
00635 return CantDetect;
00636 }
00637
00638 bool KStartupInfo::find_id( const QCString& id_P, KStartupInfoId* id_O,
00639 KStartupInfoData* data_O )
00640 {
00641 if( d == NULL )
00642 return false;
00643 kdDebug( 172 ) << "find_id:" << id_P << endl;
00644 KStartupInfoId id;
00645 id.initId( id_P );
00646 if( d->startups.contains( id ))
00647 {
00648 if( id_O != NULL )
00649 *id_O = id;
00650 if( data_O != NULL )
00651 *data_O = d->startups[ id ];
00652 kdDebug( 172 ) << "check_startup_id:match" << endl;
00653 return true;
00654 }
00655 return false;
00656 }
00657
00658 bool KStartupInfo::find_pid( pid_t pid_P, const QCString& hostname_P,
00659 KStartupInfoId* id_O, KStartupInfoData* data_O )
00660 {
00661 if( d == NULL )
00662 return false;
00663 kdDebug( 172 ) << "find_pid:" << pid_P << endl;
00664 for( QMap< KStartupInfoId, Data >::Iterator it = d->startups.begin();
00665 it != d->startups.end();
00666 ++it )
00667 {
00668 if( ( *it ).is_pid( pid_P ) && ( *it ).hostname() == hostname_P )
00669 {
00670 if( id_O != NULL )
00671 *id_O = it.key();
00672 if( data_O != NULL )
00673 *data_O = *it;
00674
00675 remove_startup_info_internal( it.key());
00676 kdDebug( 172 ) << "check_startup_pid:match" << endl;
00677 return true;
00678 }
00679 }
00680 return false;
00681 }
00682
00683 bool KStartupInfo::find_wclass( QCString res_name, QCString res_class,
00684 KStartupInfoId* id_O, KStartupInfoData* data_O )
00685 {
00686 if( d == NULL )
00687 return false;
00688 res_name = res_name.lower();
00689 res_class = res_class.lower();
00690 kdDebug( 172 ) << "find_wclass:" << res_name << ":" << res_class << endl;
00691 for( QMap< KStartupInfoId, Data >::Iterator it = d->startups.begin();
00692 it != d->startups.end();
00693 ++it )
00694 {
00695 const QCString wmclass = ( *it ).findWMClass();
00696 if( wmclass.lower() == res_name || wmclass.lower() == res_class )
00697 {
00698 if( id_O != NULL )
00699 *id_O = it.key();
00700 if( data_O != NULL )
00701 *data_O = *it;
00702
00703 remove_startup_info_internal( it.key());
00704 kdDebug( 172 ) << "check_startup_wclass:match" << endl;
00705 return true;
00706 }
00707 }
00708 return false;
00709 }
00710
00711 #if defined Q_WS_X11 && ! defined K_WS_QTONLY
00712 static Atom net_startup_atom = None;
00713 #endif
00714
00715 QCString KStartupInfo::windowStartupId( WId w_P )
00716 {
00717 #if defined Q_WS_X11 && ! defined K_WS_QTONLY
00718 if( net_startup_atom == None )
00719 net_startup_atom = XInternAtom( qt_xdisplay(), NET_STARTUP_WINDOW, False );
00720 if( utf8_string_atom == None )
00721 utf8_string_atom = XInternAtom( qt_xdisplay(), "UTF8_STRING", False );
00722 unsigned char *name_ret;
00723 QCString ret;
00724 Atom type_ret;
00725 int format_ret;
00726 unsigned long nitems_ret = 0, after_ret = 0;
00727 if( XGetWindowProperty( qt_xdisplay(), w_P, net_startup_atom, 0l, 4096,
00728 False, utf8_string_atom, &type_ret, &format_ret, &nitems_ret, &after_ret, &name_ret )
00729 == Success )
00730 {
00731 if( type_ret == utf8_string_atom && format_ret == 8 && name_ret != NULL )
00732 ret = reinterpret_cast< char* >( name_ret );
00733 if ( name_ret != NULL )
00734 XFree( name_ret );
00735 }
00736 return ret;
00737 #else
00738 return QCString();
00739 #endif
00740 }
00741
00742 void KStartupInfo::setWindowStartupId( WId w_P, const QCString& id_P )
00743 {
00744 #if defined Q_WS_X11 && ! defined K_WS_QTONLY
00745 if( id_P.isNull())
00746 return;
00747 if( net_startup_atom == None )
00748 net_startup_atom = XInternAtom( qt_xdisplay(), NET_STARTUP_WINDOW, False );
00749 if( utf8_string_atom == None )
00750 utf8_string_atom = XInternAtom( qt_xdisplay(), "UTF8_STRING", False );
00751 XChangeProperty( qt_xdisplay(), w_P, net_startup_atom, utf8_string_atom, 8,
00752 PropModeReplace, reinterpret_cast< unsigned char* >( id_P.data()), id_P.length());
00753 #endif
00754 }
00755
00756 QCString KStartupInfo::get_window_hostname( WId w_P )
00757 {
00758 #if defined Q_WS_X11 && ! defined K_WS_QTONLY
00759 XTextProperty tp;
00760 char** hh;
00761 int cnt;
00762 if( XGetWMClientMachine( qt_xdisplay(), w_P, &tp ) != 0
00763 && XTextPropertyToStringList( &tp, &hh, &cnt ) != 0 )
00764 {
00765 if( cnt == 1 )
00766 {
00767 QCString hostname = hh[ 0 ];
00768 XFreeStringList( hh );
00769 return hostname;
00770 }
00771 XFreeStringList( hh );
00772 }
00773 #endif
00774
00775 return QCString();
00776 }
00777
00778 void KStartupInfo::setTimeout( unsigned int secs_P )
00779 {
00780 timeout = secs_P;
00781
00782 QTimer::singleShot( 0, this, SLOT( startups_cleanup_no_age()));
00783 }
00784
00785 void KStartupInfo::startups_cleanup_no_age()
00786 {
00787 startups_cleanup_internal( false );
00788 }
00789
00790 void KStartupInfo::startups_cleanup()
00791 {
00792 if( d == NULL )
00793 return;
00794 if( d->startups.count() == 0 && d->silent_startups.count() == 0 )
00795 {
00796 d->cleanup->stop();
00797 return;
00798 }
00799 startups_cleanup_internal( true );
00800 }
00801
00802 void KStartupInfo::startups_cleanup_internal( bool age_P )
00803 {
00804 if( d == NULL )
00805 return;
00806 for( QMap< KStartupInfoId, Data >::Iterator it = d->startups.begin();
00807 it != d->startups.end();
00808 )
00809 {
00810 if( age_P )
00811 ( *it ).age++;
00812 int tout = timeout;
00813 if( ( *it ).silent() == Data::Yes )
00814 tout *= 20;
00815 if( ( *it ).age >= timeout )
00816 {
00817 const KStartupInfoId& key = it.key();
00818 ++it;
00819 kdDebug( 172 ) << "entry timeout:" << key.id() << endl;
00820 remove_startup_info_internal( key );
00821 }
00822 else
00823 ++it;
00824 }
00825 for( QMap< KStartupInfoId, Data >::Iterator it = d->silent_startups.begin();
00826 it != d->silent_startups.end();
00827 )
00828 {
00829 if( age_P )
00830 ( *it ).age++;
00831 int tout = timeout;
00832 if( ( *it ).silent() == Data::Yes )
00833 tout *= 20;
00834 if( ( *it ).age >= timeout )
00835 {
00836 const KStartupInfoId& key = it.key();
00837 ++it;
00838 kdDebug( 172 ) << "entry timeout:" << key.id() << endl;
00839 remove_startup_info_internal( key );
00840 }
00841 else
00842 ++it;
00843 }
00844 }
00845
00846 void KStartupInfo::clean_all_noncompliant()
00847 {
00848 if( d == NULL )
00849 return;
00850 for( QMap< KStartupInfoId, Data >::Iterator it = d->startups.begin();
00851 it != d->startups.end();
00852 )
00853 {
00854 if( ( *it ).WMClass() != "0" )
00855 {
00856 ++it;
00857 continue;
00858 }
00859 const KStartupInfoId& key = it.key();
00860 ++it;
00861 kdDebug( 172 ) << "entry cleaning:" << key.id() << endl;
00862 remove_startup_info_internal( key );
00863 }
00864 }
00865
00866 struct KStartupInfoIdPrivate
00867 {
00868 KStartupInfoIdPrivate() : id( "" ) {};
00869 QCString id;
00870 };
00871
00872 const QCString& KStartupInfoId::id() const
00873 {
00874 return d->id;
00875 }
00876
00877
00878 QString KStartupInfoId::to_text() const
00879 {
00880 return QString::fromLatin1( " ID=\"%1\" " ).arg( escape_str( id()));
00881 }
00882
00883 KStartupInfoId::KStartupInfoId( const QString& txt_P )
00884 {
00885 d = new KStartupInfoIdPrivate;
00886 QStringList items = get_fields( txt_P );
00887 const QString id_str = QString::fromLatin1( "ID=" );
00888 for( QStringList::Iterator it = items.begin();
00889 it != items.end();
00890 ++it )
00891 {
00892 if( ( *it ).startsWith( id_str ))
00893 d->id = get_cstr( *it );
00894 }
00895 }
00896
00897 void KStartupInfoId::initId( const QCString& id_P )
00898 {
00899 if( !id_P.isEmpty())
00900 {
00901 d->id = id_P;
00902 #ifdef KSTARTUPINFO_ALL_DEBUG
00903 kdDebug( 172 ) << "using: " << d->id << endl;
00904 #endif
00905 return;
00906 }
00907 const char* startup_env = getenv( NET_STARTUP_ENV );
00908 if( startup_env != NULL && *startup_env != '\0' )
00909 {
00910 d->id = startup_env;
00911 #ifdef KSTARTUPINFO_ALL_DEBUG
00912 kdDebug( 172 ) << "reusing: " << d->id << endl;
00913 #endif
00914 return;
00915 }
00916
00917
00918
00919 struct timeval tm;
00920 gettimeofday( &tm, NULL );
00921 char hostname[ 256 ];
00922 hostname[ 0 ] = '\0';
00923 if (!gethostname( hostname, 255 ))
00924 hostname[sizeof(hostname)-1] = '\0';
00925 d->id = QString( "%1;%2;%3;%4" ).arg( hostname ).arg( tm.tv_sec )
00926 .arg( tm.tv_usec ).arg( getpid()).utf8();
00927 #ifdef KSTARTUPINFO_ALL_DEBUG
00928 kdDebug( 172 ) << "creating: " << d->id << endl;
00929 #endif
00930 }
00931
00932 bool KStartupInfoId::setupStartupEnv() const
00933 {
00934 if( id().isEmpty())
00935 {
00936 unsetenv( NET_STARTUP_ENV );
00937 return false;
00938 }
00939 return setenv( NET_STARTUP_ENV, id(), true ) == 0;
00940 }
00941
00942 KStartupInfoId KStartupInfo::currentStartupIdEnv()
00943 {
00944 const char* startup_env = getenv( NET_STARTUP_ENV );
00945 KStartupInfoId id;
00946 if( startup_env != NULL && *startup_env != '\0' )
00947 id.d->id = startup_env;
00948 else
00949 id.d->id = "0";
00950 return id;
00951 }
00952
00953 void KStartupInfo::resetStartupEnv()
00954 {
00955 unsetenv( NET_STARTUP_ENV );
00956 }
00957
00958 KStartupInfoId::KStartupInfoId()
00959 {
00960 d = new KStartupInfoIdPrivate;
00961 }
00962
00963 KStartupInfoId::~KStartupInfoId()
00964 {
00965 delete d;
00966 }
00967
00968 KStartupInfoId::KStartupInfoId( const KStartupInfoId& id_P )
00969 {
00970 d = new KStartupInfoIdPrivate( *id_P.d );
00971 }
00972
00973 KStartupInfoId& KStartupInfoId::operator=( const KStartupInfoId& id_P )
00974 {
00975 if( &id_P == this )
00976 return *this;
00977 delete d;
00978 d = new KStartupInfoIdPrivate( *id_P.d );
00979 return *this;
00980 }
00981
00982 bool KStartupInfoId::operator==( const KStartupInfoId& id_P ) const
00983 {
00984 return id() == id_P.id();
00985 }
00986
00987 bool KStartupInfoId::operator!=( const KStartupInfoId& id_P ) const
00988 {
00989 return !(*this == id_P );
00990 }
00991
00992
00993 bool KStartupInfoId::operator<( const KStartupInfoId& id_P ) const
00994 {
00995 return id() < id_P.id();
00996 }
00997
00998 bool KStartupInfoId::none() const
00999 {
01000 return d->id.isEmpty() || d->id == "0";
01001 }
01002
01003 struct KStartupInfoDataPrivate
01004 {
01005 KStartupInfoDataPrivate() : desktop( 0 ), wmclass( "" ), hostname( "" ),
01006 silent( KStartupInfoData::Unknown ), timestamp( -1U ), screen( -1 ) {};
01007 QString bin;
01008 QString name;
01009 QString description;
01010 QString icon;
01011 int desktop;
01012 QValueList< pid_t > pids;
01013 QCString wmclass;
01014 QCString hostname;
01015 KStartupInfoData::TriState silent;
01016 unsigned long timestamp;
01017 int screen;
01018 };
01019
01020 QString KStartupInfoData::to_text() const
01021 {
01022 QString ret = "";
01023 if( !d->bin.isEmpty())
01024 ret += QString::fromLatin1( " BIN=\"%1\"" ).arg( escape_str( d->bin ));
01025 if( !d->name.isEmpty())
01026 ret += QString::fromLatin1( " NAME=\"%1\"" ).arg( escape_str( d->name ));
01027 if( !d->description.isEmpty())
01028 ret += QString::fromLatin1( " DESCRIPTION=\"%1\"" ).arg( escape_str( d->description ));
01029 if( !d->icon.isEmpty())
01030 ret += QString::fromLatin1( " ICON=%1" ).arg( d->icon );
01031 if( d->desktop != 0 )
01032 ret += QString::fromLatin1( " DESKTOP=%1" )
01033 .arg( d->desktop == NET::OnAllDesktops ? NET::OnAllDesktops : d->desktop - 1 );
01034 if( !d->wmclass.isEmpty())
01035 ret += QString::fromLatin1( " WMCLASS=%1" ).arg( d->wmclass );
01036 if( !d->hostname.isEmpty())
01037 ret += QString::fromLatin1( " HOSTNAME=%1" ).arg( d->hostname );
01038 for( QValueList< pid_t >::ConstIterator it = d->pids.begin();
01039 it != d->pids.end();
01040 ++it )
01041 ret += QString::fromLatin1( " PID=%1" ).arg( *it );
01042 if( d->silent != Unknown )
01043 ret += QString::fromLatin1( " SILENT=%1" ).arg( d->silent == Yes ? 1 : 0 );
01044 if( d->timestamp != -1U )
01045 ret += QString::fromLatin1( " TIMESTAMP=%1" ).arg( d->timestamp );
01046 if( d->screen != -1 )
01047 ret += QString::fromLatin1( " SCREEN=%1" ).arg( d->screen );
01048 return ret;
01049 }
01050
01051 KStartupInfoData::KStartupInfoData( const QString& txt_P )
01052 {
01053 d = new KStartupInfoDataPrivate;
01054 QStringList items = get_fields( txt_P );
01055 const QString bin_str = QString::fromLatin1( "BIN=" );
01056 const QString name_str = QString::fromLatin1( "NAME=" );
01057 const QString description_str = QString::fromLatin1( "DESCRIPTION=" );
01058 const QString icon_str = QString::fromLatin1( "ICON=" );
01059 const QString desktop_str = QString::fromLatin1( "DESKTOP=" );
01060 const QString wmclass_str = QString::fromLatin1( "WMCLASS=" );
01061 const QString hostname_str = QString::fromLatin1( "HOSTNAME=" );
01062 const QString pid_str = QString::fromLatin1( "PID=" );
01063 const QString silent_str = QString::fromLatin1( "SILENT=" );
01064 const QString timestamp_str = QString::fromLatin1( "TIMESTAMP=" );
01065 const QString screen_str = QString::fromLatin1( "SCREEN=" );
01066 for( QStringList::Iterator it = items.begin();
01067 it != items.end();
01068 ++it )
01069 {
01070 if( ( *it ).startsWith( bin_str ))
01071 d->bin = get_str( *it );
01072 else if( ( *it ).startsWith( name_str ))
01073 d->name = get_str( *it );
01074 else if( ( *it ).startsWith( description_str ))
01075 d->description = get_str( *it );
01076 else if( ( *it ).startsWith( icon_str ))
01077 d->icon = get_str( *it );
01078 else if( ( *it ).startsWith( desktop_str ))
01079 {
01080 d->desktop = get_num( *it );
01081 if( d->desktop != NET::OnAllDesktops )
01082 ++d->desktop;
01083 }
01084 else if( ( *it ).startsWith( wmclass_str ))
01085 d->wmclass = get_cstr( *it );
01086 else if( ( *it ).startsWith( hostname_str ))
01087 d->hostname = get_cstr( *it );
01088 else if( ( *it ).startsWith( pid_str ))
01089 addPid( get_num( *it ));
01090 else if( ( *it ).startsWith( silent_str ))
01091 d->silent = get_num( *it ) != 0 ? Yes : No;
01092 else if( ( *it ).startsWith( timestamp_str ))
01093 d->timestamp = get_unum( *it );
01094 else if( ( *it ).startsWith( screen_str ))
01095 d->screen = get_num( *it );
01096 }
01097 }
01098
01099 KStartupInfoData::KStartupInfoData( const KStartupInfoData& data )
01100 {
01101 d = new KStartupInfoDataPrivate( *data.d );
01102 }
01103
01104 KStartupInfoData& KStartupInfoData::operator=( const KStartupInfoData& data )
01105 {
01106 if( &data == this )
01107 return *this;
01108 delete d;
01109 d = new KStartupInfoDataPrivate( *data.d );
01110 return *this;
01111 }
01112
01113 void KStartupInfoData::update( const KStartupInfoData& data_P )
01114 {
01115 if( !data_P.bin().isEmpty())
01116 d->bin = data_P.bin();
01117 if( !data_P.name().isEmpty() && name().isEmpty())
01118 d->name = data_P.name();
01119 if( !data_P.description().isEmpty() && description().isEmpty())
01120 d->description = data_P.description();
01121 if( !data_P.icon().isEmpty() && icon().isEmpty())
01122 d->icon = data_P.icon();
01123 if( data_P.desktop() != 0 && desktop() == 0 )
01124 d->desktop = data_P.desktop();
01125 if( !data_P.d->wmclass.isEmpty())
01126 d->wmclass = data_P.d->wmclass;
01127 if( !data_P.d->hostname.isEmpty())
01128 d->hostname = data_P.d->hostname;
01129 for( QValueList< pid_t >::ConstIterator it = data_P.d->pids.begin();
01130 it != data_P.d->pids.end();
01131 ++it )
01132 addPid( *it );
01133 if( data_P.silent() != Unknown )
01134 d->silent = data_P.silent();
01135 if( data_P.timestamp() != -1U && timestamp() == -1U )
01136 d->timestamp = data_P.timestamp();
01137 if( data_P.screen() != -1 )
01138 d->screen = data_P.screen();
01139 }
01140
01141 KStartupInfoData::KStartupInfoData()
01142 {
01143 d = new KStartupInfoDataPrivate;
01144 }
01145
01146 KStartupInfoData::~KStartupInfoData()
01147 {
01148 delete d;
01149 }
01150
01151 void KStartupInfoData::setBin( const QString& bin_P )
01152 {
01153 d->bin = bin_P;
01154 }
01155
01156 const QString& KStartupInfoData::bin() const
01157 {
01158 return d->bin;
01159 }
01160
01161 void KStartupInfoData::setName( const QString& name_P )
01162 {
01163 d->name = name_P;
01164 }
01165
01166 const QString& KStartupInfoData::name() const
01167 {
01168 return d->name;
01169 }
01170
01171 const QString& KStartupInfoData::findName() const
01172 {
01173 if( !name().isEmpty())
01174 return name();
01175 return bin();
01176 }
01177
01178 void KStartupInfoData::setDescription( const QString& desc_P )
01179 {
01180 d->description = desc_P;
01181 }
01182
01183 const QString& KStartupInfoData::description() const
01184 {
01185 return d->description;
01186 }
01187
01188 const QString& KStartupInfoData::findDescription() const
01189 {
01190 if( !description().isEmpty())
01191 return description();
01192 return name();
01193 }
01194
01195 void KStartupInfoData::setIcon( const QString& icon_P )
01196 {
01197 d->icon = icon_P;
01198 }
01199
01200 const QString& KStartupInfoData::findIcon() const
01201 {
01202 if( !icon().isEmpty())
01203 return icon();
01204 return bin();
01205 }
01206
01207 const QString& KStartupInfoData::icon() const
01208 {
01209 return d->icon;
01210 }
01211
01212 void KStartupInfoData::setDesktop( int desktop_P )
01213 {
01214 d->desktop = desktop_P;
01215 }
01216
01217 int KStartupInfoData::desktop() const
01218 {
01219 return d->desktop;
01220 }
01221
01222 void KStartupInfoData::setWMClass( const QCString& wmclass_P )
01223 {
01224 d->wmclass = wmclass_P;
01225 }
01226
01227 const QCString KStartupInfoData::findWMClass() const
01228 {
01229 if( !WMClass().isEmpty() && WMClass() != "0" )
01230 return WMClass();
01231 return bin().utf8();
01232 }
01233
01234 const QCString& KStartupInfoData::WMClass() const
01235 {
01236 return d->wmclass;
01237 }
01238
01239 void KStartupInfoData::setHostname( const QCString& hostname_P )
01240 {
01241 if( !hostname_P.isNull())
01242 d->hostname = hostname_P;
01243 else
01244 {
01245 char tmp[ 256 ];
01246 tmp[ 0 ] = '\0';
01247 if (!gethostname( tmp, 255 ))
01248 tmp[sizeof(tmp)-1] = '\0';
01249 d->hostname = tmp;
01250 }
01251 }
01252
01253 const QCString& KStartupInfoData::hostname() const
01254 {
01255 return d->hostname;
01256 }
01257
01258 void KStartupInfoData::addPid( pid_t pid_P )
01259 {
01260 if( !d->pids.contains( pid_P ))
01261 d->pids.append( pid_P );
01262 }
01263
01264 void KStartupInfoData::remove_pid( pid_t pid_P )
01265 {
01266 d->pids.remove( pid_P );
01267 }
01268
01269 const QValueList< pid_t >& KStartupInfoData::pids() const
01270 {
01271 return d->pids;
01272 }
01273
01274 bool KStartupInfoData::is_pid( pid_t pid_P ) const
01275 {
01276 return d->pids.contains( pid_P );
01277 }
01278
01279 void KStartupInfoData::setSilent( TriState state_P )
01280 {
01281 d->silent = state_P;
01282 }
01283
01284 KStartupInfoData::TriState KStartupInfoData::silent() const
01285 {
01286 return d->silent;
01287 }
01288
01289 void KStartupInfoData::setTimestamp( unsigned long time )
01290 {
01291 d->timestamp = time;
01292 }
01293
01294 unsigned long KStartupInfoData::timestamp() const
01295 {
01296 return d->timestamp;
01297 }
01298
01299 void KStartupInfoData::setScreen( int screen )
01300 {
01301 d->screen = screen;
01302 }
01303
01304 int KStartupInfoData::screen() const
01305 {
01306 return d->screen;
01307 }
01308
01309 static
01310 long get_num( const QString& item_P )
01311 {
01312 unsigned int pos = item_P.find( '=' );
01313 return item_P.mid( pos + 1 ).toLong();
01314 }
01315
01316 static
01317 unsigned long get_unum( const QString& item_P )
01318 {
01319 unsigned int pos = item_P.find( '=' );
01320 return item_P.mid( pos + 1 ).toULong();
01321 }
01322
01323 static
01324 QString get_str( const QString& item_P )
01325 {
01326 unsigned int pos = item_P.find( '=' );
01327 if( item_P.length() > pos + 2 && item_P[ pos + 1 ] == '\"' )
01328 {
01329 int pos2 = item_P.left( pos + 2 ).find( '\"' );
01330 if( pos2 < 0 )
01331 return QString::null;
01332 return item_P.mid( pos + 2, pos2 - 2 - pos );
01333 }
01334 return item_P.mid( pos + 1 );
01335 }
01336
01337 static
01338 QCString get_cstr( const QString& item_P )
01339 {
01340 return get_str( item_P ).utf8();
01341 }
01342
01343 static
01344 QStringList get_fields( const QString& txt_P )
01345 {
01346 QString txt = txt_P.simplifyWhiteSpace();
01347 QStringList ret;
01348 QString item = "";
01349 bool in = false;
01350 bool escape = false;
01351 for( unsigned int pos = 0;
01352 pos < txt.length();
01353 ++pos )
01354 {
01355 if( escape )
01356 {
01357 item += txt[ pos ];
01358 escape = false;
01359 }
01360 else if( txt[ pos ] == '\\' )
01361 escape = true;
01362 else if( txt[ pos ] == '\"' )
01363 in = !in;
01364 else if( txt[ pos ] == ' ' && !in )
01365 {
01366 ret.append( item );
01367 item = "";
01368 }
01369 else
01370 item += txt[ pos ];
01371 }
01372 ret.append( item );
01373 return ret;
01374 }
01375
01376 static QString escape_str( const QString& str_P )
01377 {
01378 QString ret = "";
01379 for( unsigned int pos = 0;
01380 pos < str_P.length();
01381 ++pos )
01382 {
01383 if( str_P[ pos ] == '\\'
01384 || str_P[ pos ] == '"' )
01385 ret += '\\';
01386 ret += str_P[ pos ];
01387 }
01388 return ret;
01389 }
01390
01391 #include "kstartupinfo.moc"
01392 #endif