changeset 433:38fe08dff5cd trunk

[svn] - added status icon plugin (written from scratch, with code from gtk 2.10)
author giacomo
date Mon, 15 Jan 2007 11:54:32 -0800
parents 1716423d29fe
children 7385182ae4b8
files ChangeLog configure.ac mk/rules.mk.in src/statusicon/Makefile src/statusicon/gtktrayicon-x11.c src/statusicon/gtktrayicon.h src/statusicon/si.c src/statusicon/si.h src/statusicon/si.xpm src/statusicon/si_audacious.h src/statusicon/si_common.h src/statusicon/si_ui.c src/statusicon/si_ui.h
diffstat 13 files changed, 1156 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
--- a/ChangeLog	Mon Jan 15 08:33:07 2007 -0800
+++ b/ChangeLog	Mon Jan 15 11:54:32 2007 -0800
@@ -1,3 +1,12 @@
+2007-01-15 16:33:07 +0000  William Pitcock <nenolod@sacredspiral.co.uk>
+  revision [948]
+  - remove blanket http:// grabbing -- probing for mp3 now works by VFS 
+    (even with shoutcast)
+  
+  trunk/src/mpg123/mpg123.c |    6 +-----
+  1 file changed, 1 insertion(+), 5 deletions(-)
+
+
 2007-01-15 10:48:34 +0000  Kiyoshi Aman <kiyoshi.aman@gmail.com>
   revision [946]
   pause functionality should play if not already playing, now
--- a/configure.ac	Mon Jan 15 08:33:07 2007 -0800
+++ b/configure.ac	Mon Jan 15 11:54:32 2007 -0800
@@ -381,6 +381,26 @@
     GENERAL_PLUGINS="$GENERAL_PLUGINS evdev-plug"
 fi
 
