• Skip to content
  • Skip to link menu
KDE 4.6 API Reference
  • KDE API Reference
  • kdelibs
  • KDE Home
  • Contact Us
 

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"

KDEUI

Skip menu "KDEUI"
  • Main Page
  • Modules
  • Namespace List
  • Class Hierarchy
  • Alphabetical List
  • Class List
  • File List
  • Namespace Members
  • Class Members
  • Related Pages

kdelibs

Skip menu "kdelibs"
  • DNSSD
  • Interfaces
  •   KHexEdit
  •   KMediaPlayer
  •   KSpeech
  •   KTextEditor
  • Kate
  • kconf_update
  • KDE3Support
  •   KUnitTest
  • KDECore
  • KDED
  • KDEsu
  • KDEUI
  • KDEWebKit
  • KDocTools
  • KFile
  • KHTML
  • KImgIO
  • KInit
  • kio
  • KIOSlave
  • KJS
  •   KJS-API
  •   WTF
  • kjsembed
  • KNewStuff
  • KParts
  • KPty
  • Kross
  • KUnitConversion
  • KUtils
  • Nepomuk
  • Plasma
  • Solid
  • Sonnet
  • ThreadWeaver
Generated for kdelibs by doxygen 1.7.3
This website is maintained by Adriaan de Groot and Allen Winter.
KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal