add icon drawing support to the pager plugin (adapted from an older patch written for fbpanel-4.3 by daf@minuslab.net) Index: plugins/pager/pager.c --- plugins/pager/pager.c.orig +++ plugins/pager/pager.c @@ -27,10 +27,13 @@ #include +#include +#include #include "panel.h" #include "misc.h" #include "plugin.h" +#include "data/images/default.xpm" #include "gtkbgbox.h" //#define DEBUGPRN @@ -49,6 +52,8 @@ typedef struct _task { char *name, *iname; net_wm_state nws; net_wm_window_type nwwt; + GdkPixbuf *pixbuf; + unsigned int using_netwm_icon:1; } task; typedef struct _desk desk; @@ -81,6 +86,7 @@ struct _pager_priv { task *focusedtask; FbBg *fbbg; gint dah, daw; + GdkPixbuf *gen_pixbuf; }; @@ -152,6 +158,9 @@ task_remove_stale(Window *win, task *t, pager_priv *p) static gboolean task_remove_all(Window *win, task *t, pager_priv *p) { + if (t->pixbuf != NULL) + g_object_unref(t->pixbuf); + g_free(t); return TRUE; } @@ -221,7 +230,47 @@ task_update_pix(task *t, desk *d) widget->style->fg_gc[GTK_STATE_SELECTED] : widget->style->fg_gc[GTK_STATE_NORMAL], FALSE, - x, y, w, h); + x, y, w-1, h); + + if (w>=10 && h>=10) { + GdkPixbuf* source_buf = t->pixbuf; + if (source_buf == NULL) + source_buf = d->pg->gen_pixbuf; + + /* determine how much to scale */ + GdkPixbuf* scaled = source_buf; + int scale = 16; + int noscale = 1; + int smallest = ( (wpix, + NULL, + scaled, + 0, 0, + pixx, pixy, + -1, -1, + GDK_RGB_DITHER_NONE, + 0, 0); + + /* free it if its been scaled and its not the default */ + if (!noscale && t->pixbuf != NULL) + g_object_unref(scaled); + } RET(); } @@ -492,6 +541,315 @@ desk_free(pager_priv *pg, int i) /***************************************************************** + * Stuff to grab icons from windows - ripped from taskbar.c * + *****************************************************************/ + +static GdkColormap* +get_cmap (GdkPixmap *pixmap) +{ + GdkColormap *cmap; + + ENTER; + cmap = gdk_drawable_get_colormap (pixmap); + if (cmap) + g_object_ref (G_OBJECT (cmap)); + + if (cmap == NULL) + { + if (gdk_drawable_get_depth (pixmap) == 1) + { + /* try null cmap */ + cmap = NULL; + } + else + { + /* Try system cmap */ + GdkScreen *screen = gdk_drawable_get_screen (GDK_DRAWABLE (pixmap)); + cmap = gdk_screen_get_system_colormap (screen); + g_object_ref (G_OBJECT (cmap)); + } + } + + /* Be sure we aren't going to blow up due to visual mismatch */ + if (cmap && + (gdk_colormap_get_visual (cmap)->depth != + gdk_drawable_get_depth (pixmap))) + cmap = NULL; + + RET(cmap); +} + +static GdkPixbuf* +_wnck_gdk_pixbuf_get_from_pixmap (GdkPixbuf *dest, + Pixmap xpixmap, + int src_x, + int src_y, + int dest_x, + int dest_y, + int width, + int height) +{ + GdkDrawable *drawable; + GdkPixbuf *retval; + GdkColormap *cmap; + + ENTER; + retval = NULL; + + drawable = gdk_xid_table_lookup (xpixmap); + + if (drawable) + g_object_ref (G_OBJECT (drawable)); + else + drawable = gdk_pixmap_foreign_new (xpixmap); + + cmap = get_cmap (drawable); + + /* GDK is supposed to do this but doesn't in GTK 2.0.2, + * fixed in 2.0.3 + */ + if (width < 0) + gdk_drawable_get_size (drawable, &width, NULL); + if (height < 0) + gdk_drawable_get_size (drawable, NULL, &height); + + retval = gdk_pixbuf_get_from_drawable (dest, + drawable, + cmap, + src_x, src_y, + dest_x, dest_y, + width, height); + + if (cmap) + g_object_unref (G_OBJECT (cmap)); + g_object_unref (G_OBJECT (drawable)); + + RET(retval); +} + +static GdkPixbuf* +apply_mask (GdkPixbuf *pixbuf, + GdkPixbuf *mask) +{ + int w, h; + int i, j; + GdkPixbuf *with_alpha; + guchar *src; + guchar *dest; + int src_stride; + int dest_stride; + + ENTER; + w = MIN (gdk_pixbuf_get_width (mask), gdk_pixbuf_get_width (pixbuf)); + h = MIN (gdk_pixbuf_get_height (mask), gdk_pixbuf_get_height (pixbuf)); + + with_alpha = gdk_pixbuf_add_alpha (pixbuf, FALSE, 0, 0, 0); + + dest = gdk_pixbuf_get_pixels (with_alpha); + src = gdk_pixbuf_get_pixels (mask); + + dest_stride = gdk_pixbuf_get_rowstride (with_alpha); + src_stride = gdk_pixbuf_get_rowstride (mask); + + i = 0; + while (i < h) + { + j = 0; + while (j < w) + { + guchar *s = src + i * src_stride + j * 3; + guchar *d = dest + i * dest_stride + j * 4; + + /* s[0] == s[1] == s[2], they are 255 if the bit was set, 0 + * otherwise + */ + if (s[0] == 0) + d[3] = 0; /* transparent */ + else + d[3] = 255; /* opaque */ + + ++j; + } + + ++i; + } + + RET(with_alpha); +} + +static void +free_pixels (guchar *pixels, gpointer data) +{ + ENTER; + g_free (pixels); + RET(); +} + +static guchar * +argbdata_to_pixdata (gulong *argb_data, int len) +{ + guchar *p, *ret; + int i; + + ENTER; + ret = p = g_new (guchar, len * 4); + if (!ret) + RET(NULL); + /* One could speed this up a lot. */ + i = 0; + while (i < len) { + guint32 argb; + guint32 rgba; + + argb = argb_data[i]; + rgba = (argb << 8) | (argb >> 24); + + *p = rgba >> 24; + ++p; + *p = (rgba >> 16) & 0xff; + ++p; + *p = (rgba >> 8) & 0xff; + ++p; + *p = rgba & 0xff; + ++p; + + ++i; + } + RET(ret); +} + +static GdkPixbuf * +get_netwm_icon(Window tkwin, int iw, int ih) +{ + gulong *data; + GdkPixbuf *ret = NULL; + int n; + guchar *p; + GdkPixbuf *src; + int w, h; + + ENTER; + data = get_xaproperty(tkwin, a_NET_WM_ICON, XA_CARDINAL, &n); + if (!data) + RET(NULL); + + /* loop through all icons in data to find best fit */ + if (0) { + gulong *tmp; + int len; + + len = n/sizeof(gulong); + tmp = data; + while (len > 2) { + int size = tmp[0] * tmp[1]; + DBG("sub-icon: %dx%d %d bytes\n", tmp[0], tmp[1], size * 4); + len -= size + 2; + tmp += size; + } + } + + if (0) { + int i, j, nn; + + nn = MIN(10, n); + p = (guchar *) data; + for (i = 0; i < nn; i++) { + for (j = 0; j < sizeof(gulong); j++) + ERR("%02x ", (guint) p[i*sizeof(gulong) + j]); + ERR("\n"); + } + } + + /* check that data indeed represents icon in w + h + ARGB[] format + * with 16x16 dimension at least */ + if (n < (16 * 16 + 1 + 1)) { + ERR("win %lx: icon is too small or broken (size=%d)\n", tkwin, n); + goto out; + } + w = data[0]; + h = data[1]; + /* check that sizes are in 64-256 range */ + if (w < 16 || w > 256 || h < 16 || h > 256) { + ERR("win %lx: icon size (%d, %d) is not in 64-256 range\n", + tkwin, w, h); + goto out; + } + + DBG("orig %dx%d dest %dx%d\n", w, h, iw, ih); + p = argbdata_to_pixdata(data + 2, w * h); + if (!p) + goto out; + src = gdk_pixbuf_new_from_data (p, GDK_COLORSPACE_RGB, TRUE, + 8, w, h, w * 4, free_pixels, NULL); + if (src == NULL) + goto out; + ret = src; + if (w != iw || h != ih) { + ret = gdk_pixbuf_scale_simple(src, iw, ih, GDK_INTERP_HYPER); + g_object_unref(src); + } + +out: + XFree(data); + RET(ret); +} + +static GdkPixbuf* +get_wm_icon(Window tkwin, int iw, int ih) +{ + XWMHints *hints; + Pixmap xpixmap = None, xmask = None; + Window win; + unsigned int w, h; + int sd; + GdkPixbuf *ret, *masked, *pixmap, *mask = NULL; + + ENTER; + hints = XGetWMHints(GDK_DISPLAY(), tkwin); + DBG("\nwm_hints %s\n", hints ? "ok" : "failed"); + if (!hints) + RET(NULL); + + if ((hints->flags & IconPixmapHint)) + xpixmap = hints->icon_pixmap; + if ((hints->flags & IconMaskHint)) + xmask = hints->icon_mask; + DBG("flag=%ld xpixmap=%lx flag=%ld xmask=%lx\n", (hints->flags & IconPixmapHint), xpixmap, + (hints->flags & IconMaskHint), xmask); + XFree(hints); + if (xpixmap == None) + RET(NULL); + + if (!XGetGeometry (GDK_DISPLAY(), xpixmap, &win, &sd, &sd, &w, &h, + (guint *)&sd, (guint *)&sd)) { + DBG("XGetGeometry failed for %x pixmap\n", (unsigned int)xpixmap); + RET(NULL); + } + DBG("tkwin=%x icon pixmap w=%d h=%d\n", tkwin, w, h); + pixmap = _wnck_gdk_pixbuf_get_from_pixmap (NULL, xpixmap, 0, 0, 0, 0, w, h); + if (!pixmap) + RET(NULL); + if (xmask != None && XGetGeometry (GDK_DISPLAY(), xmask, + &win, &sd, &sd, &w, &h, (guint *)&sd, (guint *)&sd)) { + mask = _wnck_gdk_pixbuf_get_from_pixmap (NULL, xmask, 0, 0, 0, 0, w, h); + + if (mask) { + masked = apply_mask (pixmap, mask); + g_object_unref (G_OBJECT (pixmap)); + g_object_unref (G_OBJECT (mask)); + pixmap = masked; + } + } + if (!pixmap) + RET(NULL); + ret = gdk_pixbuf_scale_simple (pixmap, iw, ih, GDK_INTERP_TILES); + g_object_unref(pixmap); + + RET(ret); +} + + +/***************************************************************** * Netwm/WM Interclient Communication * *****************************************************************/ @@ -572,6 +930,11 @@ do_net_client_list_stacking(FbEv *ev, pager_priv *p) get_net_wm_state(t->win, &t->nws); get_net_wm_window_type(t->win, &t->nwwt); task_get_sizepos(t); + t->pixbuf = get_netwm_icon(t->win, 16, 16); + t->using_netwm_icon = (t->pixbuf != NULL); + if (!t->using_netwm_icon) { + t->pixbuf = get_wm_icon(t->win, 16, 16); + } g_hash_table_insert(p->htable, &t->win, t); DBG("add %lx\n", t->win); desk_set_dirty_by_win(p, t); @@ -787,6 +1150,9 @@ pager_constructor(plugin_instance *plug) g_signal_connect(G_OBJECT(pg->fbbg), "changed", G_CALLBACK(pager_bg_changed), pg); } + + pg->gen_pixbuf = gdk_pixbuf_new_from_xpm_data((const char **)icon_xpm); + pager_rebuild_all(fbev, pg); gdk_window_add_filter(NULL, (GdkFilterFunc)pager_event_filter, pg );