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

KDEUI

  • kdeui
  • kernel
kstartupinfo.cpp
Go to the documentation of this file.
1 /****************************************************************************
2 
3  Copyright (C) 2001-2003 Lubos Lunak <l.lunak@kde.org>
4 
5 Permission is hereby granted, free of charge, to any person obtaining a
6 copy of this software and associated documentation files (the "Software"),
7 to deal in the Software without restriction, including without limitation
8 the rights to use, copy, modify, merge, publish, distribute, sublicense,
9 and/or sell copies of the Software, and to permit persons to whom the
10 Software is furnished to do so, subject to the following conditions:
11 
12 The above copyright notice and this permission notice shall be included in
13 all copies or substantial portions of the Software.
14 
15 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
18 THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20 FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
21 DEALINGS IN THE SOFTWARE.
22 
23 ****************************************************************************/
24 
25 // kDebug() can't be turned off in kdeinit
26 #if 0
27 #define KSTARTUPINFO_ALL_DEBUG
28 #ifdef __GNUC__
29 #warning Extra KStartupInfo debug messages enabled.
30 #endif
31 #endif
32 
33 #include "kstartupinfo.h"
34 
35 #include <QtGui/QWidget>
36 #include <QtCore/QBool>
37 
38 #include <config.h>
39 
40 // need to resolve INT32(qglobal.h)<>INT32(Xlibint.h) conflict
41 #ifndef QT_CLEAN_NAMESPACE
42 #define QT_CLEAN_NAMESPACE
43 #endif
44 
45 #include <unistd.h>
46 #include <sys/time.h>
47 #include <stdlib.h>
48 #include <QtCore/QTimer>
49 #include <QtGui/QActionEvent>
50 #ifdef Q_WS_X11
51 #include <qx11info_x11.h>
52 #include <netwm.h>
53 #endif
54 #include <kdebug.h>
55 #include <kapplication.h>
56 #include <signal.h>
57 #include <kstandarddirs.h>
58 #ifdef Q_WS_X11
59 #include <kwindowsystem.h>
60 #include <kxmessages.h>
61 #endif
62 
63 static const char* const NET_STARTUP_MSG = "_NET_STARTUP_INFO";
64 static const char* const NET_STARTUP_WINDOW = "_NET_STARTUP_ID";
65 // DESKTOP_STARTUP_ID is used also in kinit/wrapper.c ,
66 // kdesu in both kdelibs and kdebase and who knows where else
67 static const char* const NET_STARTUP_ENV = "DESKTOP_STARTUP_ID";
68 
69 static bool auto_app_started_sending = true;
70 
71 static long get_num( const QString& item_P );
72 static unsigned long get_unum( const QString& item_P );
73 static QString get_str( const QString& item_P );
74 static QByteArray get_cstr( const QString& item_P );
75 static QStringList get_fields( const QString& txt_P );
76 static QString escape_str( const QString& str_P );
77 
78 #ifdef Q_WS_X11
79 static Atom utf8_string_atom = None;
80 #endif
81 
82 class KStartupInfo::Data
83  : public KStartupInfoData
84  {
85  public:
86  Data() : age(0) {} // just because it's in a QMap
87  Data( const QString& txt_P )
88  : KStartupInfoData( txt_P ), age( 0 ) {}
89  unsigned int age;
90  };
91 
92 struct KStartupInfoId::Private
93  {
94  Private() : id( "" ) {}
95 
96  QString to_text() const;
97 
98  QByteArray id; // id
99  };
100 
101 struct KStartupInfoData::Private
102  {
103  Private() : desktop( 0 ), wmclass( "" ), hostname( "" ),
104  silent( KStartupInfoData::Unknown ), timestamp( ~0U ), screen( -1 ), xinerama( -1 ), launched_by( 0 ) {}
105 
106  QString to_text() const;
107  void remove_pid( pid_t pid );
108 
109  QString bin;
110  QString name;
111  QString description;
112  QString icon;
113  int desktop;
114  QList< pid_t > pids;
115  QByteArray wmclass;
116  QByteArray hostname;
117  KStartupInfoData::TriState silent;
118  unsigned long timestamp;
119  int screen;
120  int xinerama;
121  WId launched_by;
122  QString application_id;
123  };
124 
125 class KStartupInfo::Private
126  {
127  public:
128  // private slots
129  void startups_cleanup();
130  void startups_cleanup_no_age();
131  void got_message( const QString& msg );
132  void window_added( WId w );
133  void slot_window_added( WId w );
134 
135  void init( int flags );
136  void got_startup_info( const QString& msg_P, bool update_only_P );
137  void got_remove_startup_info( const QString& msg_P );
138  void new_startup_info_internal( const KStartupInfoId& id_P,
139  Data& data_P, bool update_only_P );
140  void remove_startup_info_internal( const KStartupInfoId& id_P );
141  void remove_startup_pids( const KStartupInfoId& id, const KStartupInfoData& data );
142  void remove_startup_pids( const KStartupInfoData& data );
143  startup_t check_startup_internal( WId w, KStartupInfoId* id, KStartupInfoData* data );
144  bool find_id( const QByteArray& id_P, KStartupInfoId* id_O,
145  KStartupInfoData* data_O );
146  bool find_pid( pid_t pid_P, const QByteArray& hostname, KStartupInfoId* id_O,
147  KStartupInfoData* data_O );
148  bool find_wclass( const QByteArray &res_name_P, const QByteArray &res_class_P,
149  KStartupInfoId* id_O, KStartupInfoData* data_O );
150  static QByteArray get_window_hostname( WId w_P );
151  void startups_cleanup_internal( bool age_P );
152  void clean_all_noncompliant();
153  static QString check_required_startup_fields( const QString& msg,
154  const KStartupInfoData& data, int screen );
155 
156 
157  KStartupInfo *q;
158  unsigned int timeout;
159  QMap< KStartupInfoId, KStartupInfo::Data > startups;
160  // contains silenced ASN's only if !AnnounceSilencedChanges
161  QMap< KStartupInfoId, KStartupInfo::Data > silent_startups;
162  // contains ASN's that had change: but no new: yet
163  QMap< KStartupInfoId, KStartupInfo::Data > uninited_startups;
164 #ifdef Q_WS_X11
165  KXMessages msgs;
166 #endif
167  QTimer* cleanup;
168  int flags;
169 
170  Private( int flags_P, KStartupInfo *q )
171  : q( q ),
172  timeout( 60 ),
173 #ifdef Q_WS_X11
174  msgs( NET_STARTUP_MSG, NULL, false ),
175 #endif
176  flags( flags_P )
177  {
178  }
179 
180  void createConnections()
181  {
182 #ifdef Q_WS_X11
183  // d == NULL means "disabled"
184  if( !KApplication::kApplication())
185  return;
186  if( !QX11Info::display())
187  return;
188 
189  if( !( flags & DisableKWinModule )) {
190  QObject::connect( KWindowSystem::self(), SIGNAL(windowAdded(WId)), q, SLOT(slot_window_added(WId)));
191 #ifdef __GNUC__
192 #warning "systemTrayWindowAdded signal was remove from KWindowSystem class"
193 #endif
194  //QObject::connect( KWindowSystem::self(), SIGNAL(systemTrayWindowAdded(WId)), q, SLOT(slot_window_added(WId)));
195  }
196  QObject::connect( &msgs, SIGNAL(gotMessage(QString)), q, SLOT(got_message(QString)));
197  cleanup = new QTimer( q );
198  QObject::connect( cleanup, SIGNAL(timeout()), q, SLOT(startups_cleanup()));
199 #endif
200  }
201  };
202 
203 KStartupInfo::KStartupInfo( int flags_P, QObject* parent_P )
204  : QObject( parent_P ),
205  d(new Private(flags_P, this))
206  {
207  d->createConnections();
208  }
209 
210 KStartupInfo::KStartupInfo( bool clean_on_cantdetect_P, QObject* parent_P )
211  : QObject( parent_P ),
212  d(new Private(clean_on_cantdetect_P ? CleanOnCantDetect : 0, this))
213  {
214  d->createConnections();
215  }
216 
217 
218 KStartupInfo::~KStartupInfo()
219  {
220  delete d;
221  }
222 
223 void KStartupInfo::Private::got_message( const QString& msg_P )
224  {
225 #ifdef Q_WS_X11
226 // TODO do something with SCREEN= ?
227  kDebug( 172 ) << "got:" << msg_P;
228  QString msg = msg_P.trimmed();
229  if( msg.startsWith( QLatin1String("new:") )) // must match length below
230  got_startup_info( msg.mid( 4 ), false );
231  else if( msg.startsWith( QLatin1String("change:") )) // must match length below
232  got_startup_info( msg.mid( 7 ), true );
233  else if( msg.startsWith( QLatin1String("remove:") )) // must match length below
234  got_remove_startup_info( msg.mid( 7 ));
235 #endif
236  }
237 
238 // if the application stops responding for a while, KWindowSystem may get
239 // the information about the already mapped window before KXMessages
240 // actually gets the info about the started application (depends
241 // on their order in X11 event filter in KApplication)
242 // simply delay info from KWindowSystem a bit
243 // SELI???
244 namespace
245 {
246 class DelayedWindowEvent
247  : public QEvent
248  {
249  public:
250  DelayedWindowEvent( WId w_P )
251  : QEvent( uniqueType() ), w( w_P ) {}
252 #ifdef Q_WS_X11
253  Window w;
254 #else
255  WId w;
256 #endif
257  static Type uniqueType() { return Type(QEvent::User+15); }
258  };
259 }
260 
261 void KStartupInfo::Private::slot_window_added( WId w_P )
262  {
263  qApp->postEvent( q, new DelayedWindowEvent( w_P ));
264  }
265 
266 void KStartupInfo::customEvent( QEvent* e_P )
267  {
268 #ifdef Q_WS_X11
269  if( e_P->type() == DelayedWindowEvent::uniqueType() )
270  d->window_added( static_cast< DelayedWindowEvent* >( e_P )->w );
271  else
272 #endif
273  QObject::customEvent( e_P );
274  }
275 
276 void KStartupInfo::Private::window_added( WId w_P )
277  {
278  KStartupInfoId id;
279  KStartupInfoData data;
280  startup_t ret = check_startup_internal( w_P, &id, &data );
281  switch( ret )
282  {
283  case Match:
284  kDebug( 172 ) << "new window match";
285  break;
286  case NoMatch:
287  break; // nothing
288  case CantDetect:
289  if( flags & CleanOnCantDetect )
290  clean_all_noncompliant();
291  break;
292  }
293  }
294 
295 void KStartupInfo::Private::got_startup_info( const QString& msg_P, bool update_P )
296  {
297  KStartupInfoId id( msg_P );
298  if( id.none())
299  return;
300  KStartupInfo::Data data( msg_P );
301  new_startup_info_internal( id, data, update_P );
302  }
303 
304 void KStartupInfo::Private::new_startup_info_internal( const KStartupInfoId& id_P,
305  KStartupInfo::Data& data_P, bool update_P )
306  {
307  if( id_P.none())
308  return;
309  if( startups.contains( id_P ))
310  { // already reported, update
311  startups[ id_P ].update( data_P );
312  startups[ id_P ].age = 0; // CHECKME
313  kDebug( 172 ) << "updating";
314  if( startups[ id_P ].silent() == KStartupInfo::Data::Yes
315  && !( flags & AnnounceSilenceChanges ))
316  {
317  silent_startups[ id_P ] = startups[ id_P ];
318  startups.remove( id_P );
319  emit q->gotRemoveStartup( id_P, silent_startups[ id_P ] );
320  return;
321  }
322  emit q->gotStartupChange( id_P, startups[ id_P ] );
323  return;
324  }
325  if( silent_startups.contains( id_P ))
326  { // already reported, update
327  silent_startups[ id_P ].update( data_P );
328  silent_startups[ id_P ].age = 0; // CHECKME
329  kDebug( 172 ) << "updating silenced";
330  if( silent_startups[ id_P ].silent() != Data::Yes )
331  {
332  startups[ id_P ] = silent_startups[ id_P ];
333  silent_startups.remove( id_P );
334  q->emit gotNewStartup( id_P, startups[ id_P ] );
335  return;
336  }
337  emit q->gotStartupChange( id_P, silent_startups[ id_P ] );
338  return;
339  }
340  if( uninited_startups.contains( id_P ))
341  {
342  uninited_startups[ id_P ].update( data_P );
343  kDebug( 172 ) << "updating uninited";
344  if( !update_P ) // uninited finally got new:
345  {
346  startups[ id_P ] = uninited_startups[ id_P ];
347  uninited_startups.remove( id_P );
348  emit q->gotNewStartup( id_P, startups[ id_P ] );
349  return;
350  }
351  // no change announce, it's still uninited
352  return;
353  }
354  if( update_P ) // change: without any new: first
355  {
356  kDebug( 172 ) << "adding uninited";
357  uninited_startups.insert( id_P, data_P );
358  }
359  else if( data_P.silent() != Data::Yes || flags & AnnounceSilenceChanges )
360  {
361  kDebug( 172 ) << "adding";
362  startups.insert( id_P, data_P );
363  emit q->gotNewStartup( id_P, data_P );
364  }
365  else // new silenced, and silent shouldn't be announced
366  {
367  kDebug( 172 ) << "adding silent";
368  silent_startups.insert( id_P, data_P );
369  }
370  cleanup->start( 1000 ); // 1 sec
371  }
372 
373 void KStartupInfo::Private::got_remove_startup_info( const QString& msg_P )
374  {
375  KStartupInfoId id( msg_P );
376  KStartupInfoData data( msg_P );
377  if( data.pids().count() > 0 )
378  {
379  if( !id.none())
380  remove_startup_pids( id, data );
381  else
382  remove_startup_pids( data );
383  return;
384  }
385  remove_startup_info_internal( id );
386  }
387 
388 void KStartupInfo::Private::remove_startup_info_internal( const KStartupInfoId& id_P )
389  {
390  if( startups.contains( id_P ))
391  {
392  kDebug( 172 ) << "removing";
393  emit q->gotRemoveStartup( id_P, startups[ id_P ]);
394  startups.remove( id_P );
395  }
396  else if( silent_startups.contains( id_P ))
397  {
398  kDebug( 172 ) << "removing silent";
399  silent_startups.remove( id_P );
400  }
401  else if( uninited_startups.contains( id_P ))
402  {
403  kDebug( 172 ) << "removing uninited";
404  uninited_startups.remove( id_P );
405  }
406  return;
407  }
408 
409 void KStartupInfo::Private::remove_startup_pids( const KStartupInfoData& data_P )
410  { // first find the matching info
411  for( QMap< KStartupInfoId, KStartupInfo::Data >::Iterator it = startups.begin();
412  it != startups.end();
413  ++it )
414  {
415  if( ( *it ).hostname() != data_P.hostname())
416  continue;
417  if( !( *it ).is_pid( data_P.pids().first()))
418  continue; // not the matching info
419  remove_startup_pids( it.key(), data_P );
420  break;
421  }
422  }
423 
424 void KStartupInfo::Private::remove_startup_pids( const KStartupInfoId& id_P,
425  const KStartupInfoData& data_P )
426  {
427  kFatal( data_P.pids().count() == 0, 172 );
428  Data* data = NULL;
429  if( startups.contains( id_P ))
430  data = &startups[ id_P ];
431  else if( silent_startups.contains( id_P ))
432  data = &silent_startups[ id_P ];
433  else if( uninited_startups.contains( id_P ))
434  data = &uninited_startups[ id_P ];
435  else
436  return;
437  for( QList< pid_t >::ConstIterator it2 = data_P.pids().constBegin();
438  it2 != data_P.pids().constEnd();
439  ++it2 )
440  data->d->remove_pid( *it2 ); // remove all pids from the info
441  if( data->pids().count() == 0 ) // all pids removed -> remove info
442  remove_startup_info_internal( id_P );
443  }
444 
445 bool KStartupInfo::sendStartup( const KStartupInfoId& id_P, const KStartupInfoData& data_P )
446  {
447  if( id_P.none())
448  return false;
449 #ifdef Q_WS_X11
450  KXMessages msgs;
451  QString msg = QString::fromLatin1( "new: %1 %2" )
452  .arg( id_P.d->to_text()).arg( data_P.d->to_text());
453  QX11Info inf;
454  msg = Private::check_required_startup_fields( msg, data_P, inf.screen());
455  kDebug( 172 ) << "sending " << msg;
456  msgs.broadcastMessage( NET_STARTUP_MSG, msg, -1, false );
457 #endif
458  return true;
459  }
460 
461 bool KStartupInfo::sendStartupX( Display* disp_P, const KStartupInfoId& id_P,
462  const KStartupInfoData& data_P )
463  {
464  if( id_P.none())
465  return false;
466 #ifdef Q_WS_X11
467  QString msg = QString::fromLatin1( "new: %1 %2" )
468  .arg( id_P.d->to_text()).arg( data_P.d->to_text());
469  msg = Private::check_required_startup_fields( msg, data_P, DefaultScreen( disp_P ));
470 #ifdef KSTARTUPINFO_ALL_DEBUG
471  kDebug( 172 ) << "sending " << msg;
472 #endif
473  return KXMessages::broadcastMessageX( disp_P, NET_STARTUP_MSG, msg, -1, false );
474 #else
475  return true;
476 #endif
477  }
478 
479 QString KStartupInfo::Private::check_required_startup_fields( const QString& msg, const KStartupInfoData& data_P,
480  int screen )
481  {
482  QString ret = msg;
483  if( data_P.name().isEmpty())
484  {
485 // kWarning( 172 ) << "NAME not specified in initial startup message";
486  QString name = data_P.bin();
487  if( name.isEmpty())
488  name = "UNKNOWN";
489  ret += QString( " NAME=\"%1\"" ).arg( escape_str( name ));
490  }
491  if( data_P.screen() == -1 ) // add automatically if needed
492  ret += QString( " SCREEN=%1" ).arg( screen );
493  return ret;
494  }
495 
496 bool KStartupInfo::sendChange( const KStartupInfoId& id_P, const KStartupInfoData& data_P )
497  {
498  if( id_P.none())
499  return false;
500 #ifdef Q_WS_X11
501  KXMessages msgs;
502  QString msg = QString::fromLatin1( "change: %1 %2" )
503  .arg( id_P.d->to_text()).arg( data_P.d->to_text());
504  kDebug( 172 ) << "sending " << msg;
505  msgs.broadcastMessage( NET_STARTUP_MSG, msg, -1, false );
506 #endif
507  return true;
508  }
509 
510 bool KStartupInfo::sendChangeX( Display* disp_P, const KStartupInfoId& id_P,
511  const KStartupInfoData& data_P )
512  {
513  if( id_P.none())
514  return false;
515 #ifdef Q_WS_X11
516  QString msg = QString::fromLatin1( "change: %1 %2" )
517  .arg( id_P.d->to_text()).arg( data_P.d->to_text());
518 #ifdef KSTARTUPINFO_ALL_DEBUG
519  kDebug( 172 ) << "sending " << msg;
520 #endif
521  return KXMessages::broadcastMessageX( disp_P, NET_STARTUP_MSG, msg, -1, false );
522 #else
523  return true;
524 #endif
525  }
526 
527 bool KStartupInfo::sendFinish( const KStartupInfoId& id_P )
528  {
529  if( id_P.none())
530  return false;
531 #ifdef Q_WS_X11
532  KXMessages msgs;
533  QString msg = QString::fromLatin1( "remove: %1" ).arg( id_P.d->to_text());
534  kDebug( 172 ) << "sending " << msg;
535  msgs.broadcastMessage( NET_STARTUP_MSG, msg, -1, false );
536 #endif
537  return true;
538  }
539 
540 bool KStartupInfo::sendFinishX( Display* disp_P, const KStartupInfoId& id_P )
541  {
542  if( id_P.none())
543  return false;
544 #ifdef Q_WS_X11
545  QString msg = QString::fromLatin1( "remove: %1" ).arg( id_P.d->to_text());
546 #ifdef KSTARTUPINFO_ALL_DEBUG
547  kDebug( 172 ) << "sending " << msg;
548 #endif
549  return KXMessages::broadcastMessageX( disp_P, NET_STARTUP_MSG, msg, -1, false );
550 #else
551  return true;
552 #endif
553  }
554 
555 bool KStartupInfo::sendFinish( const KStartupInfoId& id_P, const KStartupInfoData& data_P )
556  {
557 // if( id_P.none()) // id may be none, the pids and hostname matter then
558 // return false;
559 #ifdef Q_WS_X11
560  KXMessages msgs;
561  QString msg = QString::fromLatin1( "remove: %1 %2" )
562  .arg( id_P.d->to_text()).arg( data_P.d->to_text());
563  kDebug( 172 ) << "sending " << msg;
564  msgs.broadcastMessage( NET_STARTUP_MSG, msg, -1, false );
565 #endif
566  return true;
567  }
568 
569 bool KStartupInfo::sendFinishX( Display* disp_P, const KStartupInfoId& id_P,
570  const KStartupInfoData& data_P )
571  {
572 // if( id_P.none()) // id may be none, the pids and hostname matter then
573 // return false;
574 #ifdef Q_WS_X11
575  QString msg = QString::fromLatin1( "remove: %1 %2" )
576  .arg( id_P.d->to_text()).arg( data_P.d->to_text());
577 #ifdef KSTARTUPINFO_ALL_DEBUG
578  kDebug( 172 ) << "sending " << msg;
579 #endif
580  return KXMessages::broadcastMessageX( disp_P, NET_STARTUP_MSG, msg, -1, false );
581 #else
582  return true;
583 #endif
584  }
585 
586 void KStartupInfo::appStarted()
587  {
588  if( kapp != NULL ) // KApplication constructor unsets the env. variable
589  {
590  appStarted( kapp->startupId());
591  kapp->clearStartupId(); // reset the id, no longer valid (must use clearStartupId() to avoid infinite loop)
592  }
593  else
594  {
595  appStarted( currentStartupIdEnv().id());
596  resetStartupEnv();
597  }
598  }
599 
600 void KStartupInfo::appStarted( const QByteArray& startup_id )
601  {
602  KStartupInfoId id;
603  id.initId( startup_id );
604  if( id.none())
605  return;
606  if( kapp != NULL )
607  KStartupInfo::sendFinish( id );
608  else if( !qgetenv( "DISPLAY" ).isEmpty() ) // don't rely on QX11Info::display()
609  {
610 #ifdef Q_WS_X11
611  Display* disp = XOpenDisplay( NULL );
612  if( disp != NULL )
613  {
614  KStartupInfo::sendFinishX( disp, id );
615  XCloseDisplay( disp );
616  }
617 #endif
618  }
619  }
620 
621 void KStartupInfo::disableAutoAppStartedSending( bool disable )
622  {
623  auto_app_started_sending = !disable;
624  }
625 
626 void KStartupInfo::silenceStartup( bool silence )
627  {
628  KStartupInfoId id;
629  id.initId( kapp->startupId());
630  if( id.none())
631  return;
632  KStartupInfoData data;
633  data.setSilent( silence ? KStartupInfoData::Yes : KStartupInfoData::No );
634  sendChange( id, data );
635  }
636 
637 void KStartupInfo::handleAutoAppStartedSending()
638  {
639  if( auto_app_started_sending )
640  appStarted();
641  }
642 
643 void KStartupInfo::setNewStartupId( QWidget* window, const QByteArray& startup_id )
644  {
645  bool activate = true;
646  kapp->setStartupId( startup_id );
647 #ifdef Q_WS_X11
648  if( window != NULL )
649  {
650  if( !startup_id.isEmpty() && startup_id != "0" )
651  {
652  NETRootInfo i( QX11Info::display(), NET::Supported );
653  if( i.isSupported( NET::WM2StartupId ))
654  {
655  KStartupInfo::setWindowStartupId( window->winId(), startup_id );
656  activate = false; // WM will take care of it
657  }
658  }
659  if( activate )
660  {
661  KWindowSystem::setOnDesktop( window->winId(), KWindowSystem::currentDesktop());
662  // This is not very nice, but there's no way how to get any
663  // usable timestamp without ASN, so force activating the window.
664  // And even with ASN, it's not possible to get the timestamp here,
665  // so if the WM doesn't have support for ASN, it can't be used either.
666  KWindowSystem::forceActiveWindow( window->winId());
667  }
668  }
669 #endif
670  KStartupInfo::handleAutoAppStartedSending();
671  }
672 
673 KStartupInfo::startup_t KStartupInfo::checkStartup( WId w_P, KStartupInfoId& id_O,
674  KStartupInfoData& data_O )
675  {
676  return d->check_startup_internal( w_P, &id_O, &data_O );
677  }
678 
679 KStartupInfo::startup_t KStartupInfo::checkStartup( WId w_P, KStartupInfoId& id_O )
680  {
681  return d->check_startup_internal( w_P, &id_O, NULL );
682  }
683 
684 KStartupInfo::startup_t KStartupInfo::checkStartup( WId w_P, KStartupInfoData& data_O )
685  {
686  return d->check_startup_internal( w_P, NULL, &data_O );
687  }
688 
689 KStartupInfo::startup_t KStartupInfo::checkStartup( WId w_P )
690  {
691  return d->check_startup_internal( w_P, NULL, NULL );
692  }
693 
694 KStartupInfo::startup_t KStartupInfo::Private::check_startup_internal( WId w_P, KStartupInfoId* id_O,
695  KStartupInfoData* data_O )
696  {
697  if( startups.count() == 0 )
698  return NoMatch; // no startups
699  // Strategy:
700  //
701  // Is this a compliant app ?
702  // - Yes - test for match
703  // - No - Is this a NET_WM compliant app ?
704  // - Yes - test for pid match
705  // - No - test for WM_CLASS match
706  kDebug( 172 ) << "check_startup";
707  QByteArray id = windowStartupId( w_P );
708  if( !id.isNull())
709  {
710  if( id.isEmpty() || id == "0" ) // means ignore this window
711  {
712  kDebug( 172 ) << "ignore";
713  return NoMatch;
714  }
715  return find_id( id, id_O, data_O ) ? Match : NoMatch;
716  }
717 #ifdef Q_WS_X11
718  NETWinInfo info( QX11Info::display(), w_P, QX11Info::appRootWindow(),
719  NET::WMWindowType | NET::WMPid | NET::WMState );
720  pid_t pid = info.pid();
721  if( pid > 0 )
722  {
723  QByteArray hostname = get_window_hostname( w_P );
724  if( !hostname.isEmpty()
725  && find_pid( pid, hostname, id_O, data_O ))
726  return Match;
727  // try XClass matching , this PID stuff sucks :(
728  }
729  XClassHint hint;
730  if( XGetClassHint( QX11Info::display(), w_P, &hint ) != 0 )
731  { // We managed to read the class hint
732  QByteArray res_name = hint.res_name;
733  QByteArray res_class = hint.res_class;
734  XFree( hint.res_name );
735  XFree( hint.res_class );
736  if( find_wclass( res_name, res_class, id_O, data_O ))
737  return Match;
738  }
739  // ignore NET::Tool and other special window types, if they can't be matched
740  NET::WindowType type = info.windowType( NET::NormalMask | NET::DesktopMask
741  | NET::DockMask | NET::ToolbarMask | NET::MenuMask | NET::DialogMask
742  | NET::OverrideMask | NET::TopMenuMask | NET::UtilityMask | NET::SplashMask );
743  if( type != NET::Normal
744  && type != NET::Override
745  && type != NET::Unknown
746  && type != NET::Dialog
747  && type != NET::Utility )
748 // && type != NET::Dock ) why did I put this here?
749  return NoMatch;
750  // lets see if this is a transient
751  Window transient_for;
752  if( XGetTransientForHint( QX11Info::display(), static_cast< Window >( w_P ), &transient_for )
753  && static_cast< WId >( transient_for ) != QX11Info::appRootWindow()
754  && transient_for != None )
755  return NoMatch;
756 #endif
757  kDebug( 172 ) << "check_startup:cantdetect";
758  return CantDetect;
759  }
760 
761 bool KStartupInfo::Private::find_id( const QByteArray& id_P, KStartupInfoId* id_O,
762  KStartupInfoData* data_O )
763  {
764  kDebug( 172 ) << "find_id:" << id_P;
765  KStartupInfoId id;
766  id.initId( id_P );
767  if( startups.contains( id ))
768  {
769  if( id_O != NULL )
770  *id_O = id;
771  if( data_O != NULL )
772  *data_O = startups[ id ];
773  kDebug( 172 ) << "check_startup_id:match";
774  return true;
775  }
776  return false;
777  }
778 
779 bool KStartupInfo::Private::find_pid( pid_t pid_P, const QByteArray& hostname_P,
780  KStartupInfoId* id_O, KStartupInfoData* data_O )
781  {
782  kDebug( 172 ) << "find_pid:" << pid_P;
783  for( QMap< KStartupInfoId, KStartupInfo::Data >::Iterator it = startups.begin();
784  it != startups.end();
785  ++it )
786  {
787  if( ( *it ).is_pid( pid_P ) && ( *it ).hostname() == hostname_P )
788  { // Found it !
789  if( id_O != NULL )
790  *id_O = it.key();
791  if( data_O != NULL )
792  *data_O = *it;
793  // non-compliant, remove on first match
794  remove_startup_info_internal( it.key());
795  kDebug( 172 ) << "check_startup_pid:match";
796  return true;
797  }
798  }
799  return false;
800  }
801 
802 bool KStartupInfo::Private::find_wclass( const QByteArray &_res_name, const QByteArray &_res_class,
803  KStartupInfoId* id_O, KStartupInfoData* data_O )
804  {
805  QByteArray res_name = _res_name.toLower();
806  QByteArray res_class = _res_class.toLower();
807  kDebug( 172 ) << "find_wclass:" << res_name << ":" << res_class;
808  for( QMap< KStartupInfoId, Data >::Iterator it = startups.begin();
809  it != startups.end();
810  ++it )
811  {
812  const QByteArray wmclass = ( *it ).findWMClass();
813  if( wmclass.toLower() == res_name || wmclass.toLower() == res_class )
814  { // Found it !
815  if( id_O != NULL )
816  *id_O = it.key();
817  if( data_O != NULL )
818  *data_O = *it;
819  // non-compliant, remove on first match
820  remove_startup_info_internal( it.key());
821  kDebug( 172 ) << "check_startup_wclass:match";
822  return true;
823  }
824  }
825  return false;
826  }
827 
828 #ifdef Q_WS_X11
829 static Atom net_startup_atom = None;
830 
831 static QByteArray read_startup_id_property( WId w_P )
832  {
833  QByteArray ret;
834  unsigned char *name_ret;
835  Atom type_ret;
836  int format_ret;
837  unsigned long nitems_ret = 0, after_ret = 0;
838  if( XGetWindowProperty( QX11Info::display(), w_P, net_startup_atom, 0l, 4096,
839  False, utf8_string_atom, &type_ret, &format_ret, &nitems_ret, &after_ret, &name_ret )
840  == Success )
841  {
842  if( type_ret == utf8_string_atom && format_ret == 8 && name_ret != NULL )
843  ret = reinterpret_cast< char* >( name_ret );
844  if ( name_ret != NULL )
845  XFree( name_ret );
846  }
847  return ret;
848  }
849 
850 #endif
851 
852 QByteArray KStartupInfo::windowStartupId( WId w_P )
853  {
854 #ifdef Q_WS_X11
855  if( net_startup_atom == None )
856  net_startup_atom = XInternAtom( QX11Info::display(), NET_STARTUP_WINDOW, False );
857  if( utf8_string_atom == None )
858  utf8_string_atom = XInternAtom( QX11Info::display(), "UTF8_STRING", False );
859  QByteArray ret = read_startup_id_property( w_P );
860  if( ret.isEmpty())
861  { // retry with window group leader, as the spec says
862  XWMHints* hints = XGetWMHints( QX11Info::display(), w_P );
863  if( hints && ( hints->flags & WindowGroupHint ) != 0 )
864  ret = read_startup_id_property( hints->window_group );
865  if( hints )
866  XFree( hints );
867  }
868  return ret;
869 #else
870  return QByteArray();
871 #endif
872  }
873 
874 void KStartupInfo::setWindowStartupId( WId w_P, const QByteArray& id_P )
875  {
876 #ifdef Q_WS_X11
877  if( id_P.isNull())
878  return;
879  if( net_startup_atom == None )
880  net_startup_atom = XInternAtom( QX11Info::display(), NET_STARTUP_WINDOW, False );
881  if( utf8_string_atom == None )
882  utf8_string_atom = XInternAtom( QX11Info::display(), "UTF8_STRING", False );
883  XChangeProperty( QX11Info::display(), w_P, net_startup_atom, utf8_string_atom, 8,
884  PropModeReplace, reinterpret_cast< const unsigned char* >( id_P.data()), id_P.length());
885 #endif
886  }
887 
888 QByteArray KStartupInfo::Private::get_window_hostname( WId w_P )
889  {
890 #ifdef Q_WS_X11
891  XTextProperty tp;
892  char** hh;
893  int cnt;
894  if( XGetWMClientMachine( QX11Info::display(), w_P, &tp ) != 0
895  && XTextPropertyToStringList( &tp, &hh, &cnt ) != 0 )
896  {
897  if( cnt == 1 )
898  {
899  QByteArray hostname = hh[ 0 ];
900  XFreeStringList( hh );
901  return hostname;
902  }
903  XFreeStringList( hh );
904  }
905 #endif
906  // no hostname
907  return QByteArray();
908  }
909 
910 void KStartupInfo::setTimeout( unsigned int secs_P )
911  {
912  d->timeout = secs_P;
913  // schedule removing entries that are older than the new timeout
914  QTimer::singleShot( 0, this, SLOT(startups_cleanup_no_age()));
915  }
916 
917 void KStartupInfo::Private::startups_cleanup_no_age()
918  {
919  startups_cleanup_internal( false );
920  }
921 
922 void KStartupInfo::Private::startups_cleanup()
923  {
924  if( startups.count() == 0 && silent_startups.count() == 0
925  && uninited_startups.count() == 0 )
926  {
927  cleanup->stop();
928  return;
929  }
930  startups_cleanup_internal( true );
931  }
932 
933 void KStartupInfo::Private::startups_cleanup_internal( bool age_P )
934  {
935  for( QMap< KStartupInfoId, KStartupInfo::Data >::Iterator it = startups.begin();
936  it != startups.end();
937  )
938  {
939  if( age_P )
940  ( *it ).age++;
941  unsigned int tout = timeout;
942  if( ( *it ).silent() == Data::Yes ) // TODO
943  tout *= 20;
944  if( ( *it ).age >= tout )
945  {
946  const KStartupInfoId& key = it.key();
947  ++it;
948  kDebug( 172 ) << "entry timeout:" << key.id();
949  remove_startup_info_internal( key );
950  }
951  else
952  ++it;
953  }
954  for( QMap< KStartupInfoId, KStartupInfo::Data >::Iterator it = silent_startups.begin();
955  it != silent_startups.end();
956  )
957  {
958  if( age_P )
959  ( *it ).age++;
960  unsigned int tout = timeout;
961  if( ( *it ).silent() == Data::Yes ) // TODO
962  tout *= 20;
963  if( ( *it ).age >= tout )
964  {
965  const KStartupInfoId& key = it.key();
966  ++it;
967  kDebug( 172 ) << "entry timeout:" << key.id();
968  remove_startup_info_internal( key );
969  }
970  else
971  ++it;
972  }
973  for( QMap< KStartupInfoId, KStartupInfo::Data >::Iterator it = uninited_startups.begin();
974  it != uninited_startups.end();
975  )
976  {
977  if( age_P )
978  ( *it ).age++;
979  unsigned int tout = timeout;
980  if( ( *it ).silent() == Data::Yes ) // TODO
981  tout *= 20;
982  if( ( *it ).age >= tout )
983  {
984  const KStartupInfoId& key = it.key();
985  ++it;
986  kDebug( 172 ) << "entry timeout:" << key.id();
987  remove_startup_info_internal( key );
988  }
989  else
990  ++it;
991  }
992  }
993 
994 void KStartupInfo::Private::clean_all_noncompliant()
995  {
996  for( QMap< KStartupInfoId, KStartupInfo::Data >::Iterator it = startups.begin();
997  it != startups.end();
998  )
999  {
1000  if( ( *it ).WMClass() != "0" )
1001  {
1002  ++it;
1003  continue;
1004  }
1005  const KStartupInfoId& key = it.key();
1006  ++it;
1007  kDebug( 172 ) << "entry cleaning:" << key.id();
1008  remove_startup_info_internal( key );
1009  }
1010  }
1011 
1012 QByteArray KStartupInfo::createNewStartupId()
1013  {
1014  // Assign a unique id, use hostname+time+pid, that should be 200% unique.
1015  // Also append the user timestamp (for focus stealing prevention).
1016  struct timeval tm;
1017  gettimeofday( &tm, NULL );
1018  char hostname[ 256 ];
1019  hostname[ 0 ] = '\0';
1020  if (!gethostname( hostname, 255 ))
1021  hostname[sizeof(hostname)-1] = '\0';
1022 #ifdef Q_WS_X11
1023  unsigned long qt_x_user_time = QX11Info::appUserTime();
1024 #else
1025  unsigned long qt_x_user_time = 0;
1026 #endif
1027  QByteArray id = QString::fromLatin1( "%1;%2;%3;%4_TIME%5" ).arg( hostname ).arg( tm.tv_sec )
1028  .arg( tm.tv_usec ).arg( getpid()).arg( qt_x_user_time ).toUtf8();
1029  kDebug( 172 ) << "creating: " << id << ":" << (qApp ? qAppName() : QString("unnamed app") /* e.g. kdeinit */);
1030  return id;
1031  }
1032 
1033 
1034 const QByteArray& KStartupInfoId::id() const
1035  {
1036  return d->id;
1037  }
1038 
1039 
1040 QString KStartupInfoId::Private::to_text() const
1041  {
1042  return QString::fromLatin1( " ID=\"%1\" " ).arg( escape_str( id));
1043  }
1044 
1045 KStartupInfoId::KStartupInfoId( const QString& txt_P ) : d(new Private)
1046  {
1047  const QStringList items = get_fields( txt_P );
1048  const QString id_str = QLatin1String( "ID=" );
1049  for( QStringList::ConstIterator it = items.begin();
1050  it != items.end();
1051  ++it )
1052  {
1053  if( ( *it ).startsWith( id_str ))
1054  d->id = get_cstr( *it );
1055  }
1056  }
1057 
1058 void KStartupInfoId::initId( const QByteArray& id_P )
1059  {
1060  if( !id_P.isEmpty())
1061  {
1062  d->id = id_P;
1063 #ifdef KSTARTUPINFO_ALL_DEBUG
1064  kDebug( 172 ) << "using: " << d->id;
1065 #endif
1066  return;
1067  }
1068  const QByteArray startup_env = qgetenv( NET_STARTUP_ENV );
1069  if( !startup_env.isEmpty() )
1070  { // already has id
1071  d->id = startup_env;
1072 #ifdef KSTARTUPINFO_ALL_DEBUG
1073  kDebug( 172 ) << "reusing: " << d->id;
1074 #endif
1075  return;
1076  }
1077  d->id = KStartupInfo::createNewStartupId();
1078  }
1079 
1080 bool KStartupInfoId::setupStartupEnv() const
1081  {
1082  if( none())
1083  {
1084  unsetenv( NET_STARTUP_ENV );
1085  return false;
1086  }
1087  return setenv( NET_STARTUP_ENV, id(), true ) == 0;
1088  }
1089 
1090 KStartupInfoId KStartupInfo::currentStartupIdEnv()
1091  {
1092  const QByteArray startup_env = qgetenv( NET_STARTUP_ENV );
1093  KStartupInfoId id;
1094  if( !startup_env.isEmpty() )
1095  id.d->id = startup_env;
1096  else
1097  id.d->id = "0";
1098  return id;
1099  }
1100 
1101 void KStartupInfo::resetStartupEnv()
1102  {
1103  unsetenv( NET_STARTUP_ENV );
1104  }
1105 
1106 KStartupInfoId::KStartupInfoId() : d(new Private)
1107  {
1108  }
1109 
1110 KStartupInfoId::~KStartupInfoId()
1111  {
1112  delete d;
1113  }
1114 
1115 KStartupInfoId::KStartupInfoId( const KStartupInfoId& id_P ) : d(new Private(*id_P.d))
1116  {
1117  }
1118 
1119 KStartupInfoId& KStartupInfoId::operator=( const KStartupInfoId& id_P )
1120  {
1121  if( &id_P == this )
1122  return *this;
1123  *d = *id_P.d;
1124  return *this;
1125  }
1126 
1127 bool KStartupInfoId::operator==( const KStartupInfoId& id_P ) const
1128  {
1129  return id() == id_P.id();
1130  }
1131 
1132 bool KStartupInfoId::operator!=( const KStartupInfoId& id_P ) const
1133  {
1134  return !(*this == id_P );
1135  }
1136 
1137 // needed for QMap
1138 bool KStartupInfoId::operator<( const KStartupInfoId& id_P ) const
1139  {
1140  return id() < id_P.id();
1141  }
1142 
1143 bool KStartupInfoId::none() const
1144  {
1145  return d->id.isEmpty() || d->id == "0";
1146  }
1147 
1148 unsigned long KStartupInfoId::timestamp() const
1149  {
1150  if( none())
1151  return 0;
1152  int pos = d->id.lastIndexOf( "_TIME" );
1153  if( pos >= 0 )
1154  {
1155  bool ok;
1156  unsigned long time = QString( d->id.mid( pos + 5 ) ).toULong( &ok );
1157  if( !ok && d->id[ pos + 5 ] == '-' ) // try if it's as a negative signed number perhaps
1158  time = QString( d->id.mid( pos + 5 ) ).toLong( &ok );
1159  if( ok )
1160  return time;
1161  }
1162  // libstartup-notification style :
1163  // qsnprintf (s, len, "%s/%s/%lu/%d-%d-%s",
1164  // canonicalized_launcher, canonicalized_launchee, (unsigned long) timestamp,
1165  // (int) getpid (), (int) sequence_number, hostbuf);
1166  int pos1 = d->id.lastIndexOf( '/' );
1167  if( pos1 > 0 )
1168  {
1169  int pos2 = d->id.lastIndexOf( '/', pos1 - 1 );
1170  if( pos2 >= 0 )
1171  {
1172  bool ok;
1173  unsigned long time = QString( d->id.mid( pos2 + 1, pos1 - pos2 - 1 ) ).toULong( &ok );
1174  if( !ok && d->id[ pos2 + 1 ] == '-' )
1175  time = QString( d->id.mid( pos2 + 1, pos1 - pos2 - 1 ) ).toLong( &ok );
1176  if( ok )
1177  return time;
1178  }
1179  }
1180  // bah ... old KStartupInfo or a problem
1181  return 0;
1182  }
1183 
1184 QString KStartupInfoData::Private::to_text() const
1185  {
1186  QString ret;
1187  if( !bin.isEmpty())
1188  ret += QString::fromLatin1( " BIN=\"%1\"" ).arg( escape_str( bin ));
1189  if( !name.isEmpty())
1190  ret += QString::fromLatin1( " NAME=\"%1\"" ).arg( escape_str( name ));
1191  if( !description.isEmpty())
1192  ret += QString::fromLatin1( " DESCRIPTION=\"%1\"" ).arg( escape_str( description ));
1193  if( !icon.isEmpty())
1194  ret += QString::fromLatin1( " ICON=\"%1\"" ).arg( icon );
1195  if( desktop != 0 )
1196  ret += QString::fromLatin1( " DESKTOP=%1" )
1197 #ifdef Q_WS_X11
1198  .arg( desktop == NET::OnAllDesktops ? NET::OnAllDesktops : desktop - 1 ); // spec counts from 0
1199 #else
1200  .arg( 0 ); // spec counts from 0
1201 #endif
1202  if( !wmclass.isEmpty())
1203  ret += QString::fromLatin1( " WMCLASS=\"%1\"" ).arg( QString( wmclass ) );
1204  if( !hostname.isEmpty())
1205  ret += QString::fromLatin1( " HOSTNAME=%1" ).arg( QString( hostname ) );
1206  for( QList< pid_t >::ConstIterator it = pids.begin();
1207  it != pids.end();
1208  ++it )
1209  ret += QString::fromLatin1( " PID=%1" ).arg( *it );
1210  if( silent != KStartupInfoData::Unknown )
1211  ret += QString::fromLatin1( " SILENT=%1" ).arg( silent == KStartupInfoData::Yes ? 1 : 0 );
1212  if( timestamp != ~0U )
1213  ret += QString::fromLatin1( " TIMESTAMP=%1" ).arg( timestamp );
1214  if( screen != -1 )
1215  ret += QString::fromLatin1( " SCREEN=%1" ).arg( screen );
1216  if( xinerama != -1 )
1217  ret += QString::fromLatin1( " XINERAMA=%1" ).arg( xinerama );
1218  if( launched_by != 0 )
1219  ret += QString::fromLatin1( " LAUNCHED_BY=%1" ).arg( (qptrdiff)launched_by );
1220  if( !application_id.isEmpty())
1221  ret += QString::fromLatin1( " APPLICATION_ID=\"%1\"" ).arg( application_id );
1222  return ret;
1223  }
1224 
1225 KStartupInfoData::KStartupInfoData( const QString& txt_P ) : d(new Private)
1226  {
1227  const QStringList items = get_fields( txt_P );
1228  const QString bin_str = QString::fromLatin1( "BIN=" );
1229  const QString name_str = QString::fromLatin1( "NAME=" );
1230  const QString description_str = QString::fromLatin1( "DESCRIPTION=" );
1231  const QString icon_str = QString::fromLatin1( "ICON=" );
1232  const QString desktop_str = QString::fromLatin1( "DESKTOP=" );
1233  const QString wmclass_str = QString::fromLatin1( "WMCLASS=" );
1234  const QString hostname_str = QString::fromLatin1( "HOSTNAME=" ); // SELI nonstd
1235  const QString pid_str = QString::fromLatin1( "PID=" ); // SELI nonstd
1236  const QString silent_str = QString::fromLatin1( "SILENT=" );
1237  const QString timestamp_str = QString::fromLatin1( "TIMESTAMP=" );
1238  const QString screen_str = QString::fromLatin1( "SCREEN=" );
1239  const QString xinerama_str = QString::fromLatin1( "XINERAMA=" );
1240  const QString launched_by_str = QString::fromLatin1( "LAUNCHED_BY=" );
1241  const QString application_id_str = QString::fromLatin1( "APPLICATION_ID=" );
1242  for( QStringList::ConstIterator it = items.begin();
1243  it != items.end();
1244  ++it )
1245  {
1246  if( ( *it ).startsWith( bin_str ))
1247  d->bin = get_str( *it );
1248  else if( ( *it ).startsWith( name_str ))
1249  d->name = get_str( *it );
1250  else if( ( *it ).startsWith( description_str ))
1251  d->description = get_str( *it );
1252  else if( ( *it ).startsWith( icon_str ))
1253  d->icon = get_str( *it );
1254  else if( ( *it ).startsWith( desktop_str ))
1255  {
1256  d->desktop = get_num( *it );
1257 #ifdef Q_WS_X11
1258  if( d->desktop != NET::OnAllDesktops )
1259 #endif
1260  ++d->desktop; // spec counts from 0
1261  }
1262  else if( ( *it ).startsWith( wmclass_str ))
1263  d->wmclass = get_cstr( *it );
1264  else if( ( *it ).startsWith( hostname_str ))
1265  d->hostname = get_cstr( *it );
1266  else if( ( *it ).startsWith( pid_str ))
1267  addPid( get_num( *it ));
1268  else if( ( *it ).startsWith( silent_str ))
1269  d->silent = get_num( *it ) != 0 ? Yes : No;
1270  else if( ( *it ).startsWith( timestamp_str ))
1271  d->timestamp = get_unum( *it );
1272  else if( ( *it ).startsWith( screen_str ))
1273  d->screen = get_num( *it );
1274  else if( ( *it ).startsWith( xinerama_str ))
1275  d->xinerama = get_num( *it );
1276  else if( ( *it ).startsWith( launched_by_str ))
1277  d->launched_by = ( WId ) get_num( *it );
1278  else if( ( *it ).startsWith( application_id_str ))
1279  d->application_id = get_str( *it );
1280  }
1281  }
1282 
1283 KStartupInfoData::KStartupInfoData( const KStartupInfoData& data ) : d(new Private(*data.d))
1284 {
1285 }
1286 
1287 KStartupInfoData& KStartupInfoData::operator=( const KStartupInfoData& data )
1288 {
1289  if( &data == this )
1290  return *this;
1291  *d = *data.d;
1292  return *this;
1293 }
1294 
1295 void KStartupInfoData::update( const KStartupInfoData& data_P )
1296  {
1297  if( !data_P.bin().isEmpty())
1298  d->bin = data_P.bin();
1299  if( !data_P.name().isEmpty() && name().isEmpty()) // don't overwrite
1300  d->name = data_P.name();
1301  if( !data_P.description().isEmpty() && description().isEmpty()) // don't overwrite
1302  d->description = data_P.description();
1303  if( !data_P.icon().isEmpty() && icon().isEmpty()) // don't overwrite
1304  d->icon = data_P.icon();
1305  if( data_P.desktop() != 0 && desktop() == 0 ) // don't overwrite
1306  d->desktop = data_P.desktop();
1307  if( !data_P.d->wmclass.isEmpty())
1308  d->wmclass = data_P.d->wmclass;
1309  if( !data_P.d->hostname.isEmpty())
1310  d->hostname = data_P.d->hostname;
1311  for( QList< pid_t >::ConstIterator it = data_P.d->pids.constBegin();
1312  it != data_P.d->pids.constEnd();
1313  ++it )
1314  addPid( *it );
1315  if( data_P.silent() != Unknown )
1316  d->silent = data_P.silent();
1317  if( data_P.timestamp() != ~0U && timestamp() == ~0U ) // don't overwrite
1318  d->timestamp = data_P.timestamp();
1319  if( data_P.screen() != -1 )
1320  d->screen = data_P.screen();
1321  if( data_P.xinerama() != -1 && xinerama() != -1 ) // don't overwrite
1322  d->xinerama = data_P.xinerama();
1323  if( data_P.launchedBy() != 0 && launchedBy() != 0 ) // don't overwrite
1324  d->launched_by = data_P.launchedBy();
1325  if( !data_P.applicationId().isEmpty() && applicationId().isEmpty()) // don't overwrite
1326  d->application_id = data_P.applicationId();
1327  }
1328 
1329 KStartupInfoData::KStartupInfoData() : d(new Private)
1330 {
1331 }
1332 
1333 KStartupInfoData::~KStartupInfoData()
1334 {
1335  delete d;
1336 }
1337 
1338 void KStartupInfoData::setBin( const QString& bin_P )
1339  {
1340  d->bin = bin_P;
1341  }
1342 
1343 const QString& KStartupInfoData::bin() const
1344  {
1345  return d->bin;
1346  }
1347 
1348 void KStartupInfoData::setName( const QString& name_P )
1349  {
1350  d->name = name_P;
1351  }
1352 
1353 const QString& KStartupInfoData::name() const
1354  {
1355  return d->name;
1356  }
1357 
1358 const QString& KStartupInfoData::findName() const
1359  {
1360  if( !name().isEmpty())
1361  return name();
1362  return bin();
1363  }
1364 
1365 void KStartupInfoData::setDescription( const QString& desc_P )
1366  {
1367  d->description = desc_P;
1368  }
1369 
1370 const QString& KStartupInfoData::description() const
1371  {
1372  return d->description;
1373  }
1374 
1375 const QString& KStartupInfoData::findDescription() const
1376  {
1377  if( !description().isEmpty())
1378  return description();
1379  return name();
1380  }
1381 
1382 void KStartupInfoData::setIcon( const QString& icon_P )
1383  {
1384  d->icon = icon_P;
1385  }
1386 
1387 const QString& KStartupInfoData::findIcon() const
1388  {
1389  if( !icon().isEmpty())
1390  return icon();
1391  return bin();
1392  }
1393 
1394 const QString& KStartupInfoData::icon() const
1395  {
1396  return d->icon;
1397  }
1398 
1399 void KStartupInfoData::setDesktop( int desktop_P )
1400  {
1401  d->desktop = desktop_P;
1402  }
1403 
1404 int KStartupInfoData::desktop() const
1405  {
1406  return d->desktop;
1407  }
1408 
1409 void KStartupInfoData::setWMClass( const QByteArray& wmclass_P )
1410  {
1411  d->wmclass = wmclass_P;
1412  }
1413 
1414 const QByteArray KStartupInfoData::findWMClass() const
1415  {
1416  if( !WMClass().isEmpty() && WMClass() != "0" )
1417  return WMClass();
1418  return bin().toUtf8();
1419  }
1420 
1421 QByteArray KStartupInfoData::WMClass() const
1422  {
1423  return d->wmclass;
1424  }
1425 
1426 void KStartupInfoData::setHostname( const QByteArray& hostname_P )
1427  {
1428  if( !hostname_P.isNull())
1429  d->hostname = hostname_P;
1430  else
1431  {
1432  char tmp[ 256 ];
1433  tmp[ 0 ] = '\0';
1434  if (!gethostname( tmp, 255 ))
1435  tmp[sizeof(tmp)-1] = '\0';
1436  d->hostname = tmp;
1437  }
1438  }
1439 
1440 QByteArray KStartupInfoData::hostname() const
1441  {
1442  return d->hostname;
1443  }
1444 
1445 void KStartupInfoData::addPid( pid_t pid_P )
1446  {
1447  if( !d->pids.contains( pid_P ))
1448  d->pids.append( pid_P );
1449  }
1450 
1451 void KStartupInfoData::Private::remove_pid( pid_t pid_P )
1452  {
1453  pids.removeAll( pid_P );
1454  }
1455 
1456 QList< pid_t > KStartupInfoData::pids() const
1457  {
1458  return d->pids;
1459  }
1460 
1461 bool KStartupInfoData::is_pid( pid_t pid_P ) const
1462  {
1463  return d->pids.contains( pid_P );
1464  }
1465 
1466 void KStartupInfoData::setSilent( TriState state_P )
1467  {
1468  d->silent = state_P;
1469  }
1470 
1471 KStartupInfoData::TriState KStartupInfoData::silent() const
1472  {
1473  return d->silent;
1474  }
1475 
1476 void KStartupInfoData::setTimestamp( unsigned long time )
1477  {
1478  d->timestamp = time;
1479  }
1480 
1481 unsigned long KStartupInfoData::timestamp() const
1482  {
1483  return d->timestamp;
1484  }
1485 
1486 void KStartupInfoData::setScreen( int _screen )
1487  {
1488  d->screen = _screen;
1489  }
1490 
1491 int KStartupInfoData::screen() const
1492  {
1493  return d->screen;
1494  }
1495 
1496 void KStartupInfoData::setXinerama( int xinerama )
1497  {
1498  d->xinerama = xinerama;
1499  }
1500 
1501 int KStartupInfoData::xinerama() const
1502  {
1503  return d->xinerama;
1504  }
1505 
1506 void KStartupInfoData::setLaunchedBy( WId window )
1507  {
1508  d->launched_by = window;
1509  }
1510 
1511 WId KStartupInfoData::launchedBy() const
1512  {
1513  return d->launched_by;
1514  }
1515 
1516 void KStartupInfoData::setApplicationId( const QString& desktop )
1517  {
1518  if( desktop.startsWith( '/' ))
1519  {
1520  d->application_id = desktop;
1521  return;
1522  }
1523  // the spec requires this is always a full path, in order for everyone to be able to find it
1524  QString desk = KStandardDirs::locate( "apps", desktop );
1525  if( desk.isEmpty())
1526  desk = KStandardDirs::locate( "services", desktop );
1527  if( desk.isEmpty())
1528  return;
1529  d->application_id = desk;
1530  }
1531 
1532 QString KStartupInfoData::applicationId() const
1533  {
1534  return d->application_id;
1535  }
1536 
1537 static
1538 long get_num( const QString& item_P )
1539  {
1540  unsigned int pos = item_P.indexOf( QLatin1Char('=') );
1541  return item_P.mid( pos + 1 ).toLong();
1542  }
1543 
1544 static
1545 unsigned long get_unum( const QString& item_P )
1546  {
1547  unsigned int pos = item_P.indexOf( QLatin1Char('=') );
1548  return item_P.mid( pos + 1 ).toULong();
1549  }
1550 
1551 static
1552 QString get_str( const QString& item_P )
1553  {
1554  int pos = item_P.indexOf( QLatin1Char('=') );
1555  if( item_P.length() > pos + 2 && item_P.at( pos + 1 ) == QLatin1Char('\"') )
1556  {
1557  int pos2 = item_P.left( pos + 2 ).indexOf( QLatin1Char('\"') );
1558  if( pos2 < 0 )
1559  return QString(); // 01234
1560  return item_P.mid( pos + 2, pos2 - 2 - pos ); // A="C"
1561  }
1562  return item_P.mid( pos + 1 );
1563  }
1564 
1565 static
1566 QByteArray get_cstr( const QString& item_P )
1567  {
1568  return get_str( item_P ).toUtf8();
1569  }
1570 
1571 static
1572 QStringList get_fields( const QString& txt_P )
1573  {
1574  QString txt = txt_P.simplified();
1575  QStringList ret;
1576  QString item = "";
1577  bool in = false;
1578  bool escape = false;
1579  for( int pos = 0;
1580  pos < txt.length();
1581  ++pos )
1582  {
1583  if( escape )
1584  {
1585  item += txt[ pos ];
1586  escape = false;
1587  }
1588  else if( txt[ pos ] == '\\' )
1589  escape = true;
1590  else if( txt[ pos ] == '\"' )
1591  in = !in;
1592  else if( txt[ pos ] == ' ' && !in )
1593  {
1594  ret.append( item );
1595  item = "";
1596  }
1597  else
1598  item += txt[ pos ];
1599  }
1600  ret.append( item );
1601  return ret;
1602  }
1603 
1604 static QString escape_str( const QString& str_P )
1605  {
1606  QString ret = "";
1607  for( int pos = 0;
1608  pos < str_P.length();
1609  ++pos )
1610  {
1611  if( str_P[ pos ] == '\\'
1612  || str_P[ pos ] == '"' )
1613  ret += '\\';
1614  ret += str_P[ pos ];
1615  }
1616  return ret;
1617  }
1618 
1619 #include "kstartupinfo.moc"
This file is part of the KDE documentation.
Documentation copyright © 1996-2012 The KDE developers.
Generated on Fri Dec 7 2012 16:04:45 by doxygen 1.8.1 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.

KDEUI

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

kdelibs-4.8.5 API Reference

Skip menu "kdelibs-4.8.5 API Reference"
  • DNSSD
  • Interfaces
  •   KHexEdit
  •   KMediaPlayer
  •   KSpeech
  •   KTextEditor
  • kconf_update
  • KDE3Support
  •   KUnitTest
  • KDECore
  • KDED
  • KDEsu
  • KDEUI
  • KDEWebKit
  • KDocTools
  • KFile
  • KHTML
  • KImgIO
  • KInit
  • kio
  • KIOSlave
  • KJS
  •   KJS-API
  •   WTF
  • kjsembed
  • KNewStuff
  • KParts
  • KPty
  • Kross
  • KUnitConversion
  • KUtils
  • Nepomuk
  • Plasma
  • Solid
  • Sonnet
  • ThreadWeaver
Report problems with this website to our bug tracking system.
Contact the specific authors with questions and comments about the page contents.

KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal