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