[Psi-devel] improved freedesktop.org trayicon patch

William Waghorn willw at litany.me.uk
Thu Aug 19 12:46:11 PDT 2004


My original freedesktop.org patch introduced a dependency between the 
X11 TrayIcon and PsiApplication.  After a bit of thought, I've decided 
that this isn't necessary.

Here's a patch which does the same thing but in a slightly cleaner way 
(fixes the race condition which caused the X trayicon to be displayed in 
its own detached window).

There is one minor issue remaining, that it is possible to close the Psi 
main window while there is no notification area available.  Psi remains 
running, and you lose any ability to control it.  Of course, re-starting 
the notification area restores the tray icon.  I'm working on a solution...

William
-------------- next part --------------
Index: cutestuff/trayicon/trayicon_x11.cpp
===================================================================
--- cutestuff/trayicon/trayicon_x11.cpp	(revision 28)
+++ cutestuff/trayicon/trayicon_x11.cpp	(revision 32)
@@ -225,6 +225,8 @@
 {
 public:
 	TrayIconFreeDesktop(TrayIcon *object, const QPixmap &pm);
+protected:
+	virtual bool x11Event(XEvent*);
 };
 
 TrayIconFreeDesktop::TrayIconFreeDesktop(TrayIcon *object, const QPixmap &pm)
@@ -239,6 +241,14 @@
 	Screen *screen = XDefaultScreenOfDisplay(dsp); // get the screen
 	int screen_id = XScreenNumberOfScreen(screen); // and it's number
 
+	// tell X that we want to see ClientMessage and Deleted events, which
+	// are picked up by QApplication::x11EventFilter
+	Window root_window = QApplication::desktop()->winId();
+	XWindowAttributes attr;
+
+	XGetWindowAttributes(dsp, root_window, &attr);
+	XSelectInput(dsp, root_window, attr.your_event_mask | StructureNotifyMask);
+
 	char buf[32];
 	snprintf(buf, sizeof(buf), "_NET_SYSTEM_TRAY_S%d", screen_id);
 	Atom selection_atom = XInternAtom(dsp, buf, false);
@@ -251,6 +261,11 @@
 
 	if ( manager_window != None )
 		send_message(dsp, manager_window, SYSTEM_TRAY_REQUEST_DOCK, winId(), 0, 0);
+	else
+	{
+		object->hide();
+		return;
+	}
 
 	// some KDE mumbo-jumbo... why is it there? anybody?
 	Atom kwm_dockwindow_atom = XInternAtom(dsp, "KWM_DOCKWINDOW", false);
@@ -263,6 +278,17 @@
 	setPixmap(pm);
 }
 
+bool TrayIconFreeDesktop::x11Event(XEvent *ev)
+{
+	switch(ev->type)
+	{
+		case ReparentNotify:
+			show();
+
+	}
+	return false;
+}
+
 //----------------------------------------------------------------------------
 // TrayIconWindowMaker
 //----------------------------------------------------------------------------
@@ -345,7 +371,9 @@
 		d = (TrayIconPrivate *)(new TrayIconFreeDesktop(this, pm));
 
 	sysUpdateToolTip();
-	d->show();
+
+	if ( v_isWMDock )
+		d->show();
 }
 
 void TrayIcon::sysRemove()
Index: cutestuff/trayicon/trayicon.cpp
===================================================================
--- cutestuff/trayicon/trayicon.cpp	(revision 28)
+++ cutestuff/trayicon/trayicon.cpp	(revision 32)
@@ -60,6 +60,19 @@
 }
 
 /*!
+  Informs the trayicon that the notification owner has probably been changed;
+  and that it should attempt to register or re-register.
+*/
+void TrayIcon::newTrayOwner()
+{
+    // detach ourself from any existing notification area.
+    hide();
+    // show ourself on the new notification area
+    show();
+}
+
+
+/*!
   Sets the context menu to \a popup. The context menu will pop up when the
   user clicks the system tray entry with the right mouse button.
 */
Index: cutestuff/trayicon/trayicon.h
===================================================================
--- cutestuff/trayicon/trayicon.h	(revision 28)
+++ cutestuff/trayicon/trayicon.h	(revision 32)
@@ -58,6 +58,8 @@
 	void show();
 	void hide();
 
+	void newTrayOwner();
+
 signals:
 	void clicked( const QPoint&, int);
 	void doubleClicked( const QPoint& );
Index: psi/src/psiapplication.h
===================================================================
--- psi/src/psiapplication.h	(revision 28)
+++ psi/src/psiapplication.h	(revision 32)
@@ -44,6 +44,8 @@
 
 signals:
 	void dockActivated();
+	void newTrayOwner();
+	void trayOwnerDied();
 
 private:
 	void init(bool GUIenabled);
Index: psi/src/psiapplication.cpp
===================================================================
--- psi/src/psiapplication.cpp	(revision 28)
+++ psi/src/psiapplication.cpp	(revision 32)
@@ -33,6 +33,12 @@
 #include <X11/Xatom.h>
 #include <X11/SM/SMlib.h>
 
+// Atoms required for monitoring the freedesktop.org notification area
+static Atom manager_atom = 0;
+static Atom tray_selection_atom = 0;
+Window root_window = 0;
+Window tray_owner = None;
+
 //static Atom atom_KdeNetUserTime;
 static Atom kde_net_wm_user_time = 0;
 
