/* * kstart.C. Part of the KDE project. * * Copyright (C) 1997-2000 Matthias Ettrich <ettrich@kde.org> * * First port to NETWM by David Faure <faure@kde.org> * Send to system tray by Richard Moore <rich@kde.org> */ #include "kstart.moc" #include "version.h" #include <fcntl.h> #include <stdlib.h> #include <tqregexp.h> #include <tqtimer.h> #include <kdebug.h> #include <kprocess.h> #include <klocale.h> #include <twin.h> #include <twinmodule.h> #include <kapplication.h> #include <kaboutdata.h> #include <kcmdlineargs.h> #include <kstartupinfo.h> #include <kxmessages.h> #include <netwm.h> // some globals static KProcess proc; static TQCString windowtitle = 0; static TQCString windowclass = 0; static int desktop = 0; static bool activate = false; static bool iconify = false; static bool toSysTray = false; static bool fullscreen = false; static unsigned long state = 0; static unsigned long mask = 0; static NET::WindowType windowtype = NET::Unknown; static KWinModule* twinmodule; KStart::KStart() :TQObject() { NETRootInfo i( tqt_xdisplay(), NET::Supported ); bool useRule = !toSysTray && i.isSupported( NET::WM2KDETemporaryRules ); if( useRule ) sendRule(); else { // connect to window add to get the NEW windows connect(twinmodule, TQT_SIGNAL(windowAdded(WId)), TQT_SLOT(windowAdded(WId))); if (windowtitle != 0) twinmodule->doNotManage( windowtitle ); } // propagate the app startup notification info to the started app KStartupInfoId id; id.initId( kapp->startupId()); id.setupStartupEnv(); //finally execute the comand if( proc.start(KProcess::DontCare) ) { KStartupInfoData data; data.addPid( proc.pid() ); TQCString bin = proc.args().first(); data.setName( bin ); data.setBin( bin.mid( bin.findRev( '/' ) + 1 )); KStartupInfo::sendChange( id, data ); } else KStartupInfo::sendFinish( id ); // failed to start TQTimer::singleShot( useRule ? 0 : 120 * 1000, kapp, TQT_SLOT( quit())); } void KStart::sendRule() { KXMessages msg; TQCString message; if( windowtitle != 0 ) message += "title=" + windowtitle + "\ntitlematch=3\n"; // 3 = regexp match if( windowclass != 0 ) message += "wmclass=" + windowclass + "\nwmclassmatch=1\n" // 1 = exact match + "wmclasscomplete=" // if windowclass contains a space (i.e. 2 words, use whole WM_CLASS) + ( windowclass.contains( ' ' ) ? "true" : "false" ) + "\n"; if( (windowtitle != 0) || (windowclass != 0) ) { // always ignore these window types message += "types=" + TQCString().setNum( -1U & ~( NET::TopMenuMask | NET::ToolbarMask | NET::DesktopMask | NET::SplashMask | NET::MenuMask )) + "\n"; } else { // accept only "normal" windows message += "types=" + TQCString().setNum( NET::NormalMask | NET::DialogMask ) + "\n"; } if ( ( desktop > 0 && desktop <= twinmodule->numberOfDesktops() ) || desktop == NETWinInfo::OnAllDesktops ) { message += "desktop=" + TQCString().setNum( desktop ) + "\ndesktoprule=3\n"; } if (activate) message += "fsplevel=0\nfsplevelrule=2\n"; if (iconify) message += "minimize=true\nminimizerule=3\n"; if ( windowtype != NET::Unknown ) { message += "type=" + TQCString().setNum( windowtype ) + "\ntyperule=2"; } if ( state ) { if( state & NET::KeepAbove ) message += "above=true\naboverule=3\n"; if( state & NET::KeepBelow ) message += "below=true\nbelowrule=3\n"; if( state & NET::SkipTaskbar ) message += "skiptaskbar=true\nskiptaskbarrule=3\n"; if( state & NET::SkipPager ) message += "skippager=true\nskippagerrule=3\n"; if( state & NET::MaxVert ) message += "maximizevert=true\nmaximizevertrule=3\n"; if( state & NET::MaxHoriz ) message += "maximizehoriz=true\nmaximizehorizrule=3\n"; if( state & NET::FullScreen ) message += "fullscreen=true\nfullscreenrule=3\n"; } msg.broadcastMessage( "_KDE_NET_WM_TEMPORARY_RULES", message, -1, false ); kapp->flushX(); } const int SUPPORTED_WINDOW_TYPES_MASK = NET::NormalMask | NET::DesktopMask | NET::DockMask | NET::ToolbarMask | NET::MenuMask | NET::DialogMask | NET::OverrideMask | NET::TopMenuMask | NET::UtilityMask | NET::SplashMask; void KStart::windowAdded(WId w){ KWin::WindowInfo info = KWin::windowInfo( w ); // always ignore these window types 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 ) return; if ( windowtitle != 0 ) { TQString title = info.name().lower(); TQRegExp r( windowtitle.lower()); if (r.match(title) == -1) return; // no match } if ( windowclass != 0 ) { XClassHint hint; if( !XGetClassHint( tqt_xdisplay(), w, &hint )) return; TQCString cls = windowclass.contains( ' ' ) ? TQCString( hint.res_name ) + ' ' + hint.res_class : TQCString( hint.res_class ); cls = cls.lower(); XFree( hint.res_name ); XFree( hint.res_class ); if( cls != windowclass ) return; } if( (windowtitle == 0) && (windowclass == 0) ) { // accept only "normal" windows 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 ) return; } applyStyle( w ); TQApplication::exit(); } extern Atom tqt_wm_state; // defined in qapplication_x11.cpp static bool wstate_withdrawn( WId winid ) { Atom type; int format; unsigned long length, after; unsigned char *data; int r = XGetWindowProperty( tqt_xdisplay(), winid, tqt_wm_state, 0, 2, FALSE, AnyPropertyType, &type, &format, &length, &after, &data ); bool withdrawn = TRUE; if ( r == Success && data && format == 32 ) { TQ_UINT32 *wstate = (TQ_UINT32*)data; withdrawn = (*wstate == WithdrawnState ); XFree( (char *)data ); } return withdrawn; } void KStart::applyStyle(WId w ) { if ( toSysTray || state || iconify || windowtype != NET::Unknown || desktop >= 1 ) { XWithdrawWindow(tqt_xdisplay(), w, tqt_xscreen()); TQApplication::flushX(); while ( !wstate_withdrawn(w) ) ; } NETWinInfo info( tqt_xdisplay(), w, tqt_xrootwin(), NET::WMState ); if ( ( desktop > 0 && desktop <= twinmodule->numberOfDesktops() ) || desktop == NETWinInfo::OnAllDesktops ) info.setDesktop( desktop ); if (iconify) { XWMHints * hints = XGetWMHints(tqt_xdisplay(), w ); if (hints ) { hints->flags |= StateHint; hints->initial_state = IconicState; XSetWMHints( tqt_xdisplay(), w, hints ); XFree(hints); } } if ( windowtype != NET::Unknown ) { info.setWindowType( windowtype ); } if ( state ) info.setState( state, mask ); if ( toSysTray ) { TQApplication::beep(); KWin::setSystemTrayWindowFor( w, tqt_xrootwin() ); } if ( fullscreen ) { TQRect r = TQApplication::desktop()->geometry(); XMoveResizeWindow( tqt_xdisplay(), w, r.x(), r.y(), r.width(), r.height() ); } XSync(tqt_xdisplay(), False); XMapWindow(tqt_xdisplay(), w ); XSync(tqt_xdisplay(), False); if (activate) KWin::forceActiveWindow( w ); TQApplication::flushX(); } // David, 05/03/2000 static KCmdLineOptions options[] = { { "!+command", I18N_NOOP("Command to execute"), 0 }, // "!" means: all options after command are treated as arguments to the command { "window <regexp>", I18N_NOOP("A regular expression matching the window title"), 0 }, { "windowclass <class>", I18N_NOOP("A string matching the window class (WM_CLASS property)\n" "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" "omitting both options is NOT recommended."), 0 }, { "desktop <number>", I18N_NOOP("Desktop on which to make the window appear"), 0 }, { "currentdesktop", I18N_NOOP("Make the window appear on the desktop that was active\nwhen starting the application"), 0 }, { "alldesktops", I18N_NOOP("Make the window appear on all desktops"), 0 }, { "iconify", I18N_NOOP("Iconify the window"), 0 }, { "maximize", I18N_NOOP("Maximize the window"), 0 }, { "maximize-vertically", I18N_NOOP("Maximize the window vertically"), 0 }, { "maximize-horizontally", I18N_NOOP("Maximize the window horizontally"), 0 }, { "fullscreen", I18N_NOOP("Show window fullscreen"), 0 }, { "type <type>", I18N_NOOP("The window type: Normal, Desktop, Dock, Tool, \nMenu, Dialog, TopMenu or Override"), 0 }, { "activate", I18N_NOOP("Jump to the window even if it is started on a \n" "different virtual desktop"), 0 }, { "ontop", 0, 0 }, { "keepabove", I18N_NOOP("Try to keep the window above other windows"), 0 }, { "onbottom", 0, 0 }, { "keepbelow", I18N_NOOP("Try to keep the window below other windows"), 0 }, { "skiptaskbar", I18N_NOOP("The window does not get an entry in the taskbar"), 0 }, { "skippager", I18N_NOOP("The window does not get an entry on the pager"), 0 }, { "tosystray", I18N_NOOP("The window is sent to the system tray in Kicker"), 0 }, KCmdLineLastOption }; int main( int argc, char *argv[] ) { // David, 05/03/2000 KAboutData aboutData( "kstart", I18N_NOOP("KStart"), KSTART_VERSION, I18N_NOOP("" "Utility to launch applications with special window properties \n" "such as iconified, maximized, a certain virtual desktop, a special decoration\n" "and so on." ), KAboutData::License_GPL, "(C) 1997-2000 Matthias Ettrich (ettrich@kde.org)" ); aboutData.addAuthor( "Matthias Ettrich", 0, "ettrich@kde.org" ); aboutData.addAuthor( "David Faure", 0, "faure@kde.org" ); aboutData.addAuthor( "Richard J. Moore", 0, "rich@kde.org" ); KCmdLineArgs::init( argc, argv, &aboutData ); KCmdLineArgs::addCmdLineOptions( options ); // Add our own options. KApplication app; KCmdLineArgs *args = KCmdLineArgs::parsedArgs(); if ( args->count() == 0 ) KCmdLineArgs::usage(i18n("No command specified")); for(int i=0; i < args->count(); i++) proc << args->arg(i); twinmodule = new KWinModule; desktop = args->getOption( "desktop" ).toInt(); if ( args->isSet ( "alldesktops") ) desktop = NETWinInfo::OnAllDesktops; if ( args->isSet ( "currentdesktop") ) desktop = twinmodule->currentDesktop(); windowtitle = args->getOption( "window" ); windowclass = args->getOption( "windowclass" ); if( windowclass != 0 ) windowclass = windowclass.lower(); if( windowtitle.isEmpty() && windowclass.isEmpty()) kdWarning() << "Omitting both --window and --windowclass arguments is not recommended" << endl; TQCString s = args->getOption( "type" ); if ( !s.isEmpty() ) { s = s.lower(); if ( s == "desktop" ) windowtype = NET::Desktop; else if ( s == "dock" ) windowtype = NET::Dock; else if ( s == "tool" ) windowtype = NET::Tool; else if ( s == "menu" ) windowtype = NET::Menu; else if ( s == "dialog" ) windowtype = NET::Dialog; else if ( s == "override" ) windowtype = NET::Override; else if ( s == "topmenu" ) windowtype = NET::TopMenu; else windowtype = NET::Normal; } if ( args->isSet( "keepabove" ) ) { state |= NET::KeepAbove; mask |= NET::KeepAbove; } else if ( args->isSet( "keepbelow" ) ) { state |= NET::KeepBelow; mask |= NET::KeepBelow; } if ( args->isSet( "skiptaskbar" ) ) { state |= NET::SkipTaskbar; mask |= NET::SkipTaskbar; } if ( args->isSet( "skippager" ) ) { state |= NET::SkipPager; mask |= NET::SkipPager; } activate = args->isSet("activate"); if ( args->isSet("maximize") ) { state |= NET::Max; mask |= NET::Max; } if ( args->isSet("maximize-vertically") ) { state |= NET::MaxVert; mask |= NET::MaxVert; } if ( args->isSet("maximize-horizontally") ) { state |= NET::MaxHoriz; mask |= NET::MaxHoriz; } iconify = args->isSet("iconify"); toSysTray = args->isSet("tosystray"); if ( args->isSet("fullscreen") ) { NETRootInfo i( tqt_xdisplay(), NET::Supported ); if( i.isSupported( NET::FullScreen )) { state |= NET::FullScreen; mask |= NET::FullScreen; } else { windowtype = NET::Override; fullscreen = true; } } fcntl(ConnectionNumber(tqt_xdisplay()), F_SETFD, 1); args->clear(); KStart start; return app.exec(); }