summaryrefslogtreecommitdiffstats
path: root/ksystraycmd
diff options
context:
space:
mode:
Diffstat (limited to 'ksystraycmd')
-rw-r--r--ksystraycmd/Makefile.am18
-rw-r--r--ksystraycmd/README29
-rw-r--r--ksystraycmd/ksystraycmd.cpp341
-rw-r--r--ksystraycmd/ksystraycmd.h89
-rw-r--r--ksystraycmd/main.cpp143
5 files changed, 620 insertions, 0 deletions
diff --git a/ksystraycmd/Makefile.am b/ksystraycmd/Makefile.am
new file mode 100644
index 000000000..0fd20878b
--- /dev/null
+++ b/ksystraycmd/Makefile.am
@@ -0,0 +1,18 @@
+####### Fiddle here
+
+INCLUDES = $(all_includes)
+LDADD = $(LIB_KDEUI)
+
+####### Files
+
+bin_PROGRAMS = ksystraycmd
+METASOURCES = ksystraycmd.moc
+noinst_HEADERS = ksystraycmd.h
+
+ksystraycmd_SOURCES = ksystraycmd.cpp main.cpp
+ksystraycmd_LDFLAGS = $(all_libraries) $(KDE_RPATH)
+
+messages:
+ $(XGETTEXT) $(ksystraycmd_SOURCES) -o $(podir)/ksystraycmd.pot
+
+
diff --git a/ksystraycmd/README b/ksystraycmd/README
new file mode 100644
index 000000000..8e41777a0
--- /dev/null
+++ b/ksystraycmd/README
@@ -0,0 +1,29 @@
+ README for KSysTrayCmd
+ ======================
+
+Introduction
+============
+
+KSysTrayCmd is a utility that allows you to run any application you
+like in the system tray, not just those designed to use it.
+
+Examples
+========
+
+ksystraycmd --window 'kmail' kmail
+
+ksystraycmd --startonshow --icon logfile --tooltip 'X Log' --hidden --window 'X Log' \
+ konsole --icon logfile --caption 'X Log' -e 'less -M ~/.xsession-errors'
+
+More Information
+================
+
+I'm currently writing an article for the dot which will describe the
+usage of both kstart and ksystraycmd in more detail, the text of the
+article will be used to improve this document.
+
+Richard Moore
+rich@kde.org
+
+
+
diff --git a/ksystraycmd/ksystraycmd.cpp b/ksystraycmd/ksystraycmd.cpp
new file mode 100644
index 000000000..77edf73ea
--- /dev/null
+++ b/ksystraycmd/ksystraycmd.cpp
@@ -0,0 +1,341 @@
+#include <qtooltip.h>
+#include <qtextstream.h>
+#include <qimage.h>
+#include <qregexp.h>
+
+#include <kdebug.h>
+#include <kapplication.h>
+#include <kglobal.h>
+#include <kiconloader.h>
+#include <klocale.h>
+#include <kpopupmenu.h>
+#include <kprocess.h>
+#include <kwinmodule.h>
+#include <kconfig.h>
+#include <ksystemtray.h>
+
+#include <netwm.h>
+
+#include "ksystraycmd.h"
+#include "ksystraycmd.moc"
+
+
+KSysTrayCmd::KSysTrayCmd()
+ : QLabel( 0, "systray_cmd" ),
+ isVisible(true), lazyStart( false ), noquit( false ), quitOnHide( false ), onTop(false), ownIcon(false),
+ win(0), client(0), kwinmodule(0), top(0), left(0)
+{
+ setAlignment( AlignCenter );
+ kwinmodule = new KWinModule( this );
+ refresh();
+}
+
+KSysTrayCmd::~KSysTrayCmd()
+{
+ delete client;
+}
+
+//
+// Main entry point to the class
+//
+
+bool KSysTrayCmd::start()
+{
+ // If we have no command we must catching an existing window
+ if ( !command ) {
+ if ( win ) {
+ setTargetWindow( win );
+ return true;
+ }
+
+ checkExistingWindows();
+ if ( win ) {
+ // Window always on top
+ if (onTop) {
+ KWin::setState(win, NET::StaysOnTop);
+ }
+ return true;
+ }
+
+ errStr = i18n( "No window matching pattern '%1' and no command specified.\n" )
+ .arg( window );
+ return false;
+ }
+
+ // Run the command and watch for its window
+ if ( !startClient() ) {
+ errStr = i18n( "KSysTrayCmd: KShellProcess cannot find a shell." );
+ clientExited();
+ return false;
+ }
+
+ return true;
+}
+
+//
+// Window related functions.
+//
+
+void KSysTrayCmd::showWindow()
+{
+ isVisible = true;
+ if ( !win )
+ return;
+ XMapWindow( qt_xdisplay(), win );
+ // We move the window to the memorized position
+ XMoveWindow( qt_xdisplay(), win, left, top);
+
+ // Window always on top
+ if (onTop)
+ {
+ KWin::setState(win, NET::StaysOnTop);
+ }
+
+ KWin::activateWindow( win );
+
+}
+
+void KSysTrayCmd::hideWindow()
+{
+ isVisible = false;
+ if ( !win )
+ return;
+ //We memorize the position of the window
+ left = KWin::windowInfo(win).frameGeometry().left();
+ top=KWin::windowInfo(win).frameGeometry().top();
+
+ XUnmapWindow( qt_xdisplay(), win );
+}
+
+void KSysTrayCmd::setTargetWindow( WId w )
+{
+ setTargetWindow( KWin::windowInfo( w ) );
+}
+
+void KSysTrayCmd::setTargetWindow( const KWin::WindowInfo &info )
+{
+ disconnect( kwinmodule, SIGNAL(windowAdded(WId)), this, SLOT(windowAdded(WId)) );
+ connect( kwinmodule, SIGNAL(windowChanged(WId)), SLOT(windowChanged(WId)) );
+ win = info.win();
+ KWin::setSystemTrayWindowFor( winId(), win );
+ refresh();
+ show();
+
+ if ( isVisible )
+ KWin::activateWindow( win );
+ else
+ hideWindow();
+
+ // Always on top ?
+ if (onTop)
+ {
+ KWin::setState(win, NET::StaysOnTop);
+ }
+}
+
+//
+// Refresh the tray icon
+//
+
+void KSysTrayCmd::refresh()
+{
+ KWin::setSystemTrayWindowFor( winId(), win ? win : winId() );
+
+ QToolTip::remove( this );
+ if ( win ) {
+ KConfig *appCfg = kapp->config();
+ KConfigGroupSaver configSaver(appCfg, "System Tray");
+ int iconWidth = appCfg->readNumEntry("systrayIconWidth", 22);
+
+ // ksystraycmd's icon or app's icon
+ if (ownIcon)
+ {
+ setPixmap( KSystemTray::loadIcon( kapp->iconName() ) );
+ }
+ else
+ {
+ setPixmap( KWin::icon( win, iconWidth, iconWidth, true ) );
+ }
+
+ QToolTip::add( this, KWin::windowInfo( win ).name() );
+ }
+ else {
+ if ( !tooltip.isEmpty() )
+ QToolTip::add( this, tooltip );
+ else if ( !command.isEmpty() )
+ QToolTip::add( this, command );
+ else
+ QToolTip::add( this, window );
+
+ setPixmap( KSystemTray::loadIcon( kapp->iconName() ) );
+ }
+}
+
+//
+// Client related functions.
+//
+
+bool KSysTrayCmd::startClient()
+{
+ client = new KShellProcess();
+ *client << command;
+ connect( kwinmodule, SIGNAL(windowAdded(WId)), SLOT(windowAdded(WId)) );
+ connect( client, SIGNAL( processExited(KProcess *) ),
+ this, SLOT( clientExited() ) );
+
+ return client->start();
+}
+
+void KSysTrayCmd::clientExited()
+{
+ delete client;
+ client = 0;
+ win = 0;
+
+ if ( lazyStart && noquit )
+ refresh();
+ else
+ qApp->quit();
+}
+
+void KSysTrayCmd::quitClient()
+{
+ if ( win ) {
+ // Before sending the close request we have to show the window
+ XMapWindow( qt_xdisplay(), win );
+ NETRootInfo ri( qt_xdisplay(), NET::CloseWindow );
+ ri.closeWindowRequest( win );
+ win=0;
+ noquit = false;
+
+ // We didn't give command, so we didn't open an application.
+ // That's why when the application is closed we aren't informed.
+ // So we quit now.
+ if ( !command ) {
+ qApp->quit();
+ }
+ }
+ else {
+ qApp->quit();
+ }
+}
+
+void KSysTrayCmd::quit()
+{
+ if ( !isVisible ) {
+ showWindow();
+ }
+ qApp->quit();
+}
+
+void KSysTrayCmd::execContextMenu( const QPoint &pos )
+{
+ KPopupMenu *menu = new KPopupMenu();
+ menu->insertTitle( *pixmap(), i18n( "KSysTrayCmd" ) );
+ int hideShowId = menu->insertItem( isVisible ? i18n( "&Hide" ) : i18n( "&Restore" ) );
+ int undockId = menu->insertItem( SmallIcon("close"), i18n( "&Undock" ) );
+ int quitId = menu->insertItem( SmallIcon("exit"), i18n( "&Quit" ) );
+
+ int cmd = menu->exec( pos );
+
+ if ( cmd == quitId )
+ quitClient();
+ else if ( cmd == undockId )
+ quit();
+ else if ( cmd == hideShowId )
+ {
+ if ( lazyStart && ( !hasRunningClient() ) )
+ {
+ start();
+ isVisible=true;
+ }
+ else if ( quitOnHide && ( hasRunningClient() ) && isVisible )
+ {
+ NETRootInfo ri( qt_xdisplay(), NET::CloseWindow );
+ ri.closeWindowRequest( win );
+ isVisible=false;
+ }
+ else
+ toggleWindow();
+ }
+
+ delete menu;
+}
+
+void KSysTrayCmd::checkExistingWindows()
+{
+ QValueList<WId>::ConstIterator it;
+ for ( it = kwinmodule->windows().begin(); it != kwinmodule->windows().end(); ++it ) {
+ windowAdded( *it );
+ if ( win )
+ break;
+ }
+}
+
+void KSysTrayCmd::windowAdded(WId w)
+{
+ if ( !window.isEmpty() && ( QRegExp( window ).search( KWin::windowInfo(w).name() ) == -1 ) )
+ return; // no match
+ setTargetWindow( w );
+}
+
+void KSysTrayCmd::windowChanged( WId w )
+{
+ if ( w != win )
+ return;
+ refresh();
+}
+
+//
+// Tray icon event handlers
+//
+
+void KSysTrayCmd::mousePressEvent( QMouseEvent *e )
+{
+ if ( e->button() == RightButton )
+ execContextMenu( e->globalPos() );
+ else if ( lazyStart && ( !hasRunningClient() ) )
+ {
+ start();
+ isVisible=true;
+ }
+ else if ( quitOnHide && ( hasRunningClient() ) && isVisible )
+ {
+ NETRootInfo ri( qt_xdisplay(), NET::CloseWindow );
+ ri.closeWindowRequest( win );
+ isVisible=false;
+ }
+ else
+ toggleWindow();
+}
+
+WId KSysTrayCmd::findRealWindow( WId w, int depth )
+{
+ if( depth > 5 )
+ return None;
+ static Atom wm_state = XInternAtom( qt_xdisplay(), "WM_STATE", False );
+ Atom type;
+ int format;
+ unsigned long nitems, after;
+ unsigned char* prop;
+ if( XGetWindowProperty( qt_xdisplay(), w, wm_state, 0, 0, False, AnyPropertyType,
+ &type, &format, &nitems, &after, &prop ) == Success ) {
+ if( prop != NULL )
+ XFree( prop );
+ if( type != None )
+ return w;
+ }
+ Window root, parent;
+ Window* children;
+ unsigned int nchildren;
+ Window ret = None;
+ if( XQueryTree( qt_xdisplay(), w, &root, &parent, &children, &nchildren ) != 0 ) {
+ for( unsigned int i = 0;
+ i < nchildren && ret == None;
+ ++i )
+ ret = findRealWindow( children[ i ], depth + 1 );
+ if( children != NULL )
+ XFree( children );
+ }
+ return ret;
+}
diff --git a/ksystraycmd/ksystraycmd.h b/ksystraycmd/ksystraycmd.h
new file mode 100644
index 000000000..ecb670fd5
--- /dev/null
+++ b/ksystraycmd/ksystraycmd.h
@@ -0,0 +1,89 @@
+// -*- c++ -*-
+
+#ifndef KSYSTRAYCMD_H
+#define KSYSTRAYCMD_H
+
+#include <qlabel.h>
+#include <kwin.h>
+
+class KShellProcess;
+class KWinModule;
+
+/**
+ * Provides a system tray icon for a normal window.
+ *
+ * @author Richard Moore, rich@kde.org
+ */
+class KSysTrayCmd : public QLabel
+{
+ Q_OBJECT
+public:
+ KSysTrayCmd();
+ ~KSysTrayCmd();
+
+ void setWindow( WId w ) { win = w; }
+ void setCommand( const QString &cmd ) { command = cmd; }
+ void setPattern( const QString &regexp ) { window = regexp; }
+ void setStartOnShow( bool enable ) { lazyStart = enable; isVisible = !enable; }
+ void setNoQuit( bool enable ) { noquit = enable; }
+ void setQuitOnHide( bool enable ) { quitOnHide = enable; }
+ void setOnTop( bool enable ) { onTop = enable; }
+ void setOwnIcon( bool enable ) { ownIcon = enable; }
+ void setDefaultTip( const QString &tip ) { tooltip = tip; }
+ bool hasTargetWindow() const { return (win != 0); }
+ bool hasRunningClient() const { return (client != 0); }
+ const QString &errorMsg() const { return errStr; }
+
+ bool start();
+
+ static WId findRealWindow( WId w, int depth = 0 );
+
+public slots:
+ void refresh();
+
+ void showWindow();
+ void hideWindow();
+ void toggleWindow() { if ( isVisible ) hideWindow(); else showWindow(); }
+
+ void setTargetWindow( WId w );
+ void execContextMenu( const QPoint &pos );
+
+ void quit();
+ void quitClient();
+
+protected slots:
+ void clientExited();
+
+ void windowAdded(WId w);
+ void windowChanged(WId w);
+
+protected:
+ bool startClient();
+ void checkExistingWindows();
+ void setTargetWindow( const KWin::WindowInfo &info );
+
+ void mousePressEvent( QMouseEvent *e );
+
+private:
+ QString command;
+ QString window;
+ QString tooltip;
+ bool isVisible;
+ bool lazyStart;
+ bool noquit;
+ bool quitOnHide;
+ bool onTop; ///< tells if window must stay on top or not
+ bool ownIcon; ///< tells if the ksystraycmd icon must be used in systray
+
+ WId win;
+ KShellProcess *client;
+ KWinModule *kwinmodule;
+ QString errStr;
+
+ /** Memorized 'top' position of the window*/
+ int top;
+ /** Memorized 'left' position of the window*/
+ int left;
+};
+
+#endif // KSYSTRAYCMD_H
diff --git a/ksystraycmd/main.cpp b/ksystraycmd/main.cpp
new file mode 100644
index 000000000..b10728fd9
--- /dev/null
+++ b/ksystraycmd/main.cpp
@@ -0,0 +1,143 @@
+#include <fcntl.h>
+
+#include <kapplication.h>
+#include <kaboutdata.h>
+#include <kcmdlineargs.h>
+#include <kdebug.h>
+#include <klocale.h>
+#include <kprocess.h>
+
+#include "ksystraycmd.h"
+
+#include <X11/Xlib.h>
+#ifndef KDE_USE_FINAL
+const int XFocusOut = FocusOut;
+const int XFocusIn = FocusIn;
+#endif
+#undef FocusOut
+#undef FocusIn
+#undef KeyPress
+#undef KeyRelease
+
+
+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\n"
+ "If you do not specify one, then the very first window\n"
+ "to appear will be taken - not recommended."), 0 },
+ { "wid <int>", I18N_NOOP("The window id of the target window\n"
+ "Specifies the id of the window to use. If the id starts with 0x\n"
+ "it is assumed to be in hex."), 0 },
+ { "hidden", I18N_NOOP( "Hide the window to the tray on startup" ), 0 },
+ { "startonshow", I18N_NOOP( "Wait until we are told to show the window before\n"
+ "executing the command" ), 0 },
+ { "tooltip <text>", I18N_NOOP( "Sets the initial tooltip for the tray icon" ), 0 },
+ { "keeprunning", I18N_NOOP( "Keep the tray icon even if the client exits. This option\n"
+ "has no effect unless startonshow is specified." ), 0 },
+ { "ownicon", I18N_NOOP( "Use ksystraycmd's icon instead of window's icon in systray\n"
+ "(should be used with --icon to specify ksystraycmd icon)" ), 0 },
+ { "ontop", I18N_NOOP( "Try to keep the window above other windows"), 0 },
+ { "quitonhide", I18N_NOOP( "Quit the client when we are told to hide the window.\n"
+ "This has no effect unless startonshow is specified and implies keeprunning." ), 0 },
+ /* { "menuitem <item>", I18N_NOOP( "Adds a custom entry to the tray icon menu\n"
+ "The item should have the form text:command." ), 0 },*/
+ KCmdLineLastOption
+};
+
+int main( int argc, char *argv[] )
+{
+ KAboutData aboutData( "ksystraycmd", I18N_NOOP( "KSysTrayCmd" ),
+ "KSysTrayCmd 0.1",
+ I18N_NOOP( "Allows any application to be kept in the system tray" ),
+ KAboutData::License_GPL,
+ "(C) 2001-2002 Richard Moore (rich@kde.org)" );
+ aboutData.addAuthor( "Richard Moore", 0, "rich@kde.org" );
+
+ KCmdLineArgs::init( argc, argv, &aboutData );
+ KCmdLineArgs::addCmdLineOptions( options ); // Add our own options.
+
+ KApplication app;
+
+ //
+ // Setup the tray icon from the arguments.
+ //
+ KCmdLineArgs *args = KCmdLineArgs::parsedArgs();
+ KSysTrayCmd cmd;
+
+ // Read the window id
+ QString wid = args->getOption( "wid" );
+ if ( !wid.isEmpty() ) {
+ int base = 10;
+ if ( wid.startsWith( "0x" ) ) {
+ base = 16;
+ wid = wid.right( wid.length() - 2 );
+ }
+
+ bool ok=true;
+ ulong w = wid.toULong( &ok, base );
+ if ( ok )
+ cmd.setTargetWindow( w );
+ else {
+ kdWarning() << "KSysTrayCmd: Got bad win id" << endl;
+ }
+ }
+
+ // Read window title regexp
+ QString title = args->getOption( "window" );
+ if ( !title.isEmpty() )
+ cmd.setPattern( title );
+
+ if ( !title && !wid && (args->count() == 0) )
+ KCmdLineArgs::usage(i18n("No command or window specified"));
+
+ // Read the command
+ QString command;
+ for ( int i = 0; i < args->count(); i++ )
+ command += KProcess::quote(QString::fromLocal8Bit( args->arg(i) )) + " ";
+ if ( !command.isEmpty() )
+ cmd.setCommand( command );
+
+ // Tooltip
+ QString tip = args->getOption( "tooltip" );
+ if ( !tip.isEmpty() )
+ cmd.setDefaultTip( tip );
+
+ // Keep running flag
+ if ( args->isSet( "keeprunning" ) )
+ cmd.setNoQuit( true );
+
+ if ( args->isSet( "quitonhide" ) ) {
+ cmd.setNoQuit( true );
+ cmd.setQuitOnHide( true );
+ }
+
+ // Start hidden
+ if ( args->isSet( "hidden" ) )
+ cmd.hideWindow();
+
+ // On top
+ if ( args->isSet( "ontop" ) )
+ cmd.setOnTop(true);
+
+ // Use ksystraycmd icon
+ if ( args->isSet( "ownicon" ) )
+ cmd.setOwnIcon(true);
+
+ // Lazy invocation flag
+ if ( args->isSet( "startonshow" ) ) {
+ cmd.setStartOnShow( true );
+ cmd.show();
+ }
+ else {
+ if ( !cmd.start() )
+ return 1;
+ }
+
+ fcntl(ConnectionNumber(qt_xdisplay()), F_SETFD, 1);
+ args->clear();
+
+ return app.exec();
+}
+