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/>.
33 #include <QDesktopWidget>
34 #include <QtGui/QApplication>
39 #include <kcomponentdata.h>
40 #include <kwindowsystem.h>
41 #include <kaboutdata.h>
42 #include <kcmdlineargs.h>
43 #include <kstartupinfo.h>
44 #include <kxmessages.h>
45 #include <kdeversion.h>
53 static KProcess
* proc
= 0;
54 static QString exe
= 0;
55 static QString windowtitle
= 0;
56 static QString windowclass
= 0;
57 static int desktop
= 0;
58 static bool activate
= false;
59 static bool iconify
= false;
60 static bool toSysTray
= false;
61 static bool fullscreen
= false;
62 static unsigned long state
= 0;
63 static unsigned long mask
= 0;
64 static NET::WindowType windowtype
= NET::Unknown
;
69 NETRootInfo
i( QX11Info::display(), NET::Supported
);
70 bool useRule
= !toSysTray
&& i
.isSupported( NET::WM2KDETemporaryRules
);
75 // connect to window add to get the NEW windows
76 connect(KWindowSystem::self(), SIGNAL(windowAdded(WId
)), SLOT(windowAdded(WId
)));
77 if (!windowtitle
.isEmpty())
78 KWindowSystem::doNotManage( windowtitle
);
80 // propagate the app startup notification info to the started app
81 // We are not using KApplication, so the env remained set
82 KStartupInfoId id
= KStartupInfo::currentStartupIdEnv();
84 //finally execute the comand
85 if( int pid
= proc
->startDetached() ) {
86 KStartupInfoData data
;
89 data
.setBin( exe
.mid( exe
.lastIndexOf( '/' ) + 1 ));
90 KStartupInfo::sendChange( id
, data
);
93 KStartupInfo::sendFinish( id
); // failed to start
95 QTimer::singleShot( useRule
? 0 : 120 * 1000, qApp
, SLOT( quit()));
98 void KStart::sendRule() {
101 if( !windowtitle
.isEmpty() )
102 message
+= "title=" + windowtitle
+ "\ntitlematch=3\n"; // 3 = regexp match
103 if( !windowclass
.isEmpty() )
104 message
+= "wmclass=" + windowclass
+ "\nwmclassmatch=1\n" // 1 = exact match
106 // if windowclass contains a space (i.e. 2 words, use whole WM_CLASS)
107 + ( windowclass
.contains( ' ' ) ? "true" : "false" ) + '\n';
108 if( (!windowtitle
.isEmpty()) || (!windowclass
.isEmpty()) ) {
109 // always ignore these window types
110 message
+= "types=" + QString().setNum( -1U &
111 ~( NET::TopMenuMask
| NET::ToolbarMask
| NET::DesktopMask
| NET::SplashMask
| NET::MenuMask
)) + '\n';
113 // accept only "normal" windows
114 message
+= "types=" + QString().setNum( NET::NormalMask
| NET::DialogMask
) + '\n';
116 if ( ( desktop
> 0 && desktop
<= KWindowSystem::numberOfDesktops() )
117 || desktop
== NETWinInfo::OnAllDesktops
) {
118 message
+= "desktop=" + QString().setNum( desktop
) + "\ndesktoprule=3\n";
121 message
+= "fsplevel=0\nfsplevelrule=2\n";
123 message
+= "minimize=true\nminimizerule=3\n";
124 if ( windowtype
!= NET::Unknown
) {
125 message
+= "type=" + QString().setNum( windowtype
) + "\ntyperule=2";
128 if( state
& NET::KeepAbove
)
129 message
+= "above=true\naboverule=3\n";
130 if( state
& NET::KeepBelow
)
131 message
+= "below=true\nbelowrule=3\n";
132 if( state
& NET::SkipTaskbar
)
133 message
+= "skiptaskbar=true\nskiptaskbarrule=3\n";
134 if( state
& NET::SkipPager
)
135 message
+= "skippager=true\nskippagerrule=3\n";
136 if( state
& NET::MaxVert
)
137 message
+= "maximizevert=true\nmaximizevertrule=3\n";
138 if( state
& NET::MaxHoriz
)
139 message
+= "maximizehoriz=true\nmaximizehorizrule=3\n";
140 if( state
& NET::FullScreen
)
141 message
+= "fullscreen=true\nfullscreenrule=3\n";
144 msg
.broadcastMessage( "_KDE_NET_WM_TEMPORARY_RULES", message
, -1, false );
148 const int SUPPORTED_WINDOW_TYPES_MASK
= NET::NormalMask
| NET::DesktopMask
| NET::DockMask
149 | NET::ToolbarMask
| NET::MenuMask
| NET::DialogMask
| NET::OverrideMask
| NET::TopMenuMask
150 | NET::UtilityMask
| NET::SplashMask
;
152 void KStart::windowAdded(WId w
){
154 KWindowInfo info
= KWindowSystem::windowInfo( w
, NET::WMWindowType
| NET::WMName
);
156 // always ignore these window types
157 if( info
.windowType( SUPPORTED_WINDOW_TYPES_MASK
) == NET::TopMenu
158 || info
.windowType( SUPPORTED_WINDOW_TYPES_MASK
) == NET::Toolbar
159 || info
.windowType( SUPPORTED_WINDOW_TYPES_MASK
) == NET::Desktop
)
162 if ( !windowtitle
.isEmpty() ) {
163 QString title
= info
.name().toLower();
164 QRegExp
r( windowtitle
.toLower());
165 if ( !r
.exactMatch(title
) )
168 if ( !windowclass
.isEmpty() ) {
170 #warning "Porting required"
174 if( !XGetClassHint( QX11Info::display(), w
, &hint
))
176 Q3CString cls
= windowclass
.contains( ' ' )
177 ? Q3CString( hint
.res_name
) + ' ' + hint
.res_class
: Q3CString( hint
.res_class
);
179 XFree( hint
.res_name
);
180 XFree( hint
.res_class
);
181 if( cls
!= windowclass
)
185 if( windowtitle
.isEmpty() && windowclass
.isEmpty() ) {
186 // accept only "normal" windows
187 if( info
.windowType( SUPPORTED_WINDOW_TYPES_MASK
) != NET::Unknown
188 && info
.windowType( SUPPORTED_WINDOW_TYPES_MASK
) != NET::Normal
189 && info
.windowType( SUPPORTED_WINDOW_TYPES_MASK
) != NET::Dialog
)
193 QApplication::exit();
197 //extern Atom qt_wm_state; // defined in qapplication_x11.cpp
198 static bool wstate_withdrawn( WId winid
)
203 #warning "Porting required."
205 //Porting info: The Qt4 equivalent for qt_wm_state is qt_x11Data->atoms[QX11Data::WM_STATE]
206 //which can be accessed via the macro ATOM(WM_STATE). Unfortunately, neither of these seem
207 //to be exported out of the Qt environment. This value may have to be acquired from somewhere else.
211 unsigned long length, after;
213 int r = XGetWindowProperty( QX11Info::display(), winid, qt_wm_state, 0, 2,
214 false, AnyPropertyType, &type, &format,
215 &length, &after, &data );
216 bool withdrawn = true;
217 if ( r == Success && data && format == 32 ) {
218 quint32 *wstate = (quint32*)data;
219 withdrawn = (*wstate == WithdrawnState );
220 XFree( (char *)data );
228 void KStart::applyStyle(WId w
) {
230 if ( toSysTray
|| state
|| iconify
|| windowtype
!= NET::Unknown
|| desktop
>= 1 ) {
233 XWithdrawWindow(QX11Info::display(), w
, info
.screen());
234 QApplication::flush();
236 while ( !wstate_withdrawn(w
) )
240 NETWinInfo
info( QX11Info::display(), w
, QX11Info::appRootWindow(), NET::WMState
);
242 if ( ( desktop
> 0 && desktop
<= KWindowSystem::numberOfDesktops() )
243 || desktop
== NETWinInfo::OnAllDesktops
)
244 info
.setDesktop( desktop
);
247 XWMHints
* hints
= XGetWMHints(QX11Info::display(), w
);
249 hints
->flags
|= StateHint
;
250 hints
->initial_state
= IconicState
;
251 XSetWMHints( QX11Info::display(), w
, hints
);
256 if ( windowtype
!= NET::Unknown
) {
257 info
.setWindowType( windowtype
);
261 info
.setState( state
, mask
);
264 QApplication::beep();
265 // KWindowSystem::setSystemTrayWindowFor( w, QX11Info::appRootWindow() );
269 QRect r
= QApplication::desktop()->screenGeometry();
270 XMoveResizeWindow( QX11Info::display(), w
, r
.x(), r
.y(), r
.width(), r
.height() );
274 XSync(QX11Info::display(), False
);
276 XMapWindow(QX11Info::display(), w
);
277 XSync(QX11Info::display(), False
);
280 KWindowSystem::forceActiveWindow( w
);
282 QApplication::flush();
285 int main( int argc
, char *argv
[] )
287 KAboutData
aboutData( "kstart", 0, ki18n("KStart"), KDE_VERSION_STRING
,
289 "Utility to launch applications with special window properties \n"
290 "such as iconified, maximized, a certain virtual desktop, a special decoration\n"
292 KAboutData::License_GPL
,
293 ki18n("(C) 1997-2000 Matthias Ettrich (ettrich@kde.org)") );
295 aboutData
.addAuthor( ki18n("Matthias Ettrich"), KLocalizedString(), "ettrich@kde.org" );
296 aboutData
.addAuthor( ki18n("David Faure"), KLocalizedString(), "faure@kde.org" );
297 aboutData
.addAuthor( ki18n("Richard J. Moore"), KLocalizedString(), "rich@kde.org" );
299 KCmdLineArgs::init( argc
, argv
, &aboutData
);
302 KCmdLineOptions options
;
304 options
.add("!+command", ki18n("Command to execute"));
305 // "!" means: all options after command are treated as arguments to the command
306 options
.add("window <regexp>", ki18n("A regular expression matching the window title"));
307 options
.add("windowclass <class>", ki18n("A string matching the window class (WM_CLASS property)\n"
308 "The window class can be found out by running\n"
309 "'xprop | grep WM_CLASS' and clicking on a window\n"
310 "(use either both parts separated by a space or only the right part).\n"
311 "NOTE: If you specify neither window title nor window class,\n"
312 "then the very first window to appear will be taken;\n"
313 "omitting both options is NOT recommended."));
314 options
.add("desktop <number>", ki18n("Desktop on which to make the window appear"));
315 options
.add("currentdesktop", ki18n("Make the window appear on the desktop that was active\nwhen starting the application"));
316 options
.add("alldesktops", ki18n("Make the window appear on all desktops"));
317 options
.add("iconify", ki18n("Iconify the window"));
318 options
.add("maximize", ki18n("Maximize the window"));
319 options
.add("maximize-vertically", ki18n("Maximize the window vertically"));
320 options
.add("maximize-horizontally", ki18n("Maximize the window horizontally"));
321 options
.add("fullscreen", ki18n("Show window fullscreen"));
322 options
.add("type <type>", ki18n("The window type: Normal, Desktop, Dock, Toolbar, \nMenu, Dialog, TopMenu or Override"));
323 options
.add("activate", ki18n("Jump to the window even if it is started on a \n"
324 "different virtual desktop"));
325 options
.add("ontop");
326 options
.add("keepabove", ki18n("Try to keep the window above other windows"));
327 options
.add("onbottom");
328 options
.add("keepbelow", ki18n("Try to keep the window below other windows"));
329 options
.add("skiptaskbar", ki18n("The window does not get an entry in the taskbar"));
330 options
.add("skippager", ki18n("The window does not get an entry on the pager"));
331 options
.add("tosystray", ki18n("The window is sent to the system tray in Kicker"));
332 KCmdLineArgs::addCmdLineOptions( options
); // Add our own options.
334 KComponentData
componentData( &aboutData
);
335 QApplication
app( KCmdLineArgs::qtArgc(), KCmdLineArgs::qtArgv() );
337 KCmdLineArgs
*args
= KCmdLineArgs::parsedArgs();
339 if ( args
->count() == 0 )
340 KCmdLineArgs::usageError(i18n("No command specified"));
344 for(int i
=0; i
< args
->count(); i
++)
345 (*proc
) << args
->arg(i
);
347 desktop
= args
->getOption( "desktop" ).toInt();
348 if ( args
->isSet ( "alldesktops") )
349 desktop
= NETWinInfo::OnAllDesktops
;
350 if ( args
->isSet ( "currentdesktop") )
351 desktop
= KWindowSystem::currentDesktop();
353 windowtitle
= args
->getOption( "window" );
354 windowclass
= args
->getOption( "windowclass" );
355 if( !windowclass
.isEmpty() )
356 windowclass
= windowclass
.toLower();
358 if( windowtitle
.isEmpty() && windowclass
.isEmpty())
359 kWarning() << "Omitting both --window and --windowclass arguments is not recommended" ;
361 QString s
= args
->getOption( "type" );
362 if ( !s
.isEmpty() ) {
364 if ( s
== "desktop" )
365 windowtype
= NET::Desktop
;
366 else if ( s
== "dock" )
367 windowtype
= NET::Dock
;
368 else if ( s
== "toolbar" )
369 windowtype
= NET::Toolbar
;
370 else if ( s
== "menu" )
371 windowtype
= NET::Menu
;
372 else if ( s
== "dialog" )
373 windowtype
= NET::Dialog
;
374 else if ( s
== "override" )
375 windowtype
= NET::Override
;
376 else if ( s
== "topmenu" )
377 windowtype
= NET::TopMenu
;
379 windowtype
= NET::Normal
;
382 if ( args
->isSet( "keepabove" ) ) {
383 state
|= NET::KeepAbove
;
384 mask
|= NET::KeepAbove
;
385 } else if ( args
->isSet( "keepbelow" ) ) {
386 state
|= NET::KeepBelow
;
387 mask
|= NET::KeepBelow
;
390 if ( args
->isSet( "skiptaskbar" ) ) {
391 state
|= NET::SkipTaskbar
;
392 mask
|= NET::SkipTaskbar
;
395 if ( args
->isSet( "skippager" ) ) {
396 state
|= NET::SkipPager
;
397 mask
|= NET::SkipPager
;
400 activate
= args
->isSet("activate");
402 if ( args
->isSet("maximize") ) {
406 if ( args
->isSet("maximize-vertically") ) {
407 state
|= NET::MaxVert
;
408 mask
|= NET::MaxVert
;
410 if ( args
->isSet("maximize-horizontally") ) {
411 state
|= NET::MaxHoriz
;
412 mask
|= NET::MaxHoriz
;
415 iconify
= args
->isSet("iconify");
416 toSysTray
= args
->isSet("tosystray");
417 if ( args
->isSet("fullscreen") ) {
418 NETRootInfo
i( QX11Info::display(), NET::Supported
);
419 if( i
.isSupported( NET::FullScreen
)) {
420 state
|= NET::FullScreen
;
421 mask
|= NET::FullScreen
;
423 windowtype
= NET::Override
;
428 fcntl(ConnectionNumber(QX11Info::display()), F_SETFD
, 1);