summaryrefslogtreecommitdiffstats
path: root/kdecore/kstartupinfo.cpp
diff options
context:
space:
mode:
authortoma <toma@283d02a7-25f6-0310-bc7c-ecb5cbfe19da>2009-11-25 17:56:58 +0000
committertoma <toma@283d02a7-25f6-0310-bc7c-ecb5cbfe19da>2009-11-25 17:56:58 +0000
commitce4a32fe52ef09d8f5ff1dd22c001110902b60a2 (patch)
tree5ac38a06f3dde268dc7927dc155896926aaf7012 /kdecore/kstartupinfo.cpp
downloadtdelibs-ce4a32fe52ef09d8f5ff1dd22c001110902b60a2.tar.gz
tdelibs-ce4a32fe52ef09d8f5ff1dd22c001110902b60a2.zip
Copy the KDE 3.5 branch to branches/trinity for new KDE 3.5 features.
BUG:215923 git-svn-id: svn://anonsvn.kde.org/home/kde/branches/trinity/kdelibs@1054174 283d02a7-25f6-0310-bc7c-ecb5cbfe19da
Diffstat (limited to 'kdecore/kstartupinfo.cpp')
-rw-r--r--kdecore/kstartupinfo.cpp1498
1 files changed, 1498 insertions, 0 deletions
diff --git a/kdecore/kstartupinfo.cpp b/kdecore/kstartupinfo.cpp
new file mode 100644
index 000000000..3de0cb288
--- /dev/null
+++ b/kdecore/kstartupinfo.cpp
@@ -0,0 +1,1498 @@
+/****************************************************************************
+
+ $Id$
+
+ Copyright (C) 2001-2003 Lubos Lunak <l.lunak@kde.org>
+
+Permission is hereby granted, free of charge, to any person obtaining a
+copy of this software and associated documentation files (the "Software"),
+to deal in the Software without restriction, including without limitation
+the rights to use, copy, modify, merge, publish, distribute, sublicense,
+and/or sell copies of the Software, and to permit persons to whom the
+Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+DEALINGS IN THE SOFTWARE.
+
+****************************************************************************/
+
+// kdDebug() can't be turned off in kdeinit
+#if 0
+#define KSTARTUPINFO_ALL_DEBUG
+#warning Extra KStartupInfo debug messages enabled.
+#endif
+
+#include <qwidget.h>
+
+#include "config.h"
+#ifdef Q_WS_X11
+//#ifdef Q_WS_X11 // FIXME(E): Re-implement in a less X11 specific way
+#include <qglobal.h>
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+// need to resolve INT32(qglobal.h)<>INT32(Xlibint.h) conflict
+#ifndef QT_CLEAN_NAMESPACE
+#define QT_CLEAN_NAMESPACE
+#endif
+
+#include "kstartupinfo.h"
+
+#include <unistd.h>
+#include <sys/time.h>
+#include <stdlib.h>
+#include <qtimer.h>
+#ifdef Q_WS_X11
+#include <netwm.h>
+#endif
+#include <kdebug.h>
+#include <kapplication.h>
+#include <signal.h>
+#ifdef Q_WS_X11
+#include <kwinmodule.h>
+#include <kxmessages.h>
+#include <kwin.h>
+#endif
+
+static const char* const NET_STARTUP_MSG = "_NET_STARTUP_INFO";
+static const char* const NET_STARTUP_WINDOW = "_NET_STARTUP_ID";
+// DESKTOP_STARTUP_ID is used also in kinit/wrapper.c ,
+// kdesu in both kdelibs and kdebase and who knows where else
+static const char* const NET_STARTUP_ENV = "DESKTOP_STARTUP_ID";
+
+static bool auto_app_started_sending = true;
+
+static long get_num( const QString& item_P );
+static unsigned long get_unum( const QString& item_P );
+static QString get_str( const QString& item_P );
+static QCString get_cstr( const QString& item_P );
+static QStringList get_fields( const QString& txt_P );
+static QString escape_str( const QString& str_P );
+
+static Atom utf8_string_atom = None;
+
+class KStartupInfo::Data
+ : public KStartupInfoData
+ {
+ public:
+ Data() : KStartupInfoData(), age(0) {} // just because it's in a QMap
+ Data( const QString& txt_P )
+ : KStartupInfoData( txt_P ), age( 0 ) {}
+ unsigned int age;
+ };
+
+struct KStartupInfoPrivate
+ {
+ public:
+ QMap< KStartupInfoId, KStartupInfo::Data > startups;
+ // contains silenced ASN's only if !AnnounceSilencedChanges
+ QMap< KStartupInfoId, KStartupInfo::Data > silent_startups;
+ // contains ASN's that had change: but no new: yet
+ QMap< KStartupInfoId, KStartupInfo::Data > uninited_startups;
+#ifdef Q_WS_X11
+ KWinModule* wm_module;
+ KXMessages msgs;
+#endif
+ QTimer* cleanup;
+ int flags;
+ KStartupInfoPrivate( int flags_P )
+ :
+#ifdef Q_WS_X11
+ msgs( NET_STARTUP_MSG, NULL, false ),
+#endif
+ flags( flags_P ) {}
+ };
+
+KStartupInfo::KStartupInfo( int flags_P, QObject* parent_P, const char* name_P )
+ : QObject( parent_P, name_P ),
+ timeout( 60 ), d( NULL )
+ {
+ init( flags_P );
+ }
+
+KStartupInfo::KStartupInfo( bool clean_on_cantdetect_P, QObject* parent_P, const char* name_P )
+ : QObject( parent_P, name_P ),
+ timeout( 60 ), d( NULL )
+ {
+ init( clean_on_cantdetect_P ? CleanOnCantDetect : 0 );
+ }
+
+void KStartupInfo::init( int flags_P )
+ {
+ // d == NULL means "disabled"
+ if( !KApplication::kApplication())
+ return;
+ if( !KApplication::kApplication()->getDisplay())
+ return;
+
+ d = new KStartupInfoPrivate( flags_P );
+#ifdef Q_WS_X11
+ if( !( d->flags & DisableKWinModule ))
+ {
+ d->wm_module = new KWinModule( this );
+ connect( d->wm_module, SIGNAL( windowAdded( WId )), SLOT( slot_window_added( WId )));
+ connect( d->wm_module, SIGNAL( systemTrayWindowAdded( WId )), SLOT( slot_window_added( WId )));
+ }
+ else
+ d->wm_module = NULL;
+ connect( &d->msgs, SIGNAL( gotMessage( const QString& )), SLOT( got_message( const QString& )));
+#endif
+ d->cleanup = new QTimer( this, "cleanup" );
+ connect( d->cleanup, SIGNAL( timeout()), SLOT( startups_cleanup()));
+ }
+
+KStartupInfo::~KStartupInfo()
+ {
+ delete d;
+ }
+
+void KStartupInfo::got_message( const QString& msg_P )
+ {
+// TODO do something with SCREEN= ?
+ kdDebug( 172 ) << "got:" << msg_P << endl;
+ QString msg = msg_P.stripWhiteSpace();
+ if( msg.startsWith( "new:" )) // must match length below
+ got_startup_info( msg.mid( 4 ), false );
+ else if( msg.startsWith( "change:" )) // must match length below
+ got_startup_info( msg.mid( 7 ), true );
+ else if( msg.startsWith( "remove:" )) // must match length below
+ got_remove_startup_info( msg.mid( 7 ));
+ }
+
+// if the application stops responding for a while, KWinModule may get
+// the information about the already mapped window before KXMessages
+// actually gets the info about the started application (depends
+// on their order in X11 event filter in KApplication)
+// simply delay info from KWinModule a bit
+// SELI???
+namespace
+{
+class DelayedWindowEvent
+ : public QCustomEvent
+ {
+ public:
+ DelayedWindowEvent( WId w_P )
+ : QCustomEvent( QEvent::User + 15 ), w( w_P ) {}
+ Window w;
+ };
+}
+
+void KStartupInfo::slot_window_added( WId w_P )
+ {
+ kapp->postEvent( this, new DelayedWindowEvent( w_P ));
+ }
+
+void KStartupInfo::customEvent( QCustomEvent* e_P )
+ {
+ if( e_P->type() == QEvent::User + 15 )
+ window_added( static_cast< DelayedWindowEvent* >( e_P )->w );
+ else
+ QObject::customEvent( e_P );
+ }
+
+void KStartupInfo::window_added( WId w_P )
+ {
+ KStartupInfoId id;
+ KStartupInfoData data;
+ startup_t ret = check_startup_internal( w_P, &id, &data );
+ switch( ret )
+ {
+ case Match:
+ kdDebug( 172 ) << "new window match" << endl;
+ break;
+ case NoMatch:
+ break; // nothing
+ case CantDetect:
+ if( d->flags & CleanOnCantDetect )
+ clean_all_noncompliant();
+ break;
+ }
+ }
+
+void KStartupInfo::got_startup_info( const QString& msg_P, bool update_P )
+ {
+ KStartupInfoId id( msg_P );
+ if( id.none())
+ return;
+ KStartupInfo::Data data( msg_P );
+ new_startup_info_internal( id, data, update_P );
+ }
+
+void KStartupInfo::new_startup_info_internal( const KStartupInfoId& id_P,
+ Data& data_P, bool update_P )
+ {
+ if( d == NULL )
+ return;
+ if( id_P.none())
+ return;
+ if( d->startups.contains( id_P ))
+ { // already reported, update
+ d->startups[ id_P ].update( data_P );
+ d->startups[ id_P ].age = 0; // CHECKME
+ kdDebug( 172 ) << "updating" << endl;
+ if( d->startups[ id_P ].silent() == Data::Yes
+ && !( d->flags & AnnounceSilenceChanges ))
+ {
+ d->silent_startups[ id_P ] = d->startups[ id_P ];
+ d->startups.remove( id_P );
+ emit gotRemoveStartup( id_P, d->silent_startups[ id_P ] );
+ return;
+ }
+ emit gotStartupChange( id_P, d->startups[ id_P ] );
+ return;
+ }
+ if( d->silent_startups.contains( id_P ))
+ { // already reported, update
+ d->silent_startups[ id_P ].update( data_P );
+ d->silent_startups[ id_P ].age = 0; // CHECKME
+ kdDebug( 172 ) << "updating silenced" << endl;
+ if( d->silent_startups[ id_P ].silent() != Data::Yes )
+ {
+ d->startups[ id_P ] = d->silent_startups[ id_P ];
+ d->silent_startups.remove( id_P );
+ emit gotNewStartup( id_P, d->startups[ id_P ] );
+ return;
+ }
+ emit gotStartupChange( id_P, d->silent_startups[ id_P ] );
+ return;
+ }
+ if( d->uninited_startups.contains( id_P ))
+ {
+ d->uninited_startups[ id_P ].update( data_P );
+ kdDebug( 172 ) << "updating uninited" << endl;
+ if( !update_P ) // uninited finally got new:
+ {
+ d->startups[ id_P ] = d->uninited_startups[ id_P ];
+ d->uninited_startups.remove( id_P );
+ emit gotNewStartup( id_P, d->startups[ id_P ] );
+ return;
+ }
+ // no change announce, it's still uninited
+ return;
+ }
+ if( update_P ) // change: without any new: first
+ {
+ kdDebug( 172 ) << "adding uninited" << endl;
+ d->uninited_startups.insert( id_P, data_P );
+ }
+ else if( data_P.silent() != Data::Yes || d->flags & AnnounceSilenceChanges )
+ {
+ kdDebug( 172 ) << "adding" << endl;
+ d->startups.insert( id_P, data_P );
+ emit gotNewStartup( id_P, data_P );
+ }
+ else // new silenced, and silent shouldn't be announced
+ {
+ kdDebug( 172 ) << "adding silent" << endl;
+ d->silent_startups.insert( id_P, data_P );
+ }
+ d->cleanup->start( 1000 ); // 1 sec
+ }
+
+void KStartupInfo::got_remove_startup_info( const QString& msg_P )
+ {
+ KStartupInfoId id( msg_P );
+ KStartupInfoData data( msg_P );
+ if( data.pids().count() > 0 )
+ {
+ if( !id.none())
+ remove_startup_pids( id, data );
+ else
+ remove_startup_pids( data );
+ return;
+ }
+ remove_startup_info_internal( id );
+ }
+
+void KStartupInfo::remove_startup_info_internal( const KStartupInfoId& id_P )
+ {
+ if( d == NULL )
+ return;
+ if( d->startups.contains( id_P ))
+ {
+ kdDebug( 172 ) << "removing" << endl;
+ emit gotRemoveStartup( id_P, d->startups[ id_P ]);
+ d->startups.remove( id_P );
+ }
+ else if( d->silent_startups.contains( id_P ))
+ {
+ kdDebug( 172 ) << "removing silent" << endl;
+ d->silent_startups.remove( id_P );
+ }
+ else if( d->uninited_startups.contains( id_P ))
+ {
+ kdDebug( 172 ) << "removing uninited" << endl;
+ d->uninited_startups.remove( id_P );
+ }
+ return;
+ }
+
+void KStartupInfo::remove_startup_pids( const KStartupInfoData& data_P )
+ { // first find the matching info
+ if( d == NULL )
+ return;
+ for( QMap< KStartupInfoId, Data >::Iterator it = d->startups.begin();
+ it != d->startups.end();
+ ++it )
+ {
+ if( ( *it ).hostname() != data_P.hostname())
+ continue;
+ if( !( *it ).is_pid( data_P.pids().first()))
+ continue; // not the matching info
+ remove_startup_pids( it.key(), data_P );
+ break;
+ }
+ }
+
+void KStartupInfo::remove_startup_pids( const KStartupInfoId& id_P,
+ const KStartupInfoData& data_P )
+ {
+ if( d == NULL )
+ return;
+ kdFatal( data_P.pids().count() == 0, 172 );
+ Data* data = NULL;
+ if( d->startups.contains( id_P ))
+ data = &d->startups[ id_P ];
+ else if( d->silent_startups.contains( id_P ))
+ data = &d->silent_startups[ id_P ];
+ else if( d->uninited_startups.contains( id_P ))
+ data = &d->uninited_startups[ id_P ];
+ else
+ return;
+ for( QValueList< pid_t >::ConstIterator it2 = data_P.pids().begin();
+ it2 != data_P.pids().end();
+ ++it2 )
+ data->remove_pid( *it2 ); // remove all pids from the info
+ if( data->pids().count() == 0 ) // all pids removed -> remove info
+ remove_startup_info_internal( id_P );
+ }
+
+bool KStartupInfo::sendStartup( const KStartupInfoId& id_P, const KStartupInfoData& data_P )
+ {
+ if( id_P.none())
+ return false;
+ KXMessages msgs;
+ QString msg = QString::fromLatin1( "new: %1 %2" )
+ .arg( id_P.to_text()).arg( data_P.to_text());
+ msg = check_required_startup_fields( msg, data_P, qt_xscreen());
+ kdDebug( 172 ) << "sending " << msg << endl;
+ msgs.broadcastMessage( NET_STARTUP_MSG, msg, -1, false );
+ return true;
+ }
+
+bool KStartupInfo::sendStartupX( Display* disp_P, const KStartupInfoId& id_P,
+ const KStartupInfoData& data_P )
+ {
+ if( id_P.none())
+ return false;
+ QString msg = QString::fromLatin1( "new: %1 %2" )
+ .arg( id_P.to_text()).arg( data_P.to_text());
+ msg = check_required_startup_fields( msg, data_P, DefaultScreen( disp_P ));
+#ifdef KSTARTUPINFO_ALL_DEBUG
+ kdDebug( 172 ) << "sending " << msg << endl;
+#endif
+ return KXMessages::broadcastMessageX( disp_P, NET_STARTUP_MSG, msg, -1, false );
+ }
+
+QString KStartupInfo::check_required_startup_fields( const QString& msg, const KStartupInfoData& data_P,
+ int screen )
+ {
+ QString ret = msg;
+ if( data_P.name().isEmpty())
+ {
+// kdWarning( 172 ) << "NAME not specified in initial startup message" << endl;
+ QString name = data_P.bin();
+ if( name.isEmpty())
+ name = "UNKNOWN";
+ ret += QString( " NAME=\"%1\"" ).arg( escape_str( name ));
+ }
+ if( data_P.screen() == -1 ) // add automatically if needed
+ ret += QString( " SCREEN=%1" ).arg( screen );
+ return ret;
+ }
+
+bool KStartupInfo::sendChange( const KStartupInfoId& id_P, const KStartupInfoData& data_P )
+ {
+ if( id_P.none())
+ return false;
+ KXMessages msgs;
+ QString msg = QString::fromLatin1( "change: %1 %2" )
+ .arg( id_P.to_text()).arg( data_P.to_text());
+ kdDebug( 172 ) << "sending " << msg << endl;
+ msgs.broadcastMessage( NET_STARTUP_MSG, msg, -1, false );
+ return true;
+ }
+
+bool KStartupInfo::sendChangeX( Display* disp_P, const KStartupInfoId& id_P,
+ const KStartupInfoData& data_P )
+ {
+ if( id_P.none())
+ return false;
+ QString msg = QString::fromLatin1( "change: %1 %2" )
+ .arg( id_P.to_text()).arg( data_P.to_text());
+#ifdef KSTARTUPINFO_ALL_DEBUG
+ kdDebug( 172 ) << "sending " << msg << endl;
+#endif
+ return KXMessages::broadcastMessageX( disp_P, NET_STARTUP_MSG, msg, -1, false );
+ }
+
+bool KStartupInfo::sendFinish( const KStartupInfoId& id_P )
+ {
+ if( id_P.none())
+ return false;
+ KXMessages msgs;
+ QString msg = QString::fromLatin1( "remove: %1" ).arg( id_P.to_text());
+ kdDebug( 172 ) << "sending " << msg << endl;
+ msgs.broadcastMessage( NET_STARTUP_MSG, msg, -1, false );
+ return true;
+ }
+
+bool KStartupInfo::sendFinishX( Display* disp_P, const KStartupInfoId& id_P )
+ {
+ if( id_P.none())
+ return false;
+ QString msg = QString::fromLatin1( "remove: %1" ).arg( id_P.to_text());
+#ifdef KSTARTUPINFO_ALL_DEBUG
+ kdDebug( 172 ) << "sending " << msg << endl;
+#endif
+ return KXMessages::broadcastMessageX( disp_P, NET_STARTUP_MSG, msg, -1, false );
+ }
+
+bool KStartupInfo::sendFinish( const KStartupInfoId& id_P, const KStartupInfoData& data_P )
+ {
+// if( id_P.none()) // id may be none, the pids and hostname matter then
+// return false;
+ KXMessages msgs;
+ QString msg = QString::fromLatin1( "remove: %1 %2" )
+ .arg( id_P.to_text()).arg( data_P.to_text());
+ kdDebug( 172 ) << "sending " << msg << endl;
+ msgs.broadcastMessage( NET_STARTUP_MSG, msg, -1, false );
+ return true;
+ }
+
+bool KStartupInfo::sendFinishX( Display* disp_P, const KStartupInfoId& id_P,
+ const KStartupInfoData& data_P )
+ {
+// if( id_P.none()) // id may be none, the pids and hostname matter then
+// return false;
+ QString msg = QString::fromLatin1( "remove: %1 %2" )
+ .arg( id_P.to_text()).arg( data_P.to_text());
+#ifdef KSTARTUPINFO_ALL_DEBUG
+ kdDebug( 172 ) << "sending " << msg << endl;
+#endif
+ return KXMessages::broadcastMessageX( disp_P, NET_STARTUP_MSG, msg, -1, false );
+ }
+
+void KStartupInfo::appStarted()
+ {
+ if( kapp != NULL ) // KApplication constructor unsets the env. variable
+ appStarted( kapp->startupId());
+ else
+ appStarted( KStartupInfo::currentStartupIdEnv().id());
+ }
+
+void KStartupInfo::appStarted( const QCString& startup_id )
+ {
+ KStartupInfoId id;
+ id.initId( startup_id );
+ if( id.none())
+ return;
+ if( kapp != NULL )
+ KStartupInfo::sendFinish( id );
+ else if( getenv( "DISPLAY" ) != NULL ) // don't rely on qt_xdisplay()
+ {
+#ifdef Q_WS_X11
+ Display* disp = XOpenDisplay( NULL );
+ if( disp != NULL )
+ {
+ KStartupInfo::sendFinishX( disp, id );
+ XCloseDisplay( disp );
+ }
+#endif
+ }
+ }
+
+void KStartupInfo::disableAutoAppStartedSending( bool disable )
+ {
+ auto_app_started_sending = !disable;
+ }
+
+void KStartupInfo::silenceStartup( bool silence )
+ {
+ KStartupInfoId id;
+ id.initId( kapp->startupId());
+ if( id.none())
+ return;
+ KStartupInfoData data;
+ data.setSilent( silence ? KStartupInfoData::Yes : KStartupInfoData::No );
+ sendChange( id, data );
+ }
+
+void KStartupInfo::handleAutoAppStartedSending()
+ {
+ if( auto_app_started_sending )
+ appStarted();
+ }
+
+void KStartupInfo::setNewStartupId( QWidget* window, const QCString& startup_id )
+ {
+ bool activate = true;
+ kapp->setStartupId( startup_id );
+ if( window != NULL )
+ {
+ if( !startup_id.isEmpty() && startup_id != "0" )
+ {
+ NETRootInfo i( qt_xdisplay(), NET::Supported );
+ if( i.isSupported( NET::WM2StartupId ))
+ {
+ KStartupInfo::setWindowStartupId( window->winId(), startup_id );
+ activate = false; // WM will take care of it
+ }
+ }
+ if( activate )
+ {
+ KWin::setOnDesktop( window->winId(), KWin::currentDesktop());
+ // This is not very nice, but there's no way how to get any
+ // usable timestamp without ASN, so force activating the window.
+ // And even with ASN, it's not possible to get the timestamp here,
+ // so if the WM doesn't have support for ASN, it can't be used either.
+ KWin::forceActiveWindow( window->winId());
+ }
+ }
+ KStartupInfo::handleAutoAppStartedSending();
+ }
+
+KStartupInfo::startup_t KStartupInfo::checkStartup( WId w_P, KStartupInfoId& id_O,
+ KStartupInfoData& data_O )
+ {
+ return check_startup_internal( w_P, &id_O, &data_O );
+ }
+
+KStartupInfo::startup_t KStartupInfo::checkStartup( WId w_P, KStartupInfoId& id_O )
+ {
+ return check_startup_internal( w_P, &id_O, NULL );
+ }
+
+KStartupInfo::startup_t KStartupInfo::checkStartup( WId w_P, KStartupInfoData& data_O )
+ {
+ return check_startup_internal( w_P, NULL, &data_O );
+ }
+
+KStartupInfo::startup_t KStartupInfo::checkStartup( WId w_P )
+ {
+ return check_startup_internal( w_P, NULL, NULL );
+ }
+
+KStartupInfo::startup_t KStartupInfo::check_startup_internal( WId w_P, KStartupInfoId* id_O,
+ KStartupInfoData* data_O )
+ {
+ if( d == NULL )
+ return NoMatch;
+ if( d->startups.count() == 0 )
+ return NoMatch; // no startups
+ // Strategy:
+ //
+ // Is this a compliant app ?
+ // - Yes - test for match
+ // - No - Is this a NET_WM compliant app ?
+ // - Yes - test for pid match
+ // - No - test for WM_CLASS match
+ kdDebug( 172 ) << "check_startup" << endl;
+ QCString id = windowStartupId( w_P );
+ if( !id.isNull())
+ {
+ if( id.isEmpty() || id == "0" ) // means ignore this window
+ {
+ kdDebug( 172 ) << "ignore" << endl;
+ return NoMatch;
+ }
+ return find_id( id, id_O, data_O ) ? Match : NoMatch;
+ }
+#ifdef Q_WS_X11
+ NETWinInfo info( qt_xdisplay(), w_P, qt_xrootwin(),
+ NET::WMWindowType | NET::WMPid | NET::WMState );
+ pid_t pid = info.pid();
+ if( pid > 0 )
+ {
+ QCString hostname = get_window_hostname( w_P );
+ if( !hostname.isEmpty()
+ && find_pid( pid, hostname, id_O, data_O ))
+ return Match;
+ // try XClass matching , this PID stuff sucks :(
+ }
+ XClassHint hint;
+ if( XGetClassHint( qt_xdisplay(), w_P, &hint ) != 0 )
+ { // We managed to read the class hint
+ QCString res_name = hint.res_name;
+ QCString res_class = hint.res_class;
+ XFree( hint.res_name );
+ XFree( hint.res_class );
+ if( find_wclass( res_name, res_class, id_O, data_O ))
+ return Match;
+ }
+ // ignore NET::Tool and other special window types, if they can't be matched
+ NET::WindowType type = info.windowType( NET::NormalMask | NET::DesktopMask
+ | NET::DockMask | NET::ToolbarMask | NET::MenuMask | NET::DialogMask
+ | NET::OverrideMask | NET::TopMenuMask | NET::UtilityMask | NET::SplashMask );
+ if( type != NET::Normal
+ && type != NET::Override
+ && type != NET::Unknown
+ && type != NET::Dialog
+ && type != NET::Utility )
+// && type != NET::Dock ) why did I put this here?
+ return NoMatch;
+ // lets see if this is a transient
+ Window transient_for;
+ if( XGetTransientForHint( qt_xdisplay(), static_cast< Window >( w_P ), &transient_for )
+ && static_cast< WId >( transient_for ) != qt_xrootwin()
+ && transient_for != None )
+ return NoMatch;
+#endif
+ kdDebug( 172 ) << "check_startup:cantdetect" << endl;
+ return CantDetect;
+ }
+
+bool KStartupInfo::find_id( const QCString& id_P, KStartupInfoId* id_O,
+ KStartupInfoData* data_O )
+ {
+ if( d == NULL )
+ return false;
+ kdDebug( 172 ) << "find_id:" << id_P << endl;
+ KStartupInfoId id;
+ id.initId( id_P );
+ if( d->startups.contains( id ))
+ {
+ if( id_O != NULL )
+ *id_O = id;
+ if( data_O != NULL )
+ *data_O = d->startups[ id ];
+ kdDebug( 172 ) << "check_startup_id:match" << endl;
+ return true;
+ }
+ return false;
+ }
+
+bool KStartupInfo::find_pid( pid_t pid_P, const QCString& hostname_P,
+ KStartupInfoId* id_O, KStartupInfoData* data_O )
+ {
+ if( d == NULL )
+ return false;
+ kdDebug( 172 ) << "find_pid:" << pid_P << endl;
+ for( QMap< KStartupInfoId, Data >::Iterator it = d->startups.begin();
+ it != d->startups.end();
+ ++it )
+ {
+ if( ( *it ).is_pid( pid_P ) && ( *it ).hostname() == hostname_P )
+ { // Found it !
+ if( id_O != NULL )
+ *id_O = it.key();
+ if( data_O != NULL )
+ *data_O = *it;
+ // non-compliant, remove on first match
+ remove_startup_info_internal( it.key());
+ kdDebug( 172 ) << "check_startup_pid:match" << endl;
+ return true;
+ }
+ }
+ return false;
+ }
+
+bool KStartupInfo::find_wclass( QCString res_name, QCString res_class,
+ KStartupInfoId* id_O, KStartupInfoData* data_O )
+ {
+ if( d == NULL )
+ return false;
+ res_name = res_name.lower();
+ res_class = res_class.lower();
+ kdDebug( 172 ) << "find_wclass:" << res_name << ":" << res_class << endl;
+ for( QMap< KStartupInfoId, Data >::Iterator it = d->startups.begin();
+ it != d->startups.end();
+ ++it )
+ {
+ const QCString wmclass = ( *it ).findWMClass();
+ if( wmclass.lower() == res_name || wmclass.lower() == res_class )
+ { // Found it !
+ if( id_O != NULL )
+ *id_O = it.key();
+ if( data_O != NULL )
+ *data_O = *it;
+ // non-compliant, remove on first match
+ remove_startup_info_internal( it.key());
+ kdDebug( 172 ) << "check_startup_wclass:match" << endl;
+ return true;
+ }
+ }
+ return false;
+ }
+
+#ifdef Q_WS_X11
+static Atom net_startup_atom = None;
+
+static QCString read_startup_id_property( WId w_P )
+ {
+ QCString ret;
+ unsigned char *name_ret;
+ Atom type_ret;
+ int format_ret;
+ unsigned long nitems_ret = 0, after_ret = 0;
+ if( XGetWindowProperty( qt_xdisplay(), w_P, net_startup_atom, 0l, 4096,
+ False, utf8_string_atom, &type_ret, &format_ret, &nitems_ret, &after_ret, &name_ret )
+ == Success )
+ {
+ if( type_ret == utf8_string_atom && format_ret == 8 && name_ret != NULL )
+ ret = reinterpret_cast< char* >( name_ret );
+ if ( name_ret != NULL )
+ XFree( name_ret );
+ }
+ return ret;
+ }
+
+#endif
+
+QCString KStartupInfo::windowStartupId( WId w_P )
+ {
+#ifdef Q_WS_X11
+ if( net_startup_atom == None )
+ net_startup_atom = XInternAtom( qt_xdisplay(), NET_STARTUP_WINDOW, False );
+ if( utf8_string_atom == None )
+ utf8_string_atom = XInternAtom( qt_xdisplay(), "UTF8_STRING", False );
+ QCString ret = read_startup_id_property( w_P );
+ if( ret.isEmpty())
+ { // retry with window group leader, as the spec says
+ XWMHints* hints = XGetWMHints( qt_xdisplay(), w_P );
+ if( hints && ( hints->flags & WindowGroupHint ) != 0 )
+ ret = read_startup_id_property( hints->window_group );
+ if( hints )
+ XFree( hints );
+ }
+ return ret;
+#else
+ return QCString();
+#endif
+ }
+
+void KStartupInfo::setWindowStartupId( WId w_P, const QCString& id_P )
+ {
+#ifdef Q_WS_X11
+ if( id_P.isNull())
+ return;
+ if( net_startup_atom == None )
+ net_startup_atom = XInternAtom( qt_xdisplay(), NET_STARTUP_WINDOW, False );
+ if( utf8_string_atom == None )
+ utf8_string_atom = XInternAtom( qt_xdisplay(), "UTF8_STRING", False );
+ XChangeProperty( qt_xdisplay(), w_P, net_startup_atom, utf8_string_atom, 8,
+ PropModeReplace, reinterpret_cast< unsigned char* >( id_P.data()), id_P.length());
+#endif
+ }
+
+QCString KStartupInfo::get_window_hostname( WId w_P )
+ {
+#ifdef Q_WS_X11
+ XTextProperty tp;
+ char** hh;
+ int cnt;
+ if( XGetWMClientMachine( qt_xdisplay(), w_P, &tp ) != 0
+ && XTextPropertyToStringList( &tp, &hh, &cnt ) != 0 )
+ {
+ if( cnt == 1 )
+ {
+ QCString hostname = hh[ 0 ];
+ XFreeStringList( hh );
+ return hostname;
+ }
+ XFreeStringList( hh );
+ }
+#endif
+ // no hostname
+ return QCString();
+ }
+
+void KStartupInfo::setTimeout( unsigned int secs_P )
+ {
+ timeout = secs_P;
+ // schedule removing entries that are older than the new timeout
+ QTimer::singleShot( 0, this, SLOT( startups_cleanup_no_age()));
+ }
+
+void KStartupInfo::startups_cleanup_no_age()
+ {
+ startups_cleanup_internal( false );
+ }
+
+void KStartupInfo::startups_cleanup()
+ {
+ if( d == NULL )
+ return;
+ if( d->startups.count() == 0 && d->silent_startups.count() == 0
+ && d->uninited_startups.count() == 0 )
+ {
+ d->cleanup->stop();
+ return;
+ }
+ startups_cleanup_internal( true );
+ }
+
+void KStartupInfo::startups_cleanup_internal( bool age_P )
+ {
+ if( d == NULL )
+ return;
+ for( QMap< KStartupInfoId, Data >::Iterator it = d->startups.begin();
+ it != d->startups.end();
+ )
+ {
+ if( age_P )
+ ( *it ).age++;
+ unsigned int tout = timeout;
+ if( ( *it ).silent() == Data::Yes ) // TODO
+ tout *= 20;
+ if( ( *it ).age >= tout )
+ {
+ const KStartupInfoId& key = it.key();
+ ++it;
+ kdDebug( 172 ) << "startups entry timeout:" << key.id() << endl;
+ remove_startup_info_internal( key );
+ }
+ else
+ ++it;
+ }
+ for( QMap< KStartupInfoId, Data >::Iterator it = d->silent_startups.begin();
+ it != d->silent_startups.end();
+ )
+ {
+ if( age_P )
+ ( *it ).age++;
+ unsigned int tout = timeout;
+ if( ( *it ).silent() == Data::Yes ) // TODO
+ tout *= 20;
+ if( ( *it ).age >= tout )
+ {
+ const KStartupInfoId& key = it.key();
+ ++it;
+ kdDebug( 172 ) << "silent entry timeout:" << key.id() << endl;
+ remove_startup_info_internal( key );
+ }
+ else
+ ++it;
+ }
+ for( QMap< KStartupInfoId, Data >::Iterator it = d->uninited_startups.begin();
+ it != d->uninited_startups.end();
+ )
+ {
+ if( age_P )
+ ( *it ).age++;
+ unsigned int tout = timeout;
+ if( ( *it ).silent() == Data::Yes ) // TODO
+ tout *= 20;
+ if( ( *it ).age >= tout )
+ {
+ const KStartupInfoId& key = it.key();
+ ++it;
+ kdDebug( 172 ) << "uninited entry timeout:" << key.id() << endl;
+ remove_startup_info_internal( key );
+ }
+ else
+ ++it;
+ }
+ }
+
+void KStartupInfo::clean_all_noncompliant()
+ {
+ if( d == NULL )
+ return;
+ for( QMap< KStartupInfoId, Data >::Iterator it = d->startups.begin();
+ it != d->startups.end();
+ )
+ {
+ if( ( *it ).WMClass() != "0" )
+ {
+ ++it;
+ continue;
+ }
+ const KStartupInfoId& key = it.key();
+ ++it;
+ kdDebug( 172 ) << "entry cleaning:" << key.id() << endl;
+ remove_startup_info_internal( key );
+ }
+ }
+
+QCString KStartupInfo::createNewStartupId()
+ {
+ // Assign a unique id, use hostname+time+pid, that should be 200% unique.
+ // Also append the user timestamp (for focus stealing prevention).
+ struct timeval tm;
+ gettimeofday( &tm, NULL );
+ char hostname[ 256 ];
+ hostname[ 0 ] = '\0';
+ if (!gethostname( hostname, 255 ))
+ hostname[sizeof(hostname)-1] = '\0';
+#ifdef Q_WS_X11
+ extern Time qt_x_user_time;
+#else
+ unsigned long qt_x_user_time = 0;
+#endif
+ QCString id = QString( "%1;%2;%3;%4_TIME%5" ).arg( hostname ).arg( tm.tv_sec )
+ .arg( tm.tv_usec ).arg( getpid()).arg( qt_x_user_time ).utf8();
+ kdDebug( 172 ) << "creating: " << id << ":" << qAppName() << endl;
+ return id;
+ }
+
+
+struct KStartupInfoIdPrivate
+ {
+ KStartupInfoIdPrivate() : id( "" ) {}
+ QCString id; // id
+ };
+
+const QCString& KStartupInfoId::id() const
+ {
+ return d->id;
+ }
+
+
+QString KStartupInfoId::to_text() const
+ {
+ return QString::fromLatin1( " ID=\"%1\" " ).arg( escape_str( id()));
+ }
+
+KStartupInfoId::KStartupInfoId( const QString& txt_P )
+ {
+ d = new KStartupInfoIdPrivate;
+ QStringList items = get_fields( txt_P );
+ const QString id_str = QString::fromLatin1( "ID=" );
+ for( QStringList::Iterator it = items.begin();
+ it != items.end();
+ ++it )
+ {
+ if( ( *it ).startsWith( id_str ))
+ d->id = get_cstr( *it );
+ }
+ }
+
+void KStartupInfoId::initId( const QCString& id_P )
+ {
+ if( !id_P.isEmpty())
+ {
+ d->id = id_P;
+#ifdef KSTARTUPINFO_ALL_DEBUG
+ kdDebug( 172 ) << "using: " << d->id << endl;
+#endif
+ return;
+ }
+ const char* startup_env = getenv( NET_STARTUP_ENV );
+ if( startup_env != NULL && *startup_env != '\0' )
+ { // already has id
+ d->id = startup_env;
+#ifdef KSTARTUPINFO_ALL_DEBUG
+ kdDebug( 172 ) << "reusing: " << d->id << endl;
+#endif
+ return;
+ }
+ d->id = KStartupInfo::createNewStartupId();
+ }
+
+bool KStartupInfoId::setupStartupEnv() const
+ {
+ if( id().isEmpty())
+ {
+ unsetenv( NET_STARTUP_ENV );
+ return false;
+ }
+ return setenv( NET_STARTUP_ENV, id(), true ) == 0;
+ }
+
+KStartupInfoId KStartupInfo::currentStartupIdEnv()
+ {
+ const char* startup_env = getenv( NET_STARTUP_ENV );
+ KStartupInfoId id;
+ if( startup_env != NULL && *startup_env != '\0' )
+ id.d->id = startup_env;
+ else
+ id.d->id = "0";
+ return id;
+ }
+
+void KStartupInfo::resetStartupEnv()
+ {
+ unsetenv( NET_STARTUP_ENV );
+ }
+
+KStartupInfoId::KStartupInfoId()
+ {
+ d = new KStartupInfoIdPrivate;
+ }
+
+KStartupInfoId::~KStartupInfoId()
+ {
+ delete d;
+ }
+
+KStartupInfoId::KStartupInfoId( const KStartupInfoId& id_P )
+ {
+ d = new KStartupInfoIdPrivate( *id_P.d );
+ }
+
+KStartupInfoId& KStartupInfoId::operator=( const KStartupInfoId& id_P )
+ {
+ if( &id_P == this )
+ return *this;
+ delete d;
+ d = new KStartupInfoIdPrivate( *id_P.d );
+ return *this;
+ }
+
+bool KStartupInfoId::operator==( const KStartupInfoId& id_P ) const
+ {
+ return id() == id_P.id();
+ }
+
+bool KStartupInfoId::operator!=( const KStartupInfoId& id_P ) const
+ {
+ return !(*this == id_P );
+ }
+
+// needed for QMap
+bool KStartupInfoId::operator<( const KStartupInfoId& id_P ) const
+ {
+ return id() < id_P.id();
+ }
+
+bool KStartupInfoId::none() const
+ {
+ return d->id.isEmpty() || d->id == "0";
+ }
+
+unsigned long KStartupInfoId::timestamp() const
+ {
+ if( none())
+ return 0;
+ int pos = d->id.findRev( "_TIME" );
+ if( pos >= 0 )
+ {
+ bool ok;
+ unsigned long time = d->id.mid( pos + 5 ).toULong( &ok );
+ if( !ok && d->id[ pos + 5 ] == '-' ) // try if it's as a negative signed number perhaps
+ time = d->id.mid( pos + 5 ).toLong( &ok );
+ if( ok )
+ return time;
+ }
+ // libstartup-notification style :
+ // snprintf (s, len, "%s/%s/%lu/%d-%d-%s",
+ // canonicalized_launcher, canonicalized_launchee, (unsigned long) timestamp,
+ // (int) getpid (), (int) sequence_number, hostbuf);
+ int pos1 = d->id.findRev( '/' );
+ if( pos1 > 0 )
+ {
+ int pos2 = d->id.findRev( '/', pos1 - 1 );
+ if( pos2 >= 0 )
+ {
+ bool ok;
+ unsigned long time = d->id.mid( pos2 + 1, pos1 - pos2 - 1 ).toULong( &ok );
+ if( !ok && d->id[ pos2 + 1 ] == '-' ) // try if it's as a negative signed number perhaps
+ time = d->id.mid( pos2 + 1, pos1 - pos2 - 1 ).toLong( &ok );
+ if( ok )
+ return time;
+ }
+ }
+ // bah ... old KStartupInfo or a problem
+ return 0;
+ }
+
+struct KStartupInfoDataPrivate
+ {
+ KStartupInfoDataPrivate() : desktop( 0 ), wmclass( "" ), hostname( "" ),
+ silent( KStartupInfoData::Unknown ), timestamp( -1U ), screen( -1 ) {}
+ QString bin;
+ QString name;
+ QString description;
+ QString icon;
+ int desktop;
+ QValueList< pid_t > pids;
+ QCString wmclass;
+ QCString hostname;
+ KStartupInfoData::TriState silent;
+ unsigned long timestamp;
+ int screen;
+ };
+
+QString KStartupInfoData::to_text() const
+ {
+ QString ret = "";
+ if( !d->bin.isEmpty())
+ ret += QString::fromLatin1( " BIN=\"%1\"" ).arg( escape_str( d->bin ));
+ if( !d->name.isEmpty())
+ ret += QString::fromLatin1( " NAME=\"%1\"" ).arg( escape_str( d->name ));
+ if( !d->description.isEmpty())
+ ret += QString::fromLatin1( " DESCRIPTION=\"%1\"" ).arg( escape_str( d->description ));
+ if( !d->icon.isEmpty())
+ ret += QString::fromLatin1( " ICON=%1" ).arg( d->icon );
+ if( d->desktop != 0 )
+ ret += QString::fromLatin1( " DESKTOP=%1" )
+ .arg( d->desktop == NET::OnAllDesktops ? NET::OnAllDesktops : d->desktop - 1 ); // spec counts from 0
+ if( !d->wmclass.isEmpty())
+ ret += QString::fromLatin1( " WMCLASS=\"%1\"" ).arg( d->wmclass );
+ if( !d->hostname.isEmpty())
+ ret += QString::fromLatin1( " HOSTNAME=%1" ).arg( d->hostname );
+ for( QValueList< pid_t >::ConstIterator it = d->pids.begin();
+ it != d->pids.end();
+ ++it )
+ ret += QString::fromLatin1( " PID=%1" ).arg( *it );
+ if( d->silent != Unknown )
+ ret += QString::fromLatin1( " SILENT=%1" ).arg( d->silent == Yes ? 1 : 0 );
+ if( d->timestamp != -1U )
+ ret += QString::fromLatin1( " TIMESTAMP=%1" ).arg( d->timestamp );
+ if( d->screen != -1 )
+ ret += QString::fromLatin1( " SCREEN=%1" ).arg( d->screen );
+ return ret;
+ }
+
+KStartupInfoData::KStartupInfoData( const QString& txt_P )
+ {
+ d = new KStartupInfoDataPrivate;
+ QStringList items = get_fields( txt_P );
+ const QString bin_str = QString::fromLatin1( "BIN=" );
+ const QString name_str = QString::fromLatin1( "NAME=" );
+ const QString description_str = QString::fromLatin1( "DESCRIPTION=" );
+ const QString icon_str = QString::fromLatin1( "ICON=" );
+ const QString desktop_str = QString::fromLatin1( "DESKTOP=" );
+ const QString wmclass_str = QString::fromLatin1( "WMCLASS=" );
+ const QString hostname_str = QString::fromLatin1( "HOSTNAME=" ); // SELI nonstd
+ const QString pid_str = QString::fromLatin1( "PID=" ); // SELI nonstd
+ const QString silent_str = QString::fromLatin1( "SILENT=" );
+ const QString timestamp_str = QString::fromLatin1( "TIMESTAMP=" );
+ const QString screen_str = QString::fromLatin1( "SCREEN=" );
+ for( QStringList::Iterator it = items.begin();
+ it != items.end();
+ ++it )
+ {
+ if( ( *it ).startsWith( bin_str ))
+ d->bin = get_str( *it );
+ else if( ( *it ).startsWith( name_str ))
+ d->name = get_str( *it );
+ else if( ( *it ).startsWith( description_str ))
+ d->description = get_str( *it );
+ else if( ( *it ).startsWith( icon_str ))
+ d->icon = get_str( *it );
+ else if( ( *it ).startsWith( desktop_str ))
+ {
+ d->desktop = get_num( *it );
+ if( d->desktop != NET::OnAllDesktops )
+ ++d->desktop; // spec counts from 0
+ }
+ else if( ( *it ).startsWith( wmclass_str ))
+ d->wmclass = get_cstr( *it );
+ else if( ( *it ).startsWith( hostname_str ))
+ d->hostname = get_cstr( *it );
+ else if( ( *it ).startsWith( pid_str ))
+ addPid( get_num( *it ));
+ else if( ( *it ).startsWith( silent_str ))
+ d->silent = get_num( *it ) != 0 ? Yes : No;
+ else if( ( *it ).startsWith( timestamp_str ))
+ d->timestamp = get_unum( *it );
+ else if( ( *it ).startsWith( screen_str ))
+ d->screen = get_num( *it );
+ }
+ }
+
+KStartupInfoData::KStartupInfoData( const KStartupInfoData& data )
+{
+ d = new KStartupInfoDataPrivate( *data.d );
+}
+
+KStartupInfoData& KStartupInfoData::operator=( const KStartupInfoData& data )
+{
+ if( &data == this )
+ return *this;
+ delete d;
+ d = new KStartupInfoDataPrivate( *data.d );
+ return *this;
+}
+
+void KStartupInfoData::update( const KStartupInfoData& data_P )
+ {
+ if( !data_P.bin().isEmpty())
+ d->bin = data_P.bin();
+ if( !data_P.name().isEmpty() && name().isEmpty()) // don't overwrite
+ d->name = data_P.name();
+ if( !data_P.description().isEmpty() && description().isEmpty()) // don't overwrite
+ d->description = data_P.description();
+ if( !data_P.icon().isEmpty() && icon().isEmpty()) // don't overwrite
+ d->icon = data_P.icon();
+ if( data_P.desktop() != 0 && desktop() == 0 ) // don't overwrite
+ d->desktop = data_P.desktop();
+ if( !data_P.d->wmclass.isEmpty())
+ d->wmclass = data_P.d->wmclass;
+ if( !data_P.d->hostname.isEmpty())
+ d->hostname = data_P.d->hostname;
+ for( QValueList< pid_t >::ConstIterator it = data_P.d->pids.begin();
+ it != data_P.d->pids.end();
+ ++it )
+ addPid( *it );
+ if( data_P.silent() != Unknown )
+ d->silent = data_P.silent();
+ if( data_P.timestamp() != -1U && timestamp() == -1U ) // don't overwrite
+ d->timestamp = data_P.timestamp();
+ if( data_P.screen() != -1 )
+ d->screen = data_P.screen();
+ }
+
+KStartupInfoData::KStartupInfoData()
+{
+ d = new KStartupInfoDataPrivate;
+}
+
+KStartupInfoData::~KStartupInfoData()
+{
+ delete d;
+}
+
+void KStartupInfoData::setBin( const QString& bin_P )
+ {
+ d->bin = bin_P;
+ }
+
+const QString& KStartupInfoData::bin() const
+ {
+ return d->bin;
+ }
+
+void KStartupInfoData::setName( const QString& name_P )
+ {
+ d->name = name_P;
+ }
+
+const QString& KStartupInfoData::name() const
+ {
+ return d->name;
+ }
+
+const QString& KStartupInfoData::findName() const
+ {
+ if( !name().isEmpty())
+ return name();
+ return bin();
+ }
+
+void KStartupInfoData::setDescription( const QString& desc_P )
+ {
+ d->description = desc_P;
+ }
+
+const QString& KStartupInfoData::description() const
+ {
+ return d->description;
+ }
+
+const QString& KStartupInfoData::findDescription() const
+ {
+ if( !description().isEmpty())
+ return description();
+ return name();
+ }
+
+void KStartupInfoData::setIcon( const QString& icon_P )
+ {
+ d->icon = icon_P;
+ }
+
+const QString& KStartupInfoData::findIcon() const
+ {
+ if( !icon().isEmpty())
+ return icon();
+ return bin();
+ }
+
+const QString& KStartupInfoData::icon() const
+ {
+ return d->icon;
+ }
+
+void KStartupInfoData::setDesktop( int desktop_P )
+ {
+ d->desktop = desktop_P;
+ }
+
+int KStartupInfoData::desktop() const
+ {
+ return d->desktop;
+ }
+
+void KStartupInfoData::setWMClass( const QCString& wmclass_P )
+ {
+ d->wmclass = wmclass_P;
+ }
+
+const QCString KStartupInfoData::findWMClass() const
+ {
+ if( !WMClass().isEmpty() && WMClass() != "0" )
+ return WMClass();
+ return bin().utf8();
+ }
+
+const QCString& KStartupInfoData::WMClass() const
+ {
+ return d->wmclass;
+ }
+
+void KStartupInfoData::setHostname( const QCString& hostname_P )
+ {
+ if( !hostname_P.isNull())
+ d->hostname = hostname_P;
+ else
+ {
+ char tmp[ 256 ];
+ tmp[ 0 ] = '\0';
+ if (!gethostname( tmp, 255 ))
+ tmp[sizeof(tmp)-1] = '\0';
+ d->hostname = tmp;
+ }
+ }
+
+const QCString& KStartupInfoData::hostname() const
+ {
+ return d->hostname;
+ }
+
+void KStartupInfoData::addPid( pid_t pid_P )
+ {
+ if( !d->pids.contains( pid_P ))
+ d->pids.append( pid_P );
+ }
+
+void KStartupInfoData::remove_pid( pid_t pid_P )
+ {
+ d->pids.remove( pid_P );
+ }
+
+const QValueList< pid_t >& KStartupInfoData::pids() const
+ {
+ return d->pids;
+ }
+
+bool KStartupInfoData::is_pid( pid_t pid_P ) const
+ {
+ return d->pids.contains( pid_P );
+ }
+
+void KStartupInfoData::setSilent( TriState state_P )
+ {
+ d->silent = state_P;
+ }
+
+KStartupInfoData::TriState KStartupInfoData::silent() const
+ {
+ return d->silent;
+ }
+
+void KStartupInfoData::setTimestamp( unsigned long time )
+ {
+ d->timestamp = time;
+ }
+
+unsigned long KStartupInfoData::timestamp() const
+ {
+ return d->timestamp;
+ }
+
+void KStartupInfoData::setScreen( int screen )
+ {
+ d->screen = screen;
+ }
+
+int KStartupInfoData::screen() const
+ {
+ return d->screen;
+ }
+
+static
+long get_num( const QString& item_P )
+ {
+ unsigned int pos = item_P.find( '=' );
+ return item_P.mid( pos + 1 ).toLong();
+ }
+
+static
+unsigned long get_unum( const QString& item_P )
+ {
+ unsigned int pos = item_P.find( '=' );
+ return item_P.mid( pos + 1 ).toULong();
+ }
+
+static
+QString get_str( const QString& item_P )
+ {
+ unsigned int pos = item_P.find( '=' );
+ if( item_P.length() > pos + 2 && item_P[ pos + 1 ] == '\"' )
+ {
+ int pos2 = item_P.left( pos + 2 ).find( '\"' );
+ if( pos2 < 0 )
+ return QString::null; // 01234
+ return item_P.mid( pos + 2, pos2 - 2 - pos ); // A="C"
+ }
+ return item_P.mid( pos + 1 );
+ }
+
+static
+QCString get_cstr( const QString& item_P )
+ {
+ return get_str( item_P ).utf8();
+ }
+
+static
+QStringList get_fields( const QString& txt_P )
+ {
+ QString txt = txt_P.simplifyWhiteSpace();
+ QStringList ret;
+ QString item = "";
+ bool in = false;
+ bool escape = false;
+ for( unsigned int pos = 0;
+ pos < txt.length();
+ ++pos )
+ {
+ if( escape )
+ {
+ item += txt[ pos ];
+ escape = false;
+ }
+ else if( txt[ pos ] == '\\' )
+ escape = true;
+ else if( txt[ pos ] == '\"' )
+ in = !in;
+ else if( txt[ pos ] == ' ' && !in )
+ {
+ ret.append( item );
+ item = "";
+ }
+ else
+ item += txt[ pos ];
+ }
+ ret.append( item );
+ return ret;
+ }
+
+static QString escape_str( const QString& str_P )
+ {
+ QString ret = "";
+ for( unsigned int pos = 0;
+ pos < str_P.length();
+ ++pos )
+ {
+ if( str_P[ pos ] == '\\'
+ || str_P[ pos ] == '"' )
+ ret += '\\';
+ ret += str_P[ pos ];
+ }
+ return ret;
+ }
+
+#include "kstartupinfo.moc"
+#endif