@@ -65,6 +71,33 @@
 //#undef Q_WS_X11
 #endif
 
+#ifdef Q_WS_X11
+void setTrayOwnerWindow(Display *dsp)
+{
+	/* This code is basically trying to mirror what happens in
+	 * eggtrayicon.c:egg_tray_icon_update_manager_window()
+	 */
+	// ignore events from the old tray owner
+	if (tray_owner != None)
+	{
+		XSelectInput(dsp, tray_owner, 0);
+	}
+
+	// obtain the Window handle for the new tray owner
+	XGrabServer(dsp);
+	tray_owner = XGetSelectionOwner(dsp, tray_selection_atom);
+
+	// we have to be able to spot DestroyNotify messages on the tray owner
+	if (tray_owner != None)
+	{
+		XSelectInput(dsp, tray_owner, StructureNotifyMask|PropertyChangeMask);
+	}
+	XUngrabServer(dsp);
+	XFlush(dsp);
+}
+
+#endif
+
 //----------------------------------------------------------------------------
 // PsiApplication
 //----------------------------------------------------------------------------
@@ -95,11 +128,32 @@
 
 		atoms[n] = &kde_net_wm_user_time;
 		names[n++] = (char *) "_NET_WM_USER_TIME";
+		atoms[n] = &manager_atom;
+		names[n++] = (char *) "MANAGER";
 
-		XInternAtoms( qt_xdisplay(), names, n, false, atoms_return );
+		Display *dsp = qt_xdisplay();
 
+		XInternAtoms( dsp, names, n, false, atoms_return );
+
 		for (int i = 0; i < n; i++ )
 			*atoms[i] = atoms_return[i];
+
+		// get the selection type we'll use to locate the notification tray
+		char buf[32];
+		snprintf(buf, sizeof(buf), "_NET_SYSTEM_TRAY_S%d", XScreenNumberOfScreen( XDefaultScreenOfDisplay(dsp) ));
+		tray_selection_atom = XInternAtom(dsp, buf, false);
+
+		// make a note of the window handle for the root window
+		root_window = QApplication::desktop()->winId();
+
+		XWindowAttributes attr;
+
+		// this is actually futile, since Qt overrides it at some
+		// unknown point in the near future.
+		XGetWindowAttributes(dsp, root_window, &attr);
+		XSelectInput(dsp, root_window, attr.your_event_mask | StructureNotifyMask);
+
+		setTrayOwnerWindow(dsp);
 	}
 #endif
 }
@@ -128,6 +182,29 @@
 bool PsiApplication::x11EventFilter( XEvent *_event )
 {
 	switch ( _event->type ) {
+
+		case ClientMessage:
+			if (_event->xclient.window == root_window && _event->xclient.message_type == manager_atom)
+			{
+				// A new notification area application has
+				// announced its presence
+				setTrayOwnerWindow(_event->xclient.display);
+				newTrayOwner();
+				break;
+			}
+		case DestroyNotify:
+			if (_event->xdestroywindow.event == tray_owner)
+			{
+				// there is now no known notification area.
+				// We're still looking out for the MANAGER
+				// message sent to the root window, at which
+				// point we'll have another look to see
+				// whether a notification area is available.
+				tray_owner = 0;
+				trayOwnerDied();
+				break;
+			}
+
 		case ButtonPress:
 	 	case XKeyPress:
 		{
Index: psi/src/mainwin_p.cpp
===================================================================
--- psi/src/mainwin_p.cpp	(revision 28)
+++ psi/src/mainwin_p.cpp	(revision 32)
@@ -21,6 +21,7 @@
 #include "common.h"
 #include "mainwin_p.h"
 
+#include <qapplication.h>
 #include <qstyle.h>
 #include <qtoolbar.h>
 #include <qtimer.h>
@@ -367,6 +368,8 @@
 	connect(d->ti, SIGNAL(clicked(const QPoint &, int)), SIGNAL(clicked(const QPoint &, int)));
 	connect(d->ti, SIGNAL(doubleClicked(const QPoint &)), SIGNAL(doubleClicked(const QPoint &)));
 	connect(d->ti, SIGNAL(closed()), SIGNAL(closed()));
+	connect(qApp, SIGNAL(newTrayOwner()), d->ti, SLOT(newTrayOwner()));
+	connect(qApp, SIGNAL(trayOwnerDied()), d->ti, SLOT(hide()));
 	d->ti->show();
 }
 
Index: psi/src/mainwin.cpp
===================================================================
--- psi/src/mainwin.cpp	(revision 28)
+++ psi/src/mainwin.cpp	(revision 32)
@@ -443,7 +443,8 @@
 	d->tray->setToolTip(PROG_NAME);
 	connect(d->tray, SIGNAL(clicked(const QPoint &, int)), SLOT(trayClicked(const QPoint &, int)));
 	connect(d->tray, SIGNAL(doubleClicked(const QPoint &)), SLOT(trayDoubleClicked()));
-	connect(d->tray, SIGNAL(closed()), SIGNAL(closeProgram()));
+	connect(d->tray, SIGNAL(closed()), SLOT(dockActivated()));
+	connect(qApp, SIGNAL(trayOwnerDied()), SLOT(dockActivated()));
 
 	updateReadNext(d->nextAnim, d->nextAmount);
 


More information about the Psi-devel-affinix.com mailing list