[PATCH] Implement _NET_WM_ICON support for retrieving the application icon

Bernhard Walle <[email protected]>
Newsgroups gmane.comp.desktop.rox.devel
Message-ID <[email protected]>
This patch tries first to retrieve the application icon from the
_NET_WM_ICON property. If that fails, it uses Motif hints as before.

Some toolkits (like Java/Swing) don't set Motif hints for icons at all.

See http://standards.freedesktop.org/wm-spec/1.3/ar01s05.html#id2523482 for
a description of that standard.

Tested with on i386-linux using ROX 2.8.


Signed-off-by: Bernhard Walle <[email protected]>

---
 ROX-Filer/src/tasklist.c |  117 ++++++++++++++++++++++++++++++++++-------------
 1 file changed, 87 insertions(+), 30 deletions(-)

--- a/ROX-Filer/src/tasklist.c
+++ b/ROX-Filer/src/tasklist.c
@@ -25,6 +25,7 @@
 #include "config.h"
 
 #include <stdlib.h>
+#include <string.h>
 
 #include <gtk/gtk.h>
 #include <gdk/gdk.h>
@@ -70,6 +71,7 @@ static GdkAtom xa_TEXT = GDK_NONE;
 static GdkAtom xa__NET_WM_VISIBLE_NAME = GDK_NONE;
 static GdkAtom xa__NET_WM_ICON_NAME = GDK_NONE;
 static GdkAtom xa__NET_CLIENT_LIST = GDK_NONE;
+static GdkAtom xa__NET_WM_ICON = GDK_NONE;
 static GdkAtom xa__NET_WM_ICON_GEOMETRY = GDK_NONE;
 static GdkAtom xa__NET_WM_STATE = GDK_NONE;
 static GdkAtom xa__NET_WM_STATE_HIDDEN = GDK_NONE;
@@ -138,6 +140,8 @@ void tasklist_set_active(gboolean active
 			gdk_atom_intern("_NET_WM_VISIBLE_NAME", FALSE);
 		xa__NET_WM_ICON_NAME =
 			gdk_atom_intern("_NET_WM_ICON_NAME", FALSE);
+		xa__NET_WM_ICON =
+			gdk_atom_intern("_NET_WM_ICON", FALSE);
 		xa__NET_WM_ICON_GEOMETRY =
 			gdk_atom_intern("_NET_WM_ICON_GEOMETRY", FALSE);
 		xa__NET_WM_STATE = gdk_atom_intern("_NET_WM_STATE", FALSE);
@@ -816,6 +820,51 @@ static GdkPixbuf *apply_window_effect(Gd
 	return new;
 }
 
+static void argb_to_abgr(guint32 *p, gsize length)
+{
+	gint i;
+
+        for (i = 0; i < length; i++)
+		p[i] = (p[i] & 0xff00ff00) | ((p[i] & 0xff) << 16) | ((p[i] >> 16) & 0xff);
+}
+
+static GdkPixbuf *read_window_icon (Display *xdisplay, Window w)
+{
+	Atom type;
+	gint format;
+	gulong length, rest;
+	guint32 *data;
+	GdkPixbuf *pixbuf = NULL;
+	guint32 *p;
+
+	if (XGetWindowProperty(xdisplay, w, gdk_x11_atom_to_xatom(xa__NET_WM_ICON), 0, G_MAXLONG,
+			FALSE, AnyPropertyType,  &type, &format, &length,
+			&rest, (guchar **)&data) != Success || length <= 2)
+		return NULL;
+
+	gint width = data[0];
+	gint height = data[1];
+
+	p = g_malloc(width * height * sizeof(guint32));
+	memcpy(p, data + 2, width * height * sizeof(guint32));
+	XFree(data);
+
+	argb_to_abgr(p, width * height);
+
+	pixbuf = gdk_pixbuf_new_from_data((guchar *)p, GDK_COLORSPACE_RGB, TRUE, 8, width, height,
+		width * 4,  NULL, NULL);
+	if (width > 24 || height > 24)
+	{
+		GdkPixbuf *scaled = gdk_pixbuf_scale_simple (pixbuf, 24, 24, GDK_INTERP_NEAREST);
+		g_object_unref (pixbuf);
+
+		pixbuf = scaled;
+	}
+
+	return pixbuf;
+}
+
+
 /* Return a suitable icon for this window. unref the result.
  * Never returns NULL.
  */
@@ -827,49 +876,57 @@ static GdkPixbuf *get_image_for(IconWind
 	XWMHints *hints;
 	GdkPixbuf *retval = NULL;
 
-	/* Try the pixmap and mask in the old WMHints... */
-	gdk_error_trap_push();
-	hints = XGetWMHints(gdk_display, win->xwindow);
-
-	if (hints)
+	retval = read_window_icon(gdk_display, win->xwindow);
+	if (retval)
 	{
-		if (hints->flags & IconPixmapHint)
-			pixmap = hints->icon_pixmap;
-		if (hints->flags & IconMaskHint)
-			mask = hints->icon_mask;
-
-		XFree(hints);
-		hints = NULL;
+		g_object_ref(retval);
 	}
-
-	if (pixmap != None)
+	else
 	{
-		GdkPixbuf *mask_pb = NULL;
-		
-		retval = pixbuf_from_pixmap(pixmap);
+		/* Try the pixmap and mask in the old WMHints... */
+		gdk_error_trap_push();
+		hints = XGetWMHints(gdk_display, win->xwindow);
 
-		if (retval && mask != None)
-			mask_pb = pixbuf_from_pixmap(mask);
+		if (hints)
+		{
+			if (hints->flags & IconPixmapHint)
+				pixmap = hints->icon_pixmap;
+			if (hints->flags & IconMaskHint)
+				mask = hints->icon_mask;
 
-		if (mask_pb)
+			XFree(hints);
+			hints = NULL;
+		}
+
+		if (pixmap != None)
 		{
-			GdkPixbuf *masked;
+			GdkPixbuf *mask_pb = NULL;
+
+			retval = pixbuf_from_pixmap(pixmap);
 
-			masked = apply_mask(retval, mask_pb);
-			g_object_unref(G_OBJECT(mask_pb));
+			if (retval && mask != None)
+				mask_pb = pixbuf_from_pixmap(mask);
 
-			if (masked)
+			if (mask_pb)
 			{
-				g_object_unref(G_OBJECT(retval));
-				retval = masked;
+				GdkPixbuf *masked;
+
+				masked = apply_mask(retval, mask_pb);
+				g_object_unref(G_OBJECT(mask_pb));
+
+				if (masked)
+				{
+					g_object_unref(G_OBJECT(retval));
+					retval = masked;
+				}
 			}
 		}
-	}
 
-	gdk_flush();
+		gdk_flush();
+
+		gdk_error_trap_pop();
+	}
 
-	gdk_error_trap_pop();
-	
 	if (!retval)
 	{
 		if (!default_icon)

-------------------------------------------------------------------------
This SF.net email is sponsored by: Microsoft
Defy all challenges. Microsoft(R) Visual Studio 2008.
http://clk.atdmt.com/MRT/go/vse0120000070mrt/direct/01/
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.