kstart.cpp 16.2 KB
Newer Older
1
2
3
/*
 * kstart.C. Part of the KDE project.
 *
4
 * Copyright (C) 1997-2000 Matthias Ettrich <ettrich@kde.org>
5
 *
6
 * First port to NETWM by David Faure <faure@kde.org>
7
 * Send to system tray by Richard Moore <rich@kde.org>
8
9
10
11
12
13

 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License as
 * published by the Free Software Foundation; either version 2 of
 * the License or (at your option) version 3 or any later version
 * accepted by the membership of KDE e.V. (or its successor approved
David Faure's avatar
David Faure committed
14
 * by the membership of KDE e.V.), which shall act as a proxy
15
16
17
18
19
20
21
22
23
 * defined in Section 14 of version 3 of the license.

 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.

 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
24
25
 */

David Faure's avatar
David Faure committed
26
#include <ktoolinvocation.h>
Aleix Pol Gonzalez's avatar
Aleix Pol Gonzalez committed
27
#include "kstart.h"
28
29

#include <fcntl.h>
Hans Petter Bieker's avatar
Hans Petter Bieker committed
30
31
#include <stdlib.h>

32
33
#include <QRegExp>
#include <QTimer>
34
#include <QDesktopWidget>
35
#include <QApplication>
36
37
#include <QCommandLineParser>
#include <QCommandLineOption>
38
#include <QDebug>
Hans Petter Bieker's avatar
Hans Petter Bieker committed
39

Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
40
#include <kprocess.h>
Luboš Luňák's avatar
Luboš Luňák committed
41
#include <kwindowsystem.h>
42
43
44
#include <kaboutdata.h>
#include <klocalizedstring.h>

45
#include <kstartupinfo.h>
46
#include <kxmessages.h>
47

Matthias Ettrich's avatar
Matthias Ettrich committed
48
#include <netwm.h>
49
#include <QX11Info>
Aleix Pol Gonzalez's avatar
Aleix Pol Gonzalez committed
50
51
#include <X11/Xlib.h>
#include <X11/Xutil.h>
Matthias Ettrich's avatar
Matthias Ettrich committed
52
53
54

// some globals

Friedrich W. H. Kossebau's avatar
Friedrich W. H. Kossebau committed
55
static KProcess* proc = nullptr;
David Faure's avatar
David Faure committed
56
57
58
59
static QString exe;
static QString url;
static QString windowtitle;
static QString windowclass;
Matthias Ettrich's avatar
Matthias Ettrich committed
60
static int desktop = 0;
61
62
static bool activate = false;
static bool iconify = false;
63
static bool fullscreen = false;
Friedrich W. H. Kossebau's avatar
Friedrich W. H. Kossebau committed
64
65
static NET::States state = {};
static NET::States mask = {};
Matthias Ettrich's avatar
Matthias Ettrich committed
66
67
68
69
70
static NET::WindowType windowtype = NET::Unknown;

KStart::KStart()
    :QObject()
{
Aleix Pol Gonzalez's avatar
Aleix Pol Gonzalez committed
71
    NETRootInfo i( QX11Info::connection(), NET::Supported );
72
    bool useRule = i.isSupported( NET::WM2KDETemporaryRules );
73
74
75
76
77

    if( useRule )
        sendRule();
    else {
        // connect to window add to get the NEW windows
Luboš Luňák's avatar
Luboš Luňák committed
78
        connect(KWindowSystem::self(), SIGNAL(windowAdded(WId)), SLOT(windowAdded(WId)));
79
    }
80
    // propagate the app startup notification info to the started app
81
82
    // We are not using KApplication, so the env remained set
    KStartupInfoId id = KStartupInfo::currentStartupIdEnv();
83

84
    //finally execute the comand
David Faure's avatar
David Faure committed
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
    if (proc) {
        if( int pid = proc->startDetached() ) {
            KStartupInfoData data;
            data.addPid( pid );
            data.setName( exe );
            data.setBin( exe.mid( exe.lastIndexOf( '/' ) + 1 ));
            KStartupInfo::sendChange( id, data );
        }
        else
            KStartupInfo::sendFinish( id ); // failed to start
    } else {
        QString error;
        QString dbusService;
        int pid;
        if (KToolInvocation::startServiceByDesktopPath(exe, url, &error, &dbusService, &pid) == 0) {
            printf("%s\n", qPrintable(dbusService));
        } else {
102
            qCritical() << error;
David Faure's avatar
David Faure committed
103
        }
104
    }
105

106
  QTimer::singleShot( useRule ? 0 : 120 * 1000, qApp, SLOT( quit()));
107
108
109
110
}

