aboutsummaryrefslogtreecommitdiffstats
path: root/matchbox/mb-wm-theme-png.c
diff options
context:
space:
mode:
Diffstat (limited to 'matchbox/mb-wm-theme-png.c')
-rw-r--r--matchbox/mb-wm-theme-png.c1306
1 files changed, 1306 insertions, 0 deletions
diff --git a/matchbox/mb-wm-theme-png.c b/matchbox/mb-wm-theme-png.c
new file mode 100644
index 0000000..a747691
--- /dev/null
+++ b/matchbox/mb-wm-theme-png.c
@@ -0,0 +1,1306 @@
+#include <math.h>
+#include <png.h>
+
+#include "mb-wm-theme-png.h"
+#include "mb-wm-theme-xml.h"
+
+#include <X11/Xft/Xft.h>
+
+#ifdef HAVE_XEXT
+#include <X11/extensions/shape.h>
+#endif
+
+static int
+mb_wm_theme_png_ximg (MBWMThemePng * theme, const char * img);
+
+static unsigned char*
+mb_wm_theme_png_load_file (const char *file, int *width, int *height);
+
+static void
+mb_wm_theme_png_paint_decor (MBWMTheme *theme, MBWMDecor *decor);
+
+static void
+mb_wm_theme_png_paint_button (MBWMTheme *theme, MBWMDecorButton *button);
+
+static void
+mb_wm_theme_png_get_decor_dimensions (MBWMTheme *, MBWindowManagerClient *,
+ int*, int*, int*, int*);
+
+static MBWMDecor *
+mb_wm_theme_png_create_decor (MBWMTheme*, MBWindowManagerClient *,
+ MBWMDecorType);
+
+static void
+mb_wm_theme_png_resize_decor (MBWMTheme *theme, MBWMDecor *decor);
+
+static void
+mb_wm_theme_png_get_button_size (MBWMTheme *, MBWMDecor *,
+ MBWMDecorButtonType, int *, int *);
+
+static void
+mb_wm_theme_png_get_button_position (MBWMTheme *, MBWMDecor *,
+ MBWMDecorButtonType,
+ int *, int *);
+
+static void
+mb_wm_theme_png_class_init (MBWMObjectClass *klass)
+{
+ MBWMThemeClass *t_class = MB_WM_THEME_CLASS (klass);
+
+ t_class->paint_decor = mb_wm_theme_png_paint_decor;
+ t_class->paint_button = mb_wm_theme_png_paint_button;
+ t_class->decor_dimensions = mb_wm_theme_png_get_decor_dimensions;
+ t_class->button_size = mb_wm_theme_png_get_button_size;
+ t_class->button_position = mb_wm_theme_png_get_button_position;
+ t_class->create_decor = mb_wm_theme_png_create_decor;
+ t_class->resize_decor = mb_wm_theme_png_resize_decor;
+
+#if MBWM_WANT_DEBUG
+ klass->klass_name = "MBWMThemePng";
+#endif
+}
+
+static void
+mb_wm_theme_png_destroy (MBWMObject *obj)
+{
+ MBWMThemePng * theme = MB_WM_THEME_PNG (obj);
+ Display * dpy = MB_WM_THEME (obj)->wm->xdpy;
+
+ XRenderFreePicture (dpy, theme->xpic);
+ XFreePixmap (dpy, theme->xdraw);
+
+ if (theme->shape_mask)
+ XFreePixmap (dpy, theme->shape_mask);
+}
+
+static int
+mb_wm_theme_png_init (MBWMObject *obj, va_list vap)
+{
+ MBWMThemePng *p_theme = MB_WM_THEME_PNG (obj);
+ MBWMTheme *theme = MB_WM_THEME (obj);
+ MBWMObjectProp prop;
+ char *img = NULL;
+#if USE_PANGO
+ Display *xdpy = theme->wm->xdpy;
+ int xscreen = theme->wm->xscreen;
+#endif
+
+ prop = va_arg(vap, MBWMObjectProp);
+ while (prop)
+ {
+ switch (prop)
+ {
+ case MBWMObjectPropThemeImg:
+ img = va_arg(vap, char *);
+ break;
+ default:
+ MBWMO_PROP_EAT (vap, prop);
+ }
+
+ prop = va_arg(vap, MBWMObjectProp);
+ }
+
+ if (!img || !mb_wm_theme_png_ximg (p_theme, img))
+ return 0;
+
+#if USE_PANGO
+ p_theme->context = pango_xft_get_context (xdpy, xscreen);
+ p_theme->fontmap = pango_xft_get_font_map (xdpy, xscreen);
+#endif
+
+ return 1;
+}
+
+int
+mb_wm_theme_png_class_type ()
+{
+ static int type = 0;
+
+ if (UNLIKELY(type == 0))
+ {
+ static MBWMObjectClassInfo info = {
+ sizeof (MBWMThemePngClass),
+ sizeof (MBWMThemePng),
+ mb_wm_theme_png_init,
+ mb_wm_theme_png_destroy,
+ mb_wm_theme_png_class_init
+ };
+
+ type = mb_wm_object_register_class (&info, MB_WM_TYPE_THEME, 0);
+ }
+
+ return type;
+}
+
+struct DecorData
+{
+ Pixmap xpix;
+ Pixmap shape_mask;
+ GC gc_mask;
+ XftDraw *xftdraw;
+ XftColor clr;
+#if USE_PANGO
+ PangoFont *font;
+#else
+ XftFont *font;
+#endif
+};
+
+static void
+decordata_free (MBWMDecor * decor, void *data)
+{
+ struct DecorData * dd = data;
+ Display * xdpy = decor->parent_client->wmref->xdpy;
+
+ XFreePixmap (xdpy, dd->xpix);
+
+ if (dd->shape_mask)
+ XFreePixmap (xdpy, dd->shape_mask);
+
+ if (dd->gc_mask)
+ XFreeGC (xdpy, dd->gc_mask);
+
+ XftDrawDestroy (dd->xftdraw);
+
+#if USE_PANGO
+ if (dd->font)
+ g_object_unref (dd->font);
+#else
+ if (dd->font)
+ XftFontClose (xdpy, dd->font);
+#endif
+
+ free (dd);
+}
+
+struct ButtonData
+{
+ Pixmap xpix_i;
+ XftDraw *xftdraw_i;
+ Pixmap xpix_a;
+ XftDraw *xftdraw_a;
+};
+
+static void
+buttondata_free (MBWMDecorButton * button, void *data)
+{
+ struct ButtonData * bd = data;
+ Display * xdpy = button->decor->parent_client->wmref->xdpy;
+
+ XFreePixmap (xdpy, bd->xpix_i);
+ XftDrawDestroy (bd->xftdraw_i);
+ XFreePixmap (xdpy, bd->xpix_a);
+ XftDrawDestroy (bd->xftdraw_a);
+
+ free (bd);
+}
+
+#if !USE_PANGO
+static XftFont *
+xft_load_font(MBWMDecor * decor, MBWMXmlDecor *d)
+{
+ char desc[512];
+ XftFont *font;
+ Display * xdpy = decor->parent_client->wmref->xdpy;
+ int xscreen = decor->parent_client->wmref->xscreen;
+ int font_size = d->font_size ? d->font_size : 18;
+
+ if (d->font_units == MBWMXmlFontUnitsPixels)
+ {
+ font_size = mb_wm_util_pixels_to_points (decor->parent_client->wmref,
+ font_size);
+ }
+
+ snprintf (desc, sizeof (desc), "%s-%i",
+ d->font_family ? d->font_family : "Sans",
+ font_size);
+
+ font = XftFontOpenName (xdpy, xscreen, desc);
+
+ return font;
+}
+#endif
+
+static void
+mb_wm_theme_png_paint_button (MBWMTheme *theme, MBWMDecorButton *button)
+{
+ MBWMDecor * decor;
+ MBWMThemePng * p_theme = MB_WM_THEME_PNG (theme);
+ MBWindowManagerClient * client;
+ MBWMClientType c_type;;
+ MBWMXmlClient * c;
+ MBWMXmlDecor * d;
+ MBWMXmlButton * b;
+
+ /*
+ * We do not paint inactive buttons, as they get painted with the decor
+ */
+ decor = button->decor;
+ client = mb_wm_decor_get_parent (decor);
+ c_type = MB_WM_CLIENT_CLIENT_TYPE (client);
+
+ if ((c = mb_wm_xml_client_find_by_type (theme->xml_clients, c_type)) &&
+ (d = mb_wm_xml_decor_find_by_type (c->decors, decor->type)) &&
+ (b = mb_wm_xml_button_find_by_type (d->buttons, button->type)))
+ {
+ Display * xdpy = theme->wm->xdpy;
+ int xscreen = theme->wm->xscreen;
+ struct DecorData * ddata = mb_wm_decor_get_theme_data (decor);
+ struct ButtonData * bdata;
+ int x, y;
+
+ if (!ddata)
+ return;
+
+ bdata = mb_wm_decor_button_get_theme_data (button);
+
+ if (!bdata)
+ {
+ int a_x = b->active_x > -1 ? b->active_x : b->x;
+ int a_y = b->active_y > -1 ? b->active_y : b->y;
+
+ int i_x = b->inactive_x > -1 ? b->inactive_x : b->x;
+ int i_y = b->inactive_y > -1 ? b->inactive_y : b->y;
+
+ bdata = malloc (sizeof (struct ButtonData));
+
+ bdata->xpix_a = XCreatePixmap(xdpy, decor->xwin,
+ button->geom.width, button->geom.height,
+ DefaultDepth(xdpy, xscreen));
+
+ bdata->xftdraw_a = XftDrawCreate (xdpy, bdata->xpix_a,
+ DefaultVisual (xdpy, xscreen),
+ DefaultColormap (xdpy, xscreen));
+
+ bdata->xpix_i = XCreatePixmap(xdpy, decor->xwin,
+ button->geom.width, button->geom.height,
+ DefaultDepth(xdpy, xscreen));
+
+ bdata->xftdraw_i = XftDrawCreate (xdpy, bdata->xpix_i,
+ DefaultVisual (xdpy, xscreen),
+ DefaultColormap (xdpy, xscreen));
+
+ /*
+ * If the background color is set for the parent decor, we do a fill
+ * with the parent color first, then composite the decor image over,
+ * and finally composite the button image. (This way we can paint the
+ * button with a simple PictOpSrc, rather than having to do
+ * composting on each draw).
+ */
+ if (d->clr_bg.set)
+ {
+ XRenderColor rclr2;
+
+ rclr2.red = (int)(d->clr_bg.r * (double)0xffff);
+ rclr2.green = (int)(d->clr_bg.g * (double)0xffff);
+ rclr2.blue = (int)(d->clr_bg.b * (double)0xffff);
+
+ /* Fill the inactive image */
+ XRenderFillRectangle (xdpy, PictOpSrc,
+ XftDrawPicture (bdata->xftdraw_i), &rclr2,
+ 0, 0, b->width, b->height);
+
+ /* Composite the decor over */
+ XRenderComposite (xdpy, PictOpOver,
+ p_theme->xpic,
+ None,
+ XftDrawPicture (bdata->xftdraw_i),
+ b->x, b->y, 0, 0, 0, 0, b->width, b->height);
+
+ /* Copy inactive button to the active one */
+ XRenderComposite (xdpy, PictOpSrc,
+ XftDrawPicture (bdata->xftdraw_i),
+ None,
+ XftDrawPicture (bdata->xftdraw_a),
+ 0, 0, 0, 0, 0, 0, b->width, b->height);
+
+ /* Composite inactive and active image on top */
+ XRenderComposite (xdpy, PictOpOver,
+ p_theme->xpic,
+ None,
+ XftDrawPicture (bdata->xftdraw_i),
+ i_x, i_y, 0, 0, 0, 0, b->width, b->height);
+
+ XRenderComposite (xdpy, PictOpOver,
+ p_theme->xpic,
+ None,
+ XftDrawPicture (bdata->xftdraw_a),
+ a_x, a_y, 0, 0, 0, 0, b->width, b->height);
+ }
+ else
+ {
+ XRenderComposite (xdpy, PictOpSrc,
+ p_theme->xpic,
+ None,
+ XftDrawPicture (bdata->xftdraw_i),
+ i_x, i_y, 0, 0, 0, 0, b->width, b->height);
+
+ XRenderComposite (xdpy, PictOpSrc,
+ p_theme->xpic,
+ None,
+ XftDrawPicture (bdata->xftdraw_a),
+ a_x, a_y, 0, 0, 0, 0, b->width, b->height);
+ }
+
+ mb_wm_decor_button_set_theme_data (button, bdata, buttondata_free);
+ }
+
+ /* Here we automagically determine if the button should be left or
+ * right aligned in the case that a decor is expanded wider than
+ * the template image. If the coordinate comes before the point
+ * where decor padding is added, it's left aligned else it's
+ * right aligned. If no padding hints were given in the theme.xml,
+ * then we assume padding happens in the center.
+ * Note: we look at pad_length because pad_offset could be 0
+ */
+ x = b->x - d->x;
+ if (x > (d->pad_length ? d->pad_offset : d->width/2) )
+ x = decor->geom.width - (d->x + d->width - b->x);
+
+ y = b->y - d->y;
+
+ XRenderComposite (xdpy, PictOpSrc,
+ button->state == MBWMDecorButtonStatePressed ?
+ XftDrawPicture (bdata->xftdraw_a) :
+ XftDrawPicture (bdata->xftdraw_i),
+ None,
+ XftDrawPicture (ddata->xftdraw),
+ 0, 0, 0, 0, x, y, b->width, b->height);
+
+ XClearWindow (xdpy, decor->xwin);
+ }
+}
+
+static void
+mb_wm_theme_png_resize_decor (MBWMTheme *theme, MBWMDecor *decor)
+{
+ /*
+ * Clear any data we have stored with the theme; this will force
+ * resize on the next paint
+ */
+ mb_wm_decor_set_theme_data (decor, NULL, NULL);
+}
+
+static void
+mb_wm_theme_png_paint_decor (MBWMTheme *theme, MBWMDecor *decor)
+{
+ MBWMThemePng * p_theme = MB_WM_THEME_PNG (theme);
+ MBWindowManagerClient * client = decor->parent_client;
+ MBWMClientType c_type = MB_WM_CLIENT_CLIENT_TYPE (client);
+ MBWMXmlClient * c;
+ MBWMXmlDecor * d;
+ Display * xdpy = theme->wm->xdpy;
+ int xscreen = theme->wm->xscreen;
+ struct DecorData * data = mb_wm_decor_get_theme_data (decor);
+ const char * title;
+ int x, y;
+ int operator = PictOpSrc;
+ Bool shaped;
+
+ if (!((c = mb_wm_xml_client_find_by_type (theme->xml_clients, c_type)) &&
+ (d = mb_wm_xml_decor_find_by_type (c->decors, decor->type))))
+ return;
+
+#ifdef HAVE_XEXT
+ shaped = theme->shaped && c->shaped && !mb_wm_client_is_argb32 (client);
+#endif
+
+ if (data && (mb_wm_decor_get_dirty_state (decor) & MBWMDecorDirtyTitle))
+ {
+ /*
+ * If the decor title is dirty, and we already have the data,
+ * free it and recreate (since the old title is already composited
+ * in the cached image).
+ */
+ mb_wm_decor_set_theme_data (decor, NULL, NULL);
+ data = NULL;
+ }
+
+ if (!data)
+ {
+ XRenderColor rclr;
+
+ data = mb_wm_util_malloc0 (sizeof (struct DecorData));
+ data->xpix = XCreatePixmap(xdpy, decor->xwin,
+ decor->geom.width, decor->geom.height,
+ DefaultDepth(xdpy, xscreen));
+
+
+#ifdef HAVE_XEXT
+ if (shaped)
+ {
+ data->shape_mask =
+ XCreatePixmap(xdpy, decor->xwin,
+ decor->geom.width, decor->geom.height, 1);
+
+ data->gc_mask = XCreateGC (xdpy, data->shape_mask, 0, NULL);
+ }
+#endif
+ data->xftdraw = XftDrawCreate (xdpy, data->xpix,
+ DefaultVisual (xdpy, xscreen),
+ DefaultColormap (xdpy, xscreen));
+
+ /*
+ * If the background color is set, we fill the pixmaps with it,
+ * and then overlay the the PNG image over (this allows a theme
+ * to provide a monochromatic PNG that can be toned, e.g., Sato)
+ */
+ if (d->clr_bg.set)
+ {
+ XRenderColor rclr2;
+
+ operator = PictOpOver;
+
+ rclr2.red = (int)(d->clr_bg.r * (double)0xffff);
+ rclr2.green = (int)(d->clr_bg.g * (double)0xffff);
+ rclr2.blue = (int)(d->clr_bg.b * (double)0xffff);
+
+ XRenderFillRectangle (xdpy, PictOpSrc,
+ XftDrawPicture (data->xftdraw), &rclr2,
+ 0, 0,
+ decor->geom.width, decor->geom.height);
+ }
+
+ rclr.red = 0;
+ rclr.green = 0;
+ rclr.blue = 0;
+ rclr.alpha = 0xffff;
+
+ if (d->clr_fg.set)
+ {
+ rclr.red = (int)(d->clr_fg.r * (double)0xffff);
+ rclr.green = (int)(d->clr_fg.g * (double)0xffff);
+ rclr.blue = (int)(d->clr_fg.b * (double)0xffff);
+ }
+
+ XftColorAllocValue (xdpy, DefaultVisual (xdpy, xscreen),
+ DefaultColormap (xdpy, xscreen),
+ &rclr, &data->clr);
+
+#if USE_PANGO
+ {
+ PangoFontDescription * pdesc;
+ char desc[512];
+
+ snprintf (desc, sizeof (desc), "%s %i%s",
+ d->font_family ? d->font_family : "Sans",
+ d->font_size ? d->font_size : 18,
+ d->font_units == MBWMXmlFontUnitsPoints ? "" : "px");
+
+ pdesc = pango_font_description_from_string (desc);
+
+ data->font = pango_font_map_load_font (p_theme->fontmap,
+ p_theme->context,
+ pdesc);
+
+ pango_font_description_free (pdesc);
+ }
+#else
+ data->font = xft_load_font (decor, d);
+#endif
+ XSetWindowBackgroundPixmap(xdpy, decor->xwin, data->xpix);
+
+ mb_wm_decor_set_theme_data (decor, data, decordata_free);
+ }
+
+ /*
+ * Since we want to support things like rounded corners, but still
+ * have the decor resizable, we need to paint it in stages
+ *
+ * We assume that the decor image is exact in it's major axis,
+ * i.e., North and South decors provide image of the exactly correct
+ * height, and West and East of width.
+ */
+ if (decor->type == MBWMDecorTypeNorth ||
+ decor->type == MBWMDecorTypeSouth)
+ {
+ if (decor->geom.width < d->width)
+ {
+ /* The decor is smaller than the template, cut bit from the
+ * midle
+ */
+ int width1 = decor->geom.width / 2;
+ int width2 = decor->geom.width - width1;
+ int x2 = d->x + d->width - width2;
+
+ XRenderComposite(xdpy, operator,
+ p_theme->xpic,
+ None,
+ XftDrawPicture (data->xftdraw),
+ d->x, d->y, 0, 0, 0, 0,
+ width1, d->height);
+
+ XRenderComposite(xdpy, operator,
+ p_theme->xpic,
+ None,
+ XftDrawPicture (data->xftdraw),
+ x2 , d->y, 0, 0,
+ width1, 0,
+ width2, d->height);
+
+#ifdef HAVE_XEXT
+ if (shaped)
+ {
+ XCopyArea (xdpy, p_theme->shape_mask, data->shape_mask,
+ data->gc_mask,
+ d->x, d->y, width1, d->height, 0, 0);
+ XCopyArea (xdpy, p_theme->shape_mask, data->shape_mask,
+ data->gc_mask,
+ x2, d->y, width2, d->height, width1, 0);
+ }
+#endif
+ }
+ else if (decor->geom.width == d->width)
+ {
+ /* Exact match */
+ XRenderComposite(xdpy, operator,
+ p_theme->xpic,
+ None,
+ XftDrawPicture (data->xftdraw),
+ d->x, d->y, 0, 0,
+ 0, 0, d->width, d->height);
+
+#ifdef HAVE_XEXT
+ if (shaped)
+ {
+ XCopyArea (xdpy, p_theme->shape_mask, data->shape_mask,
+ data->gc_mask,
+ d->x, d->y, d->width, d->height, 0, 0);
+ }
+#endif
+ }
+ else
+ {
+ /* The decor is bigger than the template, draw extra bit from
+ * the middle
+ */
+ int pad_offset = d->pad_offset;
+ int pad_length = d->pad_length;
+ int gap_length = decor->geom.width - d->width;
+
+ if (!pad_length)
+ {
+ pad_length =
+ decor->geom.width > 30 ? 10 : decor->geom.width / 4 + 1;
+ pad_offset = (d->width / 2) - (pad_length / 2);
+ }
+
+ XRenderComposite(xdpy, operator,
+ p_theme->xpic,
+ None,
+ XftDrawPicture (data->xftdraw),
+ d->x, d->y, 0, 0,
+ 0, 0,
+ pad_offset, d->height);
+
+ /* TODO: can we do this as one scaled operation? */
+ for (x = pad_offset; x < pad_offset + gap_length; x += pad_length)
+ XRenderComposite(xdpy, operator,
+ p_theme->xpic,
+ None,
+ XftDrawPicture (data->xftdraw),
+ d->x + pad_offset, d->y, 0, 0,
+ x, 0,
+ pad_length,
+ d->height);
+
+ XRenderComposite(xdpy, operator,
+ p_theme->xpic,
+ None,
+ XftDrawPicture (data->xftdraw),
+ d->x + pad_offset, d->y, 0, 0,
+ pad_offset + gap_length, 0,
+ d->width - pad_offset, d->height);
+
+#ifdef HAVE_XEXT
+ if (shaped)
+ {
+ XCopyArea (xdpy, p_theme->shape_mask, data->shape_mask,
+ data->gc_mask,
+ d->x, d->y,
+ pad_offset, d->height,
+ 0, 0);
+
+ for (x = pad_offset; x < pad_offset + gap_length; x += pad_length)
+ XCopyArea (xdpy, p_theme->shape_mask, data->shape_mask,
+ data->gc_mask,
+ d->x + pad_offset, d->y,
+ d->width - pad_offset, d->height,
+ x, 0);
+
+ XCopyArea (xdpy, p_theme->shape_mask, data->shape_mask,
+ data->gc_mask,
+ d->x + pad_offset, d->y,
+ d->width - pad_offset, d->height,
+ pad_offset + gap_length, 0);
+ }
+#endif
+ }
+ }
+ else
+ {
+ if (decor->geom.height < d->height)
+ {
+ /* The decor is smaller than the template, cut bit from the
+ * midle
+ */
+ int height1 = decor->geom.height / 2;
+ int height2 = decor->geom.height - height1;
+ int y2 = d->y + d->height - height2;
+
+ XRenderComposite(xdpy, operator,
+ p_theme->xpic,
+ None,
+ XftDrawPicture (data->xftdraw),
+ d->x, d->y, 0, 0,
+ 0, 0,
+ d->width, height1);
+
+ XRenderComposite(xdpy, operator,
+ p_theme->xpic,
+ None,
+ XftDrawPicture (data->xftdraw),
+ d->x , y2, 0, 0,
+ 0, height1,
+ d->width, height2);
+
+#ifdef HAVE_XEXT
+ if (shaped)
+ {
+ XCopyArea (xdpy, p_theme->shape_mask, data->shape_mask,
+ data->gc_mask,
+ d->x, d->y, d->width, height1, 0, 0);
+ XCopyArea (xdpy, p_theme->shape_mask, data->shape_mask,
+ data->gc_mask,
+ d->x, y2, d->width, height2, 0, height1);
+ }
+#endif
+ }
+ else if (decor->geom.height == d->height)
+ {
+ /* Exact match */
+ XRenderComposite(xdpy, operator,
+ p_theme->xpic,
+ None,
+ XftDrawPicture (data->xftdraw),
+ d->x, d->y, 0, 0,
+ 0, 0,
+ d->width, d->height);
+
+#ifdef HAVE_XEXT
+ if (shaped)
+ {
+ XCopyArea (xdpy, p_theme->shape_mask, data->shape_mask,
+ data->gc_mask,
+ d->x, d->y, d->width, d->height, 0, 0);
+ }
+#endif
+ }
+ else
+ {
+ /* The decor is bigger than the template, draw extra bit from
+ * the middle
+ */
+ int pad_offset = d->pad_offset;
+ int pad_length = d->pad_length;
+ int gap_length = decor->geom.height - d->height;
+
+ if (!pad_length)
+ {
+ pad_length =
+ decor->geom.height > 30 ? 10 : decor->geom.height / 4 + 1;
+ pad_offset = (d->height / 2) - (pad_length / 2);
+ }
+
+ XRenderComposite(xdpy, operator,
+ p_theme->xpic,
+ None,
+ XftDrawPicture (data->xftdraw),
+ d->x, d->y, 0, 0, 0, 0,
+ d->width, pad_offset);
+
+ /* TODO: can we do this as one scaled operation? */
+ for (y = pad_offset; y < pad_offset + gap_length; y += pad_length)
+ XRenderComposite(xdpy, operator,
+ p_theme->xpic,
+ None,
+ XftDrawPicture (data->xftdraw),
+ d->x, d->y + pad_offset, 0, 0, 0, y,
+ d->width,
+ pad_length);
+
+ XRenderComposite(xdpy, operator,
+ p_theme->xpic,
+ None,
+ XftDrawPicture (data->xftdraw),
+ d->x , d->y + pad_offset, 0, 0,
+ 0, pad_offset + gap_length,
+ d->width, d->height - pad_offset);
+
+#ifdef HAVE_XEXT
+ if (shaped)
+ {
+ XCopyArea (xdpy, p_theme->shape_mask, data->shape_mask,
+ data->gc_mask,
+ d->x, d->y,
+ d->width, pad_offset,
+ 0, 0);
+
+ for (y = pad_offset; y < pad_offset + gap_length; y += pad_length)
+ XCopyArea (xdpy, p_theme->shape_mask, data->shape_mask,
+ data->gc_mask,
+ d->x, d->y + pad_offset,
+ d->width, pad_length,
+ 0, y);
+
+ XCopyArea (xdpy, p_theme->shape_mask, data->shape_mask,
+ data->gc_mask,
+ d->x, d->y + pad_offset,
+ d->width, d->height - pad_offset,
+ 0, pad_offset + gap_length);
+ }
+#endif
+ }
+ }
+
+ if (d->show_title &&
+ (title = mb_wm_client_get_name (client)) &&
+ data->font)
+ {
+ XRectangle rec;
+
+ int pack_start_x = mb_wm_decor_get_pack_start_x (decor);
+ int pack_end_x = mb_wm_decor_get_pack_end_x (decor);
+ int west_width = mb_wm_client_frame_west_width (client);
+ int y, ascent, descent;
+ int len = strlen (title);
+
+#if USE_PANGO
+ PangoFontMetrics * mtx;
+ PangoGlyphString * glyphs;
+ GList * items, *l;
+ PangoRectangle rect;
+ int xoff = 0;
+
+ mtx = pango_font_get_metrics (data->font, NULL);
+
+ ascent = PANGO_PIXELS (pango_font_metrics_get_ascent (mtx));
+ descent = PANGO_PIXELS (pango_font_metrics_get_descent (mtx));
+
+ pango_font_metrics_unref (mtx);
+#else
+ ascent = data->font->ascent;
+ descent = data->font->descent;
+#endif
+
+ y = (decor->geom.height - (ascent + descent)) / 2 + ascent;
+
+ rec.x = 0;
+ rec.y = 0;
+ rec.width = pack_end_x - 2;
+ rec.height = d->height;
+
+ XftDrawSetClipRectangles (data->xftdraw, 0, 0, &rec, 1);
+
+#if USE_PANGO
+ glyphs = pango_glyph_string_new ();
+
+ /*
+ * Run the pango rendering pipeline on this text and draw with
+ * the xft backend (why Pango does not provide a convenience
+ * API for something as common as drawing a string escapes me).
+ */
+ items = pango_itemize (p_theme->context, title, 0, len, NULL, NULL);
+
+ l = items;
+ while (l)
+ {
+ PangoItem * item = l->data;
+
+ item->analysis.font = data->font;
+
+ pango_shape (title, len, &item->analysis, glyphs);
+
+ pango_xft_render (data->xftdraw,
+ &data->clr,
+ data->font,
+ glyphs,
+ xoff + west_width + pack_start_x, y);
+
+ /* Advance position */
+ pango_glyph_string_extents (glyphs, data->font, NULL, &rect);
+ xoff += PANGO_PIXELS (rect.width);
+
+ l = l->next;
+ }
+
+ if (glyphs)
+ pango_glyph_string_free (glyphs);
+
+ g_list_free (items);
+#else
+ XftDrawStringUtf8(data->xftdraw,
+ &data->clr,
+ data->font,
+ west_width + pack_start_x, y,
+ title, len);
+#endif
+
+ /* Unset the clipping rectangle */
+ rec.width = decor->geom.width;
+ rec.height = decor->geom.height;
+
+ XftDrawSetClipRectangles (data->xftdraw, 0, 0, &rec, 1);
+ }
+
+#ifdef HAVE_XEXT
+ if (shaped)
+ {
+ XShapeCombineMask (xdpy, decor->xwin,
+ ShapeBounding, 0, 0,
+ data->shape_mask, ShapeSet);
+
+ XShapeCombineShape (xdpy,
+ client->xwin_frame,
+ ShapeBounding, decor->geom.x, decor->geom.y,
+ decor->xwin,
+ ShapeBounding, ShapeUnion);
+ }
+#endif
+ XClearWindow (xdpy, decor->xwin);
+}
+
+static void
+construct_buttons (MBWMThemePng * theme, MBWMDecor * decor, MBWMXmlDecor *d)
+{
+ MBWindowManagerClient *client = decor->parent_client;
+ MBWindowManager *wm = client->wmref;
+ MBWMDecorButton *button;
+
+ if (d)
+ {
+ MBWMList * l = d->buttons;
+ while (l)
+ {
+ MBWMXmlButton * b = l->data;
+
+ button = mb_wm_decor_button_stock_new (wm,
+ b->type,
+ b->packing,
+ decor,
+ 0);
+
+ mb_wm_decor_button_show (button);
+ mb_wm_object_unref (MB_WM_OBJECT (button));
+
+ l = l->next;
+ }
+ }
+}
+
+static MBWMDecor *
+mb_wm_theme_png_create_decor (MBWMTheme *theme,
+ MBWindowManagerClient *client,
+ MBWMDecorType type)
+{
+ MBWMClientType c_type = MB_WM_CLIENT_CLIENT_TYPE (client);
+ MBWMDecor *decor = NULL;
+ MBWindowManager *wm = client->wmref;
+ MBWMXmlClient *c;
+
+ if ((c = mb_wm_xml_client_find_by_type (theme->xml_clients, c_type)))
+ {
+ MBWMXmlDecor *d;
+
+ d = mb_wm_xml_decor_find_by_type (c->decors, type);
+
+ if (d)
+ {
+ decor = mb_wm_decor_new (wm, type);
+ decor->absolute_packing = True;
+ mb_wm_decor_attach (decor, client);
+ construct_buttons (MB_WM_THEME_PNG (theme), decor, d);
+ }
+ }
+
+ return decor;
+}
+
+static void
+mb_wm_theme_png_get_button_size (MBWMTheme *theme,
+ MBWMDecor *decor,
+ MBWMDecorButtonType type,
+ int *width,
+ int *height)
+{
+ MBWindowManagerClient * client = decor->parent_client;
+ MBWMClientType c_type = MB_WM_CLIENT_CLIENT_TYPE (client);
+ MBWMThemePng *p_theme = MB_WM_THEME_PNG (theme);
+ MBWMXmlClient * c;
+ MBWMXmlDecor * d;
+
+ /* FIXME -- assumes button on the north decor only */
+ if ((c = mb_wm_xml_client_find_by_type (theme->xml_clients, c_type)) &&
+ (d = mb_wm_xml_decor_find_by_type (c->decors, decor->type)))
+ {
+ MBWMXmlButton * b = mb_wm_xml_button_find_by_type (d->buttons, type);
+
+ if (b)
+ {
+ if (width)
+ *width = b->width;
+
+ if (height)
+ *height = b->height;
+
+ return;
+ }
+ }
+
+ if (width)
+ *width = 0;
+
+ if (height)
+ *height = 0;
+}
+
+static void
+mb_wm_theme_png_get_button_position (MBWMTheme *theme,
+ MBWMDecor *decor,
+ MBWMDecorButtonType type,
+ int *x,
+ int *y)
+{
+ MBWindowManagerClient * client = decor->parent_client;
+ MBWMClientType c_type = MB_WM_CLIENT_CLIENT_TYPE (client);
+ MBWMThemePng *p_theme = MB_WM_THEME_PNG (theme);
+ MBWMXmlClient * c;
+ MBWMXmlDecor * d;
+
+ /* FIXME -- assumes button on the north decor only */
+ if ((c = mb_wm_xml_client_find_by_type (theme->xml_clients, c_type)) &&
+ (d = mb_wm_xml_decor_find_by_type (c->decors, decor->type)))
+ {
+ MBWMXmlButton * b = mb_wm_xml_button_find_by_type (d->buttons, type);
+
+ if (b)
+ {
+ if (x)
+ {
+ int button_x;
+
+ /* Here we automagically determine if the button should be left or
+ * right aligned in the case that a decor is expanded wider than
+ * the template image. If the coordinate comes before the point
+ * where decor padding is added, it's left aligned else it's
+ * right aligned. If no padding hints were given in the theme.xml,
+ * then we assume padding happens in the center.
+ * Note: we look at pad_length because pad_offset could be 0
+ */
+ button_x = b->x - d->x;
+ if (button_x > (d->pad_length ? d->pad_offset : d->width/2) )
+ button_x = decor->geom.width - (d->x + d->width - b->x);
+
+ *x = button_x;
+ }
+
+ if (y)
+ *y = b->y - d->y;
+
+ return;
+ }
+ }
+
+ if (x)
+ *x = 0;
+
+ if (y)
+ *y = 0;
+}
+
+static void
+mb_wm_theme_png_get_decor_dimensions (MBWMTheme *theme,
+ MBWindowManagerClient *client,
+ int *north,
+ int *south,
+ int *west,
+ int *east)
+{
+ MBWMClientType c_type = MB_WM_CLIENT_CLIENT_TYPE (client);
+ MBWMThemePng *p_theme = MB_WM_THEME_PNG (theme);
+ MBWMXmlClient * c;
+ MBWMXmlDecor * d;
+
+ /* FIXME -- assumes button on the north decor only */
+ if ((c = mb_wm_xml_client_find_by_type (theme->xml_clients, c_type)))
+ {
+ if (north)
+ {
+ d = mb_wm_xml_decor_find_by_type (c->decors, MBWMDecorTypeNorth);
+
+ if (d)
+ *north = d->height;
+ else
+ *north = 0;
+ }
+
+ if (south)
+ {
+ d = mb_wm_xml_decor_find_by_type (c->decors, MBWMDecorTypeSouth);
+
+ if (d)
+ *south = d->height;
+ else
+ *south = 0;
+ }
+
+ if (west)
+ {
+ d = mb_wm_xml_decor_find_by_type (c->decors, MBWMDecorTypeWest);
+
+ if (d)
+ *west = d->width;
+ else
+ *west = 0;
+ }
+
+ if (east)
+ {
+ d = mb_wm_xml_decor_find_by_type (c->decors, MBWMDecorTypeEast);
+
+ if (d)
+ *east = d->width;
+ else
+ *east = 0;
+ }
+
+ return;
+ }
+
+ if (north)
+ *north = 0;
+
+ if (south)
+ *south = 0;
+
+ if (west)
+ *west = 0;
+
+ if (east)
+ *east = 0;
+}
+
+/*
+ * From matchbox-keyboard
+ */
+static unsigned char*
+mb_wm_theme_png_load_file (const char *file,
+ int *width,
+ int *height)
+{
+ FILE *fd;
+ unsigned char *data;
+ unsigned char header[8];
+ int bit_depth, color_type;
+
+ png_uint_32 png_width, png_height, i, rowbytes;
+ png_structp png_ptr;
+ png_infop info_ptr;
+ png_bytep *row_pointers;
+
+ if ((fd = fopen (file, "rb")) == NULL)
+ return NULL;
+
+ fread (header, 1, 8, fd);
+ if (!png_check_sig (header, 8))
+ {
+ fclose(fd);
+ return NULL;
+ }
+
+ png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
+ if (!png_ptr)
+ {
+ fclose(fd);
+ return NULL;
+ }
+
+ info_ptr = png_create_info_struct(png_ptr);
+ if (!info_ptr)
+ {
+ png_destroy_read_struct (&png_ptr, NULL, NULL);
+ fclose(fd);
+ return NULL;
+ }
+
+ if (setjmp (png_ptr->jmpbuf))
+ {
+ png_destroy_read_struct( &png_ptr, &info_ptr, NULL);
+ fclose(fd);
+ return NULL;
+ }
+
+ png_init_io (png_ptr, fd);
+ png_set_sig_bytes (png_ptr, 8);
+ png_read_info (png_ptr, info_ptr);
+ png_get_IHDR (png_ptr, info_ptr, &png_width, &png_height, &bit_depth,
+ &color_type, NULL, NULL, NULL);
+
+ *width = (int) png_width;
+ *height = (int) png_height;
+
+ if (bit_depth == 16)
+ png_set_strip_16(png_ptr);
+
+ if (bit_depth < 8)
+ png_set_packing(png_ptr);
+
+ if (color_type == PNG_COLOR_TYPE_GRAY ||
+ color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
+ png_set_gray_to_rgb(png_ptr);
+
+ /* Add alpha */
+ if (color_type == PNG_COLOR_TYPE_GRAY ||
+ color_type == PNG_COLOR_TYPE_RGB)
+ png_set_add_alpha (png_ptr, 0xff, PNG_FILLER_AFTER);
+
+ if (color_type == PNG_COLOR_TYPE_PALETTE ||
+ png_get_valid (png_ptr, info_ptr, PNG_INFO_tRNS))
+ png_set_expand (png_ptr);
+
+ png_read_update_info (png_ptr, info_ptr);
+
+ /* allocate space for data and row pointers */
+ rowbytes = png_get_rowbytes (png_ptr, info_ptr);
+ data = (unsigned char*) malloc ((rowbytes * (*height + 1)));
+ row_pointers = (png_bytep *) malloc ((*height) * sizeof (png_bytep));
+
+ if (!data || !row_pointers)
+ {
+ png_destroy_read_struct (&png_ptr, &info_ptr, NULL);
+
+ if (data)
+ free (data);
+
+ if (row_pointers)
+ free (row_pointers);
+
+ return NULL;
+ }
+
+ for (i = 0; i < *height; i++)
+ row_pointers[i] = data + i * rowbytes;
+
+ png_read_image (png_ptr, row_pointers);
+ png_read_end (png_ptr, NULL);
+
+ free (row_pointers);
+ png_destroy_read_struct (&png_ptr, &info_ptr, NULL);
+ fclose(fd);
+
+ return data;
+}
+
+static int
+mb_wm_theme_png_ximg (MBWMThemePng * theme, const char * img)
+{
+ MBWindowManager * wm = MB_WM_THEME (theme)->wm;
+ Display * dpy = wm->xdpy;
+ int screen = wm->xscreen;
+
+ XImage * ximg, * shape_img;
+ GC gc, gcm;
+ int x;
+ int y;
+ int width;
+ int height;
+ XRenderPictFormat *ren_fmt;
+ XRenderPictureAttributes ren_attr;
+ unsigned char * p;
+ unsigned char * png_data = mb_wm_theme_png_load_file (img, &width, &height);
+ Bool shaped = MB_WM_THEME (theme)->shaped;
+
+ if (!png_data || !width || !height)
+ return 0;
+
+ ren_fmt = XRenderFindStandardFormat(dpy, PictStandardARGB32);
+
+ theme->xdraw =
+ XCreatePixmap (dpy, RootWindow(dpy,screen), width, height, ren_fmt->depth);
+
+ if (shaped)
+ theme->shape_mask =
+ XCreatePixmap (dpy, RootWindow(dpy,screen), width, height, 1);
+
+ XSync (dpy, False);
+
+ ren_attr.dither = True;
+ ren_attr.component_alpha = True;
+ ren_attr.repeat = False;
+
+ gc = XCreateGC (dpy, theme->xdraw, 0, NULL);
+
+ if (shaped)
+ gcm = XCreateGC (dpy, theme->shape_mask, 0, NULL);
+
+ ximg = XCreateImage (dpy, DefaultVisual (dpy, screen),
+ ren_fmt->depth, ZPixmap,
+ 0, NULL, width, height, 32, 0);
+
+ ximg->data = malloc (ximg->bytes_per_line * ximg->height);
+
+ if (shaped)
+ {
+ shape_img = XCreateImage (dpy, DefaultVisual (dpy, screen),
+ 1, ZPixmap,
+ 0, NULL, width, height, 8, 0);
+
+ shape_img->data = malloc (shape_img->bytes_per_line * shape_img->height);
+ }
+
+ p = png_data;
+
+ for (y = 0; y < height; y++)
+ for (x = 0; x < width; x++)
+ {
+ unsigned char a, r, g, b;
+ r = *p++; g = *p++; b = *p++; a = *p++;
+ r = (r * (a + 1)) / 256;
+ g = (g * (a + 1)) / 256;
+ b = (b * (a + 1)) / 256;
+
+ XPutPixel (ximg, x, y, (a << 24) | (r << 16) | (g << 8) | b);
+
+ if (shaped)
+ {
+ XPutPixel (shape_img, x, y, a ? 1 : 0);
+ }
+ }
+
+ XPutImage (dpy, theme->xdraw, gc, ximg, 0, 0, 0, 0, width, height);
+
+ if (shaped)
+ XPutImage (dpy, theme->shape_mask, gcm, shape_img,
+ 0, 0, 0, 0, width, height);
+
+ theme->xpic = XRenderCreatePicture (dpy, theme->xdraw, ren_fmt,
+ CPRepeat|CPDither|CPComponentAlpha,
+ &ren_attr);
+
+ free (ximg->data);
+ ximg->data = NULL;
+ XDestroyImage (ximg);
+ XFreeGC (dpy, gc);
+
+ if (shaped)
+ {
+ free (shape_img->data);
+ shape_img->data = NULL;
+ XDestroyImage (shape_img);
+ XFreeGC (dpy, gcm);
+ }
+
+ free (png_data);
+
+ return 1;
+}