kstartupinfo.cpp
00001 /**************************************************************************** 00002 00003 $Id$ 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 <tqwidget.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 <tqglobal.h> 00039 #ifdef HAVE_CONFIG_H 00040 #include <config.h> 00041 #endif 00042 00043 // need to resolve INT32(tqglobal.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 <tqtimer.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 TQString& item_P ); 00075 static unsigned long get_unum( const TQString& item_P ); 00076 static TQString get_str( const TQString& item_P ); 00077 static TQCString get_cstr( const TQString& item_P ); 00078 static TQStringList get_fields( const TQString& txt_P ); 00079 static TQString escape_str( const TQString& 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 TQString& txt_P ) 00089 : KStartupInfoData( txt_P ), age( 0 ) {} 00090 unsigned int age; 00091 }; 00092 00093 struct KStartupInfoPrivate 00094 { 00095 public: 00096 TQMap< KStartupInfoId, KStartupInfo::Data > startups; 00097 // contains silenced ASN's only if !AnnounceSilencedChanges 00098 TQMap< KStartupInfoId, KStartupInfo::Data > silent_startups; 00099 // contains ASN's that had change: but no new: yet 00100 TQMap< KStartupInfoId, KStartupInfo::Data > uninited_startups; 00101 #ifdef Q_WS_X11 00102 KWinModule* wm_module; 00103 KXMessages msgs; 00104 #endif 00105 TQTimer* 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, TQObject* parent_P, const char* name_P ) 00116 : TQObject( parent_P, name_P ), 00117 timeout( 60 ), d( NULL ) 00118 { 00119 init( flags_P ); 00120 } 00121 00122 KStartupInfo::KStartupInfo( bool clean_on_cantdetect_P, TQObject* parent_P, const char* name_P ) 00123 : TQObject( 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, TQT_SIGNAL( windowAdded( WId )), TQT_SLOT( slot_window_added( WId ))); 00143 connect( d->wm_module, TQT_SIGNAL( systemTrayWindowAdded( WId )), TQT_SLOT( slot_window_added( WId ))); 00144 } 00145 else 00146 d->wm_module = NULL; 00147 connect( &d->msgs, TQT_SIGNAL( gotMessage( const TQString& )), TQT_SLOT( got_message( const TQString& ))); 00148 #endif 00149 d->cleanup = new TQTimer( this, "cleanup" ); 00150 connect( d->cleanup, TQT_SIGNAL( timeout()), TQT_SLOT( startups_cleanup())); 00151 } 00152 00153 KStartupInfo::~KStartupInfo() 00154 { 00155 delete d; 00156 } 00157 00158 void KStartupInfo::got_message( const TQString& msg_P ) 00159 { 00160 // TODO do something with SCREEN= ? 00161 kdDebug( 172 ) << "[kdecore-kstartupinfo] got:" << msg_P << endl; 00162 TQString 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 TQCustomEvent 00181 { 00182 public: 00183 DelayedWindowEvent( WId w_P ) 00184 : TQCustomEvent( TQEvent::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( TQCustomEvent* e_P ) 00195 { 00196 if( e_P->type() == TQEvent::User + 15 ) 00197 window_added( static_cast< DelayedWindowEvent* >( e_P )->w ); 00198 else 00199 TQObject::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 ) << "[kdecore-kstartupinfo] 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 TQString& 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 ) << "[kdecore-kstartupinfo] 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 ) << "[kdecore-kstartupinfo] 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 ) << "[kdecore-kstartupinfo] 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 ) << "[kdecore-kstartupinfo] 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 ) << "[kdecore-kstartupinfo] 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 ) << "[kdecore-kstartupinfo] 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 TQString& 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 ) << "[kdecore-kstartupinfo] 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 ) << "[kdecore-kstartupinfo] removing silent" << endl; 00329 d->silent_startups.remove( id_P ); 00330 } 00331 else if( d->uninited_startups.contains( id_P )) 00332 { 00333 kdDebug( 172 ) << "[kdecore-kstartupinfo] 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( TQMap< 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( TQValueList< 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 TQString msg = TQString::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 ) << "[kdecore-kstartupinfo] 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 TQString msg = TQString::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 ) << "[kdecore-kstartupinfo] sending " << msg << endl; 00402 #endif 00403 return KXMessages::broadcastMessageX( disp_P, NET_STARTUP_MSG, msg, -1, false ); 00404 } 00405 00406 TQString KStartupInfo::check_required_startup_fields( const TQString& msg, const KStartupInfoData& data_P, 00407 int screen ) 00408 { 00409 TQString ret = msg; 00410 if( data_P.name().isEmpty()) 00411 { 00412 // kdWarning( 172 ) << "[kdecore-kstartupinfo] NAME not specified in initial startup message" << endl; 00413 TQString name = data_P.bin(); 00414 if( name.isEmpty()) 00415 name = "UNKNOWN"; 00416 ret += TQString( " NAME=\"%1\"" ).arg( escape_str( name )); 00417 } 00418 if( data_P.screen() == -1 ) // add automatically if needed 00419 ret += TQString( " 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 TQString msg = TQString::fromLatin1( "change: %1 %2" ) 00429 .arg( id_P.to_text()).arg( data_P.to_text()); 00430 kdDebug( 172 ) << "[kdecore-kstartupinfo] 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 TQString msg = TQString::fromLatin1( "change: %1 %2" ) 00441 .arg( id_P.to_text()).arg( data_P.to_text()); 00442 #ifdef KSTARTUPINFO_ALL_DEBUG 00443 kdDebug( 172 ) << "[kdecore-kstartupinfo] 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 TQString msg = TQString::fromLatin1( "remove: %1" ).arg( id_P.to_text()); 00454 kdDebug( 172 ) << "[kdecore-kstartupinfo] 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 TQString msg = TQString::fromLatin1( "remove: %1" ).arg( id_P.to_text()); 00464 #ifdef KSTARTUPINFO_ALL_DEBUG 00465 kdDebug( 172 ) << "[kdecore-kstartupinfo] 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 TQString msg = TQString::fromLatin1( "remove: %1 %2" ) 00476 .arg( id_P.to_text()).arg( data_P.to_text()); 00477 kdDebug( 172 ) << "[kdecore-kstartupinfo] 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 TQString msg = TQString::fromLatin1( "remove: %1 %2" ) 00488 .arg( id_P.to_text()).arg( data_P.to_text()); 00489 #ifdef KSTARTUPINFO_ALL_DEBUG 00490 kdDebug( 172 ) << "[kdecore-kstartupinfo] 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 TQCString& 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( TQWidget* window, const TQCString& 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 ) << "[kdecore-kstartupinfo] check_startup" << endl; 00610 TQCString id = windowStartupId( w_P ); 00611 if( !id.isNull()) 00612 { 00613 if( id.isEmpty() || id == "0" ) // means ignore this window 00614 { 00615 kdDebug( 172 ) << "[kdecore-kstartupinfo] 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 TQCString 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 TQCString res_name = hint.res_name; 00636 TQCString 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 ) << "[kdecore-kstartupinfo] check_startup:cantdetect" << endl; 00661 return CantDetect; 00662 } 00663 00664 bool KStartupInfo::find_id( const TQCString& id_P, KStartupInfoId* id_O, 00665 KStartupInfoData* data_O ) 00666 { 00667 if( d == NULL ) 00668 return false; 00669 kdDebug( 172 ) << "[kdecore-kstartupinfo] 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 ) << "[kdecore-kstartupinfo] check_startup_id:match" << endl; 00679 return true; 00680 } 00681 return false; 00682 } 00683 00684 bool KStartupInfo::find_pid( pid_t pid_P, const TQCString& hostname_P, 00685 KStartupInfoId* id_O, KStartupInfoData* data_O ) 00686 { 00687 if( d == NULL ) 00688 return false; 00689 kdDebug( 172 ) << "[kdecore-kstartupinfo] find_pid:" << pid_P << endl; 00690 for( TQMap< 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 ) << "[kdecore-kstartupinfo] check_startup_pid:match" << endl; 00703 return true; 00704 } 00705 } 00706 return false; 00707 } 00708 00709 bool KStartupInfo::find_wclass( TQCString res_name, TQCString 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 ) << "[kdecore-kstartupinfo] find_wclass:" << res_name << ":" << res_class << endl; 00717 for( TQMap< KStartupInfoId, Data >::Iterator it = d->startups.begin(); 00718 it != d->startups.end(); 00719 ++it ) 00720 { 00721 const TQCString 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 ) << "[kdecore-kstartupinfo] 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 TQCString read_startup_id_property( WId w_P ) 00741 { 00742 TQCString 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 TQCString 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 TQCString 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 TQCString(); 00780 #endif 00781 } 00782 00783 void KStartupInfo::setWindowStartupId( WId w_P, const TQCString& 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* >( const_cast<TQCString&>(id_P).data()), id_P.length()); 00794 #endif 00795 } 00796 00797 TQCString 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 TQCString hostname = hh[ 0 ]; 00809 XFreeStringList( hh ); 00810 return hostname; 00811 } 00812 XFreeStringList( hh ); 00813 } 00814 #endif 00815 // no hostname 00816 return TQCString(); 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 TQTimer::singleShot( 0, this, TQT_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( TQMap< 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 ) << "[kdecore-kstartupinfo] startups entry timeout:" << key.id() << endl; 00862 remove_startup_info_internal( key ); 00863 } 00864 else 00865 ++it; 00866 } 00867 for( TQMap< 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 ) << "[kdecore-kstartupinfo] silent entry timeout:" << key.id() << endl; 00881 remove_startup_info_internal( key ); 00882 } 00883 else 00884 ++it; 00885 } 00886 for( TQMap< 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 ) << "[kdecore-kstartupinfo] 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( TQMap< 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 ) << "[kdecore-kstartupinfo] entry cleaning:" << key.id() << endl; 00923 remove_startup_info_internal( key ); 00924 } 00925 } 00926 00927 TQCString 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 TQCString id = TQString(TQString( "%1;%2;%3;%4_TIME%5" ).arg( hostname ).arg( tm.tv_sec ) 00938 .arg( tm.tv_usec ).arg( getpid()).arg( GET_QT_X_USER_TIME() )).utf8(); 00939 kdDebug( 172 ) << "[kdecore-kstartupinfo] creating: " << id << ":" << tqAppName() << endl; 00940 return id; 00941 } 00942 00943 00944 struct KStartupInfoIdPrivate 00945 { 00946 KStartupInfoIdPrivate() : id( "" ) {} 00947 TQCString id; // id 00948 }; 00949 00950 const TQCString& KStartupInfoId::id() const 00951 { 00952 return d->id; 00953 } 00954 00955 00956 TQString KStartupInfoId::to_text() const 00957 { 00958 return TQString::fromLatin1( " ID=\"%1\" " ).arg( escape_str( id())); 00959 } 00960 00961 KStartupInfoId::KStartupInfoId( const TQString& txt_P ) 00962 { 00963 d = new KStartupInfoIdPrivate; 00964 TQStringList items = get_fields( txt_P ); 00965 const TQString id_str = TQString::fromLatin1( "ID=" ); 00966 for( TQStringList::Iterator it = items.begin(); 00967 it != items.end(); 00968 ++it ) 00969 { 00970 if( ( *it ).startsWith( id_str )) 00971 d->id = get_cstr( *it ); 00972 } 00973 } 00974 00975 void KStartupInfoId::initId( const TQCString& id_P ) 00976 { 00977 if( !id_P.isEmpty()) 00978 { 00979 d->id = id_P; 00980 #ifdef KSTARTUPINFO_ALL_DEBUG 00981 kdDebug( 172 ) << "[kdecore-kstartupinfo] using: " << d->id << endl; 00982 #endif 00983 return; 00984 } 00985 const char* startup_env = getenv( NET_STARTUP_ENV ); 00986 if( startup_env != NULL && *startup_env != '\0' ) 00987 { // already has id 00988 d->id = startup_env; 00989 #ifdef KSTARTUPINFO_ALL_DEBUG 00990 kdDebug( 172 ) << "[kdecore-kstartupinfo] reusing: " << d->id << endl; 00991 #endif 00992 return; 00993 } 00994 d->id = KStartupInfo::createNewStartupId(); 00995 } 00996 00997 bool KStartupInfoId::setupStartupEnv() const 00998 { 00999 if( id().isEmpty()) 01000 { 01001 unsetenv( NET_STARTUP_ENV ); 01002 return false; 01003 } 01004 return setenv( NET_STARTUP_ENV, id(), true ) == 0; 01005 } 01006 01007 KStartupInfoId KStartupInfo::currentStartupIdEnv() 01008 { 01009 const char* startup_env = getenv( NET_STARTUP_ENV ); 01010 KStartupInfoId id; 01011 if( startup_env != NULL && *startup_env != '\0' ) 01012 id.d->id = startup_env; 01013 else 01014 id.d->id = "0"; 01015 return id; 01016 } 01017 01018 void KStartupInfo::resetStartupEnv() 01019 { 01020 unsetenv( NET_STARTUP_ENV ); 01021 } 01022 01023 KStartupInfoId::KStartupInfoId() 01024 { 01025 d = new KStartupInfoIdPrivate; 01026 } 01027 01028 KStartupInfoId::~KStartupInfoId() 01029 { 01030 delete d; 01031 } 01032 01033 KStartupInfoId::KStartupInfoId( const KStartupInfoId& id_P ) 01034 { 01035 d = new KStartupInfoIdPrivate( *id_P.d ); 01036 } 01037 01038 KStartupInfoId& KStartupInfoId::operator=( const KStartupInfoId& id_P ) 01039 { 01040 if( &id_P == this ) 01041 return *this; 01042 delete d; 01043 d = new KStartupInfoIdPrivate( *id_P.d ); 01044 return *this; 01045 } 01046 01047 bool KStartupInfoId::operator==( const KStartupInfoId& id_P ) const 01048 { 01049 return id() == id_P.id(); 01050 } 01051 01052 bool KStartupInfoId::operator!=( const KStartupInfoId& id_P ) const 01053 { 01054 return !(*this == id_P ); 01055 } 01056 01057 // needed for QMap 01058 bool KStartupInfoId::operator<( const KStartupInfoId& id_P ) const 01059 { 01060 return id() < id_P.id(); 01061 } 01062 01063 bool KStartupInfoId::none() const 01064 { 01065 return d->id.isEmpty() || d->id == "0"; 01066 } 01067 01068 unsigned long KStartupInfoId::timestamp() const 01069 { 01070 if( none()) 01071 return 0; 01072 int pos = d->id.findRev( "_TIME" ); 01073 if( pos >= 0 ) 01074 { 01075 bool ok; 01076 unsigned long time = d->id.mid( pos + 5 ).toULong( &ok ); 01077 if( !ok && d->id[ pos + 5 ] == '-' ) // try if it's as a negative signed number perhaps 01078 time = d->id.mid( pos + 5 ).toLong( &ok ); 01079 if( ok ) 01080 return time; 01081 } 01082 // libstartup-notification style : 01083 // snprintf (s, len, "%s/%s/%lu/%d-%d-%s", 01084 // canonicalized_launcher, canonicalized_launchee, (unsigned long) timestamp, 01085 // (int) getpid (), (int) sequence_number, hostbuf); 01086 int pos1 = d->id.findRev( '/' ); 01087 if( pos1 > 0 ) 01088 { 01089 int pos2 = d->id.findRev( '/', pos1 - 1 ); 01090 if( pos2 >= 0 ) 01091 { 01092 bool ok; 01093 unsigned long time = d->id.mid( pos2 + 1, pos1 - pos2 - 1 ).toULong( &ok ); 01094 if( !ok && d->id[ pos2 + 1 ] == '-' ) // try if it's as a negative signed number perhaps 01095 time = d->id.mid( pos2 + 1, pos1 - pos2 - 1 ).toLong( &ok ); 01096 if( ok ) 01097 return time; 01098 } 01099 } 01100 // bah ... old KStartupInfo or a problem 01101 return 0; 01102 } 01103 01104 struct KStartupInfoDataPrivate 01105 { 01106 KStartupInfoDataPrivate() : desktop( 0 ), wmclass( "" ), hostname( "" ), 01107 silent( KStartupInfoData::Unknown ), timestamp( -1U ), screen( -1 ), xinerama( -1 ), launched_by( 0 ) {} 01108 TQString bin; 01109 TQString name; 01110 TQString description; 01111 TQString icon; 01112 int desktop; 01113 TQValueList< pid_t > pids; 01114 TQCString wmclass; 01115 TQCString hostname; 01116 KStartupInfoData::TriState silent; 01117 unsigned long timestamp; 01118 int screen; 01119 int xinerama; 01120 WId launched_by; 01121 }; 01122 01123 TQString KStartupInfoData::to_text() const 01124 { 01125 TQString ret = ""; 01126 if( !d->bin.isEmpty()) 01127 ret += TQString::fromLatin1( " BIN=\"%1\"" ).arg( escape_str( d->bin )); 01128 if( !d->name.isEmpty()) 01129 ret += TQString::fromLatin1( " NAME=\"%1\"" ).arg( escape_str( d->name )); 01130 if( !d->description.isEmpty()) 01131 ret += TQString::fromLatin1( " DESCRIPTION=\"%1\"" ).arg( escape_str( d->description )); 01132 if( !d->icon.isEmpty()) 01133 ret += TQString::fromLatin1( " ICON=%1" ).arg( d->icon ); 01134 if( d->desktop != 0 ) 01135 ret += TQString::fromLatin1( " DESKTOP=%1" ) 01136 .arg( d->desktop == NET::OnAllDesktops ? NET::OnAllDesktops : d->desktop - 1 ); // spec counts from 0 01137 if( !d->wmclass.isEmpty()) 01138 ret += TQString::fromLatin1( " WMCLASS=\"%1\"" ).arg( QString(d->wmclass) ); 01139 if( !d->hostname.isEmpty()) 01140 ret += TQString::fromLatin1( " HOSTNAME=%1" ).arg( QString(d->hostname) ); 01141 for( TQValueList< pid_t >::ConstIterator it = d->pids.begin(); 01142 it != d->pids.end(); 01143 ++it ) 01144 ret += TQString::fromLatin1( " PID=%1" ).arg( *it ); 01145 if( d->silent != Unknown ) 01146 ret += TQString::fromLatin1( " SILENT=%1" ).arg( d->silent == Yes ? 1 : 0 ); 01147 if( d->timestamp != -1U ) 01148 ret += TQString::fromLatin1( " TIMESTAMP=%1" ).arg( d->timestamp ); 01149 if( d->screen != -1 ) 01150 ret += TQString::fromLatin1( " SCREEN=%1" ).arg( d->screen ); 01151 if( d->xinerama != -1 ) 01152 ret += TQString::fromLatin1( " XINERAMA=%1" ).arg( d->xinerama ); 01153 if( d->launched_by != 0 ) 01154 ret += TQString::fromLatin1( " LAUNCHED_BY=%1" ).arg( d->launched_by ); 01155 return ret; 01156 } 01157 01158 KStartupInfoData::KStartupInfoData( const TQString& txt_P ) 01159 { 01160 d = new KStartupInfoDataPrivate; 01161 TQStringList items = get_fields( txt_P ); 01162 const TQString bin_str = TQString::fromLatin1( "BIN=" ); 01163 const TQString name_str = TQString::fromLatin1( "NAME=" ); 01164 const TQString description_str = TQString::fromLatin1( "DESCRIPTION=" ); 01165 const TQString icon_str = TQString::fromLatin1( "ICON=" ); 01166 const TQString desktop_str = TQString::fromLatin1( "DESKTOP=" ); 01167 const TQString wmclass_str = TQString::fromLatin1( "WMCLASS=" ); 01168 const TQString hostname_str = TQString::fromLatin1( "HOSTNAME=" ); // SELI nonstd 01169 const TQString pid_str = TQString::fromLatin1( "PID=" ); // SELI nonstd 01170 const TQString silent_str = TQString::fromLatin1( "SILENT=" ); 01171 const TQString timestamp_str = TQString::fromLatin1( "TIMESTAMP=" ); 01172 const TQString screen_str = TQString::fromLatin1( "SCREEN=" ); 01173 const TQString xinerama_str = TQString::fromLatin1( "XINERAMA=" ); 01174 const TQString launched_by_str = TQString::fromLatin1( "LAUNCHED_BY=" ); 01175 for( TQStringList::Iterator it = items.begin(); 01176 it != items.end(); 01177 ++it ) 01178 { 01179 if( ( *it ).startsWith( bin_str )) 01180 d->bin = get_str( *it ); 01181 else if( ( *it ).startsWith( name_str )) 01182 d->name = get_str( *it ); 01183 else if( ( *it ).startsWith( description_str )) 01184 d->description = get_str( *it ); 01185 else if( ( *it ).startsWith( icon_str )) 01186 d->icon = get_str( *it ); 01187 else if( ( *it ).startsWith( desktop_str )) 01188 { 01189 d->desktop = get_num( *it ); 01190 if( d->desktop != NET::OnAllDesktops ) 01191 ++d->desktop; // spec counts from 0 01192 } 01193 else if( ( *it ).startsWith( wmclass_str )) 01194 d->wmclass = get_cstr( *it ); 01195 else if( ( *it ).startsWith( hostname_str )) 01196 d->hostname = get_cstr( *it ); 01197 else if( ( *it ).startsWith( pid_str )) 01198 addPid( get_num( *it )); 01199 else if( ( *it ).startsWith( silent_str )) 01200 d->silent = get_num( *it ) != 0 ? Yes : No; 01201 else if( ( *it ).startsWith( timestamp_str )) 01202 d->timestamp = get_unum( *it ); 01203 else if( ( *it ).startsWith( screen_str )) 01204 d->screen = get_num( *it ); 01205 else if( ( *it ).startsWith( xinerama_str )) 01206 d->xinerama = get_num( *it ); 01207 else if( ( *it ).startsWith( launched_by_str )) 01208 d->launched_by = get_num( *it ); 01209 } 01210 } 01211 01212 KStartupInfoData::KStartupInfoData( const KStartupInfoData& data ) 01213 { 01214 d = new KStartupInfoDataPrivate( *data.d ); 01215 } 01216 01217 KStartupInfoData& KStartupInfoData::operator=( const KStartupInfoData& data ) 01218 { 01219 if( &data == this ) 01220 return *this; 01221 delete d; 01222 d = new KStartupInfoDataPrivate( *data.d ); 01223 return *this; 01224 } 01225 01226 void KStartupInfoData::update( const KStartupInfoData& data_P ) 01227 { 01228 if( !data_P.bin().isEmpty()) 01229 d->bin = data_P.bin(); 01230 if( !data_P.name().isEmpty() && name().isEmpty()) // don't overwrite 01231 d->name = data_P.name(); 01232 if( !data_P.description().isEmpty() && description().isEmpty()) // don't overwrite 01233 d->description = data_P.description(); 01234 if( !data_P.icon().isEmpty() && icon().isEmpty()) // don't overwrite 01235 d->icon = data_P.icon(); 01236 if( data_P.desktop() != 0 && desktop() == 0 ) // don't overwrite 01237 d->desktop = data_P.desktop(); 01238 if( !data_P.d->wmclass.isEmpty()) 01239 d->wmclass = data_P.d->wmclass; 01240 if( !data_P.d->hostname.isEmpty()) 01241 d->hostname = data_P.d->hostname; 01242 for( TQValueList< pid_t >::ConstIterator it = data_P.d->pids.begin(); 01243 it != data_P.d->pids.end(); 01244 ++it ) 01245 addPid( *it ); 01246 if( data_P.silent() != Unknown ) 01247 d->silent = data_P.silent(); 01248 if( data_P.timestamp() != -1U && timestamp() == -1U ) // don't overwrite 01249 d->timestamp = data_P.timestamp(); 01250 if( data_P.screen() != -1 ) 01251 d->screen = data_P.screen(); 01252 if( data_P.xinerama() != -1 && xinerama() != -1 ) // don't overwrite 01253 d->xinerama = data_P.xinerama(); 01254 if( data_P.launchedBy() != 0 && launchedBy() != 0 ) // don't overwrite 01255 d->launched_by = data_P.launchedBy(); 01256 } 01257 01258 KStartupInfoData::KStartupInfoData() 01259 { 01260 d = new KStartupInfoDataPrivate; 01261 } 01262 01263 KStartupInfoData::~KStartupInfoData() 01264 { 01265 delete d; 01266 } 01267 01268 void KStartupInfoData::setBin( const TQString& bin_P ) 01269 { 01270 d->bin = bin_P; 01271 } 01272 01273 const TQString& KStartupInfoData::bin() const 01274 { 01275 return d->bin; 01276 } 01277 01278 void KStartupInfoData::setName( const TQString& name_P ) 01279 { 01280 d->name = name_P; 01281 } 01282 01283 const TQString& KStartupInfoData::name() const 01284 { 01285 return d->name; 01286 } 01287 01288 const TQString& KStartupInfoData::findName() const 01289 { 01290 if( !name().isEmpty()) 01291 return name(); 01292 return bin(); 01293 } 01294 01295 void KStartupInfoData::setDescription( const TQString& desc_P ) 01296 { 01297 d->description = desc_P; 01298 } 01299 01300 const TQString& KStartupInfoData::description() const 01301 { 01302 return d->description; 01303 } 01304 01305 const TQString& KStartupInfoData::findDescription() const 01306 { 01307 if( !description().isEmpty()) 01308 return description(); 01309 return name(); 01310 } 01311 01312 void KStartupInfoData::setIcon( const TQString& icon_P ) 01313 { 01314 d->icon = icon_P; 01315 } 01316 01317 const TQString& KStartupInfoData::findIcon() const 01318 { 01319 if( !icon().isEmpty()) 01320 return icon(); 01321 return bin(); 01322 } 01323 01324 const TQString& KStartupInfoData::icon() const 01325 { 01326 return d->icon; 01327 } 01328 01329 void KStartupInfoData::setDesktop( int desktop_P ) 01330 { 01331 d->desktop = desktop_P; 01332 } 01333 01334 int KStartupInfoData::desktop() const 01335 { 01336 return d->desktop; 01337 } 01338 01339 void KStartupInfoData::setWMClass( const TQCString& wmclass_P ) 01340 { 01341 d->wmclass = wmclass_P; 01342 } 01343 01344 const TQCString KStartupInfoData::findWMClass() const 01345 { 01346 if( !WMClass().isEmpty() && WMClass() != "0" ) 01347 return WMClass(); 01348 return bin().utf8(); 01349 } 01350 01351 const TQCString& KStartupInfoData::WMClass() const 01352 { 01353 return d->wmclass; 01354 } 01355 01356 void KStartupInfoData::setHostname( const TQCString& hostname_P ) 01357 { 01358 if( !hostname_P.isNull()) 01359 d->hostname = hostname_P; 01360 else 01361 { 01362 char tmp[ 256 ]; 01363 tmp[ 0 ] = '\0'; 01364 if (!gethostname( tmp, 255 )) 01365 tmp[sizeof(tmp)-1] = '\0'; 01366 d->hostname = tmp; 01367 } 01368 } 01369 01370 const TQCString& KStartupInfoData::hostname() const 01371 { 01372 return d->hostname; 01373 } 01374 01375 void KStartupInfoData::addPid( pid_t pid_P ) 01376 { 01377 if( !d->pids.contains( pid_P )) 01378 d->pids.append( pid_P ); 01379 } 01380 01381 void KStartupInfoData::remove_pid( pid_t pid_P ) 01382 { 01383 d->pids.remove( pid_P ); 01384 } 01385 01386 const TQValueList< pid_t >& KStartupInfoData::pids() const 01387 { 01388 return d->pids; 01389 } 01390 01391 bool KStartupInfoData::is_pid( pid_t pid_P ) const 01392 { 01393 return d->pids.contains( pid_P ); 01394 } 01395 01396 void KStartupInfoData::setSilent( TriState state_P ) 01397 { 01398 d->silent = state_P; 01399 } 01400 01401 KStartupInfoData::TriState KStartupInfoData::silent() const 01402 { 01403 return d->silent; 01404 } 01405 01406 void KStartupInfoData::setTimestamp( unsigned long time ) 01407 { 01408 d->timestamp = time; 01409 } 01410 01411 unsigned long KStartupInfoData::timestamp() const 01412 { 01413 return d->timestamp; 01414 } 01415 01416 void KStartupInfoData::setScreen( int screen ) 01417 { 01418 d->screen = screen; 01419 } 01420 01421 int KStartupInfoData::screen() const 01422 { 01423 return d->screen; 01424 } 01425 01426 void KStartupInfoData::setXinerama( int xinerama ) 01427 { 01428 d->xinerama = xinerama; 01429 } 01430 01431 int KStartupInfoData::xinerama() const 01432 { 01433 return d->xinerama; 01434 } 01435 01436 void KStartupInfoData::setLaunchedBy( WId window ) 01437 { 01438 d->launched_by = window; 01439 } 01440 01441 WId KStartupInfoData::launchedBy() const 01442 { 01443 return d->launched_by; 01444 } 01445 01446 static 01447 long get_num( const TQString& item_P ) 01448 { 01449 unsigned int pos = item_P.find( '=' ); 01450 return item_P.mid( pos + 1 ).toLong(); 01451 } 01452 01453 static 01454 unsigned long get_unum( const TQString& item_P ) 01455 { 01456 unsigned int pos = item_P.find( '=' ); 01457 return item_P.mid( pos + 1 ).toULong(); 01458 } 01459 01460 static 01461 TQString get_str( const TQString& item_P ) 01462 { 01463 unsigned int pos = item_P.find( '=' ); 01464 if( item_P.length() > pos + 2 && item_P[ pos + 1 ] == (QChar)'\"' ) 01465 { 01466 int pos2 = item_P.left( pos + 2 ).find( '\"' ); 01467 if( pos2 < 0 ) 01468 return TQString::null; // 01234 01469 return item_P.mid( pos + 2, pos2 - 2 - pos ); // A="C" 01470 } 01471 return item_P.mid( pos + 1 ); 01472 } 01473 01474 static 01475 TQCString get_cstr( const TQString& item_P ) 01476 { 01477 return get_str( item_P ).utf8(); 01478 } 01479 01480 static 01481 TQStringList get_fields( const TQString& txt_P ) 01482 { 01483 TQString txt = txt_P.simplifyWhiteSpace(); 01484 TQStringList ret; 01485 TQString item = ""; 01486 bool in = false; 01487 bool escape = false; 01488 for( unsigned int pos = 0; 01489 pos < txt.length(); 01490 ++pos ) 01491 { 01492 if( escape ) 01493 { 01494 item += txt[ pos ]; 01495 escape = false; 01496 } 01497 else if( txt[ pos ] == '\\' ) 01498 escape = true; 01499 else if( txt[ pos ] == '\"' ) 01500 in = !in; 01501 else if( txt[ pos ] == ' ' && !in ) 01502 { 01503 ret.append( item ); 01504 item = ""; 01505 } 01506 else 01507 item += txt[ pos ]; 01508 } 01509 ret.append( item ); 01510 return ret; 01511 } 01512 01513 static TQString escape_str( const TQString& str_P ) 01514 { 01515 TQString ret = ""; 01516 for( unsigned int pos = 0; 01517 pos < str_P.length(); 01518 ++pos ) 01519 { 01520 if( str_P[ pos ] == (QChar)'\\' 01521 || str_P[ pos ] == (QChar)'"' ) 01522 ret += '\\'; 01523 ret += str_P[ pos ]; 01524 } 01525 return ret; 01526 } 01527 01528 #include "kstartupinfo.moc" 01529 #endif