void KStart::sendRule() {
    KXMessages msg;
Laurent Montel's avatar
Laurent Montel committed
111
    QString message;
112
    if( !windowtitle.isEmpty() )
113
        message += "title=" + windowtitle + "\ntitlematch=3\n"; // 3 = regexp match
114
    if( !windowclass.isEmpty() )
115
116
        message += "wmclass=" + windowclass + "\nwmclassmatch=1\n" // 1 = exact match
            + "wmclasscomplete="
117
            // if windowclass contains a space (i.e. 2 words, use whole WM_CLASS)
Thorsten Roeder's avatar
Thorsten Roeder committed
118
            + ( windowclass.contains( ' ' ) ? "true" : "false" ) + '\n';
119
    if( (!windowtitle.isEmpty()) || (!windowclass.isEmpty()) ) {
120
        // always ignore these window types
Laurent Montel's avatar
Laurent Montel committed
121
        message += "types=" + QString().setNum( -1U &
Thorsten Roeder's avatar
Thorsten Roeder committed
122
            ~( NET::TopMenuMask | NET::ToolbarMask | NET::DesktopMask | NET::SplashMask | NET::MenuMask )) + '\n';
123
124
    } else {
        // accept only "normal" windows
Thorsten Roeder's avatar
Thorsten Roeder committed
125
        message += "types=" + QString().setNum( NET::NormalMask | NET::DialogMask ) + '\n';
126
    }
Luboš Luňák's avatar
Luboš Luňák committed
127
    if ( ( desktop > 0 && desktop <= KWindowSystem::numberOfDesktops() )
Luboš Luňák's avatar
Luboš Luňák committed
128
         || desktop == NETWinInfo::OnAllDesktops ) {
Laurent Montel's avatar
Laurent Montel committed
129
	message += "desktop=" + QString().setNum( desktop ) + "\ndesktoprule=3\n";
Luboš Luňák's avatar
Luboš Luňák committed
130
131
    }
    if (activate)
132
        message += "fsplevel=0\nfsplevelrule=2\n";
133
    if (iconify)
134
        message += "minimize=true\nminimizerule=3\n";
135
    if ( windowtype != NET::Unknown ) {
Laurent Montel's avatar
Laurent Montel committed
136
        message += "type=" + QString().setNum( windowtype ) + "\ntyperule=2";
137
138
139
    }
    if ( state ) {
        if( state & NET::KeepAbove )
140
            message += "above=true\naboverule=3\n";
141
        if( state & NET::KeepBelow )
142
            message += "below=true\nbelowrule=3\n";
143
        if( state & NET::SkipTaskbar )
144
            message += "skiptaskbar=true\nskiptaskbarrule=3\n";
145
        if( state & NET::SkipPager )
146
            message += "skippager=true\nskippagerrule=3\n";
147
        if( state & NET::MaxVert )
148
            message += "maximizevert=true\nmaximizevertrule=3\n";
149
        if( state & NET::MaxHoriz )
150
            message += "maximizehoriz=true\nmaximizehorizrule=3\n";
151
        if( state & NET::FullScreen )
152
            message += "fullscreen=true\nfullscreenrule=3\n";
153
154
    }

Aleix Pol Gonzalez's avatar
Aleix Pol Gonzalez committed
155
    msg.broadcastMessage( "_KDE_NET_WM_TEMPORARY_RULES", message, -1 );
156
    qApp->flush();
157
158
}

Aleix Pol Gonzalez's avatar
Aleix Pol Gonzalez committed
159
const NET::WindowTypes SUPPORTED_WINDOW_TYPES_MASK = NET::NormalMask | NET::DesktopMask | NET::DockMask
Luboš Luňák's avatar
Luboš Luňák committed
160
161
162
    | NET::ToolbarMask | NET::MenuMask | NET::DialogMask | NET::OverrideMask | NET::TopMenuMask
    | NET::UtilityMask | NET::SplashMask;

