kstartupinfo.cpp
00001 /**************************************************************************** 00002 00003 $Id: kstartupinfo.cpp 724473 2007-10-12 12:16:50Z lunakl $ 00004 00005 Copyright (C) 2001-2003 Lubos Lunak <l.lunak@kde.org> 00006 00007 Permission is hereby granted, free of charge, to any person obtaining a 00008 copy of this software and associated documentation files (the "Software"), 00009 to deal in the Software without restriction, including without limitation 00010 the rights to use, copy, modify, merge, publish, distribute, sublicense, 00011 and/or sell copies of the Software, and to permit persons to whom the 00012 Software is furnished to do so, subject to the following conditions: 00013 00014 The above copyright notice and this permission notice shall be included in 00015 all copies or substantial portions of the Software. 00016 00017 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 00018 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 00019 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 00020 THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 00021 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 00022 FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 00023 DEALINGS IN THE SOFTWARE. 00024 00025 ****************************************************************************/ 00026 00027 // kdDebug() can't be turned off in kdeinit 00028 #if 0 00029 #define KSTARTUPINFO_ALL_DEBUG 00030 #warning Extra KStartupInfo debug messages enabled. 00031 #endif 00032 00033 #include <qwidget.h> 00034 00035 #include "config.h" 00036 #ifdef Q_WS_X11 00037 //#ifdef Q_WS_X11 // FIXME(E): Re-implement in a less X11 specific way 00038 #include <qglobal.h> 00039 #ifdef HAVE_CONFIG_H 00040 #include <config.h> 00041 #endif 00042 00043 // need to resolve INT32(qglobal.h)<>INT32(Xlibint.h) conflict 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 #ifdef Q_WS_X11 00055 #include <netwm.h> 00056 #endif 00057 #include <kdebug.h> 00058 #include <kapplication.h> 00059 #include <signal.h> 00060 #ifdef Q_WS_X11 00061 #include <kwinmodule.h> 00062 #include <kxmessages.h> 00063 #include <kwin.h> 00064 #endif 00065 00066 static const char* const NET_STARTUP_MSG = "_NET_STARTUP_INFO"; 00067 static const char* const NET_STARTUP_WINDOW = "_NET_STARTUP_ID"; 00068 // DESKTOP_STARTUP_ID is used also in kinit/wrapper.c , 00069 // kdesu in both kdelibs and kdebase and who knows where else 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() : KStartupInfoData(), age(0) {} // just because it's in a QMap 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 // contains silenced ASN's only if !AnnounceSilencedChanges 00098 QMap< KStartupInfoId, KStartupInfo::Data > silent_startups; 00099 // contains ASN's that had change: but no new: yet 00100 QMap< KStartupInfoId, KStartupInfo::Data > uninited_startups; 00101 #ifdef Q_WS_X11 00102 KWinModule* wm_module; 00103 KXMessages msgs; 00104 #endif 00105 QTimer* cleanup; 00106 int flags; 00107 KStartupInfoPrivate( int flags_P ) 00108 : 00109 #ifdef Q_WS_X11 00110 msgs( NET_STARTUP_MSG, NULL, false ), 00111 #endif 00112 flags( flags_P ) {} 00113 }; 00114 00115 KStartupInfo::KStartupInfo( int flags_P, QObject* parent_P, const char* name_P ) 00116 : QObject( parent_P, name_P ), 00117 timeout( 60 ), d( NULL ) 00118 { 00119 init( flags_P ); 00120 } 00121 00122 KStartupInfo::KStartupInfo( bool clean_on_cantdetect_P, QObject* parent_P, const char* name_P ) 00123 : QObject( parent_P, name_P ), 00124 timeout( 60 ), d( NULL ) 00125 { 00126 init( clean_on_cantdetect_P ? CleanOnCantDetect : 0 ); 00127 } 00128 00129 void KStartupInfo::init( int flags_P ) 00130 { 00131 // d == NULL means "disabled" 00132 if( !KApplication::kApplication()) 00133 return; 00134 if( !KApplication::kApplication()->getDisplay()) 00135 return; 00136 00137 d = new KStartupInfoPrivate( flags_P ); 00138 #ifdef Q_WS_X11 00139 if( !( d->flags & DisableKWinModule )) 00140 { 00141 d->wm_module = new KWinModule( this ); 00142 connect( d->wm_module, SIGNAL( windowAdded( WId )), SLOT( slot_window_added( WId ))); 00143 connect( d->wm_module, SIGNAL( systemTrayWindowAdded( WId )), SLOT( slot_window_added( WId ))); 00144 } 00145 else 00146 d->wm_module = NULL; 00147 connect( &d->msgs, SIGNAL( gotMessage( const QString& )), SLOT( got_message( const QString& ))); 00148 #endif 00149 d->cleanup = new QTimer( this, "cleanup" ); 00150 connect( d->cleanup, SIGNAL( timeout()), SLOT( startups_cleanup())); 00151 } 00152 00153 KStartupInfo::~KStartupInfo() 00154 { 00155 delete d; 00156 } 00157 00158 void KStartupInfo::got_message( const QString& msg_P ) 00159 { 00160 // TODO do something with SCREEN= ? 00161 kdDebug( 172 ) << "got:" << msg_P << endl; 00162 QString msg = msg_P.stripWhiteSpace(); 00163 if( msg.startsWith( "new:" )) // must match length below 00164 got_startup_info( msg.mid( 4 ), false ); 00165 else if( msg.startsWith( "change:" )) // must match length below 00166 got_startup_info( msg.mid( 7 ), true ); 00167 else if( msg.startsWith( "remove:" )) // must match length below 00168 got_remove_startup_info( msg.mid( 7 )); 00169 } 00170 00171 // if the application stops responding for a while, KWinModule may get 00172 // the information about the already mapped window before KXMessages 00173 // actually gets the info about the started application (depends 00174 // on their order in X11 event filter in KApplication) 00175 // simply delay info from KWinModule a bit 00176 // SELI??? 00177 namespace 00178 { 00179 class DelayedWindowEvent 00180 : public QCustomEvent 00181 { 00182 public: 00183 DelayedWindowEvent( WId w_P ) 00184 : QCustomEvent( QEvent::User + 15 ), w( w_P ) {} 00185 Window w; 00186 }; 00187 } 00188 00189 void KStartupInfo::slot_window_added( WId w_P ) 00190 { 00191 kapp->postEvent( this, new DelayedWindowEvent( w_P )); 00192 } 00193 00194 void KStartupInfo::customEvent( QCustomEvent* e_P ) 00195 { 00196 if( e_P->type() == QEvent::User + 15 ) 00197 window_added( static_cast< DelayedWindowEvent* >( e_P )->w ); 00198 else 00199 QObject::customEvent( e_P ); 00200 } 00201 00202 void KStartupInfo::window_added( WId w_P ) 00203 { 00204 KStartupInfoId id; 00205 KStartupInfoData data; 00206 startup_t ret = check_startup_internal( w_P, &id, &data ); 00207 switch( ret ) 00208 { 00209 case Match: 00210 kdDebug( 172 ) << "new window match" << endl; 00211 break; 00212 case NoMatch: 00213 break; // nothing 00214 case CantDetect: 00215 if( d->flags & CleanOnCantDetect ) 00216 clean_all_noncompliant(); 00217 break; 00218 } 00219 } 00220 00221 void KStartupInfo::got_startup_info( const QString& msg_P, bool update_P ) 00222 { 00223 KStartupInfoId id( msg_P ); 00224 if( id.none()) 00225 return; 00226 KStartupInfo::Data data( msg_P ); 00227 new_startup_info_internal( id, data, update_P ); 00228 } 00229 00230 void KStartupInfo::new_startup_info_internal( const KStartupInfoId& id_P, 00231 Data& data_P, bool update_P ) 00232 { 00233 if( d == NULL ) 00234 return; 00235 if( id_P.none()) 00236 return; 00237 if( d->startups.contains( id_P )) 00238 { // already reported, update 00239 d->startups[ id_P ].update( data_P ); 00240 d->startups[ id_P ].age = 0; // CHECKME 00241 kdDebug( 172 ) << "updating" << endl; 00242 if( d->startups[ id_P ].silent() == Data::Yes 00243 && !( d->flags & AnnounceSilenceChanges )) 00244 { 00245 d->silent_startups[ id_P ] = d->startups[ id_P ]; 00246 d->startups.remove( id_P ); 00247 emit gotRemoveStartup( id_P, d->silent_startups[ id_P ] ); 00248 return; 00249 } 00250 emit gotStartupChange( id_P, d->startups[ id_P ] ); 00251 return; 00252 } 00253 if( d->silent_startups.contains( id_P )) 00254 { // already reported, update 00255 d->silent_startups[ id_P ].update( data_P ); 00256 d->silent_startups[ id_P ].age = 0; // CHECKME 00257 kdDebug( 172 ) << "updating silenced" << endl; 00258 if( d->silent_startups[ id_P ].silent() != Data::Yes ) 00259 { 00260 d->startups[ id_P ] = d->silent_startups[ id_P ]; 00261 d->silent_startups.remove( id_P ); 00262 emit gotNewStartup( id_P, d->startups[ id_P ] ); 00263 return; 00264 } 00265 emit gotStartupChange( id_P, d->silent_startups[ id_P ] ); 00266 return; 00267 } 00268 if( d->uninited_startups.contains( id_P )) 00269 { 00270 d->uninited_startups[ id_P ].update( data_P ); 00271 kdDebug( 172 ) << "updating uninited" << endl; 00272 if( !update_P ) // uninited finally got new: 00273 { 00274 d->startups[ id_P ] = d->uninited_startups[ id_P ]; 00275 d->uninited_startups.remove( id_P ); 00276 emit gotNewStartup( id_P, d->startups[ id_P ] ); 00277 return; 00278 } 00279 // no change announce, it's still uninited 00280 return; 00281 } 00282 if( update_P ) // change: without any new: first 00283 { 00284 kdDebug( 172 ) << "adding uninited" << endl; 00285 d->uninited_startups.insert( id_P, data_P ); 00286 } 00287 else if( data_P.silent() != Data::Yes || d->flags & AnnounceSilenceChanges ) 00288 { 00289 kdDebug( 172 ) << "adding" << endl; 00290 d->startups.insert( id_P, data_P ); 00291 emit gotNewStartup( id_P, data_P ); 00292 } 00293 else // new silenced, and silent shouldn't be announced 00294 { 00295 kdDebug( 172 ) << "adding silent" << endl; 00296 d->silent_startups.insert( id_P, data_P ); 00297 } 00298 d->cleanup->start( 1000 ); // 1 sec 00299 } 00300 00301 void KStartupInfo::got_remove_startup_info( const QString& msg_P ) 00302 { 00303 KStartupInfoId id( msg_P ); 00304 KStartupInfoData data( msg_P ); 00305 if( data.pids().count() > 0 ) 00306 { 00307 if( !id.none()) 00308 remove_startup_pids( id, data ); 00309 else 00310 remove_startup_pids( data ); 00311 return; 00312 } 00313 remove_startup_info_internal( id ); 00314 } 00315 00316 void KStartupInfo::remove_startup_info_internal( const KStartupInfoId& id_P ) 00317 { 00318 if( d == NULL ) 00319 return; 00320 if( d->startups.contains( id_P )) 00321 { 00322 kdDebug( 172 ) << "removing" << endl; 00323 emit gotRemoveStartup( id_P, d->startups[ id_P ]); 00324 d->startups.remove( id_P ); 00325 } 00326 else if( d->silent_startups.contains( id_P )) 00327 { 00328 kdDebug( 172 ) << "removing silent" << endl; 00329 d->silent_startups.remove( id_P ); 00330 } 00331 else if( d->uninited_startups.contains( id_P )) 00332 { 00333 kdDebug( 172 ) << "removing uninited" << endl; 00334 d->uninited_startups.remove( id_P ); 00335 } 00336 return; 00337 } 00338 00339 void KStartupInfo::remove_startup_pids( const KStartupInfoData& data_P ) 00340 { // first find the matching info 00341 if( d == NULL ) 00342 return; 00343 for( QMap< KStartupInfoId, Data >::Iterator it = d->startups.begin(); 00344 it != d->startups.end(); 00345 ++it ) 00346 { 00347 if( ( *it ).hostname() != data_P.hostname()) 00348 continue; 00349 if( !( *it ).is_pid( data_P.pids().first())) 00350 continue; // not the matching info 00351 remove_startup_pids( it.key(), data_P ); 00352 break; 00353 } 00354 } 00355 00356 void KStartupInfo::remove_startup_pids( const KStartupInfoId& id_P, 00357 const KStartupInfoData& data_P ) 00358 { 00359 if( d == NULL ) 00360 return; 00361 kdFatal( data_P.pids().count() == 0, 172 ); 00362 Data* data = NULL; 00363 if( d->startups.contains( id_P )) 00364 data = &d->startups[ id_P ]; 00365 else if( d->silent_startups.contains( id_P )) 00366 data = &d->silent_startups[ id_P ]; 00367 else if( d->uninited_startups.contains( id_P )) 00368 data = &d->uninited_startups[ id_P ]; 00369 else 00370 return; 00371 for( QValueList< pid_t >::ConstIterator it2 = data_P.pids().begin(); 00372 it2 != data_P.pids().end(); 00373 ++it2 ) 00374 data->remove_pid( *it2 ); // remove all pids from the info 00375 if( data->pids().count() == 0 ) // all pids removed -> remove info 00376 remove_startup_info_internal( id_P ); 00377 } 00378 00379 bool KStartupInfo::sendStartup( const KStartupInfoId& id_P, const KStartupInfoData& data_P ) 00380 { 00381 if( id_P.none()) 00382 return false; 00383 KXMessages msgs; 00384 QString msg = QString::fromLatin1( "new: %1 %2" ) 00385 .arg( id_P.to_text()).arg( data_P.to_text()); 00386 msg = check_required_startup_fields( msg, data_P, qt_xscreen()); 00387 kdDebug( 172 ) << "sending " << msg << endl; 00388 msgs.broadcastMessage( NET_STARTUP_MSG, msg, -1, false ); 00389 return true; 00390 } 00391 00392 bool KStartupInfo::sendStartupX( Display* disp_P, const KStartupInfoId& id_P, 00393 const KStartupInfoData& data_P ) 00394 { 00395 if( id_P.none()) 00396 return false; 00397 QString msg = QString::fromLatin1( "new: %1 %2" ) 00398 .arg( id_P.to_text()).arg( data_P.to_text()); 00399 msg = check_required_startup_fields( msg, data_P, DefaultScreen( disp_P )); 00400 #ifdef KSTARTUPINFO_ALL_DEBUG 00401 kdDebug( 172 ) << "sending " << msg << endl; 00402 #endif 00403 return KXMessages::broadcastMessageX( disp_P, NET_STARTUP_MSG, msg, -1, false ); 00404 } 00405 00406 QString KStartupInfo::check_required_startup_fields( const QString& msg, const KStartupInfoData& data_P, 00407 int screen ) 00408 { 00409 QString ret = msg; 00410 if( data_P.name().isEmpty()) 00411 { 00412 // kdWarning( 172 ) << "NAME not specified in initial startup message" << endl; 00413 QString name = data_P.bin(); 00414 if( name.isEmpty()) 00415 name = "UNKNOWN"; 00416 ret += QString( " NAME=\"%1\"" ).arg( escape_str( name )); 00417 } 00418 if( data_P.screen() == -1 ) // add automatically if needed 00419 ret += QString( " SCREEN=%1" ).arg( screen ); 00420 return ret; 00421 } 00422 00423 bool KStartupInfo::sendChange( const KStartupInfoId& id_P, const KStartupInfoData& data_P ) 00424 { 00425 if( id_P.none()) 00426 return false; 00427 KXMessages msgs; 00428 QString msg = QString::fromLatin1( "change: %1 %2" ) 00429 .arg( id_P.to_text()).arg( data_P.to_text()); 00430 kdDebug( 172 ) << "sending " << msg << endl; 00431 msgs.broadcastMessage( NET_STARTUP_MSG, msg, -1, false ); 00432 return true; 00433 } 00434 00435 bool KStartupInfo::sendChangeX( Display* disp_P, const KStartupInfoId& id_P, 00436 const KStartupInfoData& data_P ) 00437 { 00438 if( id_P.none()) 00439 return false; 00440 QString msg = QString::fromLatin1( "change: %1 %2" ) 00441 .arg( id_P.to_text()).arg( data_P.to_text()); 00442 #ifdef KSTARTUPINFO_ALL_DEBUG 00443 kdDebug( 172 ) << "sending " << msg << endl; 00444 #endif 00445 return KXMessages::broadcastMessageX( disp_P, NET_STARTUP_MSG, msg, -1, false ); 00446 } 00447 00448 bool KStartupInfo::sendFinish( const KStartupInfoId& id_P ) 00449 { 00450 if( id_P.none()) 00451 return false; 00452 KXMessages msgs; 00453 QString msg = QString::fromLatin1( "remove: %1" ).arg( id_P.to_text()); 00454 kdDebug( 172 ) << "sending " << msg << endl; 00455 msgs.broadcastMessage( NET_STARTUP_MSG, msg, -1, false ); 00456 return true; 00457 } 00458 00459 bool KStartupInfo::sendFinishX( Display* disp_P, const KStartupInfoId& id_P ) 00460 { 00461 if( id_P.none()) 00462 return false; 00463 QString msg = QString::fromLatin1( "remove: %1" ).arg( id_P.to_text()); 00464 #ifdef KSTARTUPINFO_ALL_DEBUG 00465 kdDebug( 172 ) << "sending " << msg << endl; 00466 #endif 00467 return KXMessages::broadcastMessageX( disp_P, NET_STARTUP_MSG, msg, -1, false ); 00468 } 00469 00470 bool KStartupInfo::sendFinish( const KStartupInfoId& id_P, const KStartupInfoData& data_P ) 00471 { 00472 // if( id_P.none()) // id may be none, the pids and hostname matter then 00473 // return false; 00474 KXMessages msgs; 00475 QString msg = QString::fromLatin1( "remove: %1 %2" ) 00476 .arg( id_P.to_text()).arg( data_P.to_text()); 00477 kdDebug( 172 ) << "sending " << msg << endl; 00478 msgs.broadcastMessage( NET_STARTUP_MSG, msg, -1, false ); 00479 return true; 00480 } 00481 00482 bool KStartupInfo::sendFinishX( Display* disp_P, const KStartupInfoId& id_P, 00483 const KStartupInfoData& data_P ) 00484 { 00485 // if( id_P.none()) // id may be none, the pids and hostname matter then 00486 // return false; 00487 QString msg = QString::fromLatin1( "remove: %1 %2" ) 00488 .arg( id_P.to_text()).arg( data_P.to_text()); 00489 #ifdef KSTARTUPINFO_ALL_DEBUG 00490 kdDebug( 172 ) << "sending " << msg << endl; 00491 #endif 00492 return KXMessages::broadcastMessageX( disp_P, NET_STARTUP_MSG, msg, -1, false ); 00493 } 00494 00495 void KStartupInfo::appStarted() 00496 { 00497 if( kapp != NULL ) // KApplication constructor unsets the env. variable 00498 appStarted( kapp->startupId()); 00499 else 00500 appStarted( KStartupInfo::currentStartupIdEnv().id()); 00501 } 00502 00503 void KStartupInfo::appStarted( const QCString& startup_id ) 00504 { 00505 KStartupInfoId id; 00506 id.initId( startup_id ); 00507 if( id.none()) 00508 return; 00509 if( kapp != NULL ) 00510 KStartupInfo::sendFinish( id ); 00511 else if( getenv( "DISPLAY" ) != NULL ) // don't rely on qt_xdisplay() 00512 { 00513 #ifdef Q_WS_X11 00514 Display* disp = XOpenDisplay( NULL ); 00515 if( disp != NULL ) 00516 { 00517 KStartupInfo::sendFinishX( disp, id ); 00518 XCloseDisplay( disp ); 00519 } 00520 #endif 00521 } 00522 } 00523 00524 void KStartupInfo::disableAutoAppStartedSending( bool disable ) 00525 { 00526 auto_app_started_sending = !disable; 00527 } 00528 00529 void KStartupInfo::silenceStartup( bool silence ) 00530 { 00531 KStartupInfoId id; 00532 id.initId( kapp->startupId()); 00533 if( id.none()) 00534 return; 00535 KStartupInfoData data; 00536 data.setSilent( silence ? KStartupInfoData::Yes : KStartupInfoData::No ); 00537 sendChange( id, data ); 00538 } 00539 00540 void KStartupInfo::handleAutoAppStartedSending() 00541 { 00542 if( auto_app_started_sending ) 00543 appStarted(); 00544 } 00545 00546 void KStartupInfo::setNewStartupId( QWidget* window, const QCString& startup_id ) 00547 { 00548 bool activate = true; 00549 kapp->setStartupId( startup_id ); 00550 if( window != NULL ) 00551 { 00552 if( !startup_id.isEmpty() && startup_id != "0" ) 00553 { 00554 NETRootInfo i( qt_xdisplay(), NET::Supported ); 00555 if( i.isSupported( NET::WM2StartupId )) 00556 { 00557 KStartupInfo::setWindowStartupId( window->winId(), startup_id ); 00558 activate = false; // WM will take care of it 00559 } 00560 } 00561 if( activate ) 00562 { 00563 KWin::setOnDesktop( window->winId(), KWin::currentDesktop()); 00564 // This is not very nice, but there's no way how to get any 00565 // usable timestamp without ASN, so force activating the window. 00566 // And even with ASN, it's not possible to get the timestamp here, 00567 // so if the WM doesn't have support for ASN, it can't be used either. 00568 KWin::forceActiveWindow( window->winId()); 00569 } 00570 } 00571 KStartupInfo::handleAutoAppStartedSending(); 00572 } 00573 00574 KStartupInfo::startup_t KStartupInfo::checkStartup( WId w_P, KStartupInfoId& id_O, 00575 KStartupInfoData& data_O ) 00576 { 00577 return check_startup_internal( w_P, &id_O, &data_O ); 00578 } 00579 00580 KStartupInfo::startup_t KStartupInfo::checkStartup( WId w_P, KStartupInfoId& id_O ) 00581 { 00582 return check_startup_internal( w_P, &id_O, NULL ); 00583 } 00584 00585 KStartupInfo::startup_t KStartupInfo::checkStartup( WId w_P, KStartupInfoData& data_O ) 00586 { 00587 return check_startup_internal( w_P, NULL, &data_O ); 00588 } 00589 00590 KStartupInfo::startup_t KStartupInfo::checkStartup( WId w_P ) 00591 { 00592 return check_startup_internal( w_P, NULL, NULL ); 00593 } 00594 00595 KStartupInfo::startup_t KStartupInfo::check_startup_internal( WId w_P, KStartupInfoId* id_O, 00596 KStartupInfoData* data_O ) 00597 { 00598 if( d == NULL ) 00599 return NoMatch; 00600 if( d->startups.count() == 0 ) 00601 return NoMatch; // no startups 00602 // Strategy: 00603 // 00604 // Is this a compliant app ? 00605 // - Yes - test for match 00606 // - No - Is this a NET_WM compliant app ? 00607 // - Yes - test for pid match 00608 // - No - test for WM_CLASS match 00609 kdDebug( 172 ) << "check_startup" << endl; 00610 QCString id = windowStartupId( w_P ); 00611 if( !id.isNull()) 00612 { 00613 if( id.isEmpty() || id == "0" ) // means ignore this window 00614 { 00615 kdDebug( 172 ) << "ignore" << endl; 00616 return NoMatch; 00617 } 00618 return find_id( id, id_O, data_O ) ? Match : NoMatch; 00619 } 00620 #ifdef Q_WS_X11 00621 NETWinInfo info( qt_xdisplay(), w_P, qt_xrootwin(), 00622 NET::WMWindowType | NET::WMPid | NET::WMState ); 00623 pid_t pid = info.pid(); 00624 if( pid > 0 ) 00625 { 00626 QCString hostname = get_window_hostname( w_P ); 00627 if( !hostname.isEmpty() 00628 && find_pid( pid, hostname, id_O, data_O )) 00629 return Match; 00630 // try XClass matching , this PID stuff sucks :( 00631 } 00632 XClassHint hint; 00633 if( XGetClassHint( qt_xdisplay(), w_P, &hint ) != 0 ) 00634 { // We managed to read the class hint 00635 QCString res_name = hint.res_name; 00636 QCString res_class = hint.res_class; 00637 XFree( hint.res_name ); 00638 XFree( hint.res_class ); 00639 if( find_wclass( res_name, res_class, id_O, data_O )) 00640 return Match; 00641 } 00642 // ignore NET::Tool and other special window types, if they can't be matched 00643 NET::WindowType type = info.windowType( NET::NormalMask | NET::DesktopMask 00644 | NET::DockMask | NET::ToolbarMask | NET::MenuMask | NET::DialogMask 00645 | NET::OverrideMask | NET::TopMenuMask | NET::UtilityMask | NET::SplashMask ); 00646 if( type != NET::Normal 00647 && type != NET::Override 00648 && type != NET::Unknown 00649 && type != NET::Dialog 00650 && type != NET::Utility ) 00651 // && type != NET::Dock ) why did I put this here? 00652 return NoMatch; 00653 // lets see if this is a transient 00654 Window transient_for; 00655 if( XGetTransientForHint( qt_xdisplay(), static_cast< Window >( w_P ), &transient_for ) 00656 && static_cast< WId >( transient_for ) != qt_xrootwin() 00657 && transient_for != None ) 00658 return NoMatch; 00659 #endif 00660 kdDebug( 172 ) << "check_startup:cantdetect" << endl; 00661 return CantDetect; 00662 } 00663 00664 bool KStartupInfo::find_id( const QCString& id_P, KStartupInfoId* id_O, 00665 KStartupInfoData* data_O ) 00666 { 00667 if( d == NULL ) 00668 return false; 00669 kdDebug( 172 ) << "find_id:" << id_P << endl; 00670 KStartupInfoId id; 00671 id.initId( id_P ); 00672 if( d->startups.contains( id )) 00673 { 00674 if( id_O != NULL ) 00675 *id_O = id; 00676 if( data_O != NULL ) 00677 *data_O = d->startups[ id ]; 00678 kdDebug( 172 ) << "check_startup_id:match" << endl; 00679 return true; 00680 } 00681 return false; 00682 } 00683 00684 bool KStartupInfo::find_pid( pid_t pid_P, const QCString& hostname_P, 00685 KStartupInfoId* id_O, KStartupInfoData* data_O ) 00686 { 00687 if( d == NULL ) 00688 return false; 00689 kdDebug( 172 ) << "find_pid:" << pid_P << endl; 00690 for( QMap< KStartupInfoId, Data >::Iterator it = d->startups.begin(); 00691 it != d->startups.end(); 00692 ++it ) 00693 { 00694 if( ( *it ).is_pid( pid_P ) && ( *it ).hostname() == hostname_P ) 00695 { // Found it ! 00696 if( id_O != NULL ) 00697 *id_O = it.key(); 00698 if( data_O != NULL ) 00699 *data_O = *it; 00700 // non-compliant, remove on first match 00701 remove_startup_info_internal( it.key()); 00702 kdDebug( 172 ) << "check_startup_pid:match" << endl; 00703 return true; 00704 } 00705 } 00706 return false; 00707 } 00708 00709 bool KStartupInfo::find_wclass( QCString res_name, QCString res_class, 00710 KStartupInfoId* id_O, KStartupInfoData* data_O ) 00711 { 00712 if( d == NULL ) 00713 return false; 00714 res_name = res_name.lower(); 00715 res_class = res_class.lower(); 00716 kdDebug( 172 ) << "find_wclass:" << res_name << ":" << res_class << endl; 00717 for( QMap< KStartupInfoId, Data >::Iterator it = d->startups.begin(); 00718 it != d->startups.end(); 00719 ++it ) 00720 { 00721 const QCString wmclass = ( *it ).findWMClass(); 00722 if( wmclass.lower() == res_name || wmclass.lower() == res_class ) 00723 { // Found it ! 00724 if( id_O != NULL ) 00725 *id_O = it.key(); 00726 if( data_O != NULL ) 00727 *data_O = *it; 00728 // non-compliant, remove on first match 00729 remove_startup_info_internal( it.key()); 00730 kdDebug( 172 ) << "check_startup_wclass:match" << endl; 00731 return true; 00732 } 00733 } 00734 return false; 00735 } 00736 00737 #ifdef Q_WS_X11 00738 static Atom net_startup_atom = None; 00739 00740 static QCString read_startup_id_property( WId w_P ) 00741 { 00742 QCString ret; 00743 unsigned char *name_ret; 00744 Atom type_ret; 00745 int format_ret; 00746 unsigned long nitems_ret = 0, after_ret = 0; 00747 if( XGetWindowProperty( qt_xdisplay(), w_P, net_startup_atom, 0l, 4096, 00748 False, utf8_string_atom, &type_ret, &format_ret, &nitems_ret, &after_ret, &name_ret ) 00749 == Success ) 00750 { 00751 if( type_ret == utf8_string_atom && format_ret == 8 && name_ret != NULL ) 00752 ret = reinterpret_cast< char* >( name_ret ); 00753 if ( name_ret != NULL ) 00754 XFree( name_ret ); 00755 } 00756 return ret; 00757 } 00758 00759 #endif 00760 00761 QCString KStartupInfo::windowStartupId( WId w_P ) 00762 { 00763 #ifdef Q_WS_X11 00764 if( net_startup_atom == None ) 00765 net_startup_atom = XInternAtom( qt_xdisplay(), NET_STARTUP_WINDOW, False ); 00766 if( utf8_string_atom == None ) 00767 utf8_string_atom = XInternAtom( qt_xdisplay(), "UTF8_STRING", False ); 00768 QCString ret = read_startup_id_property( w_P ); 00769 if( ret.isEmpty()) 00770 { // retry with window group leader, as the spec says 00771 XWMHints* hints = XGetWMHints( qt_xdisplay(), w_P ); 00772 if( hints && ( hints->flags & WindowGroupHint ) != 0 ) 00773 ret = read_startup_id_property( hints->window_group ); 00774 if( hints ) 00775 XFree( hints ); 00776 } 00777 return ret; 00778 #else 00779 return QCString(); 00780 #endif 00781 } 00782 00783 void KStartupInfo::setWindowStartupId( WId w_P, const QCString& id_P ) 00784 { 00785 #ifdef Q_WS_X11 00786 if( id_P.isNull()) 00787 return; 00788 if( net_startup_atom == None ) 00789 net_startup_atom = XInternAtom( qt_xdisplay(), NET_STARTUP_WINDOW, False ); 00790 if( utf8_string_atom == None ) 00791 utf8_string_atom = XInternAtom( qt_xdisplay(), "UTF8_STRING", False ); 00792 XChangeProperty( qt_xdisplay(), w_P, net_startup_atom, utf8_string_atom, 8, 00793 PropModeReplace, reinterpret_cast< unsigned char* >( id_P.data()), id_P.length()); 00794 #endif 00795 } 00796 00797 QCString KStartupInfo::get_window_hostname( WId w_P ) 00798 { 00799 #ifdef Q_WS_X11 00800 XTextProperty tp; 00801 char** hh; 00802 int cnt; 00803 if( XGetWMClientMachine( qt_xdisplay(), w_P, &tp ) != 0 00804 && XTextPropertyToStringList( &tp, &hh, &cnt ) != 0 ) 00805 { 00806 if( cnt == 1 ) 00807 { 00808 QCString hostname = hh[ 0 ]; 00809 XFreeStringList( hh ); 00810 return hostname; 00811 } 00812 XFreeStringList( hh ); 00813 } 00814 #endif 00815 // no hostname 00816 return QCString(); 00817 } 00818 00819 void KStartupInfo::setTimeout( unsigned int secs_P ) 00820 { 00821 timeout = secs_P; 00822 // schedule removing entries that are older than the new timeout 00823 QTimer::singleShot( 0, this, SLOT( startups_cleanup_no_age())); 00824 } 00825 00826 void KStartupInfo::startups_cleanup_no_age() 00827 { 00828 startups_cleanup_internal( false ); 00829 } 00830 00831 void KStartupInfo::startups_cleanup() 00832 { 00833 if( d == NULL ) 00834 return; 00835 if( d->startups.count() == 0 && d->silent_startups.count() == 0 00836 && d->uninited_startups.count() == 0 ) 00837 { 00838 d->cleanup->stop(); 00839 return; 00840 } 00841 startups_cleanup_internal( true ); 00842 } 00843 00844 void KStartupInfo::startups_cleanup_internal( bool age_P ) 00845 { 00846 if( d == NULL ) 00847 return; 00848 for( QMap< KStartupInfoId, Data >::Iterator it = d->startups.begin(); 00849 it != d->startups.end(); 00850 ) 00851 { 00852 if( age_P ) 00853 ( *it ).age++; 00854 unsigned int tout = timeout; 00855 if( ( *it ).silent() == Data::Yes ) // TODO 00856 tout *= 20; 00857 if( ( *it ).age >= tout ) 00858 { 00859 const KStartupInfoId& key = it.key(); 00860 ++it; 00861 kdDebug( 172 ) << "startups entry timeout:" << key.id() << endl; 00862 remove_startup_info_internal( key ); 00863 } 00864 else 00865 ++it; 00866 } 00867 for( QMap< KStartupInfoId, Data >::Iterator it = d->silent_startups.begin(); 00868 it != d->silent_startups.end(); 00869 ) 00870 { 00871 if( age_P ) 00872 ( *it ).age++; 00873 unsigned int tout = timeout; 00874 if( ( *it ).silent() == Data::Yes ) // TODO 00875 tout *= 20; 00876 if( ( *it ).age >= tout ) 00877 { 00878 const KStartupInfoId& key = it.key(); 00879 ++it; 00880 kdDebug( 172 ) << "silent entry timeout:" << key.id() << endl; 00881 remove_startup_info_internal( key ); 00882 } 00883 else 00884 ++it; 00885 } 00886 for( QMap< KStartupInfoId, Data >::Iterator it = d->uninited_startups.begin(); 00887 it != d->uninited_startups.end(); 00888 ) 00889 { 00890 if( age_P ) 00891 ( *it ).age++; 00892 unsigned int tout = timeout; 00893 if( ( *it ).silent() == Data::Yes ) // TODO 00894 tout *= 20; 00895 if( ( *it ).age >= tout ) 00896 { 00897 const KStartupInfoId& key = it.key(); 00898 ++it; 00899 kdDebug( 172 ) << "uninited entry timeout:" << key.id() << endl; 00900 remove_startup_info_internal( key ); 00901 } 00902 else 00903 ++it; 00904 } 00905 } 00906 00907 void KStartupInfo::clean_all_noncompliant() 00908 { 00909 if( d == NULL ) 00910 return; 00911 for( QMap< KStartupInfoId, Data >::Iterator it = d->startups.begin(); 00912 it != d->startups.end(); 00913 ) 00914 { 00915 if( ( *it ).WMClass() != "0" ) 00916 { 00917 ++it; 00918 continue; 00919 } 00920 const KStartupInfoId& key = it.key(); 00921 ++it; 00922 kdDebug( 172 ) << "entry cleaning:" << key.id() << endl; 00923 remove_startup_info_internal( key ); 00924 } 00925 } 00926 00927 QCString KStartupInfo::createNewStartupId() 00928 { 00929 // Assign a unique id, use hostname+time+pid, that should be 200% unique. 00930 // Also append the user timestamp (for focus stealing prevention). 00931 struct timeval tm; 00932 gettimeofday( &tm, NULL ); 00933 char hostname[ 256 ]; 00934 hostname[ 0 ] = '\0'; 00935 if (!gethostname( hostname, 255 )) 00936 hostname[sizeof(hostname)-1] = '\0'; 00937 #ifdef Q_WS_X11 00938 extern Time qt_x_user_time; 00939 #else 00940 unsigned long qt_x_user_time = 0; 00941 #endif 00942 QCString id = QString( "%1;%2;%3;%4_TIME%5" ).arg( hostname ).arg( tm.tv_sec ) 00943 .arg( tm.tv_usec ).arg( getpid()).arg( qt_x_user_time ).utf8(); 00944 kdDebug( 172 ) << "creating: " << id << ":" << qAppName() << endl; 00945 return id; 00946 } 00947 00948 00949 struct KStartupInfoIdPrivate 00950 { 00951 KStartupInfoIdPrivate() : id( "" ) {} 00952 QCString id; // id 00953 }; 00954 00955 const QCString& KStartupInfoId::id() const 00956 { 00957 return d->id; 00958 } 00959 00960 00961 QString KStartupInfoId::to_text() const 00962 { 00963 return QString::fromLatin1( " ID=\"%1\" " ).arg( escape_str( id())); 00964 } 00965 00966 KStartupInfoId::KStartupInfoId( const QString& txt_P ) 00967 { 00968 d = new KStartupInfoIdPrivate; 00969 QStringList items = get_fields( txt_P ); 00970 const QString id_str = QString::fromLatin1( "ID=" ); 00971 for( QStringList::Iterator it = items.begin(); 00972 it != items.end(); 00973 ++it ) 00974 { 00975 if( ( *it ).startsWith( id_str )) 00976 d->id = get_cstr( *it ); 00977 } 00978 } 00979 00980 void KStartupInfoId::initId( const QCString& id_P ) 00981 { 00982 if( !id_P.isEmpty()) 00983 { 00984 d->id = id_P; 00985 #ifdef KSTARTUPINFO_ALL_DEBUG 00986 kdDebug( 172 ) << "using: " << d->id << endl; 00987 #endif 00988 return; 00989 } 00990 const char* startup_env = getenv( NET_STARTUP_ENV ); 00991 if( startup_env != NULL && *startup_env != '\0' ) 00992 { // already has id 00993 d->id = startup_env; 00994 #ifdef KSTARTUPINFO_ALL_DEBUG 00995 kdDebug( 172 ) << "reusing: " << d->id << endl; 00996 #endif 00997 return; 00998 } 00999 d->id = KStartupInfo::createNewStartupId(); 01000 } 01001 01002 bool KStartupInfoId::setupStartupEnv() const 01003 { 01004 if( id().isEmpty()) 01005 { 01006 unsetenv( NET_STARTUP_ENV ); 01007 return false; 01008 } 01009 return setenv( NET_STARTUP_ENV, id(), true ) == 0; 01010 } 01011 01012 KStartupInfoId KStartupInfo::currentStartupIdEnv() 01013 { 01014 const char* startup_env = getenv( NET_STARTUP_ENV ); 01015 KStartupInfoId id; 01016 if( startup_env != NULL && *startup_env != '\0' ) 01017 id.d->id = startup_env; 01018 else 01019 id.d->id = "0"; 01020 return id; 01021 } 01022 01023 void KStartupInfo::resetStartupEnv() 01024 { 01025 unsetenv( NET_STARTUP_ENV ); 01026 } 01027 01028 KStartupInfoId::KStartupInfoId() 01029 { 01030 d = new KStartupInfoIdPrivate; 01031 } 01032 01033 KStartupInfoId::~KStartupInfoId() 01034 { 01035 delete d; 01036 } 01037 01038 KStartupInfoId::KStartupInfoId( const KStartupInfoId& id_P ) 01039 { 01040 d = new KStartupInfoIdPrivate( *id_P.d ); 01041 } 01042 01043 KStartupInfoId& KStartupInfoId::operator=( const KStartupInfoId& id_P ) 01044 { 01045 if( &id_P == this ) 01046 return *this; 01047 delete d; 01048 d = new KStartupInfoIdPrivate( *id_P.d ); 01049 return *this; 01050 } 01051 01052 bool KStartupInfoId::operator==( const KStartupInfoId& id_P ) const 01053 { 01054 return id() == id_P.id(); 01055 } 01056 01057 bool KStartupInfoId::operator!=( const KStartupInfoId& id_P ) const 01058 { 01059 return !(*this == id_P ); 01060 } 01061 01062 // needed for QMap 01063 bool KStartupInfoId::operator<( const KStartupInfoId& id_P ) const 01064 { 01065 return id() < id_P.id(); 01066 } 01067 01068 bool KStartupInfoId::none() const 01069 { 01070 return d->id.isEmpty() || d->id == "0"; 01071 } 01072 01073 unsigned long KStartupInfoId::timestamp() const 01074 { 01075 if( none()) 01076 return 0; 01077 int pos = d->id.findRev( "_TIME" ); 01078 if( pos >= 0 ) 01079 { 01080 bool ok; 01081 unsigned long time = d->id.mid( pos + 5 ).toULong( &ok ); 01082 if( !ok && d->id[ pos + 5 ] == '-' ) // try if it's as a negative signed number perhaps 01083 time = d->id.mid( pos + 5 ).toLong( &ok ); 01084 if( ok ) 01085 return time; 01086 } 01087 // libstartup-notification style : 01088 // snprintf (s, len, "%s/%s/%lu/%d-%d-%s", 01089 // canonicalized_launcher, canonicalized_launchee, (unsigned long) timestamp, 01090 // (int) getpid (), (int) sequence_number, hostbuf); 01091 int pos1 = d->id.findRev( '/' ); 01092 if( pos1 > 0 ) 01093 { 01094 int pos2 = d->id.findRev( '/', pos1 - 1 ); 01095 if( pos2 >= 0 ) 01096 { 01097 bool ok; 01098 unsigned long time = d->id.mid( pos2 + 1, pos1 - pos2 - 1 ).toULong( &ok ); 01099 if( !ok && d->id[ pos2 + 1 ] == '-' ) // try if it's as a negative signed number perhaps 01100 time = d->id.mid( pos2 + 1, pos1 - pos2 - 1 ).toLong( &ok ); 01101 if( ok ) 01102 return time; 01103 } 01104 } 01105 // bah ... old KStartupInfo or a problem 01106 return 0; 01107 } 01108 01109 struct KStartupInfoDataPrivate 01110 { 01111 KStartupInfoDataPrivate() : desktop( 0 ), wmclass( "" ), hostname( "" ), 01112 silent( KStartupInfoData::Unknown ), timestamp( -1U ), screen( -1 ) {} 01113 QString bin; 01114 QString name; 01115 QString description; 01116 QString icon; 01117 int desktop; 01118 QValueList< pid_t > pids; 01119 QCString wmclass; 01120 QCString hostname; 01121 KStartupInfoData::TriState silent; 01122 unsigned long timestamp; 01123 int screen; 01124 }; 01125 01126 QString KStartupInfoData::to_text() const 01127 { 01128 QString ret = ""; 01129 if( !d->bin.isEmpty()) 01130 ret += QString::fromLatin1( " BIN=\"%1\"" ).arg( escape_str( d->bin )); 01131 if( !d->name.isEmpty()) 01132 ret += QString::fromLatin1( " NAME=\"%1\"" ).arg( escape_str( d->name )); 01133 if( !d->description.isEmpty()) 01134 ret += QString::fromLatin1( " DESCRIPTION=\"%1\"" ).arg( escape_str( d->description )); 01135 if( !d->icon.isEmpty()) 01136 ret += QString::fromLatin1( " ICON=%1" ).arg( d->icon ); 01137 if( d->desktop != 0 ) 01138 ret += QString::fromLatin1( " DESKTOP=%1" ) 01139 .arg( d->desktop == NET::OnAllDesktops ? NET::OnAllDesktops : d->desktop - 1 ); // spec counts from 0 01140 if( !d->wmclass.isEmpty()) 01141 ret += QString::fromLatin1( " WMCLASS=\"%1\"" ).arg( d->wmclass ); 01142 if( !d->hostname.isEmpty()) 01143 ret += QString::fromLatin1( " HOSTNAME=%1" ).arg( d->hostname ); 01144 for( QValueList< pid_t >::ConstIterator it = d->pids.begin(); 01145 it != d->pids.end(); 01146 ++it ) 01147 ret += QString::fromLatin1( " PID=%1" ).arg( *it ); 01148 if( d->silent != Unknown ) 01149 ret += QString::fromLatin1( " SILENT=%1" ).arg( d->silent == Yes ? 1 : 0 ); 01150 if( d->timestamp != -1U ) 01151 ret += QString::fromLatin1( " TIMESTAMP=%1" ).arg( d->timestamp ); 01152 if( d->screen != -1 ) 01153 ret += QString::fromLatin1( " SCREEN=%1" ).arg( d->screen ); 01154 return ret; 01155 } 01156 01157 KStartupInfoData::KStartupInfoData( const QString& txt_P ) 01158 { 01159 d = new KStartupInfoDataPrivate; 01160 QStringList items = get_fields( txt_P ); 01161 const QString bin_str = QString::fromLatin1( "BIN=" ); 01162 const QString name_str = QString::fromLatin1( "NAME=" ); 01163 const QString description_str = QString::fromLatin1( "DESCRIPTION=" ); 01164 const QString icon_str = QString::fromLatin1( "ICON=" ); 01165 const QString desktop_str = QString::fromLatin1( "DESKTOP=" ); 01166 const QString wmclass_str = QString::fromLatin1( "WMCLASS=" ); 01167 const QString hostname_str = QString::fromLatin1( "HOSTNAME=" ); // SELI nonstd 01168 const QString pid_str = QString::fromLatin1( "PID=" ); // SELI nonstd 01169 const QString silent_str = QString::fromLatin1( "SILENT=" ); 01170 const QString timestamp_str = QString::fromLatin1( "TIMESTAMP=" ); 01171 const QString screen_str = QString::fromLatin1( "SCREEN=" ); 01172 for( QStringList::Iterator it = items.begin(); 01173 it != items.end(); 01174 ++it ) 01175 { 01176 if( ( *it ).startsWith( bin_str )) 01177 d->bin = get_str( *it ); 01178 else if( ( *it ).startsWith( name_str )) 01179 d->name = get_str( *it ); 01180 else if( ( *it ).startsWith( description_str )) 01181 d->description = get_str( *it ); 01182 else if( ( *it ).startsWith( icon_str )) 01183 d->icon = get_str( *it ); 01184 else if( ( *it ).startsWith( desktop_str )) 01185 { 01186 d->desktop = get_num( *it ); 01187 if( d->desktop != NET::OnAllDesktops ) 01188 ++d->desktop; // spec counts from 0 01189 } 01190 else if( ( *it ).startsWith( wmclass_str )) 01191 d->wmclass = get_cstr( *it ); 01192 else if( ( *it ).startsWith( hostname_str )) 01193 d->hostname = get_cstr( *it ); 01194 else if( ( *it ).startsWith( pid_str )) 01195 addPid( get_num( *it )); 01196 else if( ( *it ).startsWith( silent_str )) 01197 d->silent = get_num( *it ) != 0 ? Yes : No; 01198 else if( ( *it ).startsWith( timestamp_str )) 01199 d->timestamp = get_unum( *it ); 01200 else if( ( *it ).startsWith( screen_str )) 01201 d->screen = get_num( *it ); 01202 } 01203 } 01204 01205 KStartupInfoData::KStartupInfoData( const KStartupInfoData& data ) 01206 { 01207 d = new KStartupInfoDataPrivate( *data.d ); 01208 } 01209 01210 KStartupInfoData& KStartupInfoData::operator=( const KStartupInfoData& data ) 01211 { 01212 if( &data == this ) 01213 return *this; 01214 delete d; 01215 d = new KStartupInfoDataPrivate( *data.d ); 01216 return *this; 01217 } 01218 01219 void KStartupInfoData::update( const KStartupInfoData& data_P ) 01220 { 01221 if( !data_P.bin().isEmpty()) 01222 d->bin = data_P.bin(); 01223 if( !data_P.name().isEmpty() && name().isEmpty()) // don't overwrite 01224 d->name = data_P.name(); 01225 if( !data_P.description().isEmpty() && description().isEmpty()) // don't overwrite 01226 d->description = data_P.description(); 01227 if( !data_P.icon().isEmpty() && icon().isEmpty()) // don't overwrite 01228 d->icon = data_P.icon(); 01229 if( data_P.desktop() != 0 && desktop() == 0 ) // don't overwrite 01230 d->desktop = data_P.desktop(); 01231 if( !data_P.d->wmclass.isEmpty()) 01232 d->wmclass = data_P.d->wmclass; 01233 if( !data_P.d->hostname.isEmpty()) 01234 d->hostname = data_P.d->hostname; 01235 for( QValueList< pid_t >::ConstIterator it = data_P.d->pids.begin(); 01236 it != data_P.d->pids.end(); 01237 ++it ) 01238 addPid( *it ); 01239 if( data_P.silent() != Unknown ) 01240 d->silent = data_P.silent(); 01241 if( data_P.timestamp() != -1U && timestamp() == -1U ) // don't overwrite 01242 d->timestamp = data_P.timestamp(); 01243 if( data_P.screen() != -1 ) 01244 d->screen = data_P.screen(); 01245 } 01246 01247 KStartupInfoData::KStartupInfoData() 01248 { 01249 d = new KStartupInfoDataPrivate; 01250 } 01251 01252 KStartupInfoData::~KStartupInfoData() 01253 { 01254 delete d; 01255 } 01256 01257 void KStartupInfoData::setBin( const QString& bin_P ) 01258 { 01259 d->bin = bin_P; 01260 } 01261 01262 const QString& KStartupInfoData::bin() const 01263 { 01264 return d->bin; 01265 } 01266 01267 void KStartupInfoData::setName( const QString& name_P ) 01268 { 01269 d->name = name_P; 01270 } 01271 01272 const QString& KStartupInfoData::name() const 01273 { 01274 return d->name; 01275 } 01276 01277 const QString& KStartupInfoData::findName() const 01278 { 01279 if( !name().isEmpty()) 01280 return name(); 01281 return bin(); 01282 } 01283 01284 void KStartupInfoData::setDescription( const QString& desc_P ) 01285 { 01286 d->description = desc_P; 01287 } 01288 01289 const QString& KStartupInfoData::description() const 01290 { 01291 return d->description; 01292 } 01293 01294 const QString& KStartupInfoData::findDescription() const 01295 { 01296 if( !description().isEmpty()) 01297 return description(); 01298 return name(); 01299 } 01300 01301 void KStartupInfoData::setIcon( const QString& icon_P ) 01302 { 01303 d->icon = icon_P; 01304 } 01305 01306 const QString& KStartupInfoData::findIcon() const 01307 { 01308 if( !icon().isEmpty()) 01309 return icon(); 01310 return bin(); 01311 } 01312 01313 const QString& KStartupInfoData::icon() const 01314 { 01315 return d->icon; 01316 } 01317 01318 void KStartupInfoData::setDesktop( int desktop_P ) 01319 { 01320 d->desktop = desktop_P; 01321 } 01322 01323 int KStartupInfoData::desktop() const 01324 { 01325 return d->desktop; 01326 } 01327 01328 void KStartupInfoData::setWMClass( const QCString& wmclass_P ) 01329 { 01330 d->wmclass = wmclass_P; 01331 } 01332 01333 const QCString KStartupInfoData::findWMClass() const 01334 { 01335 if( !WMClass().isEmpty() && WMClass() != "0" ) 01336 return WMClass(); 01337 return bin().utf8(); 01338 } 01339 01340 const QCString& KStartupInfoData::WMClass() const 01341 { 01342 return d->wmclass; 01343 } 01344 01345 void KStartupInfoData::setHostname( const QCString& hostname_P ) 01346 { 01347 if( !hostname_P.isNull()) 01348 d->hostname = hostname_P; 01349 else 01350 { 01351 char tmp[ 256 ]; 01352 tmp[ 0 ] = '\0'; 01353 if (!gethostname( tmp, 255 )) 01354 tmp[sizeof(tmp)-1] = '\0'; 01355 d->hostname = tmp; 01356 } 01357 } 01358 01359 const QCString& KStartupInfoData::hostname() const 01360 { 01361 return d->hostname; 01362 } 01363 01364 void KStartupInfoData::addPid( pid_t pid_P ) 01365 { 01366 if( !d->pids.contains( pid_P )) 01367 d->pids.append( pid_P ); 01368 } 01369 01370 void KStartupInfoData::remove_pid( pid_t pid_P ) 01371 { 01372 d->pids.remove( pid_P ); 01373 } 01374 01375 const QValueList< pid_t >& KStartupInfoData::pids() const 01376 { 01377 return d->pids; 01378 } 01379 01380 bool KStartupInfoData::is_pid( pid_t pid_P ) const 01381 { 01382 return d->pids.contains( pid_P ); 01383 } 01384 01385 void KStartupInfoData::setSilent( TriState state_P ) 01386 { 01387 d->silent = state_P; 01388 } 01389 01390 KStartupInfoData::TriState KStartupInfoData::silent() const 01391 { 01392 return d->silent; 01393 } 01394 01395 void KStartupInfoData::setTimestamp( unsigned long time ) 01396 { 01397 d->timestamp = time; 01398 } 01399 01400 unsigned long KStartupInfoData::timestamp() const 01401 { 01402 return d->timestamp; 01403 } 01404 01405 void KStartupInfoData::setScreen( int screen ) 01406 { 01407 d->screen = screen; 01408 } 01409 01410 int KStartupInfoData::screen() const 01411 { 01412 return d->screen; 01413 } 01414 01415 static 01416 long get_num( const QString& item_P ) 01417 { 01418 unsigned int pos = item_P.find( '=' ); 01419 return item_P.mid( pos + 1 ).toLong(); 01420 } 01421 01422 static 01423 unsigned long get_unum( const QString& item_P ) 01424 { 01425 unsigned int pos = item_P.find( '=' ); 01426 return item_P.mid( pos + 1 ).toULong(); 01427 } 01428 01429 static 01430 QString get_str( const QString& item_P ) 01431 { 01432 unsigned int pos = item_P.find( '=' ); 01433 if( item_P.length() > pos + 2 && item_P[ pos + 1 ] == '\"' ) 01434 { 01435 int pos2 = item_P.left( pos + 2 ).find( '\"' ); 01436 if( pos2 < 0 ) 01437 return QString::null; // 01234 01438 return item_P.mid( pos + 2, pos2 - 2 - pos ); // A="C" 01439 } 01440 return item_P.mid( pos + 1 ); 01441 } 01442 01443 static 01444 QCString get_cstr( const QString& item_P ) 01445 { 01446 return get_str( item_P ).utf8(); 01447 } 01448 01449 static 01450 QStringList get_fields( const QString& txt_P ) 01451 { 01452 QString txt = txt_P.simplifyWhiteSpace(); 01453 QStringList ret; 01454 QString item = ""; 01455 bool in = false; 01456 bool escape = false; 01457 for( unsigned int pos = 0; 01458 pos < txt.length(); 01459 ++pos ) 01460 { 01461 if( escape ) 01462 { 01463 item += txt[ pos ]; 01464 escape = false; 01465 } 01466 else if( txt[ pos ] == '\\' ) 01467 escape = true; 01468 else if( txt[ pos ] == '\"' ) 01469 in = !in; 01470 else if( txt[ pos ] == ' ' && !in ) 01471 { 01472 ret.append( item ); 01473 item = ""; 01474 } 01475 else 01476 item += txt[ pos ]; 01477 } 01478 ret.append( item ); 01479 return ret; 01480 } 01481 01482 static QString escape_str( const QString& str_P ) 01483 { 01484 QString ret = ""; 01485 for( unsigned int pos = 0; 01486 pos < str_P.length(); 01487 ++pos ) 01488 { 01489 if( str_P[ pos ] == '\\' 01490 || str_P[ pos ] == '"' ) 01491 ret += '\\'; 01492 ret += str_P[ pos ]; 01493 } 01494 return ret; 01495 } 01496 01497 #include "kstartupinfo.moc" 01498 #endif