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