163
void KStart::windowAdded(WId w){
164

165
    KWindowInfo info( w, NET::WMWindowType | NET::WMName );
166

167
    // always ignore these window types
Luboš Luňák's avatar
Luboš Luňák committed
168
169
170
    if( info.windowType( SUPPORTED_WINDOW_TYPES_MASK ) == NET::TopMenu
        || info.windowType( SUPPORTED_WINDOW_TYPES_MASK ) == NET::Toolbar
        || info.windowType( SUPPORTED_WINDOW_TYPES_MASK ) == NET::Desktop )
171
        return;
172

173
    if ( !windowtitle.isEmpty() ) {
Laurent Montel's avatar
Laurent Montel committed
174
175
	QString title = info.name().toLower();
	QRegExp r( windowtitle.toLower());
176
	if ( !r.exactMatch(title) )
Matthias Ettrich's avatar
Matthias Ettrich committed
177
	    return; // no match
178
    }
179
    if ( !windowclass.isEmpty() ) {
180
#ifdef __GNUC__
181
#warning "Porting required"
182
#endif
183
#if 0
184
        XClassHint hint;
185
        if( !XGetClassHint( QX11Info::display(), w, &hint ))
186
            return;
187
188
        Q3CString cls = windowclass.contains( ' ' )
            ? Q3CString( hint.res_name ) + ' ' + hint.res_class : Q3CString( hint.res_class );
Laurent Montel's avatar
Laurent Montel committed
189
        cls = cls.toLower();
190
191
192
193
        XFree( hint.res_name );
	XFree( hint.res_class );
        if( cls != windowclass )
            return;
194
#endif
195
    }
196
    if( windowtitle.isEmpty() && windowclass.isEmpty() ) {
197
        // accept only "normal" windows
Luboš Luňák's avatar
Luboš Luňák committed
198
199
200
        if( info.windowType( SUPPORTED_WINDOW_TYPES_MASK ) != NET::Unknown
            && info.windowType( SUPPORTED_WINDOW_TYPES_MASK ) != NET::Normal
            && info.windowType( SUPPORTED_WINDOW_TYPES_MASK ) != NET::Dialog )
201
            return;
202
    }
Matthias Ettrich's avatar
Matthias Ettrich committed
203
204
205
206
207
    applyStyle( w );
    QApplication::exit();
}


208
//extern Atom qt_wm_state; // defined in qapplication_x11.cpp
Matthias Ettrich's avatar
Matthias Ettrich committed
209
210
static bool wstate_withdrawn( WId winid )
{
Bill Egert's avatar
Bill Egert committed
211
212
    Q_UNUSED(winid);

213
#ifdef __GNUC__
214
#warning "Porting required."
215
#endif
216
217
//Porting info: The Qt4 equivalent for qt_wm_state is qt_x11Data->atoms[QX11Data::WM_STATE]
//which can be accessed via the macro ATOM(WM_STATE). Unfortunately, neither of these seem
Thorsten Roeder's avatar
Thorsten Roeder committed
218
//to be exported out of the Qt environment. This value may have to be acquired from somewhere else.
219
/*
Matthias Ettrich's avatar
Matthias Ettrich committed
220
221
222
223
    Atom type;
    int format;
    unsigned long length, after;
    unsigned char *data;
224
    int r = XGetWindowProperty( QX11Info::display(), winid, qt_wm_state, 0, 2,
Allen Winter's avatar
Allen Winter committed
225
				false, AnyPropertyType, &type, &format,
Matthias Ettrich's avatar
Matthias Ettrich committed
226
				&length, &after, &data );
Allen Winter's avatar
Allen Winter committed
227
    bool withdrawn = true;
Matthias Ettrich's avatar
Matthias Ettrich committed
228
    if ( r == Success && data && format == 32 ) {
Laurent Montel's avatar
Laurent Montel committed
229
	quint32 *wstate = (quint32*)data;
Matthias Ettrich's avatar
Matthias Ettrich committed
230
231
	withdrawn  = (*wstate == WithdrawnState );
	XFree( (char *)data );
232
    }
Matthias Ettrich's avatar
Matthias Ettrich committed
233
    return withdrawn;
234
*/
Allen Winter's avatar
Allen Winter committed
235
	return true;
236
237
}

