tdestartupinfo.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 tdeinit 00028 #if 0 00029 #define KSTARTUPINFO_ALL_DEBUG 00030 #warning Extra TDEStartupInfo 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 "tdestartupinfo.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 <tdeapplication.h> 00059 #include <signal.h> 00060 #ifdef Q_WS_X11 00061 #include <twinmodule.h> 00062 #include <kxmessages.h> 00063 #include <twin.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 tdeinit/wrapper.c , 00069 // tdesu in both tdelibs and tdebase 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 TDEStartupInfo::Data 00084 : public TDEStartupInfoData 00085 { 00086 public: 00087 Data() : TDEStartupInfoData(), age(0) {} // just because it's in a QMap 00088 Data( const TQString& txt_P ) 00089 : TDEStartupInfoData( txt_P ), age( 0 ) {} 00090 unsigned int age; 00091 }; 00092 00093 struct TDEStartupInfoPrivate 00094 { 00095 public: 00096 TQMap< TDEStartupInfoId, TDEStartupInfo::Data > startups; 00097 // contains silenced ASN's only if !AnnounceSilencedChanges 00098 TQMap< TDEStartupInfoId, TDEStartupInfo::Data > silent_startups; 00099 // contains ASN's that had change: but no new: yet 00100 TQMap< TDEStartupInfoId, TDEStartupInfo::Data > uninited_startups; 00101 #ifdef Q_WS_X11 00102 KWinModule* wm_module; 00103 KXMessages msgs; 00104 #endif 00105 TQTimer* cleanup; 00106 int flags; 00107 TDEStartupInfoPrivate( 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 TDEStartupInfo::TDEStartupInfo( 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 TDEStartupInfo::TDEStartupInfo( 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 TDEStartupInfo::init( int flags_P ) 00130 { 00131 // d == NULL means "disabled" 00132 if( !TDEApplication::kApplication()) 00133 return; 00134 if( !TDEApplication::kApplication()->getDisplay()) 00135 return; 00136 00137 d = new TDEStartupInfoPrivate( 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 TDEStartupInfo::~TDEStartupInfo() 00154 { 00155 delete d; 00156 } 00157 00158 void TDEStartupInfo::got_message( const TQString& msg_P ) 00159 { 00160 // TODO do something with SCREEN= ? 00161 kdDebug( 172 ) << "[tdecore-tdestartupinfo] 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 TDEApplication) 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 TDEStartupInfo::slot_window_added( WId w_P ) 00190 { 00191 kapp->postEvent( this, new DelayedWindowEvent( w_P )); 00192 } 00193 00194 void TDEStartupInfo::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 TDEStartupInfo::window_added( WId w_P ) 00203 { 00204 TDEStartupInfoId id; 00205 TDEStartupInfoData data; 00206 startup_t ret = check_startup_internal( w_P, &id, &data ); 00207 switch( ret ) 00208 { 00209 case Match: 00210 kdDebug( 172 ) << "[tdecore-tdestartupinfo] 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 TDEStartupInfo::got_startup_info( const TQString& msg_P, bool update_P ) 00222 { 00223 TDEStartupInfoId id( msg_P ); 00224 if( id.none()) 00225 return; 00226 TDEStartupInfo::Data data( msg_P ); 00227 new_startup_info_internal( id, data, update_P ); 00228 } 00229 00230 void TDEStartupInfo::new_startup_info_internal( const TDEStartupInfoId& 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 ) << "[tdecore-tdestartupinfo] 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 ) << "[tdecore-tdestartupinfo] 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 ) << "[tdecore-tdestartupinfo] 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 ) << "[tdecore-tdestartupinfo] 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 ) << "[tdecore-tdestartupinfo] 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 ) << "[tdecore-tdestartupinfo] adding silent" << endl; 00296 d->silent_startups.insert( id_P, data_P ); 00297 } 00298 d->cleanup->start( 1000 ); // 1 sec 00299 } 00300 00301 void TDEStartupInfo::got_remove_startup_info( const TQString& msg_P ) 00302 { 00303 TDEStartupInfoId id( msg_P ); 00304 TDEStartupInfoData 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 TDEStartupInfo::remove_startup_info_internal( const TDEStartupInfoId& id_P ) 00317 { 00318 if( d == NULL ) 00319 return; 00320 if( d->startups.contains( id_P )) 00321 { 00322 kdDebug( 172 ) << "[tdecore-tdestartupinfo] 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 ) << "[tdecore-tdestartupinfo] removing silent" << endl; 00329 d->silent_startups.remove( id_P ); 00330 } 00331 else if( d->uninited_startups.contains( id_P )) 00332 { 00333 kdDebug( 172 ) << "[tdecore-tdestartupinfo] removing uninited" << endl; 00334 d->uninited_startups.remove( id_P ); 00335 } 00336 return; 00337 } 00338 00339 void TDEStartupInfo::remove_startup_pids( const TDEStartupInfoData& data_P ) 00340 { // first find the matching info 00341 if( d == NULL ) 00342 return; 00343 for( TQMap< TDEStartupInfoId, 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 TDEStartupInfo::remove_startup_pids( const TDEStartupInfoId& id_P, 00357 const TDEStartupInfoData& 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 TDEStartupInfo::sendStartup( const TDEStartupInfoId& id_P, const TDEStartupInfoData& 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, tqt_xscreen()); 00387 kdDebug( 172 ) << "[tdecore-tdestartupinfo] sending " << msg << endl; 00388 msgs.broadcastMessage( NET_STARTUP_MSG, msg, -1, false ); 00389 return true; 00390 } 00391 00392 bool TDEStartupInfo::sendStartupX( Display* disp_P, const TDEStartupInfoId& id_P, 00393 const TDEStartupInfoData& 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 ) << "[tdecore-tdestartupinfo] sending " << msg << endl; 00402 #endif 00403 return KXMessages::broadcastMessageX( disp_P, NET_STARTUP_MSG, msg, -1, false ); 00404 } 00405 00406 TQString TDEStartupInfo::check_required_startup_fields( const TQString& msg, const TDEStartupInfoData& data_P, 00407 int screen ) 00408 { 00409 TQString ret = msg; 00410 if( data_P.name().isEmpty()) 00411 { 00412 // kdWarning( 172 ) << "[tdecore-tdestartupinfo] 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 TDEStartupInfo::sendChange( const TDEStartupInfoId& id_P, const TDEStartupInfoData& 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 ) << "[tdecore-tdestartupinfo] sending " << msg << endl; 00431 msgs.broadcastMessage( NET_STARTUP_MSG, msg, -1, false ); 00432 return true; 00433 } 00434 00435 bool TDEStartupInfo::sendChangeX( Display* disp_P, const TDEStartupInfoId& id_P, 00436 const TDEStartupInfoData& 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 ) << "[tdecore-tdestartupinfo] sending " << msg << endl; 00444 #endif 00445 return KXMessages::broadcastMessageX( disp_P, NET_STARTUP_MSG, msg, -1, false ); 00446 } 00447 00448 bool TDEStartupInfo::sendFinish( const TDEStartupInfoId& 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 ) << "[tdecore-tdestartupinfo] sending " << msg << endl; 00455 msgs.broadcastMessage( NET_STARTUP_MSG, msg, -1, false ); 00456 return true; 00457 } 00458 00459 bool TDEStartupInfo::sendFinishX( Display* disp_P, const TDEStartupInfoId& 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 ) << "[tdecore-tdestartupinfo] sending " << msg << endl; 00466 #endif 00467 return KXMessages::broadcastMessageX( disp_P, NET_STARTUP_MSG, msg, -1, false ); 00468 } 00469 00470 bool TDEStartupInfo::sendFinish( const TDEStartupInfoId& id_P, const TDEStartupInfoData& 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 ) << "[tdecore-tdestartupinfo] sending " << msg << endl; 00478 msgs.broadcastMessage( NET_STARTUP_MSG, msg, -1, false ); 00479 return true; 00480 } 00481 00482 bool TDEStartupInfo::sendFinishX( Display* disp_P, const TDEStartupInfoId& id_P, 00483 const TDEStartupInfoData& 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 ) << "[tdecore-tdestartupinfo] sending " << msg << endl; 00491 #endif 00492 return KXMessages::broadcastMessageX( disp_P, NET_STARTUP_MSG, msg, -1, false ); 00493 } 00494 00495 void TDEStartupInfo::appStarted() 00496 { 00497 if( kapp != NULL ) // TDEApplication constructor unsets the env. variable 00498 appStarted( kapp->startupId()); 00499 else 00500 appStarted( TDEStartupInfo::currentStartupIdEnv().id()); 00501 } 00502 00503 void TDEStartupInfo::appStarted( const TQCString& startup_id ) 00504 { 00505 TDEStartupInfoId id; 00506 id.initId( startup_id ); 00507 if( id.none()) 00508 return; 00509 if( kapp != NULL ) 00510 TDEStartupInfo::sendFinish( id ); 00511 else if( getenv( "DISPLAY" ) != NULL ) // don't rely on tqt_xdisplay() 00512 { 00513 #ifdef Q_WS_X11 00514 Display* disp = XOpenDisplay( NULL ); 00515 if( disp != NULL ) 00516 { 00517 TDEStartupInfo::sendFinishX( disp, id ); 00518 XCloseDisplay( disp ); 00519 } 00520 #endif 00521 } 00522 } 00523 00524 void TDEStartupInfo::disableAutoAppStartedSending( bool disable ) 00525 { 00526 auto_app_started_sending = !disable; 00527 } 00528 00529 void TDEStartupInfo::silenceStartup( bool silence ) 00530 { 00531 TDEStartupInfoId id; 00532 id.initId( kapp->startupId()); 00533 if( id.none()) 00534 return; 00535 TDEStartupInfoData data; 00536 data.setSilent( silence ? TDEStartupInfoData::Yes : TDEStartupInfoData::No ); 00537 sendChange( id, data ); 00538 } 00539 00540 void TDEStartupInfo::handleAutoAppStartedSending() 00541 { 00542 if( auto_app_started_sending ) 00543 appStarted(); 00544 } 00545 00546 void TDEStartupInfo::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( tqt_xdisplay(), NET::Supported ); 00555 if( i.isSupported( NET::WM2StartupId )) 00556 { 00557 TDEStartupInfo::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 TDEStartupInfo::handleAutoAppStartedSending(); 00572 } 00573 00574 TDEStartupInfo::startup_t TDEStartupInfo::checkStartup( WId w_P, TDEStartupInfoId& id_O, 00575 TDEStartupInfoData& data_O ) 00576 { 00577 return check_startup_internal( w_P, &id_O, &data_O ); 00578 } 00579 00580 TDEStartupInfo::startup_t TDEStartupInfo::checkStartup( WId w_P, TDEStartupInfoId& id_O ) 00581 { 00582 return check_startup_internal( w_P, &id_O, NULL ); 00583 } 00584 00585 TDEStartupInfo::startup_t TDEStartupInfo::checkStartup( WId w_P, TDEStartupInfoData& data_O ) 00586 { 00587 return check_startup_internal( w_P, NULL, &data_O ); 00588 } 00589 00590 TDEStartupInfo::startup_t TDEStartupInfo::checkStartup( WId w_P ) 00591 { 00592 return check_startup_internal( w_P, NULL, NULL ); 00593 } 00594 00595 TDEStartupInfo::startup_t TDEStartupInfo::check_startup_internal( WId w_P, TDEStartupInfoId* id_O, 00596 TDEStartupInfoData* 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 ) << "[tdecore-tdestartupinfo] 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 ) << "[tdecore-tdestartupinfo] 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( tqt_xdisplay(), w_P, tqt_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( tqt_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( tqt_xdisplay(), static_cast< Window >( w_P ), &transient_for ) 00656 && static_cast< WId >( transient_for ) != tqt_xrootwin() 00657 && transient_for != None ) 00658 return NoMatch; 00659 #endif 00660 kdDebug( 172 ) << "[tdecore-tdestartupinfo] check_startup:cantdetect" << endl; 00661 return CantDetect; 00662 } 00663 00664 bool TDEStartupInfo::find_id( const TQCString& id_P, TDEStartupInfoId* id_O, 00665 TDEStartupInfoData* data_O ) 00666 { 00667 if( d == NULL ) 00668 return false; 00669 kdDebug( 172 ) << "[tdecore-tdestartupinfo] find_id:" << id_P << endl; 00670 TDEStartupInfoId 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 ) << "[tdecore-tdestartupinfo] check_startup_id:match" << endl; 00679 return true; 00680 } 00681 return false; 00682 } 00683 00684 bool TDEStartupInfo::find_pid( pid_t pid_P, const TQCString& hostname_P, 00685 TDEStartupInfoId* id_O, TDEStartupInfoData* data_O ) 00686 { 00687 if( d == NULL ) 00688 return false; 00689 kdDebug( 172 ) << "[tdecore-tdestartupinfo] find_pid:" << pid_P << endl; 00690 for( TQMap< TDEStartupInfoId, 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 ) << "[tdecore-tdestartupinfo] check_startup_pid:match" << endl; 00703 return true; 00704 } 00705 } 00706 return false; 00707 } 00708 00709 bool TDEStartupInfo::find_wclass( TQCString res_name, TQCString res_class, 00710 TDEStartupInfoId* id_O, TDEStartupInfoData* 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 ) << "[tdecore-tdestartupinfo] find_wclass:" << res_name << ":" << res_class << endl; 00717 for( TQMap< TDEStartupInfoId, 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 ) << "[tdecore-tdestartupinfo] 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( tqt_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 TDEStartupInfo::windowStartupId( WId w_P ) 00762 { 00763 #ifdef Q_WS_X11 00764 if( net_startup_atom == None ) 00765 net_startup_atom = XInternAtom( tqt_xdisplay(), NET_STARTUP_WINDOW, False ); 00766 if( utf8_string_atom == None ) 00767 utf8_string_atom = XInternAtom( tqt_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( tqt_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 TDEStartupInfo::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( tqt_xdisplay(), NET_STARTUP_WINDOW, False ); 00790 if( utf8_string_atom == None ) 00791 utf8_string_atom = XInternAtom( tqt_xdisplay(), "UTF8_STRING", False ); 00792 XChangeProperty( tqt_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 TDEStartupInfo::get_window_hostname( WId w_P ) 00798 { 00799 #ifdef Q_WS_X11 00800 XTextProperty tp; 00801 char** hh; 00802 int cnt; 00803 if( XGetWMClientMachine( tqt_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 TDEStartupInfo::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 TDEStartupInfo::startups_cleanup_no_age() 00827 { 00828 startups_cleanup_internal( false ); 00829 } 00830 00831 void TDEStartupInfo::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 TDEStartupInfo::startups_cleanup_internal( bool age_P ) 00845 { 00846 if( d == NULL ) 00847 return; 00848 for( TQMap< TDEStartupInfoId, 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 TDEStartupInfoId& key = it.key(); 00860 ++it; 00861 kdDebug( 172 ) << "[tdecore-tdestartupinfo] startups entry timeout:" << key.id() << endl; 00862 remove_startup_info_internal( key ); 00863 } 00864 else 00865 ++it; 00866 } 00867 for( TQMap< TDEStartupInfoId, 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 TDEStartupInfoId& key = it.key(); 00879 ++it; 00880 kdDebug( 172 ) << "[tdecore-tdestartupinfo] silent entry timeout:" << key.id() << endl; 00881 remove_startup_info_internal( key ); 00882 } 00883 else 00884 ++it; 00885 } 00886 for( TQMap< TDEStartupInfoId, 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 TDEStartupInfoId& key = it.key(); 00898 ++it; 00899 kdDebug( 172 ) << "[tdecore-tdestartupinfo] uninited entry timeout:" << key.id() << endl; 00900 remove_startup_info_internal( key ); 00901 } 00902 else 00903 ++it; 00904 } 00905 } 00906 00907 void TDEStartupInfo::clean_all_noncompliant() 00908 { 00909 if( d == NULL ) 00910 return; 00911 for( TQMap< TDEStartupInfoId, 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 TDEStartupInfoId& key = it.key(); 00921 ++it; 00922 kdDebug( 172 ) << "[tdecore-tdestartupinfo] entry cleaning:" << key.id() << endl; 00923 remove_startup_info_internal( key ); 00924 } 00925 } 00926 00927 TQCString TDEStartupInfo::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 ) << "[tdecore-tdestartupinfo] creating: " << id << ":" << tqAppName() << endl; 00940 return id; 00941 } 00942 00943 00944 struct TDEStartupInfoIdPrivate 00945 { 00946 TDEStartupInfoIdPrivate() : id( "" ) {} 00947 TQCString id; // id 00948 }; 00949 00950 const TQCString& TDEStartupInfoId::id() const 00951 { 00952 return d->id; 00953 } 00954 00955 00956 TQString TDEStartupInfoId::to_text() const 00957 { 00958 return TQString::fromLatin1( " ID=\"%1\" " ).arg( escape_str( id())); 00959 } 00960 00961 TDEStartupInfoId::TDEStartupInfoId( const TQString& txt_P ) 00962 { 00963 d = new TDEStartupInfoIdPrivate; 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 TDEStartupInfoId::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 ) << "[tdecore-tdestartupinfo] 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 ) << "[tdecore-tdestartupinfo] reusing: " << d->id << endl; 00991 #endif 00992 return; 00993 } 00994 d->id = TDEStartupInfo::createNewStartupId(); 00995 } 00996 00997 bool TDEStartupInfoId::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 TDEStartupInfoId TDEStartupInfo::currentStartupIdEnv() 01008 { 01009 const char* startup_env = getenv( NET_STARTUP_ENV ); 01010 TDEStartupInfoId 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 TDEStartupInfo::resetStartupEnv() 01019 { 01020 unsetenv( NET_STARTUP_ENV ); 01021 } 01022 01023 TDEStartupInfoId::TDEStartupInfoId() 01024 { 01025 d = new TDEStartupInfoIdPrivate; 01026 } 01027 01028 TDEStartupInfoId::~TDEStartupInfoId() 01029 { 01030 delete d; 01031 } 01032 01033 TDEStartupInfoId::TDEStartupInfoId( const TDEStartupInfoId& id_P ) 01034 { 01035 d = new TDEStartupInfoIdPrivate( *id_P.d ); 01036 } 01037 01038 TDEStartupInfoId& TDEStartupInfoId::operator=( const TDEStartupInfoId& id_P ) 01039 { 01040 if( &id_P == this ) 01041 return *this; 01042 delete d; 01043 d = new TDEStartupInfoIdPrivate( *id_P.d ); 01044 return *this; 01045 } 01046 01047 bool TDEStartupInfoId::operator==( const TDEStartupInfoId& id_P ) const 01048 { 01049 return id() == id_P.id(); 01050 } 01051 01052 bool TDEStartupInfoId::operator!=( const TDEStartupInfoId& id_P ) const 01053 { 01054 return !(*this == id_P ); 01055 } 01056 01057 // needed for QMap 01058 bool TDEStartupInfoId::operator<( const TDEStartupInfoId& id_P ) const 01059 { 01060 return id() < id_P.id(); 01061 } 01062 01063 bool TDEStartupInfoId::none() const 01064 { 01065 return d->id.isEmpty() || d->id == "0"; 01066 } 01067 01068 unsigned long TDEStartupInfoId::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 TDEStartupInfo or a problem 01101 return 0; 01102 } 01103 01104 struct TDEStartupInfoDataPrivate 01105 { 01106 TDEStartupInfoDataPrivate() : desktop( 0 ), wmclass( "" ), hostname( "" ), 01107 silent( TDEStartupInfoData::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 TDEStartupInfoData::TriState silent; 01117 unsigned long timestamp; 01118 int screen; 01119 int xinerama; 01120 WId launched_by; 01121 }; 01122 01123 TQString TDEStartupInfoData::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 TDEStartupInfoData::TDEStartupInfoData( const TQString& txt_P ) 01159 { 01160 d = new TDEStartupInfoDataPrivate; 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 TDEStartupInfoData::TDEStartupInfoData( const TDEStartupInfoData& data ) 01213 { 01214 d = new TDEStartupInfoDataPrivate( *data.d ); 01215 } 01216 01217 TDEStartupInfoData& TDEStartupInfoData::operator=( const TDEStartupInfoData& data ) 01218 { 01219 if( &data == this ) 01220 return *this; 01221 delete d; 01222 d = new TDEStartupInfoDataPrivate( *data.d ); 01223 return *this; 01224 } 01225 01226 void TDEStartupInfoData::update( const TDEStartupInfoData& 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 TDEStartupInfoData::TDEStartupInfoData() 01259 { 01260 d = new TDEStartupInfoDataPrivate; 01261 } 01262 01263 TDEStartupInfoData::~TDEStartupInfoData() 01264 { 01265 delete d; 01266 } 01267 01268 void TDEStartupInfoData::setBin( const TQString& bin_P ) 01269 { 01270 d->bin = bin_P; 01271 } 01272 01273 const TQString& TDEStartupInfoData::bin() const 01274 { 01275 return d->bin; 01276 } 01277 01278 void TDEStartupInfoData::setName( const TQString& name_P ) 01279 { 01280 d->name = name_P; 01281 } 01282 01283 const TQString& TDEStartupInfoData::name() const 01284 { 01285 return d->name; 01286 } 01287 01288 const TQString& TDEStartupInfoData::findName() const 01289 { 01290 if( !name().isEmpty()) 01291 return name(); 01292 return bin(); 01293 } 01294 01295 void TDEStartupInfoData::setDescription( const TQString& desc_P ) 01296 { 01297 d->description = desc_P; 01298 } 01299 01300 const TQString& TDEStartupInfoData::description() const 01301 { 01302 return d->description; 01303 } 01304 01305 const TQString& TDEStartupInfoData::findDescription() const 01306 { 01307 if( !description().isEmpty()) 01308 return description(); 01309 return name(); 01310 } 01311 01312 void TDEStartupInfoData::setIcon( const TQString& icon_P ) 01313 { 01314 d->icon = icon_P; 01315 } 01316 01317 const TQString& TDEStartupInfoData::findIcon() const 01318 { 01319 if( !icon().isEmpty()) 01320 return icon(); 01321 return bin(); 01322 } 01323 01324 const TQString& TDEStartupInfoData::icon() const 01325 { 01326 return d->icon; 01327 } 01328 01329 void TDEStartupInfoData::setDesktop( int desktop_P ) 01330 { 01331 d->desktop = desktop_P; 01332 } 01333 01334 int TDEStartupInfoData::desktop() const 01335 { 01336 return d->desktop; 01337 } 01338 01339 void TDEStartupInfoData::setWMClass( const TQCString& wmclass_P ) 01340 { 01341 d->wmclass = wmclass_P; 01342 } 01343 01344 const TQCString TDEStartupInfoData::findWMClass() const 01345 { 01346 if( !WMClass().isEmpty() && WMClass() != "0" ) 01347 return WMClass(); 01348 return bin().utf8(); 01349 } 01350 01351 const TQCString& TDEStartupInfoData::WMClass() const 01352 { 01353 return d->wmclass; 01354 } 01355 01356 void TDEStartupInfoData::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& TDEStartupInfoData::hostname() const 01371 { 01372 return d->hostname; 01373 } 01374 01375 void TDEStartupInfoData::addPid( pid_t pid_P ) 01376 { 01377 if( !d->pids.contains( pid_P )) 01378 d->pids.append( pid_P ); 01379 } 01380 01381 void TDEStartupInfoData::remove_pid( pid_t pid_P ) 01382 { 01383 d->pids.remove( pid_P ); 01384 } 01385 01386 const TQValueList< pid_t >& TDEStartupInfoData::pids() const 01387 { 01388 return d->pids; 01389 } 01390 01391 bool TDEStartupInfoData::is_pid( pid_t pid_P ) const 01392 { 01393 return d->pids.contains( pid_P ); 01394 } 01395 01396 void TDEStartupInfoData::setSilent( TriState state_P ) 01397 { 01398 d->silent = state_P; 01399 } 01400 01401 TDEStartupInfoData::TriState TDEStartupInfoData::silent() const 01402 { 01403 return d->silent; 01404 } 01405 01406 void TDEStartupInfoData::setTimestamp( unsigned long time ) 01407 { 01408 d->timestamp = time; 01409 } 01410 01411 unsigned long TDEStartupInfoData::timestamp() const 01412 { 01413 return d->timestamp; 01414 } 01415 01416 void TDEStartupInfoData::setScreen( int screen ) 01417 { 01418 d->screen = screen; 01419 } 01420 01421 int TDEStartupInfoData::screen() const 01422 { 01423 return d->screen; 01424 } 01425 01426 void TDEStartupInfoData::setXinerama( int xinerama ) 01427 { 01428 d->xinerama = xinerama; 01429 } 01430 01431 int TDEStartupInfoData::xinerama() const 01432 { 01433 return d->xinerama; 01434 } 01435 01436 void TDEStartupInfoData::setLaunchedBy( WId window ) 01437 { 01438 d->launched_by = window; 01439 } 01440 01441 WId TDEStartupInfoData::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 "tdestartupinfo.moc" 01529 #endif