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>
34 #include <QDesktopWidget>
35 #include <QtGui/QApplication>
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>
54 static KProcess
* proc
= 0;
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
;
71 NETRootInfo
i( QX11Info::display(), NET::Supported
);
72 bool useRule
= !toSysTray
&& i
.isSupported( NET::WM2KDETemporaryRules
);
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
88 if( int pid
= proc
->startDetached() ) {
89 KStartupInfoData data
;
92 data
.setBin( exe
.mid( exe
.lastIndexOf( '/' ) + 1 ));
93 KStartupInfo::sendChange( id
, data
);
96 KStartupInfo::sendFinish( id
); // failed to start
101 if (KToolInvocation::startServiceByDesktopPath(exe
, url
, &error
, &dbusService
, &pid
) == 0) {
102 printf("%s\n", qPrintable(dbusService
));
108 QTimer::singleShot( useRule
? 0 : 120 * 1000, qApp
, SLOT( quit()));
111 void KStart::sendRule() {
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
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';
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";
134 message
+= "fsplevel=0\nfsplevelrule=2\n";
136 message
+= "minimize=true\nminimizerule=3\n";
137 if ( windowtype
!= NET::Unknown
) {
138 message
+= "type=" + QString().setNum( windowtype
) + "\ntyperule=2";
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 );
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
)
175 if ( !windowtitle
.isEmpty() ) {
176 QString title
= info
.name().toLower();
177 QRegExp
r( windowtitle
.toLower());
178 if ( !r
.exactMatch(title
) )
181 if ( !windowclass
.isEmpty() ) {
183 #warning "Porting required"
187 if( !XGetClassHint( QX11Info::display(), w
, &hint
))
189 Q3CString cls
= windowclass
.contains( ' ' )
190 ? Q3CString( hint
.res_name
) + ' ' + hint
.res_class
: Q3CString( hint
.res_class
);
192 XFree( hint
.res_name
);
193 XFree( hint
.res_class
);
194 if( cls
!= windowclass
)
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
)
206 QApplication::exit();
210 //extern Atom qt_wm_state; // defined in qapplication_x11.cpp
211 static bool wstate_withdrawn( WId winid
)
216 #warning "Porting required."
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.
224 unsigned long length, after;
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 );
241 void KStart::applyStyle(WId w
) {
243 if ( toSysTray
|| state
|| iconify
|| windowtype
!= NET::Unknown
|| desktop
>= 1 ) {
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
);
260 XWMHints
* hints
= XGetWMHints(QX11Info::display(), w
);
262 hints
->flags
|= StateHint
;
263 hints
->initial_state
= IconicState
;
264 XSetWMHints( QX11Info::display(), w
, hints
);
269 if ( windowtype
!= NET::Unknown
) {
270 info
.setWindowType( windowtype
);
274 info
.setState( state
, mask
);
277 QApplication::beep();
278 // KWindowSystem::setSystemTrayWindowFor( w, QX11Info::appRootWindow() );
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
);
293 KWindowSystem::forceActiveWindow( w
);
295 QApplication::flush();
298 int main( int argc
, char *argv
[] )
300 KAboutData
aboutData( "kstart", 0, ki18n("KStart"), KDE_VERSION_STRING
,
302 "Utility to launch applications with special window properties \n"
303 "such as iconified, maximized, a certain virtual desktop, a special decoration\n"
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");
358 if ( args
->count() == 0 )
359 KCmdLineArgs::usageError(i18n("No command specified"));
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() ) {
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
;
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") ) {
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
;
443 windowtype
= NET::Override
;
448 fcntl(ConnectionNumber(QX11Info::display()), F_SETFD
, 1);