00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
#if 0
00029
#define KSTARTUPINFO_ALL_DEBUG
00030
#warning Extra KStartupInfo debug messages enabled.
00031
#endif
00032
00033
#include <qwidget.h>
00034
00035
#include "config.h"
00036
#if defined Q_WS_X11 && ! defined K_WS_QTONLY
00037
00038
#include <qglobal.h>
00039
#ifdef HAVE_CONFIG_H
00040
#include <config.h>
00041
#endif
00042
00043
00044
#ifndef QT_CLEAN_NAMESPACE
00045
#define QT_CLEAN_NAMESPACE
00046
#endif
00047
00048
#include "kstartupinfo.h"
00049
00050
#include <unistd.h>
00051
#include <sys/time.h>
00052
#include <stdlib.h>
00053
#include <qtimer.h>
00054
#if defined Q_WS_X11 && ! defined K_WS_QTONLY
00055
#include <netwm.h>
00056
#endif
00057
#include <kdebug.h>
00058
#include <kapplication.h>
00059
#include <signal.h>
00060
#if defined Q_WS_X11 && ! defined K_WS_QTONLY
00061
#include <kwinmodule.h>
00062
#include <kxmessages.h>
00063
#include <kwin.h>
00064
extern Time qt_x_time;
00065
#endif
00066
00067
static const char*
const NET_STARTUP_MSG =
"_NET_STARTUP_INFO";
00068
static const char*
const NET_STARTUP_WINDOW =
"_NET_STARTUP_ID";
00069
00070
static const char*
const NET_STARTUP_ENV =
"DESKTOP_STARTUP_ID";
00071
00072
static bool auto_app_started_sending =
true;
00073
00074
static long get_num(
const QString& item_P );
00075
static unsigned long get_unum(
const QString& item_P );
00076
static QString get_str(
const QString& item_P );
00077
static QCString get_cstr(
const QString& item_P );
00078
static QStringList get_fields(
const QString& txt_P );
00079
static QString escape_str(
const QString& str_P );
00080
00081
static Atom utf8_string_atom = None;
00082
00083
class KStartupInfo::Data
00084 :
public KStartupInfoData
00085 {
00086
public:
00087 Data() {};
00088 Data(
const QString& txt_P )
00089 :
KStartupInfoData( txt_P ), age( 0 ) {};
00090
unsigned int age;
00091 };
00092
00093
struct KStartupInfoPrivate
00094 {
00095
public:
00096
QMap< KStartupInfoId, KStartupInfo::Data > startups;
00097
00098
QMap< KStartupInfoId, KStartupInfo::Data > silent_startups;
00099
#if defined Q_WS_X11 && ! defined K_WS_QTONLY
00100
KWinModule* wm_module;
00101 KXMessages msgs;
00102
#endif
00103
QTimer* cleanup;
00104
int flags;
00105 KStartupInfoPrivate(
int flags_P )
00106 :
00107 #if defined Q_WS_X11 && ! defined K_WS_QTONLY
00108 msgs( NET_STARTUP_MSG, NULL, false ),
00109 #endif
00110 flags( flags_P ) {}
00111 };
00112
00113
KStartupInfo::KStartupInfo(
int flags_P,
QObject* parent_P,
const char* name_P )
00114 :
QObject( parent_P, name_P ),
00115 timeout( 60 ), d( NULL )
00116 {
00117 init( flags_P );
00118 }
00119
00120
KStartupInfo::KStartupInfo(
bool clean_on_cantdetect_P,
QObject* parent_P,
const char* name_P )
00121 :
QObject( parent_P, name_P ),
00122 timeout( 60 ), d( NULL )
00123 {
00124 init( clean_on_cantdetect_P ? CleanOnCantDetect : 0 );
00125 }
00126
00127
void KStartupInfo::init(
int flags_P )
00128 {
00129
00130
if( !
KApplication::kApplication())
00131
return;
00132
if( !
KApplication::kApplication()->getDisplay())
00133
return;
00134
00135 d =
new KStartupInfoPrivate( flags_P );
00136
#if defined Q_WS_X11 && ! defined K_WS_QTONLY
00137
if( !( d->flags & DisableKWinModule ))
00138 {
00139 d->wm_module =
new KWinModule(
this );
00140 connect( d->wm_module, SIGNAL( windowAdded( WId )), SLOT( slot_window_added( WId )));
00141 connect( d->wm_module, SIGNAL( systemTrayWindowAdded( WId )), SLOT( slot_window_added( WId )));
00142 }
00143
else
00144 d->wm_module = NULL;
00145 connect( &d->msgs, SIGNAL( gotMessage(
const QString& )), SLOT( got_message(
const QString& )));
00146
#endif
00147
d->cleanup =
new QTimer(
this );
00148 connect( d->cleanup, SIGNAL( timeout()), SLOT( startups_cleanup()));
00149 }
00150
00151 KStartupInfo::~KStartupInfo()
00152 {
00153
delete d;
00154 }
00155
00156
void KStartupInfo::got_message(
const QString& msg_P )
00157 {
00158
00159 kdDebug( 172 ) <<
"got:" << msg_P <<
endl;
00160
QString msg = msg_P.stripWhiteSpace();
00161
if( msg.startsWith(
"new:" ))
00162 got_startup_info( msg.mid( 4 ),
false );
00163
else if( msg.startsWith(
"change:" ))
00164 got_startup_info( msg.mid( 7 ),
true );
00165
else if( msg.startsWith(
"remove:" ))
00166 got_remove_startup_info( msg.mid( 7 ));
00167 }
00168
00169
00170
00171
00172
00173
00174
00175
namespace
00176
{
00177
class DelayedWindowEvent
00178 :
public QCustomEvent
00179 {
00180
public:
00181 DelayedWindowEvent( WId w_P )
00182 :
QCustomEvent(
QEvent::User + 15 ), w( w_P ) {}
00183 Window w;
00184 };
00185 }
00186
00187
void KStartupInfo::slot_window_added( WId w_P )
00188 {
00189 kapp->postEvent(
this,
new DelayedWindowEvent( w_P ));
00190 }
00191
00192
void KStartupInfo::customEvent(
QCustomEvent* e_P )
00193 {
00194
if( e_P->type() == QEvent::User + 15 )
00195 window_added( static_cast< DelayedWindowEvent* >( e_P )->w );
00196
else
00197 QObject::customEvent( e_P );
00198 }
00199
00200
void KStartupInfo::window_added( WId w_P )
00201 {
00202
KStartupInfoId id;
00203
KStartupInfoData data;
00204
startup_t ret = check_startup_internal( w_P, &
id, &data );
00205
switch( ret )
00206 {
00207
case Match:
00208 kdDebug( 172 ) <<
"new window match" <<
endl;
00209
break;
00210
case NoMatch:
00211
break;
00212
case CantDetect:
00213
if( d->flags & CleanOnCantDetect )
00214 clean_all_noncompliant();
00215
break;
00216 }
00217 }
00218
00219
void KStartupInfo::got_startup_info(
const QString& msg_P,
bool update_only_P )
00220 {
00221
KStartupInfoId id( msg_P );
00222
if(
id.none())
00223
return;
00224 KStartupInfo::Data data( msg_P );
00225 new_startup_info_internal(
id, data, update_only_P );
00226 }
00227
00228
void KStartupInfo::new_startup_info_internal(
const KStartupInfoId& id_P,
00229 Data& data_P,
bool update_only_P )
00230 {
00231
if( d == NULL )
00232
return;
00233
if( id_P.
none())
00234
return;
00235
if( d->startups.contains( id_P ))
00236 {
00237 d->startups[ id_P ].update( data_P );
00238 d->startups[ id_P ].age = 0;
00239 kdDebug( 172 ) <<
"updating" <<
endl;
00240
if( d->startups[ id_P ].silent() == Data::Yes
00241 && !( d->flags & AnnounceSilenceChanges ))
00242 {
00243 d->silent_startups[ id_P ] = d->startups[ id_P ];
00244 d->startups.remove( id_P );
00245 emit
gotRemoveStartup( id_P, d->silent_startups[ id_P ] );
00246
return;
00247 }
00248 emit
gotStartupChange( id_P, d->startups[ id_P ] );
00249
return;
00250 }
00251
if( d->silent_startups.contains( id_P ))
00252 {
00253 d->silent_startups[ id_P ].update( data_P );
00254 d->silent_startups[ id_P ].age = 0;
00255 kdDebug( 172 ) <<
"updating silenced" <<
endl;
00256
if( d->silent_startups[ id_P ].silent() != Data::Yes )
00257 {
00258 d->startups[ id_P ] = d->silent_startups[ id_P ];
00259 d->silent_startups.remove( id_P );
00260 emit
gotNewStartup( id_P, d->startups[ id_P ] );
00261
return;
00262 }
00263 emit
gotStartupChange( id_P, d->startups[ id_P ] );
00264
return;
00265 }
00266
if( update_only_P )
00267
return;
00268
if( data_P.silent() != Data::Yes || d->flags & AnnounceSilenceChanges )
00269 {
00270 kdDebug( 172 ) <<
"adding" <<
endl;
00271 d->startups.insert( id_P, data_P );
00272 emit
gotNewStartup( id_P, data_P );
00273 }
00274
else
00275 {
00276 kdDebug( 172 ) <<
"adding silent" <<
endl;
00277 d->silent_startups.insert( id_P, data_P );
00278 }
00279 d->cleanup->start( 1000 );
00280 }
00281
00282
void KStartupInfo::got_remove_startup_info(
const QString& msg_P )
00283 {
00284
KStartupInfoId id( msg_P );
00285
KStartupInfoData data( msg_P );
00286
if( data.
pids().count() > 0 )
00287 {
00288
if( !
id.none())
00289 remove_startup_pids(
id, data );
00290
else
00291 remove_startup_pids( data );
00292
return;
00293 }
00294 remove_startup_info_internal(
id );
00295 }
00296
00297
void KStartupInfo::remove_startup_info_internal(
const KStartupInfoId& id_P )
00298 {
00299
if( d == NULL )
00300
return;
00301
if( d->startups.contains( id_P ))
00302 {
00303 kdDebug( 172 ) <<
"removing" <<
endl;
00304 emit
gotRemoveStartup( id_P, d->startups[ id_P ]);
00305 d->startups.remove( id_P );
00306 }
00307
else if( d->silent_startups.contains( id_P ))
00308 {
00309 kdDebug( 172 ) <<
"removing silent" <<
endl;
00310 d->silent_startups.remove( id_P );
00311 }
00312
return;
00313 }
00314
00315
void KStartupInfo::remove_startup_pids(
const KStartupInfoData& data_P )
00316 {
00317
if( d == NULL )
00318
return;
00319
for(
QMap< KStartupInfoId, Data >::Iterator it = d->startups.begin();
00320 it != d->startups.end();
00321 ++it )
00322 {
00323
if( ( *it ).hostname() != data_P.
hostname())
00324
continue;
00325
if( !( *it ).is_pid( data_P.
pids().first()))
00326
continue;
00327 remove_startup_pids( it.key(), data_P );
00328
break;
00329 }
00330 }
00331
00332
void KStartupInfo::remove_startup_pids(
const KStartupInfoId& id_P,
00333
const KStartupInfoData& data_P )
00334 {
00335
if( d == NULL )
00336
return;
00337 kdFatal( data_P.
pids().count() == 0, 172 );
00338 Data* data = NULL;
00339
if( d->startups.contains( id_P ))
00340 data = &d->startups[ id_P ];
00341
else if( d->silent_startups.contains( id_P ))
00342 data = &d->silent_startups[ id_P ];
00343
else
00344
return;
00345
for(
QValueList< pid_t >::ConstIterator it2 = data_P.
pids().begin();
00346 it2 != data_P.
pids().end();
00347 ++it2 )
00348 data->remove_pid( *it2 );
00349
if( data->pids().count() == 0 )
00350 remove_startup_info_internal( id_P );
00351 }
00352
00353
bool KStartupInfo::sendStartup(
const KStartupInfoId& id_P,
const KStartupInfoData& data_P )
00354 {
00355
if( id_P.
none())
00356
return false;
00357 KXMessages msgs;
00358
QString msg = QString::fromLatin1(
"new: %1 %2" )
00359 .arg( id_P.
to_text()).arg( data_P.
to_text());
00360 msg = check_required_startup_fields( msg, data_P, qt_xscreen());
00361 kdDebug( 172 ) <<
"sending " << msg <<
endl;
00362 msgs.broadcastMessage( NET_STARTUP_MSG, msg, -1,
false );
00363
return true;
00364 }
00365
00366
bool KStartupInfo::sendStartupX( Display* disp_P,
const KStartupInfoId& id_P,
00367
const KStartupInfoData& data_P )
00368 {
00369
if( id_P.
none())
00370
return false;
00371
QString msg = QString::fromLatin1(
"new: %1 %2" )
00372 .arg( id_P.
to_text()).arg( data_P.
to_text());
00373 msg = check_required_startup_fields( msg, data_P, DefaultScreen( disp_P ));
00374
#ifdef KSTARTUPINFO_ALL_DEBUG
00375
kdDebug( 172 ) <<
"sending " << msg <<
endl;
00376
#endif
00377
return KXMessages::broadcastMessageX( disp_P, NET_STARTUP_MSG, msg, -1,
false );
00378 }
00379
00380
QString KStartupInfo::check_required_startup_fields(
const QString& msg,
const KStartupInfoData& data_P,
00381
int screen )
00382 {
00383
QString ret = msg;
00384
if( data_P.
name().isEmpty())
00385 {
00386
00387
QString name = data_P.
bin();
00388
if(
name.isEmpty())
00389
name =
"UNKNOWN";
00390 ret +=
QString(
" NAME=\"%1\"" ).arg( escape_str( name ));
00391 }
00392
if( data_P.
screen() == -1 )
00393 ret += QString(
" SCREEN=%1" ).arg( screen );
00394
return ret;
00395 }
00396
00397
bool KStartupInfo::sendChange(
const KStartupInfoId& id_P,
const KStartupInfoData& data_P )
00398 {
00399
if( id_P.
none())
00400
return false;
00401 KXMessages msgs;
00402
QString msg = QString::fromLatin1(
"change: %1 %2" )
00403 .arg( id_P.
to_text()).arg( data_P.
to_text());
00404 kdDebug( 172 ) <<
"sending " << msg <<
endl;
00405 msgs.broadcastMessage( NET_STARTUP_MSG, msg, -1,
false );
00406
return true;
00407 }
00408
00409
bool KStartupInfo::sendChangeX( Display* disp_P,
const KStartupInfoId& id_P,
00410
const KStartupInfoData& data_P )
00411 {
00412
if( id_P.
none())
00413
return false;
00414
QString msg = QString::fromLatin1(
"change: %1 %2" )
00415 .arg( id_P.
to_text()).arg( data_P.
to_text());
00416
#ifdef KSTARTUPINFO_ALL_DEBUG
00417
kdDebug( 172 ) <<
"sending " << msg <<
endl;
00418
#endif
00419
return KXMessages::broadcastMessageX( disp_P, NET_STARTUP_MSG, msg, -1,
false );
00420 }
00421
00422
bool KStartupInfo::sendFinish(
const KStartupInfoId& id_P )
00423 {
00424
if( id_P.
none())
00425
return false;
00426 KXMessages msgs;
00427
QString msg = QString::fromLatin1(
"remove: %1" ).arg( id_P.
to_text());
00428 kdDebug( 172 ) <<
"sending " << msg <<
endl;
00429 msgs.broadcastMessage( NET_STARTUP_MSG, msg, -1,
false );
00430
return true;
00431 }
00432
00433
bool KStartupInfo::sendFinishX( Display* disp_P,
const KStartupInfoId& id_P )
00434 {
00435
if( id_P.
none())
00436
return false;
00437
QString msg = QString::fromLatin1(
"remove: %1" ).arg( id_P.
to_text());
00438
#ifdef KSTARTUPINFO_ALL_DEBUG
00439
kdDebug( 172 ) <<
"sending " << msg <<
endl;
00440
#endif
00441
return KXMessages::broadcastMessageX( disp_P, NET_STARTUP_MSG, msg, -1,
false );
00442 }
00443
00444
bool KStartupInfo::sendFinish(
const KStartupInfoId& id_P,
const KStartupInfoData& data_P )
00445 {
00446
00447
00448 KXMessages msgs;
00449
QString msg = QString::fromLatin1(
"remove: %1 %2" )
00450 .arg( id_P.
to_text()).arg( data_P.
to_text());
00451 kdDebug( 172 ) <<
"sending " << msg <<
endl;
00452 msgs.broadcastMessage( NET_STARTUP_MSG, msg, -1,
false );
00453
return true;
00454 }
00455
00456
bool KStartupInfo::sendFinishX( Display* disp_P,
const KStartupInfoId& id_P,
00457
const KStartupInfoData& data_P )
00458 {
00459
00460
00461
QString msg = QString::fromLatin1(
"remove: %1 %2" )
00462 .arg( id_P.
to_text()).arg( data_P.
to_text());
00463
#ifdef KSTARTUPINFO_ALL_DEBUG
00464
kdDebug( 172 ) <<
"sending " << msg <<
endl;
00465
#endif
00466
return KXMessages::broadcastMessageX( disp_P, NET_STARTUP_MSG, msg, -1,
false );
00467 }
00468
00469
void KStartupInfo::appStarted()
00470 {
00471
if( kapp != NULL )
00472
appStarted( kapp->startupId());
00473
else
00474
appStarted( KStartupInfo::currentStartupIdEnv().
id());
00475 }
00476
00477
void KStartupInfo::appStarted(
const QCString& startup_id )
00478 {
00479
KStartupInfoId id;
00480
id.initId( startup_id );
00481
if(
id.none())
00482
return;
00483
if( kapp != NULL )
00484
KStartupInfo::sendFinish(
id );
00485
else if( getenv(
"DISPLAY" ) != NULL )
00486 {
00487
#if defined Q_WS_X11 && ! defined K_WS_QTONLY
00488
Display* disp = XOpenDisplay( NULL );
00489
if( disp != NULL )
00490 {
00491
KStartupInfo::sendFinishX( disp,
id );
00492 XCloseDisplay( disp );
00493 }
00494
#endif
00495
}
00496 }
00497
00498
void KStartupInfo::disableAutoAppStartedSending(
bool disable )
00499 {
00500 auto_app_started_sending = !disable;
00501 }
00502
00503
void KStartupInfo::silenceStartup(
bool silence )
00504 {
00505
KStartupInfoId id;
00506
id.initId( kapp->startupId());
00507
if(
id.none())
00508
return;
00509
KStartupInfoData data;
00510 data.
setSilent( silence ? KStartupInfoData::Yes : KStartupInfoData::No );
00511
sendChange(
id, data );
00512 }
00513
00514
void KStartupInfo::handleAutoAppStartedSending()
00515 {
00516
if( auto_app_started_sending )
00517
appStarted();
00518 }
00519
00520
void KStartupInfo::setNewStartupId(
QWidget* window,
const QCString& startup_id )
00521 {
00522
long activate =
true;
00523 kapp->setStartupId( startup_id );
00524
if( window != NULL )
00525 {
00526
if( !startup_id.isEmpty() && startup_id !=
"0" )
00527 {
00528 NETRootInfo i( qt_xdisplay(), NET::Supported );
00529
if( i.isSupported( NET::WM2StartupId ))
00530 {
00531
KStartupInfo::setWindowStartupId( window->winId(), startup_id );
00532 activate =
false;
00533 }
00534 }
00535
if( activate )
00536
00537
00538
00539
00540
KWin::forceActiveWindow( window->winId());
00541 }
00542 KStartupInfo::handleAutoAppStartedSending();
00543 }
00544
00545
KStartupInfo::startup_t KStartupInfo::checkStartup( WId w_P,
KStartupInfoId& id_O,
00546
KStartupInfoData& data_O )
00547 {
00548
return check_startup_internal( w_P, &id_O, &data_O );
00549 }
00550
00551
KStartupInfo::startup_t KStartupInfo::checkStartup( WId w_P,
KStartupInfoId& id_O )
00552 {
00553
return check_startup_internal( w_P, &id_O, NULL );
00554 }
00555
00556
KStartupInfo::startup_t KStartupInfo::checkStartup( WId w_P,
KStartupInfoData& data_O )
00557 {
00558
return check_startup_internal( w_P, NULL, &data_O );
00559 }
00560
00561
KStartupInfo::startup_t KStartupInfo::checkStartup( WId w_P )
00562 {
00563
return check_startup_internal( w_P, NULL, NULL );
00564 }
00565
00566
KStartupInfo::startup_t KStartupInfo::check_startup_internal( WId w_P,
KStartupInfoId* id_O,
00567
KStartupInfoData* data_O )
00568 {
00569
if( d == NULL )
00570
return NoMatch;
00571
if( d->startups.count() == 0 )
00572
return NoMatch;
00573
#if defined Q_WS_X11 && ! defined K_WS_QTONLY
00574
00575 NETWinInfo info( qt_xdisplay(), w_P, qt_xrootwin(),
00576 NET::WMWindowType | NET::WMPid | NET::WMState );
00577
00578
NET::WindowType type = info.windowType( NET::NormalMask | NET::DesktopMask
00579 | NET::DockMask | NET::ToolbarMask | NET::MenuMask | NET::DialogMask
00580 | NET::OverrideMask | NET::TopMenuMask | NET::UtilityMask | NET::SplashMask );
00581
if( type != NET::Normal
00582 && type != NET::Override
00583 && type != NET::Unknown
00584 && type != NET::Dialog
00585 && type !=
NET::Utility )
00586
00587
return NoMatch;
00588
00589 Window transient_for;
00590
if( XGetTransientForHint( qt_xdisplay(), static_cast< Window >( w_P ), &transient_for )
00591 && static_cast< WId >( transient_for ) != qt_xrootwin()
00592 && transient_for != None )
00593
return NoMatch;
00594
#endif
00595
00596
00597
00598
00599
00600
00601
00602 kdDebug( 172 ) <<
"check_startup" <<
endl;
00603
QCString id =
windowStartupId( w_P );
00604
if( !
id.isNull())
00605 {
00606
if(
id.isEmpty() ||
id ==
"0" )
00607 {
00608 kdDebug( 172 ) <<
"ignore" <<
endl;
00609
return NoMatch;
00610 }
00611
return find_id(
id, id_O, data_O ) ? Match : NoMatch;
00612 }
00613
#if defined Q_WS_X11 && ! defined K_WS_QTONLY
00614
pid_t pid = info.pid();
00615
if( pid > 0 )
00616 {
00617
QCString hostname = get_window_hostname( w_P );
00618
if( !hostname.isEmpty()
00619 && find_pid( pid, hostname, id_O, data_O ))
00620
return Match;
00621
00622 }
00623 XClassHint hint;
00624
if( XGetClassHint( qt_xdisplay(), w_P, &hint ) != 0 )
00625 {
00626
QCString res_name = hint.res_name;
00627
QCString res_class = hint.res_class;
00628 XFree( hint.res_name );
00629 XFree( hint.res_class );
00630
if( find_wclass( res_name, res_class, id_O, data_O ))
00631
return Match;
00632 }
00633
#endif
00634
kdDebug( 172 ) <<
"check_startup:cantdetect" <<
endl;
00635
return CantDetect;
00636 }
00637
00638
bool KStartupInfo::find_id(
const QCString& id_P,
KStartupInfoId* id_O,
00639
KStartupInfoData* data_O )
00640 {
00641
if( d == NULL )
00642
return false;
00643 kdDebug( 172 ) <<
"find_id:" << id_P <<
endl;
00644
KStartupInfoId id;
00645
id.initId( id_P );
00646
if( d->startups.contains(
id ))
00647 {
00648
if( id_O != NULL )
00649 *id_O =
id;
00650
if( data_O != NULL )
00651 *data_O = d->startups[
id ];
00652 kdDebug( 172 ) <<
"check_startup_id:match" <<
endl;
00653
return true;
00654 }
00655
return false;
00656 }
00657
00658
bool KStartupInfo::find_pid( pid_t pid_P,
const QCString& hostname_P,
00659
KStartupInfoId* id_O,
KStartupInfoData* data_O )
00660 {
00661
if( d == NULL )
00662
return false;
00663 kdDebug( 172 ) <<
"find_pid:" << pid_P <<
endl;
00664
for(
QMap< KStartupInfoId, Data >::Iterator it = d->startups.begin();
00665 it != d->startups.end();
00666 ++it )
00667 {
00668
if( ( *it ).is_pid( pid_P ) && ( *it ).hostname() == hostname_P )
00669 {
00670
if( id_O != NULL )
00671 *id_O = it.key();
00672
if( data_O != NULL )
00673 *data_O = *it;
00674
00675 remove_startup_info_internal( it.key());
00676 kdDebug( 172 ) <<
"check_startup_pid:match" <<
endl;
00677
return true;
00678 }
00679 }
00680
return false;
00681 }
00682
00683
bool KStartupInfo::find_wclass(
QCString res_name,
QCString res_class,
00684
KStartupInfoId* id_O,
KStartupInfoData* data_O )
00685 {
00686
if( d == NULL )
00687
return false;
00688 res_name = res_name.lower();
00689 res_class = res_class.lower();
00690 kdDebug( 172 ) <<
"find_wclass:" << res_name <<
":" << res_class <<
endl;
00691
for(
QMap< KStartupInfoId, Data >::Iterator it = d->startups.begin();
00692 it != d->startups.end();
00693 ++it )
00694 {
00695
const QCString wmclass = ( *it ).findWMClass();
00696
if( wmclass.lower() == res_name || wmclass.lower() == res_class )
00697 {
00698
if( id_O != NULL )
00699 *id_O = it.key();
00700
if( data_O != NULL )
00701 *data_O = *it;
00702
00703 remove_startup_info_internal( it.key());
00704 kdDebug( 172 ) <<
"check_startup_wclass:match" <<
endl;
00705
return true;
00706 }
00707 }
00708
return false;
00709 }
00710
00711
#if defined Q_WS_X11 && ! defined K_WS_QTONLY
00712
static Atom net_startup_atom = None;
00713
#endif
00714
00715
QCString KStartupInfo::windowStartupId( WId w_P )
00716 {
00717
#if defined Q_WS_X11 && ! defined K_WS_QTONLY
00718
if( net_startup_atom == None )
00719 net_startup_atom = XInternAtom( qt_xdisplay(), NET_STARTUP_WINDOW, False );
00720
if( utf8_string_atom == None )
00721 utf8_string_atom = XInternAtom( qt_xdisplay(),
"UTF8_STRING", False );
00722
unsigned char *name_ret;
00723
QCString ret;
00724 Atom type_ret;
00725
int format_ret;
00726
unsigned long nitems_ret = 0, after_ret = 0;
00727
if( XGetWindowProperty( qt_xdisplay(), w_P, net_startup_atom, 0l, 4096,
00728 False, utf8_string_atom, &type_ret, &format_ret, &nitems_ret, &after_ret, &name_ret )
00729 == Success )
00730 {
00731
if( type_ret == utf8_string_atom && format_ret == 8 && name_ret != NULL )
00732 ret = reinterpret_cast< char* >( name_ret );
00733
if ( name_ret != NULL )
00734 XFree( name_ret );
00735 }
00736
return ret;
00737
#else
00738
return QCString();
00739
#endif
00740
}
00741
00742
void KStartupInfo::setWindowStartupId( WId w_P,
const QCString& id_P )
00743 {
00744
#if defined Q_WS_X11 && ! defined K_WS_QTONLY
00745
if( id_P.isNull())
00746
return;
00747
if( net_startup_atom == None )
00748 net_startup_atom = XInternAtom( qt_xdisplay(), NET_STARTUP_WINDOW, False );
00749
if( utf8_string_atom == None )
00750 utf8_string_atom = XInternAtom( qt_xdisplay(),
"UTF8_STRING", False );
00751 XChangeProperty( qt_xdisplay(), w_P, net_startup_atom, utf8_string_atom, 8,
00752 PropModeReplace, reinterpret_cast< unsigned char* >( id_P.data()), id_P.length());
00753
#endif
00754
}
00755
00756
QCString KStartupInfo::get_window_hostname( WId w_P )
00757 {
00758
#if defined Q_WS_X11 && ! defined K_WS_QTONLY
00759
XTextProperty tp;
00760
char** hh;
00761
int cnt;
00762
if( XGetWMClientMachine( qt_xdisplay(), w_P, &tp ) != 0
00763 && XTextPropertyToStringList( &tp, &hh, &cnt ) != 0 )
00764 {
00765
if( cnt == 1 )
00766 {
00767
QCString hostname = hh[ 0 ];
00768 XFreeStringList( hh );
00769
return hostname;
00770 }
00771 XFreeStringList( hh );
00772 }
00773
#endif
00774
00775
return QCString();
00776 }
00777
00778
void KStartupInfo::setTimeout(
unsigned int secs_P )
00779 {
00780 timeout = secs_P;
00781
00782 QTimer::singleShot( 0,
this, SLOT( startups_cleanup_no_age()));
00783 }
00784
00785
void KStartupInfo::startups_cleanup_no_age()
00786 {
00787 startups_cleanup_internal(
false );
00788 }
00789
00790
void KStartupInfo::startups_cleanup()
00791 {
00792
if( d == NULL )
00793
return;
00794
if( d->startups.count() == 0 && d->silent_startups.count() == 0 )
00795 {
00796 d->cleanup->stop();
00797
return;
00798 }
00799 startups_cleanup_internal(
true );
00800 }
00801
00802
void KStartupInfo::startups_cleanup_internal(
bool age_P )
00803 {
00804
if( d == NULL )
00805
return;
00806
for(
QMap< KStartupInfoId, Data >::Iterator it = d->startups.begin();
00807 it != d->startups.end();
00808 )
00809 {
00810
if( age_P )
00811 ( *it ).age++;
00812
int tout = timeout;
00813
if( ( *it ).silent() == Data::Yes )
00814 tout *= 20;
00815
if( ( *it ).age >= timeout )
00816 {
00817
const KStartupInfoId&
key = it.key();
00818 ++it;
00819 kdDebug( 172 ) <<
"entry timeout:" <<
key.id() <<
endl;
00820 remove_startup_info_internal( key );
00821 }
00822
else
00823 ++it;
00824 }
00825
for(
QMap< KStartupInfoId, Data >::Iterator it = d->silent_startups.begin();
00826 it != d->silent_startups.end();
00827 )
00828 {
00829
if( age_P )
00830 ( *it ).age++;
00831
int tout = timeout;
00832
if( ( *it ).silent() == Data::Yes )
00833 tout *= 20;
00834
if( ( *it ).age >= timeout )
00835 {
00836
const KStartupInfoId&
key = it.key();
00837 ++it;
00838 kdDebug( 172 ) <<
"entry timeout:" <<
key.id() <<
endl;
00839 remove_startup_info_internal( key );
00840 }
00841
else
00842 ++it;
00843 }
00844 }
00845
00846
void KStartupInfo::clean_all_noncompliant()
00847 {
00848
if( d == NULL )
00849
return;
00850
for(
QMap< KStartupInfoId, Data >::Iterator it = d->startups.begin();
00851 it != d->startups.end();
00852 )
00853 {
00854
if( ( *it ).WMClass() !=
"0" )
00855 {
00856 ++it;
00857
continue;
00858 }
00859
const KStartupInfoId&
key = it.key();
00860 ++it;
00861 kdDebug( 172 ) <<
"entry cleaning:" <<
key.id() <<
endl;
00862 remove_startup_info_internal( key );
00863 }
00864 }
00865
00866
struct KStartupInfoIdPrivate
00867 {
00868 KStartupInfoIdPrivate() : id( "" ) {};
00869
QCString id;
00870 };
00871
00872
const QCString&
KStartupInfoId::id()
const
00873
{
00874
return d->id;
00875 }
00876
00877
00878
QString KStartupInfoId::to_text()
const
00879
{
00880
return QString::fromLatin1(
" ID=\"%1\" " ).arg( escape_str(
id()));
00881 }
00882
00883
KStartupInfoId::KStartupInfoId(
const QString& txt_P )
00884 {
00885 d =
new KStartupInfoIdPrivate;
00886
QStringList items = get_fields( txt_P );
00887
const QString id_str = QString::fromLatin1(
"ID=" );
00888
for( QStringList::Iterator it = items.begin();
00889 it != items.end();
00890 ++it )
00891 {
00892
if( ( *it ).startsWith( id_str ))
00893 d->id = get_cstr( *it );
00894 }
00895 }
00896
00897
void KStartupInfoId::initId(
const QCString& id_P )
00898 {
00899
if( !id_P.isEmpty())
00900 {
00901 d->id = id_P;
00902
#ifdef KSTARTUPINFO_ALL_DEBUG
00903
kdDebug( 172 ) <<
"using: " << d->id <<
endl;
00904
#endif
00905
return;
00906 }
00907
const char* startup_env = getenv( NET_STARTUP_ENV );
00908
if( startup_env != NULL && *startup_env !=
'\0' )
00909 {
00910 d->id = startup_env;
00911
#ifdef KSTARTUPINFO_ALL_DEBUG
00912
kdDebug( 172 ) <<
"reusing: " << d->id <<
endl;
00913
#endif
00914
return;
00915 }
00916
00917
00918
00919
struct timeval tm;
00920 gettimeofday( &tm, NULL );
00921
char hostname[ 256 ];
00922 hostname[ 0 ] =
'\0';
00923
if (!gethostname( hostname, 255 ))
00924 hostname[
sizeof(hostname)-1] =
'\0';
00925 d->id =
QString(
"%1;%2;%3;%4" ).arg( hostname ).arg( tm.tv_sec )
00926 .arg( tm.tv_usec ).arg( getpid()).utf8();
00927
#ifdef KSTARTUPINFO_ALL_DEBUG
00928
kdDebug( 172 ) <<
"creating: " << d->id <<
endl;
00929
#endif
00930
}
00931
00932
bool KStartupInfoId::setupStartupEnv()
const
00933
{
00934
if(
id().isEmpty())
00935 {
00936 unsetenv( NET_STARTUP_ENV );
00937
return false;
00938 }
00939
return setenv( NET_STARTUP_ENV,
id(),
true ) == 0;
00940 }
00941
00942
KStartupInfoId KStartupInfo::currentStartupIdEnv()
00943 {
00944
const char* startup_env = getenv( NET_STARTUP_ENV );
00945
KStartupInfoId id;
00946
if( startup_env != NULL && *startup_env !=
'\0' )
00947
id.d->id = startup_env;
00948
else
00949
id.d->id =
"0";
00950
return id;
00951 }
00952
00953
void KStartupInfo::resetStartupEnv()
00954 {
00955 unsetenv( NET_STARTUP_ENV );
00956 }
00957
00958
KStartupInfoId::KStartupInfoId()
00959 {
00960 d =
new KStartupInfoIdPrivate;
00961 }
00962
00963 KStartupInfoId::~KStartupInfoId()
00964 {
00965
delete d;
00966 }
00967
00968
KStartupInfoId::KStartupInfoId(
const KStartupInfoId& id_P )
00969 {
00970 d =
new KStartupInfoIdPrivate( *id_P.
d );
00971 }
00972
00973
KStartupInfoId& KStartupInfoId::operator=(
const KStartupInfoId& id_P )
00974 {
00975
if( &id_P ==
this )
00976
return *
this;
00977
delete d;
00978 d =
new KStartupInfoIdPrivate( *id_P.
d );
00979
return *
this;
00980 }
00981
00982
bool KStartupInfoId::operator==(
const KStartupInfoId& id_P )
const
00983
{
00984
return id() == id_P.
id();
00985 }
00986
00987
bool KStartupInfoId::operator!=(
const KStartupInfoId& id_P )
const
00988
{
00989
return !(*
this == id_P );
00990 }
00991
00992
00993
bool KStartupInfoId::operator<(
const KStartupInfoId& id_P )
const
00994
{
00995
return id() < id_P.
id();
00996 }
00997
00998
bool KStartupInfoId::none()
const
00999
{
01000
return d->id.isEmpty() || d->id ==
"0";
01001 }
01002
01003
struct KStartupInfoDataPrivate
01004 {
01005 KStartupInfoDataPrivate() : desktop( 0 ), wmclass( "" ), hostname( "" ),
01006 silent(
KStartupInfoData::Unknown ), timestamp( -1U ), screen( -1 ) {};
01007
QString bin;
01008
QString name;
01009
QString description;
01010
QString icon;
01011
int desktop;
01012
QValueList< pid_t > pids;
01013
QCString wmclass;
01014
QCString hostname;
01015 KStartupInfoData::TriState silent;
01016
unsigned long timestamp;
01017
int screen;
01018 };
01019
01020
QString KStartupInfoData::to_text()
const
01021
{
01022
QString ret =
"";
01023
if( !d->bin.isEmpty())
01024 ret += QString::fromLatin1(
" BIN=\"%1\"" ).arg( escape_str( d->bin ));
01025
if( !d->name.isEmpty())
01026 ret += QString::fromLatin1(
" NAME=\"%1\"" ).arg( escape_str( d->name ));
01027
if( !d->description.isEmpty())
01028 ret += QString::fromLatin1(
" DESCRIPTION=\"%1\"" ).arg( escape_str( d->description ));
01029
if( !d->icon.isEmpty())
01030 ret += QString::fromLatin1(
" ICON=%1" ).arg( d->icon );
01031
if( d->desktop != 0 )
01032 ret += QString::fromLatin1(
" DESKTOP=%1" )
01033 .arg( d->desktop == NET::OnAllDesktops ? NET::OnAllDesktops : d->desktop - 1 );
01034
if( !d->wmclass.isEmpty())
01035 ret += QString::fromLatin1(
" WMCLASS=%1" ).arg( d->wmclass );
01036
if( !d->hostname.isEmpty())
01037 ret += QString::fromLatin1(
" HOSTNAME=%1" ).arg( d->hostname );
01038
for(
QValueList< pid_t >::ConstIterator it = d->pids.begin();
01039 it != d->pids.end();
01040 ++it )
01041 ret += QString::fromLatin1(
" PID=%1" ).arg( *it );
01042
if( d->silent != Unknown )
01043 ret += QString::fromLatin1(
" SILENT=%1" ).arg( d->silent == Yes ? 1 : 0 );
01044
if( d->timestamp != -1U )
01045 ret += QString::fromLatin1(
" TIMESTAMP=%1" ).arg( d->timestamp );
01046
if( d->screen != -1 )
01047 ret += QString::fromLatin1(
" SCREEN=%1" ).arg( d->screen );
01048
return ret;
01049 }
01050
01051
KStartupInfoData::KStartupInfoData(
const QString& txt_P )
01052 {
01053 d =
new KStartupInfoDataPrivate;
01054
QStringList items = get_fields( txt_P );
01055
const QString bin_str = QString::fromLatin1(
"BIN=" );
01056
const QString name_str = QString::fromLatin1(
"NAME=" );
01057
const QString description_str = QString::fromLatin1(
"DESCRIPTION=" );
01058
const QString icon_str = QString::fromLatin1(
"ICON=" );
01059
const QString desktop_str = QString::fromLatin1(
"DESKTOP=" );
01060
const QString wmclass_str = QString::fromLatin1(
"WMCLASS=" );
01061
const QString hostname_str = QString::fromLatin1(
"HOSTNAME=" );
01062
const QString pid_str = QString::fromLatin1(
"PID=" );
01063
const QString silent_str = QString::fromLatin1(
"SILENT=" );
01064
const QString timestamp_str = QString::fromLatin1(
"TIMESTAMP=" );
01065
const QString screen_str = QString::fromLatin1(
"SCREEN=" );
01066
for( QStringList::Iterator it = items.begin();
01067 it != items.end();
01068 ++it )
01069 {
01070
if( ( *it ).startsWith( bin_str ))
01071 d->bin = get_str( *it );
01072
else if( ( *it ).startsWith( name_str ))
01073 d->name = get_str( *it );
01074
else if( ( *it ).startsWith( description_str ))
01075 d->description = get_str( *it );
01076
else if( ( *it ).startsWith( icon_str ))
01077 d->icon = get_str( *it );
01078
else if( ( *it ).startsWith( desktop_str ))
01079 {
01080 d->desktop = get_num( *it );
01081
if( d->desktop != NET::OnAllDesktops )
01082 ++d->desktop;
01083 }
01084
else if( ( *it ).startsWith( wmclass_str ))
01085 d->wmclass = get_cstr( *it );
01086
else if( ( *it ).startsWith( hostname_str ))
01087 d->hostname = get_cstr( *it );
01088
else if( ( *it ).startsWith( pid_str ))
01089
addPid( get_num( *it ));
01090
else if( ( *it ).startsWith( silent_str ))
01091 d->silent = get_num( *it ) != 0 ? Yes : No;
01092
else if( ( *it ).startsWith( timestamp_str ))
01093 d->timestamp = get_unum( *it );
01094
else if( ( *it ).startsWith( screen_str ))
01095 d->screen = get_num( *it );
01096 }
01097 }
01098
01099
KStartupInfoData::KStartupInfoData(
const KStartupInfoData& data )
01100 {
01101 d =
new KStartupInfoDataPrivate( *data.
d );
01102 }
01103
01104
KStartupInfoData& KStartupInfoData::operator=(
const KStartupInfoData& data )
01105 {
01106
if( &data ==
this )
01107
return *
this;
01108
delete d;
01109 d =
new KStartupInfoDataPrivate( *data.
d );
01110
return *
this;
01111 }
01112
01113
void KStartupInfoData::update(
const KStartupInfoData& data_P )
01114 {
01115
if( !data_P.
bin().isEmpty())
01116 d->bin = data_P.
bin();
01117
if( !data_P.
name().isEmpty() &&
name().isEmpty())
01118 d->name = data_P.
name();
01119
if( !data_P.
description().isEmpty() &&
description().isEmpty())
01120 d->description = data_P.
description();
01121
if( !data_P.
icon().isEmpty() &&
icon().isEmpty())
01122 d->icon = data_P.
icon();
01123
if( data_P.
desktop() != 0 &&
desktop() == 0 )
01124 d->desktop = data_P.
desktop();
01125
if( !data_P.
d->wmclass.isEmpty())
01126 d->wmclass = data_P.
d->wmclass;
01127
if( !data_P.
d->hostname.isEmpty())
01128 d->hostname = data_P.
d->hostname;
01129
for(
QValueList< pid_t >::ConstIterator it = data_P.
d->pids.begin();
01130 it != data_P.
d->pids.end();
01131 ++it )
01132
addPid( *it );
01133
if( data_P.
silent() != Unknown )
01134 d->silent = data_P.
silent();
01135
if( data_P.
timestamp() != -1U &&
timestamp() == -1U )
01136 d->timestamp = data_P.
timestamp();
01137
if( data_P.
screen() != -1 )
01138 d->screen = data_P.
screen();
01139 }
01140
01141
KStartupInfoData::KStartupInfoData()
01142 {
01143 d =
new KStartupInfoDataPrivate;
01144 }
01145
01146 KStartupInfoData::~KStartupInfoData()
01147 {
01148
delete d;
01149 }
01150
01151
void KStartupInfoData::setBin(
const QString& bin_P )
01152 {
01153 d->bin = bin_P;
01154 }
01155
01156
const QString&
KStartupInfoData::bin()
const
01157
{
01158
return d->bin;
01159 }
01160
01161
void KStartupInfoData::setName(
const QString& name_P )
01162 {
01163 d->name = name_P;
01164 }
01165
01166
const QString&
KStartupInfoData::name()
const
01167
{
01168
return d->name;
01169 }
01170
01171
const QString&
KStartupInfoData::findName()
const
01172
{
01173
if( !
name().isEmpty())
01174
return name();
01175
return bin();
01176 }
01177
01178
void KStartupInfoData::setDescription(
const QString& desc_P )
01179 {
01180 d->description = desc_P;
01181 }
01182
01183
const QString&
KStartupInfoData::description()
const
01184
{
01185
return d->description;
01186 }
01187
01188
const QString&
KStartupInfoData::findDescription()
const
01189
{
01190
if( !
description().isEmpty())
01191
return description();
01192
return name();
01193 }
01194
01195
void KStartupInfoData::setIcon(
const QString& icon_P )
01196 {
01197 d->icon = icon_P;
01198 }
01199
01200
const QString&
KStartupInfoData::findIcon()
const
01201
{
01202
if( !
icon().isEmpty())
01203
return icon();
01204
return bin();
01205 }
01206
01207
const QString&
KStartupInfoData::icon()
const
01208
{
01209
return d->icon;
01210 }
01211
01212
void KStartupInfoData::setDesktop(
int desktop_P )
01213 {
01214 d->desktop = desktop_P;
01215 }
01216
01217
int KStartupInfoData::desktop()
const
01218
{
01219
return d->desktop;
01220 }
01221
01222
void KStartupInfoData::setWMClass(
const QCString& wmclass_P )
01223 {
01224 d->wmclass = wmclass_P;
01225 }
01226
01227
const QCString KStartupInfoData::findWMClass()
const
01228
{
01229
if( !
WMClass().isEmpty() &&
WMClass() !=
"0" )
01230
return WMClass();
01231
return bin().utf8();
01232 }
01233
01234
const QCString&
KStartupInfoData::WMClass()
const
01235
{
01236
return d->wmclass;
01237 }
01238
01239
void KStartupInfoData::setHostname(
const QCString& hostname_P )
01240 {
01241
if( !hostname_P.isNull())
01242 d->hostname = hostname_P;
01243
else
01244 {
01245
char tmp[ 256 ];
01246 tmp[ 0 ] =
'\0';
01247
if (!gethostname( tmp, 255 ))
01248 tmp[
sizeof(tmp)-1] =
'\0';
01249 d->hostname = tmp;
01250 }
01251 }
01252
01253
const QCString&
KStartupInfoData::hostname()
const
01254
{
01255
return d->hostname;
01256 }
01257
01258
void KStartupInfoData::addPid( pid_t pid_P )
01259 {
01260
if( !d->pids.contains( pid_P ))
01261 d->pids.append( pid_P );
01262 }
01263
01264
void KStartupInfoData::remove_pid( pid_t pid_P )
01265 {
01266 d->pids.remove( pid_P );
01267 }
01268
01269
const QValueList< pid_t >&
KStartupInfoData::pids()
const
01270
{
01271
return d->pids;
01272 }
01273
01274
bool KStartupInfoData::is_pid( pid_t pid_P )
const
01275
{
01276
return d->pids.contains( pid_P );
01277 }
01278
01279
void KStartupInfoData::setSilent( TriState state_P )
01280 {
01281 d->silent = state_P;
01282 }
01283
01284 KStartupInfoData::TriState
KStartupInfoData::silent()
const
01285
{
01286
return d->silent;
01287 }
01288
01289
void KStartupInfoData::setTimestamp(
unsigned long time )
01290 {
01291 d->timestamp = time;
01292 }
01293
01294
unsigned long KStartupInfoData::timestamp()
const
01295
{
01296
return d->timestamp;
01297 }
01298
01299
void KStartupInfoData::setScreen(
int screen )
01300 {
01301 d->screen = screen;
01302 }
01303
01304
int KStartupInfoData::screen()
const
01305
{
01306
return d->screen;
01307 }
01308
01309
static
01310
long get_num(
const QString& item_P )
01311 {
01312
unsigned int pos = item_P.find(
'=' );
01313
return item_P.mid( pos + 1 ).toLong();
01314 }
01315
01316
static
01317
unsigned long get_unum(
const QString& item_P )
01318 {
01319
unsigned int pos = item_P.find(
'=' );
01320
return item_P.mid( pos + 1 ).toULong();
01321 }
01322
01323
static
01324
QString get_str(
const QString& item_P )
01325 {
01326
unsigned int pos = item_P.find(
'=' );
01327
if( item_P.length() > pos + 2 && item_P[ pos + 1 ] ==
'\"' )
01328 {
01329
int pos2 = item_P.left( pos + 2 ).find(
'\"' );
01330
if( pos2 < 0 )
01331
return QString::null;
01332
return item_P.mid( pos + 2, pos2 - 2 - pos );
01333 }
01334
return item_P.mid( pos + 1 );
01335 }
01336
01337
static
01338
QCString get_cstr(
const QString& item_P )
01339 {
01340
return get_str( item_P ).utf8();
01341 }
01342
01343
static
01344
QStringList get_fields(
const QString& txt_P )
01345 {
01346
QString txt = txt_P.simplifyWhiteSpace();
01347
QStringList ret;
01348
QString item =
"";
01349
bool in =
false;
01350
bool escape =
false;
01351
for(
unsigned int pos = 0;
01352 pos < txt.length();
01353 ++pos )
01354 {
01355
if( escape )
01356 {
01357 item += txt[ pos ];
01358 escape =
false;
01359 }
01360
else if( txt[ pos ] ==
'\\' )
01361 escape =
true;
01362
else if( txt[ pos ] ==
'\"' )
01363 in = !in;
01364
else if( txt[ pos ] ==
' ' && !in )
01365 {
01366 ret.append( item );
01367 item =
"";
01368 }
01369
else
01370 item += txt[ pos ];
01371 }
01372 ret.append( item );
01373
return ret;
01374 }
01375
01376
static QString escape_str(
const QString& str_P )
01377 {
01378
QString ret =
"";
01379
for(
unsigned int pos = 0;
01380 pos < str_P.length();
01381 ++pos )
01382 {
01383
if( str_P[ pos ] ==
'\\'
01384 || str_P[ pos ] ==
'"' )
01385 ret +=
'\\';
01386 ret += str_P[ pos ];
01387 }
01388
return ret;
01389 }
01390
01391
#include "kstartupinfo.moc"
01392
#endif