delay a few things on startup, such as setting the visibility mode, which ensures...
[personal-kdebase.git] / runtime / kstart / kstart.cpp
blob17c8c7cdd9f12d743f5638da4ca2e7da0fbe6cd5
1 /*
2 * kstart.C. Part of the KDE project.
4 * Copyright (C) 1997-2000 Matthias Ettrich <ettrich@kde.org>
6 * First port to NETWM by David Faure <faure@kde.org>
7 * Send to system tray by Richard Moore <rich@kde.org>
9 * This program is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU General Public License as
11 * published by the Free Software Foundation; either version 2 of
12 * the License or (at your option) version 3 or any later version
13 * accepted by the membership of KDE e.V. (or its successor approved
14 * by the membership of KDE e.V.), which shall act as a proxy
15 * defined in Section 14 of version 3 of the license.
17 * This program is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 * GNU General Public License for more details.
22 * You should have received a copy of the GNU General Public License
23 * along with this program. If not, see <http://www.gnu.org/licenses/>.
26 #include <ktoolinvocation.h>
27 #include "kstart.moc"
29 #include <fcntl.h>
30 #include <stdlib.h>
32 #include <QRegExp>
33 #include <QTimer>
34 #include <QDesktopWidget>
35 #include <QtGui/QApplication>
37 #include <kdebug.h>
38 #include <kprocess.h>
39 #include <klocale.h>
40 #include <kcomponentdata.h>
41 #include <kwindowsystem.h>
42 #include <kaboutdata.h>
43 #include <kcmdlineargs.h>
44 #include <kstartupinfo.h>
45 #include <kxmessages.h>
46 #include <kdeversion.h>
48 #include <netwm.h>
49 #include <QX11Info>
52 // some globals
54 static KProcess* proc = 0;
55 static QString exe;
56 static QString url;
57 static QString windowtitle;
58 static QString windowclass;
59 static int desktop = 0;
60 static bool activate = false;
61 static bool iconify = false;
62 static bool toSysTray = false;
63 static bool fullscreen = false;
64 static unsigned long state = 0;
65 static unsigned long mask = 0;
66 static NET::WindowType windowtype = NET::Unknown;
68 KStart::KStart()
69 :QObject()
71 NETRootInfo i( QX11Info::display(), NET::Supported );
72 bool useRule = !toSysTray && i.isSupported( NET::WM2KDETemporaryRules );
74 if( useRule )
75 sendRule();
76 else {
77 // connect to window add to get the NEW windows
78 connect(KWindowSystem::self(), SIGNAL(windowAdded(WId)), SLOT(windowAdded(WId)));
79 if (!windowtitle.isEmpty())
80 KWindowSystem::doNotManage( windowtitle );
82 // propagate the app startup notification info to the started app
83 // We are not using KApplication, so the env remained set
84 KStartupInfoId id = KStartupInfo::currentStartupIdEnv();
86 //finally execute the comand
87 if (proc) {
88 if( int pid = proc->startDetached() ) {
89 KStartupInfoData data;
90 data.addPid( pid );
91 data.setName( exe );
92 data.setBin( exe.mid( exe.lastIndexOf( '/' ) + 1 ));
93 KStartupInfo::sendChange( id, data );
95 else
96 KStartupInfo::sendFinish( id ); // failed to start
97 } else {
98 QString error;
99 QString dbusService;
100 int pid;
101 if (KToolInvocation::startServiceByDesktopPath(exe, url, &error, &dbusService, &pid) == 0) {
102 printf("%s\n", qPrintable(dbusService));
103 } else {
104 kError() << error;
108 QTimer::singleShot( useRule ? 0 : 120 * 1000, qApp, SLOT( quit()));
111 void KStart::sendRule() {
112 KXMessages msg;
113 QString message;
114 if( !windowtitle.isEmpty() )
115 message += "title=" + windowtitle + "\ntitlematch=3\n"; // 3 = regexp match
116 if( !windowclass.isEmpty() )
117 message += "wmclass=" + windowclass + "\nwmclassmatch=1\n" // 1 = exact match
118 + "wmclasscomplete="
119 // if windowclass contains a space (i.e. 2 words, use whole WM_CLASS)
120 + ( windowclass.contains( ' ' ) ? "true" : "false" ) + '\n';
121 if( (!windowtitle.isEmpty()) || (!windowclass.isEmpty()) ) {
122 // always ignore these window types
123 message += "types=" + QString().setNum( -1U &
124 ~( NET::TopMenuMask | NET::ToolbarMask | NET::DesktopMask | NET::SplashMask | NET::MenuMask )) + '\n';
125 } else {
126 // accept only "normal" windows
127 message += "types=" + QString().setNum( NET::NormalMask | NET::DialogMask ) + '\n';
129 if ( ( desktop > 0 && desktop <= KWindowSystem::numberOfDesktops() )
130 || desktop == NETWinInfo::OnAllDesktops ) {
131 message += "desktop=" + QString().setNum( desktop ) + "\ndesktoprule=3\n";
133 if (activate)
134 message += "fsplevel=0\nfsplevelrule=2\n";
135 if (iconify)
136 message += "minimize=true\nminimizerule=3\n";
137 if ( windowtype != NET::Unknown ) {
138 message += "type=" + QString().setNum( windowtype ) + "\ntyperule=2";
140 if ( state ) {
141 if( state & NET::KeepAbove )
142 message += "above=true\naboverule=3\n";
143 if( state & NET::KeepBelow )
144 message += "below=true\nbelowrule=3\n";
145 if( state & NET::SkipTaskbar )
146 message += "skiptaskbar=true\nskiptaskbarrule=3\n";
147 if( state & NET::SkipPager )
148 message += "skippager=true\nskippagerrule=3\n";
149 if( state & NET::MaxVert )
150 message += "maximizevert=true\nmaximizevertrule=3\n";
151 if( state & NET::MaxHoriz )
152 message += "maximizehoriz=true\nmaximizehorizrule=3\n";
153 if( state & NET::FullScreen )
154 message += "fullscreen=true\nfullscreenrule=3\n";
157 msg.broadcastMessage( "_KDE_NET_WM_TEMPORARY_RULES", message, -1, false );
158 qApp->flush();
161 const int SUPPORTED_WINDOW_TYPES_MASK = NET::NormalMask | NET::DesktopMask | NET::DockMask
162 | NET::ToolbarMask | NET::MenuMask | NET::DialogMask | NET::OverrideMask | NET::TopMenuMask
163 | NET::UtilityMask | NET::SplashMask;
165 void KStart::windowAdded(WId w){
167 KWindowInfo info = KWindowSystem::windowInfo( w, NET::WMWindowType | NET::WMName );
169 // always ignore these window types
170 if( info.windowType( SUPPORTED_WINDOW_TYPES_MASK ) == NET::TopMenu
171 || info.windowType( SUPPORTED_WINDOW_TYPES_MASK ) == NET::Toolbar
172 || info.windowType( SUPPORTED_WINDOW_TYPES_MASK ) == NET::Desktop )
173 return;
175 if ( !windowtitle.isEmpty() ) {
176 QString title = info.name().toLower();
177 QRegExp r( windowtitle.toLower());
178 if ( !r.exactMatch(title) )
179 return; // no match
181 if ( !windowclass.isEmpty() ) {
182 #ifdef __GNUC__
183 #warning "Porting required"
184 #endif
185 #if 0
186 XClassHint hint;
187 if( !XGetClassHint( QX11Info::display(), w, &hint ))
188 return;
189 Q3CString cls = windowclass.contains( ' ' )
190 ? Q3CString( hint.res_name ) + ' ' + hint.res_class : Q3CString( hint.res_class );
191 cls = cls.toLower();
192 XFree( hint.res_name );
193 XFree( hint.res_class );
194 if( cls != windowclass )
195 return;
196 #endif
198 if( windowtitle.isEmpty() && windowclass.isEmpty() ) {
199 // accept only "normal" windows
200 if( info.windowType( SUPPORTED_WINDOW_TYPES_MASK ) != NET::Unknown
201 && info.windowType( SUPPORTED_WINDOW_TYPES_MASK ) != NET::Normal
202 && info.windowType( SUPPORTED_WINDOW_TYPES_MASK ) != NET::Dialog )
203 return;
205 applyStyle( w );
206 QApplication::exit();
210 //extern Atom qt_wm_state; // defined in qapplication_x11.cpp
211 static bool wstate_withdrawn( WId winid )
213 Q_UNUSED(winid);
215 #ifdef __GNUC__
216 #warning "Porting required."
217 #endif
218 //Porting info: The Qt4 equivalent for qt_wm_state is qt_x11Data->atoms[QX11Data::WM_STATE]
219 //which can be accessed via the macro ATOM(WM_STATE). Unfortunately, neither of these seem
220 //to be exported out of the Qt environment. This value may have to be acquired from somewhere else.
222 Atom type;
223 int format;
224 unsigned long length, after;
225 unsigned char *data;
226 int r = XGetWindowProperty( QX11Info::display(), winid, qt_wm_state, 0, 2,
227 false, AnyPropertyType, &type, &format,
228 &length, &after, &data );
229 bool withdrawn = true;
230 if ( r == Success && data && format == 32 ) {
231 quint32 *wstate = (quint32*)data;
232 withdrawn = (*wstate == WithdrawnState );
233 XFree( (char *)data );
235 return withdrawn;
237 return true;
241 void KStart::applyStyle(WId w ) {
243 if ( toSysTray || state || iconify || windowtype != NET::Unknown || desktop >= 1 ) {
245 QX11Info info;
246 XWithdrawWindow(QX11Info::display(), w, info.screen());
247 QApplication::flush();
249 while ( !wstate_withdrawn(w) )
253 NETWinInfo info( QX11Info::display(), w, QX11Info::appRootWindow(), NET::WMState );
255 if ( ( desktop > 0 && desktop <= KWindowSystem::numberOfDesktops() )
256 || desktop == NETWinInfo::OnAllDesktops )
257 info.setDesktop( desktop );
259 if (iconify) {
260 XWMHints * hints = XGetWMHints(QX11Info::display(), w );
261 if (hints ) {
262 hints->flags |= StateHint;
263 hints->initial_state = IconicState;
264 XSetWMHints( QX11Info::display(), w, hints );
265 XFree(hints);
269 if ( windowtype != NET::Unknown ) {
270 info.setWindowType( windowtype );
273 if ( state )
274 info.setState( state, mask );
276 if ( toSysTray ) {
277 QApplication::beep();
278 // KWindowSystem::setSystemTrayWindowFor( w, QX11Info::appRootWindow() );
281 if ( fullscreen ) {
282 QRect r = QApplication::desktop()->screenGeometry();
283 XMoveResizeWindow( QX11Info::display(), w, r.x(), r.y(), r.width(), r.height() );
287 XSync(QX11Info::display(), False);
289 XMapWindow(QX11Info::display(), w );
290 XSync(QX11Info::display(), False);
292 if (activate)
293 KWindowSystem::forceActiveWindow( w );
295 QApplication::flush();
298 int main( int argc, char *argv[] )
300 KAboutData aboutData( "kstart", 0, ki18n("KStart"), KDE_VERSION_STRING,
301 ki18n(""
302 "Utility to launch applications with special window properties \n"
303 "such as iconified, maximized, a certain virtual desktop, a special decoration\n"
304 "and so on." ),
305 KAboutData::License_GPL,
306 ki18n("(C) 1997-2000 Matthias Ettrich (ettrich@kde.org)") );
308 aboutData.addAuthor( ki18n("Matthias Ettrich"), KLocalizedString(), "ettrich@kde.org" );
309 aboutData.addAuthor( ki18n("David Faure"), KLocalizedString(), "faure@kde.org" );
310 aboutData.addAuthor( ki18n("Richard J. Moore"), KLocalizedString(), "rich@kde.org" );
312 KCmdLineArgs::init( argc, argv, &aboutData );
315 KCmdLineOptions options;
317 options.add("!+command", ki18n("Command to execute"));
318 options.add("service <desktopfile>", ki18n("Alternative to <command>: desktop file to start. DBUS service will be printed to stdout"));
319 options.add("url <url>", ki18n("Optional URL to pass <desktopfile>, when using --service"));
320 // "!" means: all options after command are treated as arguments to the command
321 options.add("window <regexp>", ki18n("A regular expression matching the window title"));
322 options.add("windowclass <class>", ki18n("A string matching the window class (WM_CLASS property)\n"
323 "The window class can be found out by running\n"
324 "'xprop | grep WM_CLASS' and clicking on a window\n"
325 "(use either both parts separated by a space or only the right part).\n"
326 "NOTE: If you specify neither window title nor window class,\n"
327 "then the very first window to appear will be taken;\n"
328 "omitting both options is NOT recommended."));
329 options.add("desktop <number>", ki18n("Desktop on which to make the window appear"));
330 options.add("currentdesktop", ki18n("Make the window appear on the desktop that was active\nwhen starting the application"));
331 options.add("alldesktops", ki18n("Make the window appear on all desktops"));
332 options.add("iconify", ki18n("Iconify the window"));
333 options.add("maximize", ki18n("Maximize the window"));
334 options.add("maximize-vertically", ki18n("Maximize the window vertically"));
335 options.add("maximize-horizontally", ki18n("Maximize the window horizontally"));
336 options.add("fullscreen", ki18n("Show window fullscreen"));
337 options.add("type <type>", ki18n("The window type: Normal, Desktop, Dock, Toolbar, \nMenu, Dialog, TopMenu or Override"));
338 options.add("activate", ki18n("Jump to the window even if it is started on a \n"
339 "different virtual desktop"));
340 options.add("ontop");
341 options.add("keepabove", ki18n("Try to keep the window above other windows"));
342 options.add("onbottom");
343 options.add("keepbelow", ki18n("Try to keep the window below other windows"));
344 options.add("skiptaskbar", ki18n("The window does not get an entry in the taskbar"));
345 options.add("skippager", ki18n("The window does not get an entry on the pager"));
346 options.add("tosystray", ki18n("The window is sent to the system tray in Kicker"));
347 KCmdLineArgs::addCmdLineOptions( options ); // Add our own options.
349 KComponentData componentData( &aboutData );
350 QApplication app( KCmdLineArgs::qtArgc(), KCmdLineArgs::qtArgv() );
352 KCmdLineArgs *args = KCmdLineArgs::parsedArgs();
354 if (args->isSet("service")) {
355 exe = args->getOption("service");
356 url = args->getOption("url");
357 } else {
358 if ( args->count() == 0 )
359 KCmdLineArgs::usageError(i18n("No command specified"));
361 exe = args->arg(0);
362 proc = new KProcess;
363 for(int i=0; i < args->count(); i++)
364 (*proc) << args->arg(i);
367 desktop = args->getOption( "desktop" ).toInt();
368 if ( args->isSet ( "alldesktops") )
369 desktop = NETWinInfo::OnAllDesktops;
370 if ( args->isSet ( "currentdesktop") )
371 desktop = KWindowSystem::currentDesktop();
373 windowtitle = args->getOption( "window" );
374 windowclass = args->getOption( "windowclass" );
375 if( !windowclass.isEmpty() )
376 windowclass = windowclass.toLower();
378 if( windowtitle.isEmpty() && windowclass.isEmpty())
379 kWarning() << "Omitting both --window and --windowclass arguments is not recommended" ;
381 QString s = args->getOption( "type" );
382 if ( !s.isEmpty() ) {
383 s = s.toLower();
384 if ( s == "desktop" )
385 windowtype = NET::Desktop;
386 else if ( s == "dock" )
387 windowtype = NET::Dock;
388 else if ( s == "toolbar" )
389 windowtype = NET::Toolbar;
390 else if ( s == "menu" )
391 windowtype = NET::Menu;
392 else if ( s == "dialog" )
393 windowtype = NET::Dialog;
394 else if ( s == "override" )
395 windowtype = NET::Override;
396 else if ( s == "topmenu" )
397 windowtype = NET::TopMenu;
398 else
399 windowtype = NET::Normal;
402 if ( args->isSet( "keepabove" ) ) {
403 state |= NET::KeepAbove;
404 mask |= NET::KeepAbove;
405 } else if ( args->isSet( "keepbelow" ) ) {
406 state |= NET::KeepBelow;
407 mask |= NET::KeepBelow;
410 if ( args->isSet( "skiptaskbar" ) ) {
411 state |= NET::SkipTaskbar;
412 mask |= NET::SkipTaskbar;
415 if ( args->isSet( "skippager" ) ) {
416 state |= NET::SkipPager;
417 mask |= NET::SkipPager;
420 activate = args->isSet("activate");
422 if ( args->isSet("maximize") ) {
423 state |= NET::Max;
424 mask |= NET::Max;
426 if ( args->isSet("maximize-vertically") ) {
427 state |= NET::MaxVert;
428 mask |= NET::MaxVert;
430 if ( args->isSet("maximize-horizontally") ) {
431 state |= NET::MaxHoriz;
432 mask |= NET::MaxHoriz;
435 iconify = args->isSet("iconify");
436 toSysTray = args->isSet("tosystray");
437 if ( args->isSet("fullscreen") ) {
438 NETRootInfo i( QX11Info::display(), NET::Supported );
439 if( i.isSupported( NET::FullScreen )) {
440 state |= NET::FullScreen;
441 mask |= NET::FullScreen;
442 } else {
443 windowtype = NET::Override;
444 fullscreen = true;
448 fcntl(ConnectionNumber(QX11Info::display()), F_SETFD, 1);
449 args->clear();
451 KStart start;
453 return app.exec();