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