diff options
Diffstat (limited to 'matchbox/mb-wm-theme-png.c')
-rw-r--r-- | matchbox/mb-wm-theme-png.c | 1306 |
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; +} |