+dnl *** Status Icon plugin (for X11 only)
+
+AC_ARG_ENABLE(statusicon,
+    [  --disable-statusicon    disable X11 Status Icon plugin (default=enabled)],
+    [enable_statusicon=$enableval],
+    [enable_statusicon="yes"]
+)
+
+if test "x$enable_statusicon" = "xyes"; then
+    have_statusicon="yes"
+    PKG_CHECK_MODULES(GDKX11,[gdk-x11-2.0 >= 2.6.0],,[have_statusicon="no"])
+else
+    AC_MSG_RESULT([*** X11 Status Icon plugin disabled per user request ***])
+    have_statusicon="yes"
+fi
+
+if test "x$have_statusicon" = "xyes"; then
+    GENERAL_PLUGINS="$GENERAL_PLUGINS statusicon"
+fi
+
 dnl *** AdPlug requirement (libbinio)
 
 AC_ARG_ENABLE(adplug,
@@ -1175,6 +1195,7 @@
 echo "  -------"
 echo "  Alarm:                                  yes"
 echo "  Song Change:                            yes"
+echo "  Status Icon:                            $have_statusicon"
 echo "  Control via event device (evdev-plug):  $have_evdevplug"
 echo "  LIRC:                                   $have_lirc"
 echo "  AudioScrobbler Client:                  $scrobbler"
--- a/mk/rules.mk.in	Mon Jan 15 08:33:07 2007 -0800
+++ b/mk/rules.mk.in	Mon Jan 15 11:54:32 2007 -0800
@@ -127,6 +127,8 @@
 FLUIDSYNTH_LIBS ?= @FLUIDSYNTH_LIBS@
 GCONF_CFLAGS ?= @GCONF_CFLAGS@
 GCONF_LIBS ?= @GCONF_LIBS@
+GDKX11_CFLAGS ?= @GDKX11_CFLAGS@
+GDKX11_LIBS ?= @GDKX11_LIBS@
 GENCAT ?= @GENCAT@
 GENERAL_PLUGINS ?= @GENERAL_PLUGINS@
 GENERAL_PLUGIN_DIR ?= @GENERAL_PLUGIN_DIR@
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/statusicon/Makefile	Mon Jan 15 11:54:32 2007 -0800
@@ -0,0 +1,18 @@
+include ../../mk/rules.mk
+include ../../mk/init.mk
+
+OBJECTIVE_LIBS = libstatusicon$(SHARED_SUFFIX)
+
+noinst_HEADERS = si.h si_ui.h si_audacious.h si_common.h gtktrayicon.h
+
+LIBDIR = $(plugindir)/$(GENERAL_PLUGIN_DIR)
+
+LIBADD = $(GTK_LIBS) $(GDKX11_LIBS)
+SOURCES = si.c si_ui.c gtktrayicon-x11.c
+
+OBJECTS = ${SOURCES:.c=.o}
+
+CFLAGS += $(PICFLAGS) $(GTK_CFLAGS) $(GDKX11_CFLAGS) \
+	-I../../intl -I../.. -I..
+
+include ../../mk/objective.mk
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/statusicon/gtktrayicon-x11.c	Mon Jan 15 11:54:32 2007 -0800
@@ -0,0 +1,522 @@
+/* gtktrayicon.c
+ * Copyright (C) 2002 Anders Carlsson <andersca@gnu.org>
+ *
+ * Adapted by Giacomo Lozito for Audacious Status Icon Plugin
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+/*
+ * This is an implementation of the freedesktop.org "system tray" spec,
+ * http://www.freedesktop.org/wiki/Standards/systemtray-spec
+ */
+
+#include "si_common.h"
+#include <string.h>
+#include <glib.h>
+#include <glib/gi18n.h>
+
+#include <gtk/gtkprivate.h>
+#include "gtktrayicon.h"
+
+#include <gdk/gdkx.h>
+#include <X11/Xatom.h>
+
+#define SYSTEM_TRAY_REQUEST_DOCK    0
+#define SYSTEM_TRAY_BEGIN_MESSAGE   1
+#define SYSTEM_TRAY_CANCEL_MESSAGE  2
+
+#define SYSTEM_TRAY_ORIENTATION_HORZ 0
+#define SYSTEM_TRAY_ORIENTATION_VERT 1
+
+enum {
+  PROP_0,
+  PROP_ORIENTATION
+};
+
+struct _GtkTrayIconPrivate
+{
+  guint stamp;
+  
+  Atom selection_atom;
+  Atom manager_atom;
+  Atom system_tray_opcode_atom;
+  Atom orientation_atom;
+  Window manager_window;
+
+  GtkOrientation orientation;
+};
+         
+static void gtk_tray_icon_get_property  (GObject     *object,
+				 	 guint        prop_id,
+					 GValue      *value,
+					 GParamSpec  *pspec);
+
+static void     gtk_tray_icon_realize   (GtkWidget   *widget);
+static void     gtk_tray_icon_unrealize (GtkWidget   *widget);
+static gboolean gtk_tray_icon_delete    (GtkWidget   *widget,
+					 GdkEventAny *event);
+static gboolean gtk_tray_icon_expose    (GtkWidget      *widget, 
+					 GdkEventExpose *event);
+
+static void gtk_tray_icon_update_manager_window    (GtkTrayIcon *icon,
+						    gboolean     dock_if_realized);
+static void gtk_tray_icon_manager_window_destroyed (GtkTrayIcon *icon);
+
+G_DEFINE_TYPE (GtkTrayIcon, gtk_tray_icon, GTK_TYPE_PLUG)
+
+static void
+gtk_tray_icon_class_init (GtkTrayIconClass *class)
+{
+  GObjectClass *gobject_class = (GObjectClass *)class;
+  GtkWidgetClass *widget_class = (GtkWidgetClass *)class;
+
+  gobject_class->get_property = gtk_tray_icon_get_property;
+
+  widget_class->realize   = gtk_tray_icon_realize;
+  widget_class->unrealize = gtk_tray_icon_unrealize;
+  widget_class->delete_event = gtk_tray_icon_delete;
+  widget_class->expose_event = gtk_tray_icon_expose;
+
+  g_object_class_install_property (gobject_class,
+				   PROP_ORIENTATION,
+				   g_param_spec_enum ("orientation",
+						      _("Orientation"),
+						      _("The orientation of the tray"),
+						      GTK_TYPE_ORIENTATION,
+						      GTK_ORIENTATION_HORIZONTAL,
+						      GTK_PARAM_READABLE));
+
+  g_type_class_add_private (class, sizeof (GtkTrayIconPrivate));
+}
+
+static void
+gtk_tray_icon_init (GtkTrayIcon *icon)
+{
+  icon->priv = G_TYPE_INSTANCE_GET_PRIVATE (icon, GTK_TYPE_TRAY_ICON,
+					    GtkTrayIconPrivate);
+  
+  icon->priv->stamp = 1;
+  icon->priv->orientation = GTK_ORIENTATION_HORIZONTAL;
+
+  gtk_widget_set_app_paintable (GTK_WIDGET (icon), TRUE);
+  gtk_widget_set_double_buffered (GTK_WIDGET (icon), FALSE);
+  gtk_widget_add_events (GTK_WIDGET (icon), GDK_PROPERTY_CHANGE_MASK);
+}
+
+static void
+gtk_tray_icon_get_property (GObject    *object,
+			    guint       prop_id,
+			    GValue     *value,
+			    GParamSpec *pspec)
+{
+  GtkTrayIcon *icon = GTK_TRAY_ICON (object);
+
+  switch (prop_id)
+    {
+    case PROP_ORIENTATION:
+      g_value_set_enum (value, icon->priv->orientation);
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+    }
+}
+
+static gboolean
+gtk_tray_icon_expose (GtkWidget      *widget, 
+		      GdkEventExpose *event)
+{
+  gdk_window_clear_area (widget->window, event->area.x, event->area.y,
+			 event->area.width, event->area.height);
+
+  if (GTK_WIDGET_CLASS (gtk_tray_icon_parent_class)->expose_event)  
+    return GTK_WIDGET_CLASS (gtk_tray_icon_parent_class)->expose_event (widget, event);
+
+  return FALSE;
+}
+
+static void
+gtk_tray_icon_get_orientation_property (GtkTrayIcon *icon)
+{
+  Display *xdisplay;
+  Atom type;
+  int format;
+  union {
+	gulong *prop;
+	guchar *prop_ch;
+  } prop = { NULL };
+  gulong nitems;
+  gulong bytes_after;
+  int error, result;
+
+  g_assert (icon->priv->manager_window != None);
+  
+  xdisplay = GDK_DISPLAY_XDISPLAY (gtk_widget_get_display (GTK_WIDGET (icon)));
+
+  gdk_error_trap_push ();
+  type = None;
+  result = XGetWindowProperty (xdisplay,
+			       icon->priv->manager_window,
+			       icon->priv->orientation_atom,
+			       0, G_MAXLONG, FALSE,
+			       XA_CARDINAL,
+			       &type, &format, &nitems,
+			       &bytes_after, &(prop.prop_ch));
+  error = gdk_error_trap_pop ();
+
+  if (error || result != Success)
+    return;
+
+  if (type == XA_CARDINAL)
+    {
+      GtkOrientation orientation;
+
+      orientation = (prop.prop [0] == SYSTEM_TRAY_ORIENTATION_HORZ) ?
+					GTK_ORIENTATION_HORIZONTAL :
+					GTK_ORIENTATION_VERTICAL;
+
+      if (icon->priv->orientation != orientation)
+	{
+	  icon->priv->orientation = orientation;
+
+	  g_object_notify (G_OBJECT (icon), "orientation");
+	}
+    }
+
+  if (prop.prop)
+    XFree (prop.prop);
+}
+
+static GdkFilterReturn
+gtk_tray_icon_manager_filter (GdkXEvent *xevent, 
+			      GdkEvent  *event, 
+			      gpointer   user_data)
+{
+  GtkTrayIcon *icon = user_data;
+  XEvent *xev = (XEvent *)xevent;
+
+  if (xev->xany.type == ClientMessage &&
+      xev->xclient.message_type == icon->priv->manager_atom &&
+      xev->xclient.data.l[1] == icon->priv->selection_atom)
+    {
+      gtk_tray_icon_update_manager_window (icon, TRUE);
+    }
+  else if (xev->xany.window == icon->priv->manager_window)
+    {
+      if (xev->xany.type == PropertyNotify &&
+	  xev->xproperty.atom == icon->priv->orientation_atom)
+	{
+	  gtk_tray_icon_get_orientation_property (icon);
+	}
+      if (xev->xany.type == DestroyNotify)
+	{
+	  gtk_tray_icon_manager_window_destroyed (icon);
+	}
+    }
+  
+  return GDK_FILTER_CONTINUE;
+}
+
+static void
+gtk_tray_icon_unrealize (GtkWidget *widget)
+{
+  GtkTrayIcon *icon = GTK_TRAY_ICON (widget);
+  GdkWindow *root_window;
+
+  if (icon->priv->manager_window != None)
+    {
+      GdkWindow *gdkwin;
+
+      gdkwin = gdk_window_lookup_for_display (gtk_widget_get_display (widget),
+                                              icon->priv->manager_window);
+      
+      gdk_window_remove_filter (gdkwin, gtk_tray_icon_manager_filter, icon);
+    }
+
+  root_window = gdk_screen_get_root_window (gtk_widget_get_screen (widget));
+
+  gdk_window_remove_filter (root_window, gtk_tray_icon_manager_filter, icon);
+
+  if (GTK_WIDGET_CLASS (gtk_tray_icon_parent_class)->unrealize)
+    (* GTK_WIDGET_CLASS (gtk_tray_icon_parent_class)->unrealize) (widget);
+}
+
+static void
+gtk_tray_icon_send_manager_message (GtkTrayIcon *icon,
+				    long         message,
+				    Window       window,
+				    long         data1,
+				    long         data2,
+				    long         data3)
+{
+  XClientMessageEvent ev;
+  Display *display;
+  
+  memset (&ev, 0, sizeof (ev));
+  ev.type = ClientMessage;
+  ev.window = window;
+  ev.message_type = icon->priv->system_tray_opcode_atom;
+  ev.format = 32;
+  ev.data.l[0] = gdk_x11_get_server_time (GTK_WIDGET (icon)->window);
+  ev.data.l[1] = message;
+  ev.data.l[2] = data1;
+  ev.data.l[3] = data2;
+  ev.data.l[4] = data3;
+
+  display = GDK_DISPLAY_XDISPLAY (gtk_widget_get_display (GTK_WIDGET (icon)));
+  
+  gdk_error_trap_push ();
+  XSendEvent (display,
+	      icon->priv->manager_window, False, NoEventMask, (XEvent *)&ev);
+  XSync (display, False);
+  gdk_error_trap_pop ();
+}
+
+static void
+gtk_tray_icon_send_dock_request (GtkTrayIcon *icon)
+{
+  gtk_tray_icon_send_manager_message (icon,
+				      SYSTEM_TRAY_REQUEST_DOCK,
+				      icon->priv->manager_window,
+				      gtk_plug_get_id (GTK_PLUG (icon)),
+				      0, 0);
+}
+
+static void
+gtk_tray_icon_update_manager_window (GtkTrayIcon *icon,
+				     gboolean     dock_if_realized)
+{
+  Display *xdisplay;
+  
+  if (icon->priv->manager_window != None)
+    return;
+
+  xdisplay = GDK_DISPLAY_XDISPLAY (gtk_widget_get_display (GTK_WIDGET (icon)));
+  
+  XGrabServer (xdisplay);
+  
+  icon->priv->manager_window = XGetSelectionOwner (xdisplay,
+						   icon->priv->selection_atom);
+
+  if (icon->priv->manager_window != None)
+    XSelectInput (xdisplay,
+		  icon->priv->manager_window, StructureNotifyMask|PropertyChangeMask);
+
+  XUngrabServer (xdisplay);
+  XFlush (xdisplay);
+  
+  if (icon->priv->manager_window != None)
+    {
+      GdkWindow *gdkwin;
+
+      gdkwin = gdk_window_lookup_for_display (gtk_widget_get_display (GTK_WIDGET (icon)),
+					      icon->priv->manager_window);
+      
+      gdk_window_add_filter (gdkwin, gtk_tray_icon_manager_filter, icon);
+
+      if (dock_if_realized && GTK_WIDGET_REALIZED (icon))
+	gtk_tray_icon_send_dock_request (icon);
+
+      gtk_tray_icon_get_orientation_property (icon);
+    }
+}
+
+static void
+gtk_tray_icon_manager_window_destroyed (GtkTrayIcon *icon)
+{
+  GdkWindow *gdkwin;
+  
+  g_return_if_fail (icon->priv->manager_window != None);
+
+  gdkwin = gdk_window_lookup_for_display (gtk_widget_get_display (GTK_WIDGET (icon)),
+					  icon->priv->manager_window);
+      
+  gdk_window_remove_filter (gdkwin, gtk_tray_icon_manager_filter, icon);
+
+  icon->priv->manager_window = None;
+
+  gtk_tray_icon_update_manager_window (icon, TRUE);
+}
+
+static gboolean 
+gtk_tray_icon_delete (GtkWidget   *widget,
+		      GdkEventAny *event)
+{
+  GtkTrayIcon *icon = GTK_TRAY_ICON (widget);
+  GdkWindow *gdkwin;
+
+  if (icon->priv->manager_window != None)
+    {  
+      gdkwin = gdk_window_lookup_for_display (gtk_widget_get_display (GTK_WIDGET (icon)),
+					      icon->priv->manager_window);
+      
+      gdk_window_remove_filter (gdkwin, gtk_tray_icon_manager_filter, icon);
+      
+      icon->priv->manager_window = None;
+    }
+
+  gtk_tray_icon_update_manager_window (icon, TRUE);  
+
+  return TRUE;
+}
+
+static void
+gtk_tray_icon_realize (GtkWidget *widget)
+{
+  GtkTrayIcon *icon = GTK_TRAY_ICON (widget);
+  GdkScreen *screen;
+  GdkDisplay *display;
+  Display *xdisplay;
+  char buffer[256];
+  GdkWindow *root_window;
+
+  if (GTK_WIDGET_CLASS (gtk_tray_icon_parent_class)->realize)
+    GTK_WIDGET_CLASS (gtk_tray_icon_parent_class)->realize (widget);
+
+  screen = gtk_widget_get_screen (widget);
+  display = gdk_screen_get_display (screen);
+  xdisplay = gdk_x11_display_get_xdisplay (display);
+
+  /* Now see if there's a manager window around */
+  g_snprintf (buffer, sizeof (buffer),
+	      "_NET_SYSTEM_TRAY_S%d",
+	      gdk_screen_get_number (screen));
+
+  icon->priv->selection_atom = XInternAtom (xdisplay, buffer, False);
+  
+  icon->priv->manager_atom = XInternAtom (xdisplay, "MANAGER", False);
+  
+  icon->priv->system_tray_opcode_atom = XInternAtom (xdisplay,
+						     "_NET_SYSTEM_TRAY_OPCODE",
+						     False);
+
+  icon->priv->orientation_atom = XInternAtom (xdisplay,
+					      "_NET_SYSTEM_TRAY_ORIENTATION",
+					      False);
+
+  gtk_tray_icon_update_manager_window (icon, FALSE);
+  gtk_tray_icon_send_dock_request (icon);
+
+  root_window = gdk_screen_get_root_window (screen);
+  
+  /* Add a root window filter so that we get changes on MANAGER */
+  gdk_window_add_filter (root_window,
+			 gtk_tray_icon_manager_filter, icon);
+}
+
+guint
+_gtk_tray_icon_send_message (GtkTrayIcon *icon,
+			     gint         timeout,
+			     const gchar *message,
+			     gint         len)
+{
+  guint stamp;
+  
+  g_return_val_if_fail (GTK_IS_TRAY_ICON (icon), 0);
+  g_return_val_if_fail (timeout >= 0, 0);
+  g_return_val_if_fail (message != NULL, 0);
+		     
+  if (icon->priv->manager_window == None)
+    return 0;
+
+  if (len < 0)
+    len = strlen (message);
+
+  stamp = icon->priv->stamp++;
+  
+  /* Get ready to send the message */
+  gtk_tray_icon_send_manager_message (icon, SYSTEM_TRAY_BEGIN_MESSAGE,
+				      (Window)gtk_plug_get_id (GTK_PLUG (icon)),
+				      timeout, len, stamp);
+
+  /* Now to send the actual message */
+  gdk_error_trap_push ();
+  while (len > 0)
+    {
+      XClientMessageEvent ev;
+      Display *xdisplay;
+
+      xdisplay = GDK_DISPLAY_XDISPLAY (gtk_widget_get_display (GTK_WIDGET (icon)));
+      
+      memset (&ev, 0, sizeof (ev));
+      ev.type = ClientMessage;
+      ev.window = (Window)gtk_plug_get_id (GTK_PLUG (icon));
+      ev.format = 8;
+      ev.message_type = XInternAtom (xdisplay,
+				     "_NET_SYSTEM_TRAY_MESSAGE_DATA", False);
+      if (len > 20)
+	{
+	  memcpy (&ev.data, message, 20);
+	  len -= 20;
+	  message += 20;
+	}
+      else
+	{
+	  memcpy (&ev.data, message, len);
+	  len = 0;
+	}
+
+      XSendEvent (xdisplay,
+		  icon->priv->manager_window, False, 
+		  StructureNotifyMask, (XEvent *)&ev);
+      XSync (xdisplay, False);
+    }
+
+  gdk_error_trap_pop ();
+
+  return stamp;
+}
+
+void
+_gtk_tray_icon_cancel_message (GtkTrayIcon *icon,
+			       guint        id)
+{
+  g_return_if_fail (GTK_IS_TRAY_ICON (icon));
+  g_return_if_fail (id > 0);
+  
+  gtk_tray_icon_send_manager_message (icon, SYSTEM_TRAY_CANCEL_MESSAGE,
+				      (Window)gtk_plug_get_id (GTK_PLUG (icon)),
+				      id, 0, 0);
+}
+
+GtkTrayIcon *
+_gtk_tray_icon_new_for_screen (GdkScreen  *screen, 
+			       const gchar *name)
+{
+  g_return_val_if_fail (GDK_IS_SCREEN (screen), NULL);
+
+  return g_object_new (GTK_TYPE_TRAY_ICON, 
+		       "screen", screen, 
+		       "title", name, 
+		       NULL);
+}
+
+GtkTrayIcon*
+_gtk_tray_icon_new (const gchar *name)
+{
+  return g_object_new (GTK_TYPE_TRAY_ICON, 
+		       "title", name, 
+		       NULL);
+}
+
+GtkOrientation
+_gtk_tray_icon_get_orientation (GtkTrayIcon *icon)
+{
+  g_return_val_if_fail (GTK_IS_TRAY_ICON (icon), GTK_ORIENTATION_HORIZONTAL);
+
+  return icon->priv->orientation;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/statusicon/gtktrayicon.h	Mon Jan 15 11:54:32 2007 -0800
@@ -0,0 +1,75 @@
+/* gtktrayicon.h
+ * Copyright (C) 2002 Anders Carlsson <andersca@gnu.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __GTK_TRAY_ICON_H__
+#define __GTK_TRAY_ICON_H__
+
+#include <gtk/gtkplug.h>
+
+G_BEGIN_DECLS
+
+#define GTK_TYPE_TRAY_ICON		(gtk_tray_icon_get_type ())
+#define GTK_TRAY_ICON(obj)		(G_TYPE_CHECK_INSTANCE_CAST ((obj), GTK_TYPE_TRAY_ICON, GtkTrayIcon))
+#define GTK_TRAY_ICON_CLASS(klass)	(G_TYPE_CHECK_CLASS_CAST ((klass), GTK_TYPE_TRAY_ICON, GtkTrayIconClass))
+#define GTK_IS_TRAY_ICON(obj)		(G_TYPE_CHECK_INSTANCE_TYPE ((obj), GTK_TYPE_TRAY_ICON))
+#define GTK_IS_TRAY_ICON_CLASS(klass)	(G_TYPE_CHECK_CLASS_TYPE ((klass), GTK_TYPE_TRAY_ICON))
+#define GTK_TRAY_ICON_GET_CLASS(obj)	(G_TYPE_INSTANCE_GET_CLASS ((obj), GTK_TYPE_TRAY_ICON, GtkTrayIconClass))
+	
+typedef struct _GtkTrayIcon	   GtkTrayIcon;
+typedef struct _GtkTrayIconPrivate GtkTrayIconPrivate;
+typedef struct _GtkTrayIconClass   GtkTrayIconClass;
+
+struct _GtkTrayIcon
+{
+  GtkPlug parent_instance;
+
+  GtkTrayIconPrivate *priv;
+};
+
+struct _GtkTrayIconClass
+{
+  GtkPlugClass parent_class;
+
+  void (*__gtk_reserved1);
+  void (*__gtk_reserved2);
+  void (*__gtk_reserved3);
+  void (*__gtk_reserved4);
+  void (*__gtk_reserved5);
+  void (*__gtk_reserved6);
+};
+
+GType          gtk_tray_icon_get_type         (void) G_GNUC_CONST;
+
+GtkTrayIcon   *_gtk_tray_icon_new_for_screen  (GdkScreen   *screen,
+					       const gchar *name);
+
+GtkTrayIcon   *_gtk_tray_icon_new             (const gchar *name);
+
+guint          _gtk_tray_icon_send_message    (GtkTrayIcon *icon,
+					       gint         timeout,
+					       const gchar *message,
+					       gint         len);
+void           _gtk_tray_icon_cancel_message  (GtkTrayIcon *icon,
+					       guint        id);
+
+GtkOrientation _gtk_tray_icon_get_orientation (GtkTrayIcon *icon);
+					    
+G_END_DECLS
+
+#endif /* __GTK_TRAY_ICON_H__ */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/statusicon/si.c	Mon Jan 15 11:54:32 2007 -0800
@@ -0,0 +1,103 @@
+/*
+*
+* Author: Giacomo Lozito <james@develia.org>, (C) 2005-2007
+*
+* This program is free software; you can redistribute it and/or modify it
+* under the terms of the GNU General Public License as published by the
+* Free Software Foundation; either version 2 of the License, or (at your
+* option) any later version.
+*
+* This program is distributed in the hope that it will be useful, but
+* WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+* General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License along
+* with this program; if not, write to the Free Software Foundation, Inc.,
+* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301  USA
+*
+*/
+
+#include "si.h"
+#include "si_ui.h"
+#include "si_audacious.h"
+#include "si_common.h"
+
+
+static gboolean plugin_active = FALSE;
+
+
+/* ***************** */
+/* plug-in functions */
+
+GeneralPlugin *get_gplugin_info()
+{
+   return &si_gp;
+}
+
+
+void
+si_init ( void )
+{
+  g_type_init();
+  g_log_set_handler( NULL , G_LOG_LEVEL_WARNING , g_log_default_handler , NULL );
+  plugin_active = TRUE;
+  si_ui_statusicon_show();
+  return;
+}
+
+
+void
+si_cleanup ( void )
+{
+  if ( plugin_active == TRUE )
+  {
+    plugin_active = FALSE;
+    si_ui_statusicon_hide();
+  }
+  return;
+}
+
+
+void
+si_about ( void )
+{
+  si_ui_about_show();
+}
+
+
+
+/* ***************** */
+/* audacious actions */
+
+
+void
+si_audacious_toggle_visibility ( void )
+{
+  static gboolean eq_prevstatus = FALSE;
+  static gboolean pl_prevstatus = FALSE;
+
+  /* use the main window visibility status to toggle show/hide */
+  if ( xmms_remote_is_main_win( si_gp.xmms_session ) == TRUE )
+  {
+    /* remember the visibility status of equalizer and playlist windows */
+    eq_prevstatus = xmms_remote_is_eq_win( si_gp.xmms_session );
+    pl_prevstatus = xmms_remote_is_pl_win( si_gp.xmms_session );
+    /* now hide all of the player windows */
+    xmms_remote_main_win_toggle( si_gp.xmms_session , FALSE );
+    if ( eq_prevstatus == TRUE )
+      xmms_remote_eq_win_toggle( si_gp.xmms_session , FALSE );
+    if ( pl_prevstatus == TRUE )
+      xmms_remote_pl_win_toggle( si_gp.xmms_session , FALSE );
+  }
+  else
+  {
+    /* show the main player window again */
+    xmms_remote_main_win_toggle( si_gp.xmms_session , TRUE );
+    /* show the equalizer and playlist window if they were visible before */
+    if ( eq_prevstatus == TRUE )
+      xmms_remote_eq_win_toggle( si_gp.xmms_session , TRUE );
+    if ( pl_prevstatus == TRUE )
+      xmms_remote_pl_win_toggle( si_gp.xmms_session , TRUE );
+  }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/statusicon/si.h	Mon Jan 15 11:54:32 2007 -0800
@@ -0,0 +1,44 @@
+/*
+*
+* Author: Giacomo Lozito <james@develia.org>, (C) 2005-2007
+*
+* This program is free software; you can redistribute it and/or modify it
+* under the terms of the GNU General Public License as published by the
+* Free Software Foundation; either version 2 of the License, or (at your
+* option) any later version.
+*
+* This program is distributed in the hope that it will be useful, but
+* WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+* General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License along
+* with this program; if not, write to the Free Software Foundation, Inc.,
+* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301  USA
+*
+*/
+
+#ifndef _I_SI_H
+#define _I_SI_H 1
+
+#include "si_common.h"
+#include <glib.h>
+#include <audacious/plugin.h>
+
+void si_init ( void );
+void si_cleanup ( void );
+void si_about ( void );
+
+GeneralPlugin si_gp =
+{
+    NULL,					/* handle */
+    NULL,					/* filename */
+    -1,						/* session */
+    "Status Icon Plugin " SI_VERSION_PLUGIN,	/* description */
+    si_init,					/* init */
+    si_about,					/* about */
+    NULL,					/* configure */
+    si_cleanup					/* cleanup */
+};
+
+#endif /* !_I_SI_H */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/statusicon/si.xpm	Mon Jan 15 11:54:32 2007 -0800
@@ -0,0 +1,117 @@
+/* XPM */
+static char * si_xpm[] = {
+"22 22 92 2",
+"  	c None",
+". 	c #000000",
+"+ 	c #0D0D0D",
+"@ 	c #202020",
+"# 	c #080808",
+"$ 	c #444444",
+"% 	c #949494",
+"& 	c #BEBEBE",
+"* 	c #C6C6C6",
+"= 	c #929292",
+"- 	c #434343",
+"; 	c #1F1F1F",
+"> 	c #919191",
+", 	c #D1D1D1",
+"' 	c #D9D9D9",
+") 	c #D0D0D0",
+"! 	c #909090",
+"~ 	c #1E1E1E",
+"{ 	c #A8A8A8",
+"] 	c #A7A7A7",
+"^ 	c #D6D6D6",
+"/ 	c #B0B0B0",
+"( 	c #8E8E8E",
+"_ 	c #6F6F6F",
+": 	c #686868",
+"< 	c #C5C5C5",
+"[ 	c #D8D8D8",
+"} 	c #939393",
+"| 	c #454545",
+"1 	c #ADADAD",
+"2 	c #101010",
+"3 	c #2B2B2B",
+"4 	c #010101",
+"5 	c #959595",
+"6 	c #B9B9B9",
+"7 	c #414141",
+"8 	c #424242",
+"9 	c #040404",
+"0 	c #4C4C4C",
+"a 	c #D5D5D5",
+"b 	c #979797",
+"c 	c #111111",
+"d 	c #C0C0C0",
+"e 	c #CFCFCF",
+"f 	c #0B0B0B",
+"g 	c #262626",
+"h 	c #242424",
+"i 	c #C8C8C8",
+"j 	c #AAAAAA",
+"k 	c #151515",
+"l 	c #BCBCBC",
+"m 	c #252525",
+"n 	c #383838",
+"o 	c #353535",
+"p 	c #282828",
+"q 	c #C7C7C7",
+"r 	c #131313",
+"s 	c #C1C1C1",
+"t 	c #B8B8B8",
+"u 	c #0F0F0F",
+"v 	c #020202",
+"w 	c #636363",
+"x 	c #565656",
+"y 	c #373737",
+"z 	c #9C9C9C",
+"A 	c #2C2C2C",
+"B 	c #030303",
+"C 	c #C9C9C9",
+"D 	c #9E9E9E",
+"E 	c #D3D3D3",
+"F 	c #A0A0A0",
+"G 	c #505050",
+"H 	c #3B3B3B",
+"I 	c #1C1C1C",
+"J 	c #707070",
+"K 	c #4D4D4D",
+"L 	c #999999",
+"M 	c #C4C4C4",
+"N 	c #A9A9A9",
+"O 	c #D4D4D4",
+"P 	c #9A9A9A",
+"Q 	c #AFAFAF",
+"R 	c #AEAEAE",
+"S 	c #9B9B9B",
+"T 	c #0C0C0C",
+"U 	c #A1A1A1",
+"V 	c #C2C2C2",
+"W 	c #CACACA",
+"X 	c #9F9F9F",
+"Y 	c #4E4E4E",
+"Z 	c #161616",
+"` 	c #292929",
+". . . . . . . . . . . . . . . . . . . . . . ",
+". . . . . . . . . . . . . . . . . . . . . . ",
+". . . . . . . . . + @ @ + . . . . . . . . . ",
+". . . . . . # $ % & * * & = - # . . . . . . ",
+". . . . . ; > , ' ' ' ' ' ' ) ! ~ . . . . . ",
+". . . . ; { ' ' ' ' ' ' ' ' ' ' ] ; . . . . ",
+". . . # = ' ' ^ / ( _ : > < [ ' ' } # . . . ",
+". . . | , ' ' 1 2 . . . . 3 / ' ' , | . . . ",
+". . 4 5 ' ' ' 6 7 | 8 9 . . 0 a ' ' b 4 . . ",
+". . c d ' ' ' ' e ] _ f . . g < ' ' d 2 . . ",
+". . h i ' ' ' j ; . . . . . k l ' ' i m . . ",
+". . m i ' ' < g . 4 n o 4 . p q ' ' i m . . ",
+". . r s ' ' t u . v w x 4 . y e ' ' s r . . ",
+". . 4 z ' ' i A . . B B . . A C ' ' D 4 . . ",
+". . . 0 E ' [ F g v h G H I J a ' E K . . . ",
+". . . f L ' ' [ M N M [ e d O ' ' P f . . . ",
+". . . . m Q ' ' ' ' ' ' ' ' ' ' R h . . . . ",
+". . . . . g S E ' ' ' ' ' ' E S m . . . . . ",
+". . . . . . T G U V W W V X Y f . . . . . . ",
+". . . . . . . . 4 Z ` ` k 4 . . . . . . . . ",
+". . . . . . . . . . . . . . . . . . . . . . ",
+". . . . . . . . . . . . . . . . . . . . . . "};
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/statusicon/si_audacious.h	Mon Jan 15 11:54:32 2007 -0800
@@ -0,0 +1,30 @@
+/*
+*
+* Author: Giacomo Lozito <james@develia.org>, (C) 2005-2007
+*
+* This program is free software; you can redistribute it and/or modify it
+* under the terms of the GNU General Public License as published by the
+* Free Software Foundation; either version 2 of the License, or (at your
+* option) any later version.
+*
+* This program is distributed in the hope that it will be useful, but
+* WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+* General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License along
+* with this program; if not, write to the Free Software Foundation, Inc.,
+* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301  USA
+*
+*/
+
+#ifndef _I_SI_AUDACIOUS_H
+#define _I_SI_AUDACIOUS_H 1
+
+#include "si_common.h"
+
+
+void si_audacious_toggle_visibility ( void );
+
+
+#endif /* !_I_SI_AUDACIOUS_H */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/statusicon/si_common.h	Mon Jan 15 11:54:32 2007 -0800
@@ -0,0 +1,35 @@
+/*
+*
+* Author: Giacomo Lozito <james@develia.org>, (C) 2005-2007
+*
+* This program is free software; you can redistribute it and/or modify it
+* under the terms of the GNU General Public License as published by the
+* Free Software Foundation; either version 2 of the License, or (at your
+* option) any later version.
+*
+* This program is distributed in the hope that it will be useful, but
+* WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+* General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License along
+* with this program; if not, write to the Free Software Foundation, Inc.,
+* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301  USA
+*
+*/
+
+#ifndef _I_SI_COMMON_H
+#define _I_SI_COMMON_H 1
+
+#ifdef DEBUG
+#include <stdio.h>
+#define DEBUGMSG(...) { fprintf(stderr, "statusicon(%s:%s:%d): ", __FILE__, __FUNCTION__, (int) __LINE__); fprintf(stderr, __VA_ARGS__); }
+#else
+#define DEBUGMSG(...)
+#endif /* DEBUG */
+
+#include "../../config.h"
+
+#define SI_VERSION_PLUGIN "0.1beta"
+
+#endif /* !_I_ED_COMMON_H */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/statusicon/si_ui.c	Mon Jan 15 11:54:32 2007 -0800
@@ -0,0 +1,148 @@
+/*
+*
+* Author: Giacomo Lozito <james@develia.org>, (C) 2005-2007
+*
+* This program is free software; you can redistribute it and/or modify it
+* under the terms of the GNU General Public License as published by the
+* Free Software Foundation; either version 2 of the License, or (at your
+* option) any later version.
+*
+* This program is distributed in the hope that it will be useful, but
+* WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+* General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License along
+* with this program; if not, write to the Free Software Foundation, Inc.,
+* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301  USA
+*
+*/
+
+#include "si_ui.h"
+#include "si_audacious.h"
+#include "si_common.h"
+#include "gtktrayicon.h"
+#include "si.xpm"
+#include <glib.h>
+#include <glib/gi18n.h>
+#include <gtk/gtk.h>
+
+
+static GtkWidget * si_evbox = NULL;
+
+
+static GtkTrayIcon *
+si_ui_statusicon_create ( void )
+{
+  GtkTrayIcon *si_applet = NULL;
+
+  si_applet = _gtk_tray_icon_new( "audacious" );
+
+  gtk_widget_show( GTK_WIDGET(si_applet) );
+
+  return si_applet;
+}
+
+
+static GtkWidget *
+si_ui_rmenu_create ( GtkWidget * evbox )
+{
+  GtkWidget *menu;
+  GtkWidget *menuitem;
+
+  menu = gtk_menu_new();
+
+  /* gtk_widget_show_all( GTK_WIDGET(menu) ); */
+  return menu;
+}
+
+
+static gboolean
+si_ui_statusicon_cb_btpress ( GtkWidget * evbox , GdkEventButton * event )
+{
+  switch ( event->button )
+  {
+    case 1:
+    {
+      si_audacious_toggle_visibility();
+      break;
+    }
+
+/*
+    case 3:
+    {
+      GtkWidget *si_rmenu = GTK_WIDGET(g_object_get_data( G_OBJECT(evbox) , "rmenu" ));
+      gtk_menu_popup( GTK_MENU(si_rmenu) , NULL , NULL ,
+                      NULL , NULL , event->button , event->time );
+      break;
+    }
+*/
+  }
+
+  return FALSE;
+}
+
+
+void
+si_ui_statusicon_show ( void )
+{
+  GtkWidget *si_image;
+  GtkWidget *si_rmenu;
+  GdkPixbuf *si_pixbuf;
+  GtkTrayIcon *si_applet;
+
+  si_applet = si_ui_statusicon_create();
+  if ( si_applet == NULL )
+  {
+    g_warning( "StatusIcon plugin: unable to create a status icon.\n" );
+    return;
+  }
+
+  si_pixbuf = gdk_pixbuf_new_from_xpm_data( (const char**)si_xpm );
+  si_image = gtk_image_new_from_pixbuf( si_pixbuf );
+  g_object_unref( si_pixbuf );
+
+  si_evbox = gtk_event_box_new();
+  si_rmenu = si_ui_rmenu_create( si_evbox );
+
+  g_object_set_data( G_OBJECT(si_evbox) , "rmenu" , si_rmenu );
+  g_object_set_data( G_OBJECT(si_evbox) , "applet" , si_applet );
+  g_signal_connect( G_OBJECT(si_evbox) , "button-press-event" ,
+                    G_CALLBACK(si_ui_statusicon_cb_btpress) , NULL );
+
+  gtk_container_add( GTK_CONTAINER(si_evbox), si_image );
+  gtk_container_add( GTK_CONTAINER(si_applet), si_evbox );
+
+  gtk_widget_show_all( GTK_WIDGET(si_applet) );
+  return;
+}
+
+
+void
+si_ui_statusicon_hide ( void )
+{
+  if ( si_evbox != NULL )
+  {
+    GtkTrayIcon *si_applet = g_object_get_data( G_OBJECT(si_evbox) , "applet" );
+    GtkWidget *si_rmenu = g_object_get_data( G_OBJECT(si_evbox) , "rmenu" );
+    gtk_widget_destroy( GTK_WIDGET(si_evbox) );
+    gtk_widget_destroy( GTK_WIDGET(si_rmenu) );
+    gtk_widget_destroy( GTK_WIDGET(si_applet) );
+  }
+  return;
+}
+
+
+void
+si_ui_about_show ( void )
+{
+  GtkWidget *about_dlg = gtk_message_dialog_new(
+    NULL , 0 , GTK_MESSAGE_INFO , GTK_BUTTONS_CLOSE ,
+    _( "Status Icon Plugin " SI_VERSION_PLUGIN "\n"
+       "written by Giacomo Lozito < james@develia.org >\n\n"
+       "This plugin provides a status icon, placed in\n"
+       "the system tray area of the window manager.\n" ) );
+  gtk_dialog_run( GTK_DIALOG(about_dlg) );
+  gtk_widget_destroy( about_dlg );
+  return;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/statusicon/si_ui.h	Mon Jan 15 11:54:32 2007 -0800
@@ -0,0 +1,32 @@
+/*
+*
+* Author: Giacomo Lozito <james@develia.org>, (C) 2005-2007
+*
+* This program is free software; you can redistribute it and/or modify it
+* under the terms of the GNU General Public License as published by the
+* Free Software Foundation; either version 2 of the License, or (at your
+* option) any later version.
+*
+* This program is distributed in the hope that it will be useful, but
+* WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+* General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License along
+* with this program; if not, write to the Free Software Foundation, Inc.,
+* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301  USA
+*
+*/
+
+#ifndef _I_SI_UI_H
+#define _I_SI_UI_H 1
+
+#include "si_ui.h"
+
+
+void si_ui_statusicon_show( void );
+void si_ui_statusicon_hide( void );
+void si_ui_about_show( void );
+
+
+#endif /* !_I_SI_UI_H */