436 lines
12 KiB
Plaintext
436 lines
12 KiB
Plaintext
$OpenBSD: patch-plugins_pager_pager_c,v 1.2 2012/03/29 08:17:41 dcoppa Exp $
|
|
|
|
add icon drawing support to the pager plugin
|
|
(adapted from an older patch written for fbpanel-4.3 by daf@minuslab.net)
|
|
|
|
--- plugins/pager/pager.c.orig Wed Apr 28 13:39:20 2010
|
|
+++ plugins/pager/pager.c Thu Mar 29 10:04:48 2012
|
|
@@ -27,10 +27,13 @@
|
|
|
|
|
|
#include <gdk-pixbuf/gdk-pixbuf.h>
|
|
+#include <gdk-pixbuf-xlib/gdk-pixbuf-xlib.h>
|
|
+#include <gdk/gdk.h>
|
|
|
|
#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 = ( (w<h) ? w : h );
|
|
+ if (smallest < 18) {
|
|
+ noscale = 0;
|
|
+ scale = smallest - 2;
|
|
+ if (scale % 2 != 0)
|
|
+ scale++;
|
|
+
|
|
+ scaled = gdk_pixbuf_scale_simple(source_buf,
|
|
+ scale, scale,
|
|
+ GDK_INTERP_BILINEAR);
|
|
+ }
|
|
+
|
|
+ /* position */
|
|
+ int pixx = x+((w/2)-(scale/2))+1;
|
|
+ int pixy = y+((h/2)-(scale/2))+1;
|
|
+
|
|
+ /* draw it */
|
|
+ gdk_draw_pixbuf(d->pix,
|
|
+ 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);
|
|
@@ -786,6 +1149,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 );
|