Matthias Ettrich's avatar
Matthias Ettrich committed
238
239

void KStart::applyStyle(WId w ) {
240

241
    if ( state || iconify || windowtype != NET::Unknown || desktop >= 1 ) {
242

Aleix Pol Gonzalez's avatar
Aleix Pol Gonzalez committed
243
	XWithdrawWindow(QX11Info::display(), w, QX11Info::appScreen());
244
	QApplication::flush();
Matthias Ettrich's avatar
Matthias Ettrich committed
245
246
247

	while ( !wstate_withdrawn(w) )
	    ;
248
    }
249

Aleix Pol Gonzalez's avatar
Aleix Pol Gonzalez committed
250
    NETWinInfo info( QX11Info::connection(), w, QX11Info::appRootWindow(), NET::WMState );
Matthias Ettrich's avatar
Matthias Ettrich committed
251

Luboš Luňák's avatar
Luboš Luňák committed
252
    if ( ( desktop > 0 && desktop <= KWindowSystem::numberOfDesktops() )
253
         || desktop == NETWinInfo::OnAllDesktops )
Matthias Ettrich's avatar
Matthias Ettrich committed
254
	info.setDesktop( desktop );
255

Matthias Ettrich's avatar
Matthias Ettrich committed
256
    if (iconify) {
257
	XWMHints * hints = XGetWMHints(QX11Info::display(), w );
Matthias Ettrich's avatar
Matthias Ettrich committed
258
259
260
	if (hints ) {
	    hints->flags |= StateHint;
	    hints->initial_state = IconicState;
261
	    XSetWMHints( QX11Info::display(), w, hints );
Matthias Ettrich's avatar
Matthias Ettrich committed
262
263
	    XFree(hints);
	}
264
    }
265

Matthias Ettrich's avatar
Matthias Ettrich committed
266
267
    if ( windowtype != NET::Unknown ) {
	info.setWindowType( windowtype );
268
    }
269

Matthias Ettrich's avatar
Matthias Ettrich committed
270
271
    if ( state )
	info.setState( state, mask );
272

273
    if ( fullscreen ) {
274
275
	QRect r = QApplication::desktop()->screenGeometry();
	XMoveResizeWindow( QX11Info::display(), w, r.x(), r.y(), r.width(), r.height() );
276
277
    }

278

279
    XSync(QX11Info::display(), False);
280

281
282
    XMapWindow(QX11Info::display(), w );
    XSync(QX11Info::display(), False);
283

284
    if (activate)
Luboš Luňák's avatar
Luboš Luňák committed
285
      KWindowSystem::forceActiveWindow( w );
286

287
    QApplication::flush();
288
289
290
291
}

int main( int argc, char *argv[] )
{
292
  QApplication app(argc, argv);
293
  KLocalizedString::setApplicationDomain( "kstart5" );
294

295
296
  KAboutData aboutData(QStringLiteral("kstart"), i18n("KStart"), PROJECT_VERSION,
      i18n(""
297
       "Utility to launch applications with special window properties \n"
298
       "such as iconified, maximized, a certain virtual desktop, a special decoration\n"
299
       "and so on." ),
300
301
      KAboutLicense::GPL,
      i18n("(C) 1997-2000 Matthias Ettrich (ettrich@kde.org)"));
302

303
304
305
  aboutData.addAuthor( i18n("Matthias Ettrich"), QString(), "ettrich@kde.org" );
  aboutData.addAuthor( i18n("David Faure"), QString(), "faure@kde.org" );
  aboutData.addAuthor( i18n("Richard J. Moore"), QString(), "rich@kde.org" );
306

307
308
309
310
311
  QCommandLineParser parser;
  KAboutData::setApplicationData(aboutData);
  parser.addVersionOption();
  parser.addHelpOption();
  aboutData.setupCommandLine(&parser);
312
313


314
315
316
  parser.addOption(QCommandLineOption(QStringList() << QLatin1String("!+command"), i18n("Command to execute")));
  parser.addOption(QCommandLineOption(QStringList() << QLatin1String("service"), i18n("Alternative to <command>: desktop file to start. D-Bus service will be printed to stdout"), QLatin1String("desktopfile")));
  parser.addOption(QCommandLineOption(QStringList() << QLatin1String("url"), i18n("Optional URL to pass <desktopfile>, when using --service"), QLatin1String("url")));
317
  // "!" means: all options after command are treated as arguments to the command
318
  parser.addOption(QCommandLineOption(QStringList() << QLatin1String("window"), i18n("A regular expression matching the window title"), QLatin1String("regexp")));
319
  parser.addOption(QCommandLineOption(QStringList() << QLatin1String("windowclass"),
320
             i18n("A string matching the window class (WM_CLASS property)\n"
321
322
323
324
325
                  "The window class can be found out by running\n"
                  "'xprop | grep WM_CLASS' and clicking on a window\n"
                  "(use either both parts separated by a space or only the right part).\n"
                  "NOTE: If you specify neither window title nor window class,\n"
                  "then the very first window to appear will be taken;\n"
326
                  "omitting both options is NOT recommended."), QLatin1String("class")));
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
  parser.addOption(QCommandLineOption(QStringList() << QLatin1String("desktop"), i18n("Desktop on which to make the window appear"), QLatin1String("number")));
  parser.addOption(QCommandLineOption(QStringList() << QLatin1String("currentdesktop"), i18n("Make the window appear on the desktop that was active\nwhen starting the application")));
  parser.addOption(QCommandLineOption(QStringList() << QLatin1String("alldesktops"), i18n("Make the window appear on all desktops")));
  parser.addOption(QCommandLineOption(QStringList() << QLatin1String("iconify"), i18n("Iconify the window")));
  parser.addOption(QCommandLineOption(QStringList() << QLatin1String("maximize"), i18n("Maximize the window")));
  parser.addOption(QCommandLineOption(QStringList() << QLatin1String("maximize-vertically"), i18n("Maximize the window vertically")));
  parser.addOption(QCommandLineOption(QStringList() << QLatin1String("maximize-horizontally"), i18n("Maximize the window horizontally")));
  parser.addOption(QCommandLineOption(QStringList() << QLatin1String("fullscreen"), i18n("Show window fullscreen")));
  parser.addOption(QCommandLineOption(QStringList() << QLatin1String("type"), i18n("The window type: Normal, Desktop, Dock, Toolbar, \nMenu, Dialog, TopMenu or Override"), QLatin1String("type")));
  parser.addOption(QCommandLineOption(QStringList() << QLatin1String("activate"),
                    i18n("Jump to the window even if it is started on a \n"
                         "different virtual desktop")));
  parser.addOption(QCommandLineOption(QStringList() << QLatin1String("ontop") << QLatin1String("keepabove"), i18n("Try to keep the window above other windows")));
  parser.addOption(QCommandLineOption(QStringList() << QLatin1String("onbottom") << QLatin1String("keepbelow"), i18n("Try to keep the window below other windows")));
  parser.addOption(QCommandLineOption(QStringList() << QLatin1String("skiptaskbar"), i18n("The window does not get an entry in the taskbar")));
  parser.addOption(QCommandLineOption(QStringList() << QLatin1String("skippager"), i18n("The window does not get an entry on the pager")));

  parser.process(app);
  aboutData.processCommandLine(&parser);

  if (parser.isSet("service")) {
      exe = parser.value("service");
      url = parser.value("url");
David Faure's avatar
David Faure committed
350
  } else {
351
352
      if ( parser.positionalArguments().count() == 0 )
          qCritical() << i18n("No command specified");
David Faure's avatar
David Faure committed
353

354
      exe = parser.positionalArguments().at(0);
David Faure's avatar
David Faure committed
355
      proc = new KProcess;
356
357
      for(int i=0; i < parser.positionalArguments().count(); i++)
          (*proc) << parser.positionalArguments().at(i);
David Faure's avatar
David Faure committed
358
  }
359

360
361
  desktop = parser.value( "desktop" ).toInt();
  if ( parser.isSet ( "alldesktops")  )
Matthias Ettrich's avatar
Matthias Ettrich committed
362
      desktop = NETWinInfo::OnAllDesktops;
363
  if ( parser.isSet ( "currentdesktop")  )
Luboš Luňák's avatar
Luboš Luňák committed
364
      desktop = KWindowSystem::currentDesktop();
365

366
367
  windowtitle = parser.value( "window" );
  windowclass = parser.value( "windowclass" );
368
  if( !windowclass.isEmpty() )
Laurent Montel's avatar
Laurent Montel committed
369
      windowclass = windowclass.toLower();
370

371
  if( windowtitle.isEmpty() && windowclass.isEmpty())
372
      qWarning() << "Omitting both --window and --windowclass arguments is not recommended" ;
Matthias Ettrich's avatar
Matthias Ettrich committed
373

374
  QString s = parser.value( "type" );
Matthias Ettrich's avatar
Matthias Ettrich committed
375
  if ( !s.isEmpty() ) {
Laurent Montel's avatar
Laurent Montel committed
376
      s = s.toLower();
Matthias Ettrich's avatar
Matthias Ettrich committed
377
378
379
380
      if ( s == "desktop" )
	  windowtype = NET::Desktop;
      else if ( s == "dock" )
	  windowtype = NET::Dock;
Luboš Luňák's avatar
Luboš Luňák committed
381
382
      else if ( s == "toolbar" )
	  windowtype = NET::Toolbar;
Matthias Ettrich's avatar
Matthias Ettrich committed
383
384
385
386
387
388
      else if ( s == "menu" )
	  windowtype = NET::Menu;
      else if ( s == "dialog" )
	  windowtype = NET::Dialog;
      else if ( s == "override" )
	  windowtype = NET::Override;
389
390
      else if ( s == "topmenu" )
	  windowtype = NET::TopMenu;
391
      else
Matthias Ettrich's avatar
Matthias Ettrich committed
392
393
	  windowtype = NET::Normal;
  }
394

395
  if ( parser.isSet( "keepabove" ) ) {
Luboš Luňák's avatar
Luboš Luňák committed
396
397
      state |= NET::KeepAbove;
      mask |= NET::KeepAbove;
398
  } else if ( parser.isSet( "keepbelow" ) ) {
Luboš Luňák's avatar
Luboš Luňák committed
399
400
      state |= NET::KeepBelow;
      mask |= NET::KeepBelow;
Matthias Ettrich's avatar
Matthias Ettrich committed
401
  }
402

403
  if ( parser.isSet( "skiptaskbar" ) ) {
Matthias Ettrich's avatar
Matthias Ettrich committed
404
405
406
      state |= NET::SkipTaskbar;
      mask |= NET::SkipTaskbar;
  }
407

408
  if ( parser.isSet( "skippager" ) ) {
409
410
411
412
      state |= NET::SkipPager;
      mask |= NET::SkipPager;
  }

413
  activate = parser.isSet("activate");
414

415
  if ( parser.isSet("maximize") ) {
Matthias Ettrich's avatar
Matthias Ettrich committed
416
417
418
      state |= NET::Max;
      mask |= NET::Max;
  }
419
  if ( parser.isSet("maximize-vertically") ) {
420
421
422
      state |= NET::MaxVert;
      mask |= NET::MaxVert;
  }
423
  if ( parser.isSet("maximize-horizontally") ) {
424
425
426
      state |= NET::MaxHoriz;
      mask |= NET::MaxHoriz;
  }
427

428
429
  iconify = parser.isSet("iconify");
  if ( parser.isSet("fullscreen") ) {
Aleix Pol Gonzalez's avatar
Aleix Pol Gonzalez committed
430
      NETRootInfo i( QX11Info::connection(), NET::Supported );
Luboš Luňák's avatar
Luboš Luňák committed
431
432
433
434
435
436
437
438
      if( i.isSupported( NET::FullScreen )) {
          state |= NET::FullScreen;
          mask |= NET::FullScreen;
      } else {
          windowtype = NET::Override;
          fullscreen = true;
      }
  }
439

Aleix Pol Gonzalez's avatar
Aleix Pol Gonzalez committed
440
  fcntl(XConnectionNumber(QX11Info::display()), F_SETFD, 1);
441

Matthias Ettrich's avatar
Matthias Ettrich committed
442
  KStart start;
443

444
  return app.exec();
445
}