summaryrefslogtreecommitdiff
path: root/obrender
diff options
context:
space:
mode:
authorDana Jansens <danakj@orodu.net>2009-12-21 10:53:41 -0500
committerDana Jansens <danakj@orodu.net>2009-12-21 14:08:34 -0500
commit7aae8cc5262c1b36e3196845d62489b76af9063f (patch)
tree4c6b9c8a471fec380264cc5844c3f73fe75ccb18 /obrender
parenta03b817aeb7fe32be881c30d50fab79455db08c5 (diff)
Set up work for making a 3.5 prerelease.
Set version stuff to 3.5.0-rc1. Copy the CHANGELOG from 3.4-working. Rename the obt-4.0 and obrender-4.0 pkgconfig stuff to obt-3.5 and obrender-3.5 Rename the "render" directory to "obrender" so that the public headers can be installed in <obrender/*>
Diffstat (limited to 'obrender')
-rw-r--r--obrender/Makefile4
-rw-r--r--obrender/color.c361
-rw-r--r--obrender/color.h51
-rw-r--r--obrender/font.c380
-rw-r--r--obrender/font.h42
-rw-r--r--obrender/geom.h38
-rw-r--r--obrender/gradient.c836
-rw-r--r--obrender/gradient.h27
-rw-r--r--obrender/icon.h422
-rw-r--r--obrender/image.c516
-rw-r--r--obrender/image.h35
-rw-r--r--obrender/imagecache.c144
-rw-r--r--obrender/imagecache.h51
-rw-r--r--obrender/instance.c309
-rw-r--r--obrender/instance.h57
-rw-r--r--obrender/mask.c82
-rw-r--r--obrender/mask.h32
-rw-r--r--obrender/obrender-3.5.pc.in14
-rw-r--r--obrender/render.c564
-rw-r--r--obrender/render.h350
-rw-r--r--obrender/test.c115
-rw-r--r--obrender/theme.c2007
-rw-r--r--obrender/theme.h266
-rw-r--r--obrender/version.h.in15
24 files changed, 6718 insertions, 0 deletions
diff --git a/obrender/Makefile b/obrender/Makefile
new file mode 100644
index 00000000..b90edacf
--- /dev/null
+++ b/obrender/Makefile
@@ -0,0 +1,4 @@
+all clean install:
+ $(MAKE) -C .. -$(MAKEFLAGS) $@
+
+.PHONY: all clean install
diff --git a/obrender/color.c b/obrender/color.c
new file mode 100644
index 00000000..5e3f2169
--- /dev/null
+++ b/obrender/color.c
@@ -0,0 +1,361 @@
+/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
+
+ color.c for the Openbox window manager
+ Copyright (c) 2006 Mikael Magnusson
+ Copyright (c) 2003-2007 Dana Jansens
+ Copyright (c) 2003 Derek Foreman
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ See the COPYING file for a copy of the GNU General Public License.
+*/
+
+#include "render.h"
+#include "color.h"
+#include "instance.h"
+
+#include <X11/Xlib.h>
+#include <X11/Xutil.h>
+#include <string.h>
+
+void RrColorAllocateGC(RrColor *in)
+{
+ XGCValues gcv;
+
+ gcv.foreground = in->pixel;
+ gcv.cap_style = CapProjecting;
+ in->gc = XCreateGC(RrDisplay(in->inst),
+ RrRootWindow(in->inst),
+ GCForeground | GCCapStyle, &gcv);
+}
+
+RrColor *RrColorParse(const RrInstance *inst, gchar *colorname)
+{
+ XColor xcol;
+
+ g_assert(colorname != NULL);
+ /* get rgb values from colorname */
+
+ xcol.red = 0;
+ xcol.green = 0;
+ xcol.blue = 0;
+ xcol.pixel = 0;
+ if (!XParseColor(RrDisplay(inst), RrColormap(inst), colorname, &xcol)) {
+ g_message("Unable to parse color '%s'", colorname);
+ return NULL;
+ }
+ return RrColorNew(inst, xcol.red >> 8, xcol.green >> 8, xcol.blue >> 8);
+}
+
+/*#define NO_COLOR_CACHE*/
+#ifdef DEBUG
+gint id;
+#endif
+
+RrColor *RrColorNew(const RrInstance *inst, gint r, gint g, gint b)
+{
+ /* this should be replaced with something far cooler */
+ RrColor *out = NULL;
+ XColor xcol;
+ gint key;
+
+ g_assert(r >= 0 && r < 256);
+ g_assert(g >= 0 && g < 256);
+ g_assert(b >= 0 && b < 256);
+
+ key = (r << 24) + (g << 16) + (b << 8);
+#ifndef NO_COLOR_CACHE
+ if ((out = g_hash_table_lookup(RrColorHash(inst), &key))) {
+ out->refcount++;
+ } else {
+#endif
+ xcol.red = (r << 8) | r;
+ xcol.green = (g << 8) | g;
+ xcol.blue = (b << 8) | b;
+ if (XAllocColor(RrDisplay(inst), RrColormap(inst), &xcol)) {
+ out = g_new(RrColor, 1);
+ out->inst = inst;
+ out->r = xcol.red >> 8;
+ out->g = xcol.green >> 8;
+ out->b = xcol.blue >> 8;
+ out->gc = None;
+ out->pixel = xcol.pixel;
+ out->key = key;
+ out->refcount = 1;
+#ifdef DEBUG
+ out->id = id++;
+#endif
+#ifndef NO_COLOR_CACHE
+ g_hash_table_insert(RrColorHash(inst), &out->key, out);
+ }
+#endif
+ }
+ return out;
+}
+
+void RrColorFree(RrColor *c)
+{
+ if (c) {
+ if (--c->refcount < 1) {
+#ifndef NO_COLOR_CACHE
+ g_assert(g_hash_table_lookup(RrColorHash(c->inst), &c->key));
+ g_hash_table_remove(RrColorHash(c->inst), &c->key);
+#endif
+ if (c->pixel) XFreeColors(RrDisplay(c->inst), RrColormap(c->inst),
+ &c->pixel, 1, 0);
+ if (c->gc) XFreeGC(RrDisplay(c->inst), c->gc);
+ g_free(c);
+ }
+ }
+}
+
+void RrReduceDepth(const RrInstance *inst, RrPixel32 *data, XImage *im)
+{
+ gint r, g, b;
+ gint x,y;
+ RrPixel32 *p32 = (RrPixel32 *) im->data;
+ RrPixel16 *p16 = (RrPixel16 *) im->data;
+ RrPixel8 *p8 = (RrPixel8 *) im->data;
+ switch (im->bits_per_pixel) {
+ case 32:
+ if ((RrRedOffset(inst) != RrDefaultRedOffset) ||
+ (RrBlueOffset(inst) != RrDefaultBlueOffset) ||
+ (RrGreenOffset(inst) != RrDefaultGreenOffset)) {
+ for (y = 0; y < im->height; y++) {
+ for (x = 0; x < im->width; x++) {
+ r = (data[x] >> RrDefaultRedOffset) & 0xFF;
+ g = (data[x] >> RrDefaultGreenOffset) & 0xFF;
+ b = (data[x] >> RrDefaultBlueOffset) & 0xFF;
+ p32[x] = (r << RrRedOffset(inst))
+ + (g << RrGreenOffset(inst))
+ + (b << RrBlueOffset(inst));
+ }
+ data += im->width;
+ p32 += im->width;
+ }
+ } else im->data = (gchar*) data;
+ break;
+ case 24:
+ {
+ /* reverse the ordering, shifting left 16bit should be the first byte
+ out of three, etc */
+ const guint roff = (16 - RrRedOffset(inst)) / 8;
+ const guint goff = (16 - RrGreenOffset(inst)) / 8;
+ const guint boff = (16 - RrBlueOffset(inst)) / 8;
+ gint outx;
+ for (y = 0; y < im->height; y++) {
+ for (x = 0, outx = 0; x < im->width; x++, outx += 3) {
+ r = (data[x] >> RrDefaultRedOffset) & 0xFF;
+ g = (data[x] >> RrDefaultGreenOffset) & 0xFF;
+ b = (data[x] >> RrDefaultBlueOffset) & 0xFF;
+ p8[outx+roff] = r;
+ p8[outx+goff] = g;
+ p8[outx+boff] = b;
+ }
+ data += im->width;
+ p8 += im->bytes_per_line;
+ }
+ break;
+ }
+ case 16:
+ for (y = 0; y < im->height; y++) {
+ for (x = 0; x < im->width; x++) {
+ r = (data[x] >> RrDefaultRedOffset) & 0xFF;
+ r = r >> RrRedShift(inst);
+ g = (data[x] >> RrDefaultGreenOffset) & 0xFF;
+ g = g >> RrGreenShift(inst);
+ b = (data[x] >> RrDefaultBlueOffset) & 0xFF;
+ b = b >> RrBlueShift(inst);
+ p16[x] = (r << RrRedOffset(inst))
+ + (g << RrGreenOffset(inst))
+ + (b << RrBlueOffset(inst));
+ }
+ data += im->width;
+ p16 += im->bytes_per_line/2;
+ }
+ break;
+ case 8:
+ if (RrVisual(inst)->class == TrueColor) {
+ for (y = 0; y < im->height; y++) {
+ for (x = 0; x < im->width; x++) {
+ r = (data[x] >> RrDefaultRedOffset) & 0xFF;
+ r = r >> RrRedShift(inst);
+ g = (data[x] >> RrDefaultGreenOffset) & 0xFF;
+ g = g >> RrGreenShift(inst);
+ b = (data[x] >> RrDefaultBlueOffset) & 0xFF;
+ b = b >> RrBlueShift(inst);
+ p8[x] = (r << RrRedOffset(inst))
+ + (g << RrGreenOffset(inst))
+ + (b << RrBlueOffset(inst));
+ }
+ data += im->width;
+ p8 += im->bytes_per_line;
+ }
+ } else {
+ for (y = 0; y < im->height; y++) {
+ for (x = 0; x < im->width; x++) {
+ p8[x] = RrPickColor(inst,
+ data[x] >> RrDefaultRedOffset,
+ data[x] >> RrDefaultGreenOffset,
+ data[x] >> RrDefaultBlueOffset)->pixel;
+ }
+ data += im->width;
+ p8 += im->bytes_per_line;
+ }
+ }
+ break;
+ default:
+ g_error("This image bit depth (%i) is currently unhandled", im->bits_per_pixel);
+
+ }
+}
+
+XColor *RrPickColor(const RrInstance *inst, gint r, gint g, gint b)
+{
+ r = (r & 0xff) >> (8-RrPseudoBPC(inst));
+ g = (g & 0xff) >> (8-RrPseudoBPC(inst));
+ b = (b & 0xff) >> (8-RrPseudoBPC(inst));
+ return &RrPseudoColors(inst)[(r << (2*RrPseudoBPC(inst))) +
+ (g << (1*RrPseudoBPC(inst))) +
+ b];
+}
+
+static void swap_byte_order(XImage *im)
+{
+ gint x, y, di;
+
+ di = 0;
+ for (y = 0; y < im->height; ++y) {
+ for (x = 0; x < im->height; ++x) {
+ gchar *c = &im->data[di + x * im->bits_per_pixel / 8];
+ gchar t;
+
+ switch (im->bits_per_pixel) {
+ case 32:
+ t = c[2];
+ c[2] = c[3];
+ c[3] = t;
+ case 16:
+ t = c[0];
+ c[0] = c[1];
+ c[1] = t;
+ case 8:
+ case 1:
+ break;
+ default:
+ g_error("Your bit depth (%i) is currently unhandled",
+ im->bits_per_pixel);
+ }
+ }
+ di += im->bytes_per_line;
+ }
+
+ if (im->byte_order == LSBFirst)
+ im->byte_order = MSBFirst;
+ else
+ im->byte_order = LSBFirst;
+}
+
+void RrIncreaseDepth(const RrInstance *inst, RrPixel32 *data, XImage *im)
+{
+ gint r, g, b;
+ gint x,y;
+ RrPixel32 *p32 = (RrPixel32 *) im->data;
+ RrPixel16 *p16 = (RrPixel16 *) im->data;
+ guchar *p8 = (guchar *)im->data;
+
+ if (im->byte_order != LSBFirst)
+ swap_byte_order(im);
+
+ switch (im->bits_per_pixel) {
+ case 32:
+ for (y = 0; y < im->height; y++) {
+ for (x = 0; x < im->width; x++) {
+ r = (p32[x] >> RrRedOffset(inst)) & 0xff;
+ g = (p32[x] >> RrGreenOffset(inst)) & 0xff;
+ b = (p32[x] >> RrBlueOffset(inst)) & 0xff;
+ data[x] = (r << RrDefaultRedOffset)
+ + (g << RrDefaultGreenOffset)
+ + (b << RrDefaultBlueOffset)
+ + (0xff << RrDefaultAlphaOffset);
+ }
+ data += im->width;
+ p32 += im->bytes_per_line/4;
+ }
+ break;
+ case 16:
+ for (y = 0; y < im->height; y++) {
+ for (x = 0; x < im->width; x++) {
+ r = (p16[x] & RrRedMask(inst)) >>
+ RrRedOffset(inst) <<
+ RrRedShift(inst);
+ g = (p16[x] & RrGreenMask(inst)) >>
+ RrGreenOffset(inst) <<
+ RrGreenShift(inst);
+ b = (p16[x] & RrBlueMask(inst)) >>
+ RrBlueOffset(inst) <<
+ RrBlueShift(inst);
+ data[x] = (r << RrDefaultRedOffset)
+ + (g << RrDefaultGreenOffset)
+ + (b << RrDefaultBlueOffset)
+ + (0xff << RrDefaultAlphaOffset);
+ }
+ data += im->width;
+ p16 += im->bytes_per_line/2;
+ }
+ break;
+ case 8:
+ g_error("This image bit depth (%i) is currently unhandled", 8);
+ break;
+ case 1:
+ for (y = 0; y < im->height; y++) {
+ for (x = 0; x < im->width; x++) {
+ if (!(((p8[x / 8]) >> (x % 8)) & 0x1))
+ data[x] = 0xff << RrDefaultAlphaOffset; /* black */
+ else
+ data[x] = 0xffffffff; /* white */
+ }
+ data += im->width;
+ p8 += im->bytes_per_line;
+ }
+ break;
+ default:
+ g_error("This image bit depth (%i) is currently unhandled",
+ im->bits_per_pixel);
+ }
+}
+
+gint RrColorRed(const RrColor *c)
+{
+ return c->r;
+}
+
+gint RrColorGreen(const RrColor *c)
+{
+ return c->g;
+}
+
+gint RrColorBlue(const RrColor *c)
+{
+ return c->b;
+}
+
+gulong RrColorPixel(const RrColor *c)
+{
+ return c->pixel;
+}
+
+GC RrColorGC(RrColor *c)
+{
+ if (!c->gc)
+ RrColorAllocateGC(c);
+ return c->gc;
+}
diff --git a/obrender/color.h b/obrender/color.h
new file mode 100644
index 00000000..26fa7afe
--- /dev/null
+++ b/obrender/color.h
@@ -0,0 +1,51 @@
+/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
+
+ color.h for the Openbox window manager
+ Copyright (c) 2003-2007 Dana Jansens
+ Copyright (c) 2003 Derek Foreman
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ See the COPYING file for a copy of the GNU General Public License.
+*/
+
+#ifndef __color_h
+#define __color_h
+
+#include "render.h"
+
+#include <X11/Xlib.h>
+#include <X11/Xutil.h>
+#include <glib.h>
+
+struct _RrColor {
+ const RrInstance *inst;
+
+ gint r;
+ gint g;
+ gint b;
+ gulong pixel;
+ GC gc;
+
+ gint key;
+ gint refcount;
+
+#ifdef DEBUG
+ gint id;
+#endif
+};
+
+void RrColorAllocateGC(RrColor *in);
+XColor *RrPickColor(const RrInstance *inst, gint r, gint g, gint b);
+void RrReduceDepth(const RrInstance *inst, RrPixel32 *data, XImage *im);
+void RrIncreaseDepth(const RrInstance *inst, RrPixel32 *data, XImage *im);
+
+#endif /* __color_h */
diff --git a/obrender/font.c b/obrender/font.c
new file mode 100644
index 00000000..cde0d030
--- /dev/null
+++ b/obrender/font.c
@@ -0,0 +1,380 @@
+/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
+
+ font.c for the Openbox window manager
+ Copyright (c) 2006 Mikael Magnusson
+ Copyright (c) 2003-2007 Dana Jansens
+ Copyright (c) 2003 Derek Foreman
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ See the COPYING file for a copy of the GNU General Public License.
+*/
+
+#include "font.h"
+#include "color.h"
+#include "mask.h"
+#include "theme.h"
+#include "geom.h"
+#include "instance.h"
+#include "gettext.h"
+
+#include <glib.h>
+#include <string.h>
+#include <stdlib.h>
+#include <locale.h>
+
+static void measure_font(const RrInstance *inst, RrFont *f)
+{
+ PangoFontMetrics *metrics;
+ static PangoLanguage *lang = NULL;
+
+ if (lang == NULL) {
+#if PANGO_VERSION_MAJOR > 1 || \
+ (PANGO_VERSION_MAJOR == 1 && PANGO_VERSION_MINOR >= 16)
+ lang = pango_language_get_default();
+#else
+ gchar *locale, *p;
+ /* get the default language from the locale
+ (based on gtk_get_default_language in gtkmain.c) */
+ locale = g_strdup(setlocale(LC_CTYPE, NULL));
+ if ((p = strchr(locale, '.'))) *p = '\0'; /* strip off the . */
+ if ((p = strchr(locale, '@'))) *p = '\0'; /* strip off the @ */
+ lang = pango_language_from_string(locale);
+ g_free(locale);
+#endif
+ }
+
+ /* measure the ascent and descent */
+ metrics = pango_context_get_metrics(inst->pango, f->font_desc, lang);
+ f->ascent = pango_font_metrics_get_ascent(metrics);
+ f->descent = pango_font_metrics_get_descent(metrics);
+ pango_font_metrics_unref(metrics);
+
+}
+
+RrFont *RrFontOpen(const RrInstance *inst, const gchar *name, gint size,
+ RrFontWeight weight, RrFontSlant slant)
+{
+ RrFont *out;
+ PangoWeight pweight;
+ PangoStyle pstyle;
+ PangoAttrList *attrlist;
+
+ out = g_new(RrFont, 1);
+ out->inst = inst;
+ out->ref = 1;
+ out->font_desc = pango_font_description_new();
+ out->layout = pango_layout_new(inst->pango);
+ out->shortcut_underline = pango_attr_underline_new(PANGO_UNDERLINE_LOW);
+ out->shortcut_underline->start_index = 0;
+ out->shortcut_underline->end_index = 0;
+
+ attrlist = pango_attr_list_new();
+ /* shortcut_underline is owned by the attrlist */
+ pango_attr_list_insert(attrlist, out->shortcut_underline);
+ /* the attributes are owned by the layout */
+ pango_layout_set_attributes(out->layout, attrlist);
+ pango_attr_list_unref(attrlist);
+
+ switch (weight) {
+ case RR_FONTWEIGHT_LIGHT: pweight = PANGO_WEIGHT_LIGHT; break;
+ case RR_FONTWEIGHT_NORMAL: pweight = PANGO_WEIGHT_NORMAL; break;
+ case RR_FONTWEIGHT_SEMIBOLD: pweight = PANGO_WEIGHT_SEMIBOLD; break;
+ case RR_FONTWEIGHT_BOLD: pweight = PANGO_WEIGHT_BOLD; break;
+ case RR_FONTWEIGHT_ULTRABOLD: pweight = PANGO_WEIGHT_ULTRABOLD; break;
+ default: g_assert_not_reached();
+ }
+
+ switch (slant) {
+ case RR_FONTSLANT_NORMAL: pstyle = PANGO_STYLE_NORMAL; break;
+ case RR_FONTSLANT_ITALIC: pstyle = PANGO_STYLE_ITALIC; break;
+ case RR_FONTSLANT_OBLIQUE: pstyle = PANGO_STYLE_OBLIQUE; break;
+ default: g_assert_not_reached();
+ }
+
+ /* setup the font */
+ pango_font_description_set_family(out->font_desc, name);
+ pango_font_description_set_weight(out->font_desc, pweight);
+ pango_font_description_set_style(out->font_desc, pstyle);
+ pango_font_description_set_size(out->font_desc, size * PANGO_SCALE);
+
+ /* setup the layout */
+ pango_layout_set_font_description(out->layout, out->font_desc);
+ pango_layout_set_wrap(out->layout, PANGO_WRAP_WORD_CHAR);
+
+ /* get the ascent and descent */
+ measure_font(inst, out);
+
+ return out;
+}
+
+RrFont *RrFontOpenDefault(const RrInstance *inst)
+{
+ return RrFontOpen(inst, RrDefaultFontFamily, RrDefaultFontSize,
+ RrDefaultFontWeight, RrDefaultFontSlant);
+}
+
+void RrFontRef(RrFont *f)
+{
+ ++f->ref;
+}
+
+void RrFontClose(RrFont *f)
+{
+ if (f) {
+ if (--f->ref < 1) {
+ g_object_unref(f->layout);
+ pango_font_description_free(f->font_desc);
+ g_free(f);
+ }
+ }
+}
+
+static void font_measure_full(const RrFont *f, const gchar *str,
+ gint *x, gint *y, gint shadow_x, gint shadow_y,
+ gboolean flow, gint maxwidth)
+{
+ PangoRectangle rect;
+
+ pango_layout_set_text(f->layout, str, -1);
+ if (flow) {
+ pango_layout_set_single_paragraph_mode(f->layout, FALSE);
+ pango_layout_set_width(f->layout, maxwidth * PANGO_SCALE);
+ pango_layout_set_ellipsize(f->layout, PANGO_ELLIPSIZE_NONE);
+ }
+ else {
+ /* single line mode */
+ pango_layout_set_single_paragraph_mode(f->layout, TRUE);
+ pango_layout_set_width(f->layout, -1);
+ pango_layout_set_ellipsize(f->layout, PANGO_ELLIPSIZE_MIDDLE);
+ }
+
+ /* pango_layout_get_pixel_extents lies! this is the right way to get the
+ size of the text's area */
+ pango_layout_get_extents(f->layout, NULL, &rect);
+#if PANGO_VERSION_MAJOR > 1 || \
+ (PANGO_VERSION_MAJOR == 1 && PANGO_VERSION_MINOR >= 16)
+ /* pass the logical rect as the ink rect, this is on purpose so we get the
+ full area for the text */
+ pango_extents_to_pixels(&rect, NULL);
+#else
+ rect.width = (rect.width + PANGO_SCALE - 1) / PANGO_SCALE;
+ rect.height = (rect.height + PANGO_SCALE - 1) / PANGO_SCALE;
+#endif
+ *x = rect.width + ABS(shadow_x) + 4 /* we put a 2 px edge on each side */;
+ *y = rect.height + ABS(shadow_y);
+}
+
+RrSize *RrFontMeasureString(const RrFont *f, const gchar *str,
+ gint shadow_x, gint shadow_y,
+ gboolean flow, gint maxwidth)
+{
+ RrSize *size;
+
+ g_assert(!flow || maxwidth > 0);
+
+ size = g_new(RrSize, 1);
+ font_measure_full(f, str, &size->width, &size->height, shadow_x, shadow_y,
+ flow, maxwidth);
+ return size;
+}
+
+gint RrFontHeight(const RrFont *f, gint shadow_y)
+{
+ return (f->ascent + f->descent) / PANGO_SCALE + ABS(shadow_y);
+}
+
+static inline int font_calculate_baseline(RrFont *f, gint height)
+{
+/* For my own reference:
+ * _________
+ * ^space/2 ^height ^baseline
+ * v_________|_ |
+ * | ^ascent | _ _
+ * | | | | |_ _____ _| |_ _ _
+ * | | | | _/ -_) \ / _| || |
+ * | v_________v \__\___/_\_\\__|\_, |
+ * | ^descent |__/
+ * __________|_v
+ * ^space/2 |
+ * V_________v
+ */
+ return (((height * PANGO_SCALE) /* height of the space in pango units */
+ - (f->ascent + f->descent)) /* minus space taken up by text */
+ / 2 /* divided by two -> half of the empty space (this is the top
+ of the text) */
+ + f->ascent) /* now move down to the baseline */
+ / PANGO_SCALE; /* back to pixels */
+}
+
+void RrFontDraw(XftDraw *d, RrTextureText *t, RrRect *area)
+{
+ gint x,y,w;
+ XftColor c;
+ gint mw;
+ PangoRectangle rect;
+ PangoAttrList *attrlist;
+ PangoEllipsizeMode ell;
+
+ g_assert(!t->flow || t->maxwidth > 0);
+
+ y = area->y;
+ if (!t->flow)
+ /* center the text vertically
+ We do this centering based on the 'baseline' since different fonts
+ have different top edges. It looks bad when the whole string is
+ moved when 1 character from a non-default language is included in
+ the string */
+ y += font_calculate_baseline(t->font, area->height);
+
+ /* the +2 and -4 leave a small blank edge on the sides */
+ x = area->x + 2;
+ w = area->width;
+ if (t->flow) w = MAX(w, t->maxwidth);
+ w -= 4;
+ /* h = area->height; */
+
+ if (t->flow)
+ ell = PANGO_ELLIPSIZE_NONE;
+ else {
+ switch (t->ellipsize) {
+ case RR_ELLIPSIZE_NONE:
+ ell = PANGO_ELLIPSIZE_NONE;
+ break;
+ case RR_ELLIPSIZE_START:
+ ell = PANGO_ELLIPSIZE_START;
+ break;
+ case RR_ELLIPSIZE_MIDDLE:
+ ell = PANGO_ELLIPSIZE_MIDDLE;
+ break;
+ case RR_ELLIPSIZE_END:
+ ell = PANGO_ELLIPSIZE_END;
+ break;
+ default:
+ g_assert_not_reached();
+ }
+ }
+
+ pango_layout_set_text(t->font->layout, t->string, -1);
+ pango_layout_set_width(t->font->layout, w * PANGO_SCALE);
+ pango_layout_set_ellipsize(t->font->layout, ell);
+ pango_layout_set_single_paragraph_mode(t->font->layout, !t->flow);
+
+ /* * * end of setting up the layout * * */
+
+ pango_layout_get_pixel_extents(t->font->layout, NULL, &rect);
+ mw = rect.width;
+
+ /* pango_layout_set_alignment doesn't work with
+ pango_xft_render_layout_line */
+ switch (t->justify) {
+ case RR_JUSTIFY_LEFT:
+ break;
+ case RR_JUSTIFY_RIGHT:
+ x += (w - mw);
+ break;
+ case RR_JUSTIFY_CENTER:
+ x += (w - mw) / 2;
+ break;
+ }
+
+ if (t->shadow_offset_x || t->shadow_offset_y) {
+ /* From nvidia's readme (chapter 23):
+
+ When rendering to a 32-bit window, keep in mind that the X RENDER
+ extension, used by most composite managers, expects "premultiplied
+ alpha" colors. This means that if your color has components (r,g,b)
+ and alpha value a, then you must render (a*r, a*g, a*b, a) into the
+ target window.
+ */
+ c.color.red = (t->shadow_color->r | t->shadow_color->r << 8) *
+ t->shadow_alpha / 255;
+ c.color.green = (t->shadow_color->g | t->shadow_color->g << 8) *
+ t->shadow_alpha / 255;
+ c.color.blue = (t->shadow_color->b | t->shadow_color->b << 8) *
+ t->shadow_alpha / 255;
+ c.color.alpha = 0xffff * t->shadow_alpha / 255;
+ c.pixel = t->shadow_color->pixel;
+
+ /* see below... */
+ if (!t->flow) {
+ pango_xft_render_layout_line
+ (d, &c,
+#if PANGO_VERSION_MAJOR > 1 || \
+ (PANGO_VERSION_MAJOR == 1 && PANGO_VERSION_MINOR >= 16)
+ pango_layout_get_line_readonly(t->font->layout, 0),
+#else
+ pango_layout_get_line(t->font->layout, 0),
+#endif
+ (x + t->shadow_offset_x) * PANGO_SCALE,
+ (y + t->shadow_offset_y) * PANGO_SCALE);
+ }
+ else {
+ pango_xft_render_layout(d, &c, t->font->layout,
+ (x + t->shadow_offset_x) * PANGO_SCALE,
+ (y + t->shadow_offset_y) * PANGO_SCALE);
+ }
+ }
+
+ c.color.red = t->color->r | t->color->r << 8;
+ c.color.green = t->color->g | t->color->g << 8;
+ c.color.blue = t->color->b | t->color->b << 8;
+ c.color.alpha = 0xff | 0xff << 8; /* fully opaque text */
+ c.pixel = t->color->pixel;
+
+ if (t->shortcut) {
+ const gchar *s = t->string + t->shortcut_pos;
+
+ t->font->shortcut_underline->start_index = t->shortcut_pos;
+ t->font->shortcut_underline->end_index = t->shortcut_pos +
+ (g_utf8_next_char(s) - s);
+
+ /* the attributes are owned by the layout.
+ re-add the attributes to the layout after changing the
+ start and end index */
+ attrlist = pango_layout_get_attributes(t->font->layout);
+ pango_attr_list_ref(attrlist);
+ pango_layout_set_attributes(t->font->layout, attrlist);
+ pango_attr_list_unref(attrlist);
+ }
+
+ /* layout_line() uses y to specify the baseline
+ The line doesn't need to be freed, it's a part of the layout */
+ if (!t->flow) {
+ pango_xft_render_layout_line
+ (d, &c,
+#if PANGO_VERSION_MAJOR > 1 || \
+ (PANGO_VERSION_MAJOR == 1 && PANGO_VERSION_MINOR >= 16)
+ pango_layout_get_line_readonly(t->font->layout, 0),
+#else
+ pango_layout_get_line(t->font->layout, 0),
+#endif
+ x * PANGO_SCALE,
+ y * PANGO_SCALE);
+ }
+ else {
+ pango_xft_render_layout(d, &c, t->font->layout,
+ x * PANGO_SCALE,
+ y * PANGO_SCALE);
+ }
+
+ if (t->shortcut) {
+ t->font->shortcut_underline->start_index = 0;
+ t->font->shortcut_underline->end_index = 0;
+ /* the attributes are owned by the layout.
+ re-add the attributes to the layout after changing the
+ start and end index */
+ attrlist = pango_layout_get_attributes(t->font->layout);
+ pango_attr_list_ref(attrlist);
+ pango_layout_set_attributes(t->font->layout, attrlist);
+ pango_attr_list_unref(attrlist);
+ }
+}
diff --git a/obrender/font.h b/obrender/font.h
new file mode 100644
index 00000000..07d648d1
--- /dev/null
+++ b/obrender/font.h
@@ -0,0 +1,42 @@
+/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
+
+ font.h for the Openbox window manager
+ Copyright (c) 2003-2007 Dana Jansens
+ Copyright (c) 2003 Derek Foreman
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ See the COPYING file for a copy of the GNU General Public License.
+*/
+
+#ifndef __font_h
+#define __font_h
+#include "render.h"
+#include "geom.h"
+#include <pango/pango.h>
+
+struct _RrFont {
+ const RrInstance *inst;
+ gint ref;
+ PangoFontDescription *font_desc;
+ PangoLayout *layout; /*!< Used for measuring and rendering strings */
+ PangoAttribute *shortcut_underline; /*< For underlining the shortcut key */
+ gint ascent; /*!< The font's ascent in pango-units */
+ gint descent; /*!< The font's descent in pango-units */
+};
+
+void RrFontDraw(XftDraw *d, RrTextureText *t, RrRect *position);
+
+/*! Increment the references for this font, RrFontClose will decrement until 0
+ and then really close it */
+void RrFontRef(RrFont *f);
+
+#endif /* __font_h */
diff --git a/obrender/geom.h b/obrender/geom.h
new file mode 100644
index 00000000..4d81e4b3
--- /dev/null
+++ b/obrender/geom.h
@@ -0,0 +1,38 @@
+/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
+
+ geom.h for the Openbox window manager
+ Copyright (c) 2006 Mikael Magnusson
+ Copyright (c) 2003-2007 Dana Jansens
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ See the COPYING file for a copy of the GNU General Public License.
+*/
+
+#ifndef __render_geom_h
+#define __render_geom_h
+
+typedef struct {
+ int width;
+ int height;
+} RrSize;
+
+typedef struct {
+ int x;
+ int y;
+ int width;
+ int height;
+} RrRect;
+
+#define RECT_SET(r, nx, ny, w, h) \
+ (r).x = (nx), (r).y = (ny), (r).width = (w), (r).height = (h)
+
+#endif
diff --git a/obrender/gradient.c b/obrender/gradient.c
new file mode 100644
index 00000000..60a0a555
--- /dev/null
+++ b/obrender/gradient.c
@@ -0,0 +1,836 @@
+/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
+
+ gradient.c for the Openbox window manager
+ Copyright (c) 2006 Mikael Magnusson
+ Copyright (c) 2003-2008 Dana Jansens
+ Copyright (c) 2003 Derek Foreman
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ See the COPYING file for a copy of the GNU General Public License.
+*/
+
+#include "render.h"
+#include "gradient.h"
+#include "color.h"
+#include <glib.h>
+#include <string.h>
+
+static void highlight(RrSurface *s, RrPixel32 *x, RrPixel32 *y,
+ gboolean raised);
+static void gradient_parentrelative(RrAppearance *a, gint w, gint h);
+static void gradient_solid(RrAppearance *l, gint w, gint h);
+static void gradient_splitvertical(RrAppearance *a, gint w, gint h);
+static void gradient_vertical(RrSurface *sf, gint w, gint h);
+static void gradient_horizontal(RrSurface *sf, gint w, gint h);
+static void gradient_mirrorhorizontal(RrSurface *sf, gint w, gint h);
+static void gradient_diagonal(RrSurface *sf, gint w, gint h);
+static void gradient_crossdiagonal(RrSurface *sf, gint w, gint h);
+static void gradient_pyramid(RrSurface *sf, gint inw, gint inh);
+
+void RrRender(RrAppearance *a, gint w, gint h)
+{
+ RrPixel32 *data = a->surface.pixel_data;
+ RrPixel32 current;
+ guint r,g,b;
+ register gint off, x;
+
+ switch (a->surface.grad) {
+ case RR_SURFACE_PARENTREL:
+ gradient_parentrelative(a, w, h);
+ break;
+ case RR_SURFACE_SOLID:
+ gradient_solid(a, w, h);
+ break;
+ case RR_SURFACE_SPLIT_VERTICAL:
+ gradient_splitvertical(a, w, h);
+ break;
+ case RR_SURFACE_VERTICAL:
+ gradient_vertical(&a->surface, w, h);
+ break;
+ case RR_SURFACE_HORIZONTAL:
+ gradient_horizontal(&a->surface, w, h);
+ break;
+ case RR_SURFACE_MIRROR_HORIZONTAL:
+ gradient_mirrorhorizontal(&a->surface, w, h);
+ break;
+ case RR_SURFACE_DIAGONAL:
+ gradient_diagonal(&a->surface, w, h);
+ break;
+ case RR_SURFACE_CROSS_DIAGONAL:
+ gradient_crossdiagonal(&a->surface, w, h);
+ break;
+ case RR_SURFACE_PYRAMID:
+ gradient_pyramid(&a->surface, w, h);
+ break;
+ default:
+ g_assert_not_reached(); /* unhandled gradient */
+ return;
+ }
+
+ if (a->surface.interlaced) {
+ gint i;
+ RrPixel32 *p;
+
+ r = a->surface.interlace_color->r;
+ g = a->surface.interlace_color->g;
+ b = a->surface.interlace_color->b;
+ current = (r << RrDefaultRedOffset)
+ + (g << RrDefaultGreenOffset)
+ + (b << RrDefaultBlueOffset);
+ p = data;
+ for (i = 0; i < h; i += 2, p += w)
+ for (x = 0; x < w; ++x, ++p)
+ *p = current;
+ }
+
+ if (a->surface.relief == RR_RELIEF_FLAT && a->surface.border) {
+ r = a->surface.border_color->r;
+ g = a->surface.border_color->g;
+ b = a->surface.border_color->b;
+ current = (r << RrDefaultRedOffset)
+ + (g << RrDefaultGreenOffset)
+ + (b << RrDefaultBlueOffset);
+ for (off = 0, x = 0; x < w; ++x, off++) {
+ *(data + off) = current;
+ *(data + off + ((h-1) * w)) = current;
+ }
+ for (off = 0, x = 0; x < h; ++x, off++) {
+ *(data + (off * w)) = current;
+ *(data + (off * w) + w - 1) = current;
+ }
+ }
+
+ if (a->surface.relief != RR_RELIEF_FLAT) {
+ if (a->surface.bevel == RR_BEVEL_1) {
+ for (off = 1, x = 1; x < w - 1; ++x, off++)
+ highlight(&a->surface, data + off,
+ data + off + (h-1) * w,
+ a->surface.relief==RR_RELIEF_RAISED);
+ for (off = 0, x = 0; x < h; ++x, off++)
+ highlight(&a->surface, data + off * w,
+ data + off * w + w - 1,
+ a->surface.relief==RR_RELIEF_RAISED);
+ }
+
+ if (a->surface.bevel == RR_BEVEL_2) {
+ for (off = 2, x = 2; x < w - 2; ++x, off++)
+ highlight(&a->surface, data + off + w,
+ data + off + (h-2) * w,
+ a->surface.relief==RR_RELIEF_RAISED);
+ for (off = 1, x = 1; x < h-1; ++x, off++)
+ highlight(&a->surface, data + off * w + 1,
+ data + off * w + w - 2,
+ a->surface.relief==RR_RELIEF_RAISED);
+ }
+ }
+}
+
+static void highlight(RrSurface *s, RrPixel32 *x, RrPixel32 *y, gboolean raised)
+{
+ register gint r, g, b;
+
+ RrPixel32 *up, *down;
+ if (raised) {
+ up = x;
+ down = y;
+ } else {
+ up = y;
+ down = x;
+ }
+
+ r = (*up >> RrDefaultRedOffset) & 0xFF;
+ r += (r * s->bevel_light_adjust) >> 8;
+ g = (*up >> RrDefaultGreenOffset) & 0xFF;
+ g += (g * s->bevel_light_adjust) >> 8;
+ b = (*up >> RrDefaultBlueOffset) & 0xFF;
+ b += (b * s->bevel_light_adjust) >> 8;
+ if (r > 0xFF) r = 0xFF;
+ if (g > 0xFF) g = 0xFF;
+ if (b > 0xFF) b = 0xFF;
+ *up = (r << RrDefaultRedOffset) + (g << RrDefaultGreenOffset)
+ + (b << RrDefaultBlueOffset);
+
+ r = (*down >> RrDefaultRedOffset) & 0xFF;
+ r -= (r * s->bevel_dark_adjust) >> 8;
+ g = (*down >> RrDefaultGreenOffset) & 0xFF;
+ g -= (g * s->bevel_dark_adjust) >> 8;
+ b = (*down >> RrDefaultBlueOffset) & 0xFF;
+ b -= (b * s->bevel_dark_adjust) >> 8;
+ *down = (r << RrDefaultRedOffset) + (g << RrDefaultGreenOffset)
+ + (b << RrDefaultBlueOffset);
+}
+
+static void create_bevel_colors(RrAppearance *l)
+{
+ register gint r, g, b;
+
+ /* light color */
+ r = l->surface.primary->r;
+ r += (r * l->surface.bevel_light_adjust) >> 8;
+ g = l->surface.primary->g;
+ g += (g * l->surface.bevel_light_adjust) >> 8;
+ b = l->surface.primary->b;
+ b += (b * l->surface.bevel_light_adjust) >> 8;
+ if (r > 0xFF) r = 0xFF;
+ if (g > 0xFF) g = 0xFF;
+ if (b > 0xFF) b = 0xFF;
+ g_assert(!l->surface.bevel_light);
+ l->surface.bevel_light = RrColorNew(l->inst, r, g, b);
+
+ /* dark color */
+ r = l->surface.primary->r;
+ r -= (r * l->surface.bevel_dark_adjust) >> 8;
+ g = l->surface.primary->g;
+ g -= (g * l->surface.bevel_dark_adjust) >> 8;
+ b = l->surface.primary->b;
+ b -= (b * l->surface.bevel_dark_adjust) >> 8;
+ g_assert(!l->surface.bevel_dark);
+ l->surface.bevel_dark = RrColorNew(l->inst, r, g, b);
+}
+
+/*! Repeat the first pixel over the entire block of memory
+ @param start The block of memory. start[0] will be copied
+ to the rest of the block.
+ @param w The width of the block of memory (including the already-set first
+ element
+*/
+static inline void repeat_pixel(RrPixel32 *start, gint w)
+{
+ register gint x;
+ RrPixel32 *dest;
+
+ dest = start + 1;
+
+ /* for really small things, just copy ourselves */
+ if (w < 8) {
+ for (x = w-1; x > 0; --x)
+ *(dest++) = *start;
+ }
+
+ /* for >= 8, then use O(log n) memcpy's... */
+ else {
+ gchar *cdest;
+ gint lenbytes;
+
+ /* copy the first 3 * 32 bits (3 words) ourselves - then we have
+ 3 + the original 1 = 4 words to make copies of at a time
+
+ this is faster than doing memcpy for 1 or 2 words at a time
+ */
+ for (x = 3; x > 0; --x)
+ *(dest++) = *start;
+
+ /* cdest is a pointer to the pixel data that is typed char* so that
+ adding 1 to its position moves it only one byte
+
+ lenbytes is the amount of bytes that we will be copying each
+ iteration. this doubles each time through the loop.
+
+ x is the number of bytes left to copy into. lenbytes will alwaysa
+ be bounded by x
+
+ this loop will run O(log n) times (n is the number of bytes we
+ need to copy into), since the size of the copy is doubled each
+ iteration. it seems that gcc does some nice optimizations to make
+ this memcpy very fast on hardware with support for vector operations
+ such as mmx or see. here is an idea of the kind of speed up we are
+ getting by doing this (splitvertical3 switches from doing
+ "*(data++) = color" n times to doing this memcpy thing log n times:
+
+ % cumulative self self total
+ time seconds seconds calls ms/call ms/call name
+ 49.44 0.88 0.88 1063 0.83 0.83 splitvertical1
+ 47.19 1.72 0.84 1063 0.79 0.79 splitvertical2
+ 2.81 1.77 0.05 1063 0.05 0.05 splitvertical3
+ */
+ cdest = (gchar*)dest;
+ lenbytes = 4 * sizeof(RrPixel32);
+ for (x = (w - 4) * sizeof(RrPixel32); x > 0;) {
+ memcpy(cdest, start, lenbytes);
+ x -= lenbytes;
+ cdest += lenbytes;
+ lenbytes <<= 1;
+ if (lenbytes > x)
+ lenbytes = x;
+ }
+ }
+}
+
+static void gradient_parentrelative(RrAppearance *a, gint w, gint h)
+{
+ RrPixel32 *source, *dest;
+ gint sw, sh, partial_w, partial_h;
+ register gint i;
+
+ g_assert (a->surface.parent);
+ g_assert (a->surface.parent->w);
+
+ sw = a->surface.parent->w;
+ sh = a->surface.parent->h;
+
+ /* This is a little hack. When a texture is parentrelative, and the same
+ area as the parent, and has a bevel, it will draw its bevel on top
+ of the parent's, amplifying it. So instead, rerender the child with
+ the parent's settings, but the child's bevel and interlace */
+ if (a->surface.relief != RR_RELIEF_FLAT &&
+ (a->surface.parent->surface.relief != RR_RELIEF_FLAT ||
+ a->surface.parent->surface.border) &&
+ !a->surface.parentx && !a->surface.parenty &&
+ sw == w && sh == h)
+ {
+ RrSurface old = a->surface;
+ a->surface = a->surface.parent->surface;
+
+ /* turn these off for the parent */
+ a->surface.relief = RR_RELIEF_FLAT;
+ a->surface.border = FALSE;
+
+ a->surface.pixel_data = old.pixel_data;
+
+ RrRender(a, w, h);
+ a->surface = old;
+ } else {
+ source = (a->surface.parent->surface.pixel_data +
+ a->surface.parentx + sw * a->surface.parenty);
+ dest = a->surface.pixel_data;
+
+ if (a->surface.parentx + w > sw) {
+ partial_w = sw - a->surface.parentx;
+ } else partial_w = w;
+
+ if (a->surface.parenty + h > sh) {
+ partial_h = sh - a->surface.parenty;
+ } else partial_h = h;
+
+ for (i = 0; i < partial_h; i++, source += sw, dest += w) {
+ memcpy(dest, source, partial_w * sizeof(RrPixel32));
+ }
+ }
+}
+
+static void gradient_solid(RrAppearance *l, gint w, gint h)
+{
+ register gint i;
+ RrPixel32 pix;
+ RrPixel32 *data = l->surface.pixel_data;
+ RrSurface *sp = &l->surface;
+ gint left = 0, top = 0, right = w - 1, bottom = h - 1;
+
+ pix = (sp->primary->r << RrDefaultRedOffset)
+ + (sp->primary->g << RrDefaultGreenOffset)
+ + (sp->primary->b << RrDefaultBlueOffset);
+
+ for (i = 0; i < w * h; i++)
+ *data++ = pix;
+
+ if (sp->interlaced)
+ return;
+
+ XFillRectangle(RrDisplay(l->inst), l->pixmap, RrColorGC(sp->primary),
+ 0, 0, w, h);
+
+ switch (sp->relief) {
+ case RR_RELIEF_RAISED:
+ if (!sp->bevel_dark)
+ create_bevel_colors(l);
+
+ switch (sp->bevel) {
+ case RR_BEVEL_1:
+ XDrawLine(RrDisplay(l->inst), l->pixmap, RrColorGC(sp->bevel_dark),
+ left, bottom, right, bottom);
+ XDrawLine(RrDisplay(l->inst), l->pixmap, RrColorGC(sp->bevel_dark),
+ right, bottom, right, top);
+
+ XDrawLine(RrDisplay(l->inst), l->pixmap,RrColorGC(sp->bevel_light),
+ left, top, right, top);
+ XDrawLine(RrDisplay(l->inst), l->pixmap,RrColorGC(sp->bevel_light),
+ left, bottom, left, top);
+ break;
+ case RR_BEVEL_2:
+ XDrawLine(RrDisplay(l->inst), l->pixmap, RrColorGC(sp->bevel_dark),
+ left + 2, bottom - 1, right - 2, bottom - 1);
+ XDrawLine(RrDisplay(l->inst), l->pixmap, RrColorGC(sp->bevel_dark),
+ right - 1, bottom - 1, right - 1, top + 1);
+
+ XDrawLine(RrDisplay(l->inst), l->pixmap,RrColorGC(sp->bevel_light),
+ left + 2, top + 1, right - 2, top + 1);
+ XDrawLine(RrDisplay(l->inst), l->pixmap,RrColorGC(sp->bevel_light),
+ left + 1, bottom - 1, left + 1, top + 1);
+ break;
+ default:
+ g_assert_not_reached(); /* unhandled BevelType */
+ }
+ break;
+ case RR_RELIEF_SUNKEN:
+ if (!sp->bevel_dark)
+ create_bevel_colors(l);
+
+ switch (sp->bevel) {
+ case RR_BEVEL_1:
+ XDrawLine(RrDisplay(l->inst), l->pixmap,RrColorGC(sp->bevel_light),
+ left, bottom, right, bottom);
+ XDrawLine(RrDisplay(l->inst), l->pixmap,RrColorGC(sp->bevel_light),
+ right, bottom, right, top);
+
+ XDrawLine(RrDisplay(l->inst), l->pixmap, RrColorGC(sp->bevel_dark),
+ left, top, right, top);
+ XDrawLine(RrDisplay(l->inst), l->pixmap, RrColorGC(sp->bevel_dark),
+ left, bottom, left, top);
+ break;
+ case RR_BEVEL_2:
+ XDrawLine(RrDisplay(l->inst), l->pixmap,RrColorGC(sp->bevel_light),
+ left + 2, bottom - 1, right - 2, bottom - 1);
+ XDrawLine(RrDisplay(l->inst), l->pixmap,RrColorGC(sp->bevel_light),
+ right - 1, bottom - 1, right - 1, top + 1);
+
+ XDrawLine(RrDisplay(l->inst), l->pixmap, RrColorGC(sp->bevel_dark),
+ left + 2, top + 1, right - 2, top + 1);
+ XDrawLine(RrDisplay(l->inst), l->pixmap, RrColorGC(sp->bevel_dark),
+ left + 1, bottom - 1, left + 1, top + 1);
+ break;
+ default:
+ g_assert_not_reached(); /* unhandled BevelType */
+ }
+ break;
+ case RR_RELIEF_FLAT:
+ if (sp->border) {
+ XDrawRectangle(RrDisplay(l->inst), l->pixmap,
+ RrColorGC(sp->border_color),
+ left, top, right, bottom);
+ }
+ break;
+ default:
+ g_assert_not_reached(); /* unhandled ReliefType */
+ }
+}
+
+/* * * * * * * * * * * * * * GRADIENT MAGIC WOOT * * * * * * * * * * * * * * */
+
+#define VARS(x) \
+ register gint len##x; \
+ guint color##x[3]; \
+ gint cdelta##x[3], error##x[3] = { 0, 0, 0 }, inc##x[3]; \
+ gboolean bigslope##x[3] /* color slope > 1 */
+
+#define SETUP(x, from, to, w) \
+ len##x = w; \
+ \
+ color##x[0] = from->r; \
+ color##x[1] = from->g; \
+ color##x[2] = from->b; \
+ \
+ cdelta##x[0] = to->r - from->r; \
+ cdelta##x[1] = to->g - from->g; \
+ cdelta##x[2] = to->b - from->b; \
+ \
+ if (cdelta##x[0] < 0) { \
+ cdelta##x[0] = -cdelta##x[0]; \
+ inc##x[0] = -1; \
+ } else \
+ inc##x[0] = 1; \
+ if (cdelta##x[1] < 0) { \
+ cdelta##x[1] = -cdelta##x[1]; \
+ inc##x[1] = -1; \
+ } else \
+ inc##x[1] = 1; \
+ if (cdelta##x[2] < 0) { \
+ cdelta##x[2] = -cdelta##x[2]; \
+ inc##x[2] = -1; \
+ } else \
+ inc##x[2] = 1; \
+ bigslope##x[0] = cdelta##x[0] > w;\
+ bigslope##x[1] = cdelta##x[1] > w;\
+ bigslope##x[2] = cdelta##x[2] > w
+
+#define COLOR_RR(x, c) \
+ c->r = color##x[0]; \
+ c->g = color##x[1]; \
+ c->b = color##x[2]
+
+#define COLOR(x) \
+ ((color##x[0] << RrDefaultRedOffset) + \
+ (color##x[1] << RrDefaultGreenOffset) + \
+ (color##x[2] << RrDefaultBlueOffset))
+
+#define INCREMENT(x, i) \
+ (inc##x[i])
+
+#define NEXT(x) \
+{ \
+ register gint i; \
+ for (i = 2; i >= 0; --i) { \
+ if (!cdelta##x[i]) continue; \
+ \
+ if (!bigslope##x[i]) { \
+ /* Y (color) is dependant on X */ \
+ error##x[i] += cdelta##x[i]; \
+ if ((error##x[i] << 1) >= len##x) { \
+ color##x[i] += INCREMENT(x, i); \
+ error##x[i] -= len##x; \
+ } \
+ } else { \
+ /* X is dependant on Y (color) */ \
+ while (1) { \
+ color##x[i] += INCREMENT(x, i); \
+ error##x[i] += len##x; \
+ if ((error##x[i] << 1) >= cdelta##x[i]) { \
+ error##x[i] -= cdelta##x[i]; \
+ break; \
+ } \
+ } \
+ } \
+ } \
+}
+
+static void gradient_splitvertical(RrAppearance *a, gint w, gint h)
+{
+ register gint y1, y2, y3;
+ RrSurface *sf = &a->surface;
+ RrPixel32 *data;
+ register gint y1sz, y2sz, y3sz;
+
+ VARS(y1);
+ VARS(y2);
+ VARS(y3);
+
+ /* if h <= 5, then a 0 or 1px middle gradient.
+ if h > 5, then always a 1px middle gradient.
+ */
+ if (h <= 5) {
+ y1sz = MAX(h/2, 0);
+ y2sz = (h < 3) ? 0 : (h & 1);
+ y3sz = MAX(h/2, 1);
+ }
+ else {
+ y1sz = h/2 - (1 - (h & 1));
+ y2sz = 1;
+ y3sz = h/2;
+ }
+
+ SETUP(y1, sf->split_primary, sf->primary, y1sz);
+ if (y2sz) {
+ /* setup to get the colors _in between_ these other 2 */
+ SETUP(y2, sf->primary, sf->secondary, y2sz + 2);
+ NEXT(y2); /* skip the first one, its the same as the last of y1 */
+ }
+ SETUP(y3, sf->secondary, sf->split_secondary, y3sz);
+
+ /* find the color for the first pixel of each row first */
+ data = sf->pixel_data;
+
+ for (y1 = y1sz-1; y1 > 0; --y1) {
+ *data = COLOR(y1);
+ data += w;
+ NEXT(y1);
+ }
+ *data = COLOR(y1);
+ data += w;
+ if (y2sz) {
+ for (y2 = y2sz-1; y2 > 0; --y2) {
+ *data = COLOR(y2);
+ data += w;
+ NEXT(y2);
+ }
+ *data = COLOR(y2);
+ data += w;
+ }
+ for (y3 = y3sz-1; y3 > 0; --y3) {
+ *data = COLOR(y3);
+ data += w;
+ NEXT(y3);
+ }
+ *data = COLOR(y3);
+
+ /* copy the first pixels into the whole rows */
+ data = sf->pixel_data;
+ for (y1 = h; y1 > 0; --y1) {
+ repeat_pixel(data, w);
+ data += w;
+ }
+}
+
+static void gradient_horizontal(RrSurface *sf, gint w, gint h)
+{
+ register gint x, y, cpbytes;
+ RrPixel32 *data = sf->pixel_data, *datav;
+ gchar *datac;
+
+ VARS(x);
+ SETUP(x, sf->primary, sf->secondary, w);
+
+ /* set the color values for the first row */
+ datav = data;
+ for (x = w - 1; x > 0; --x) { /* 0 -> w - 1 */
+ *datav = COLOR(x);
+ ++datav;
+ NEXT(x);
+ }
+ *datav = COLOR(x);
+ ++datav;
+
+ /* copy the first row to the rest in O(logn) copies */
+ datac = (gchar*)datav;
+ cpbytes = 1 * w * sizeof(RrPixel32);
+ for (y = (h - 1) * w * sizeof(RrPixel32); y > 0;) {
+ memcpy(datac, data, cpbytes);
+ y -= cpbytes;
+ datac += cpbytes;
+ cpbytes <<= 1;
+ if (cpbytes > y)
+ cpbytes = y;
+ }
+}
+
+static void gradient_mirrorhorizontal(RrSurface *sf, gint w, gint h)
+{
+ register gint x, y, half1, half2, cpbytes;
+ RrPixel32 *data = sf->pixel_data, *datav;
+ gchar *datac;
+
+ VARS(x);
+
+ half1 = (w + 1) / 2;
+ half2 = w / 2;
+
+ /* set the color values for the first row */
+
+ SETUP(x, sf->primary, sf->secondary, half1);
+ datav = data;
+ for (x = half1 - 1; x > 0; --x) { /* 0 -> half1 - 1 */
+ *datav = COLOR(x);
+ ++datav;
+ NEXT(x);
+ }
+ *datav = COLOR(x);
+ ++datav;
+
+ if (half2 > 0) {
+ SETUP(x, sf->secondary, sf->primary, half2);
+ for (x = half2 - 1; x > 0; --x) { /* 0 -> half2 - 1 */
+ *datav = COLOR(x);
+ ++datav;
+ NEXT(x);
+ }
+ *datav = COLOR(x);
+ ++datav;
+ }
+
+ /* copy the first row to the rest in O(logn) copies */
+ datac = (gchar*)datav;
+ cpbytes = 1 * w * sizeof(RrPixel32);
+ for (y = (h - 1) * w * sizeof(RrPixel32); y > 0;) {
+ memcpy(datac, data, cpbytes);
+ y -= cpbytes;
+ datac += cpbytes;
+ cpbytes <<= 1;
+ if (cpbytes > y)
+ cpbytes = y;
+ }
+}
+
+static void gradient_vertical(RrSurface *sf, gint w, gint h)
+{
+ register gint y;
+ RrPixel32 *data;
+
+ VARS(y);
+ SETUP(y, sf->primary, sf->secondary, h);
+
+ /* find the color for the first pixel of each row first */
+ data = sf->pixel_data;
+
+ for (y = h - 1; y > 0; --y) { /* 0 -> h-1 */
+ *data = COLOR(y);
+ data += w;
+ NEXT(y);
+ }
+ *data = COLOR(y);
+
+ /* copy the first pixels into the whole rows */
+ data = sf->pixel_data;
+ for (y = h; y > 0; --y) {
+ repeat_pixel(data, w);
+ data += w;
+ }
+}
+
+static void gradient_diagonal(RrSurface *sf, gint w, gint h)
+{
+ register gint x, y;
+ RrPixel32 *data = sf->pixel_data;
+ RrColor left, right;
+ RrColor extracorner;
+
+ VARS(lefty);
+ VARS(righty);
+ VARS(x);
+
+ extracorner.r = (sf->primary->r + sf->secondary->r) / 2;
+ extracorner.g = (sf->primary->g + sf->secondary->g) / 2;
+ extracorner.b = (sf->primary->b + sf->secondary->b) / 2;
+
+ SETUP(lefty, sf->primary, (&extracorner), h);
+ SETUP(righty, (&extracorner), sf->secondary, h);
+
+ for (y = h - 1; y > 0; --y) { /* 0 -> h-1 */
+ COLOR_RR(lefty, (&left));
+ COLOR_RR(righty, (&right));
+
+ SETUP(x, (&left), (&right), w);
+
+ for (x = w - 1; x > 0; --x) { /* 0 -> w-1 */
+ *(data++) = COLOR(x);
+
+ NEXT(x);
+ }
+ *(data++) = COLOR(x);
+
+ NEXT(lefty);
+ NEXT(righty);
+ }
+ COLOR_RR(lefty, (&left));
+ COLOR_RR(righty, (&right));
+
+ SETUP(x, (&left), (&right), w);
+
+ for (x = w - 1; x > 0; --x) { /* 0 -> w-1 */
+ *(data++) = COLOR(x);
+
+ NEXT(x);
+ }
+ *data = COLOR(x);
+}
+
+static void gradient_crossdiagonal(RrSurface *sf, gint w, gint h)
+{
+ register gint x, y;
+ RrPixel32 *data = sf->pixel_data;
+ RrColor left, right;
+ RrColor extracorner;
+
+ VARS(lefty);
+ VARS(righty);
+ VARS(x);
+
+ extracorner.r = (sf->primary->r + sf->secondary->r) / 2;
+ extracorner.g = (sf->primary->g + sf->secondary->g) / 2;
+ extracorner.b = (sf->primary->b + sf->secondary->b) / 2;
+
+ SETUP(lefty, (&extracorner), sf->secondary, h);
+ SETUP(righty, sf->primary, (&extracorner), h);
+
+ for (y = h - 1; y > 0; --y) { /* 0 -> h-1 */
+ COLOR_RR(lefty, (&left));
+ COLOR_RR(righty, (&right));
+
+ SETUP(x, (&left), (&right), w);
+
+ for (x = w - 1; x > 0; --x) { /* 0 -> w-1 */
+ *(data++) = COLOR(x);
+
+ NEXT(x);
+ }
+ *(data++) = COLOR(x);
+
+ NEXT(lefty);
+ NEXT(righty);
+ }
+ COLOR_RR(lefty, (&left));
+ COLOR_RR(righty, (&right));
+
+ SETUP(x, (&left), (&right), w);
+
+ for (x = w - 1; x > 0; --x) { /* 0 -> w-1 */
+ *(data++) = COLOR(x);
+
+ NEXT(x);
+ }
+ *data = COLOR(x);
+}
+
+static void gradient_pyramid(RrSurface *sf, gint w, gint h)
+{
+ RrPixel32 *ldata, *rdata;
+ RrPixel32 *cp;
+ RrColor left, right;
+ RrColor extracorner;
+ register gint x, y, halfw, halfh, midx, midy;
+
+ VARS(lefty);
+ VARS(righty);
+ VARS(x);
+
+ extracorner.r = (sf->primary->r + sf->secondary->r) / 2;
+ extracorner.g = (sf->primary->g + sf->secondary->g) / 2;
+ extracorner.b = (sf->primary->b + sf->secondary->b) / 2;
+
+ halfw = w >> 1;
+ halfh = h >> 1;
+ midx = w - halfw - halfw; /* 0 or 1, depending if w is even or odd */
+ midy = h - halfh - halfh; /* 0 or 1, depending if h is even or odd */
+
+ SETUP(lefty, sf->primary, (&extracorner), halfh + midy);
+ SETUP(righty, (&extracorner), sf->secondary, halfh + midy);
+
+ /* draw the top half
+
+ it is faster to draw both top quarters together than to draw one and
+ then copy it over to the other side.
+ */
+
+ ldata = sf->pixel_data;
+ rdata = ldata + w - 1;
+ for (y = halfh + midy; y > 0; --y) { /* 0 -> (h+1)/2 */
+ RrPixel32 c;
+
+ COLOR_RR(lefty, (&left));
+ COLOR_RR(righty, (&right));
+
+ SETUP(x, (&left), (&right), halfw + midx);
+
+ for (x = halfw + midx - 1; x > 0; --x) { /* 0 -> (w+1)/2 */
+ c = COLOR(x);
+ *(ldata++) = *(rdata--) = c;
+
+ NEXT(x);
+ }
+ c = COLOR(x);
+ *ldata = *rdata = c;
+ ldata += halfw + 1;
+ rdata += halfw - 1 + midx + w;
+
+ NEXT(lefty);
+ NEXT(righty);
+ }
+
+ /* copy the top half into the bottom half, mirroring it, so we can only
+ copy one row at a time
+
+ it is faster, to move the writing pointer forward, and the reading
+ pointer backward
+
+ this is the current code, moving the write pointer forward and read
+ pointer backward
+ 41.78 4.26 1.78 504 3.53 3.53 gradient_pyramid2
+ this is the opposite, moving the read pointer forward and the write
+ pointer backward
+ 42.27 4.40 1.86 504 3.69 3.69 gradient_pyramid2
+
+ */
+ ldata = sf->pixel_data + (halfh - 1) * w;
+ cp = ldata + (midy + 1) * w;
+ for (y = halfh; y > 0; --y) {
+ memcpy(cp, ldata, w * sizeof(RrPixel32));
+ ldata -= w;
+ cp += w;
+ }
+}
diff --git a/obrender/gradient.h b/obrender/gradient.h
new file mode 100644
index 00000000..8613f0ce
--- /dev/null
+++ b/obrender/gradient.h
@@ -0,0 +1,27 @@
+/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
+
+ gradient.h for the Openbox window manager
+ Copyright (c) 2003-2007 Dana Jansens
+ Copyright (c) 2003 Derek Foreman
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ See the COPYING file for a copy of the GNU General Public License.
+*/
+
+#ifndef __gradient_h
+#define __gradient_h
+
+#include "render.h"
+
+void RrRender(RrAppearance *a, gint w, gint h);
+
+#endif /* __gradient_h */
diff --git a/obrender/icon.h b/obrender/icon.h
new file mode 100644
index 00000000..f0b2d267
--- /dev/null
+++ b/obrender/icon.h
@@ -0,0 +1,422 @@
+/* GIMP RGBA C-Source image dump (icon.h) */
+
+/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
+
+ icon.h for the Openbox window manager
+ Copyright (c) 2003-2007 Dana Jansens
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ See the COPYING file for a copy of the GNU General Public License.
+*/
+
+#define OB_DEFAULT_ICON_WIDTH (48)
+#define OB_DEFAULT_ICON_HEIGHT (48)
+#define OB_DEFAULT_ICON_BYTES_PER_PIXEL (4) /* 3:RGB, 4:RGBA */
+#define OB_DEFAULT_ICON_COMMENT \
+ "To recreate this file, save an image as \"C-Source\" in The Gimp. Use \"ob_default_icon\" as the Prefixed Name. Enable Glib Types. Enable Save Alpha Channel. Enable Use Macros instead of Struct."
+#define OB_DEFAULT_ICON_PIXEL_DATA ((guint8*) OB_DEFAULT_ICON_pixel_data)
+static const guint8 OB_DEFAULT_ICON_pixel_data[48 * 48 * 4 + 1] =
+("\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
+ "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
+ "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
+ "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
+ "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
+ "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
+ "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
+ "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
+ "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
+ "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
+ "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\40J\207\15\40J\207\23\40J\207"
+ "\23\40J\207\23\40J\207\23\40J\207\23\40J\207\23\40J\207\23\40J\207\23\40J"
+ "\207\23\40J\207\23\40J\207\23\40J\207\23\40J\207\23\40J\207\23\40J\207\23"
+ "\40J\207\23\40J\207\23\40J\207\23\40J\207\23\40J\207\23\40J\207\23\40J\207"
+ "\23\40J\207\23\40J\207\23\40J\207\23\40J\207\23\40J\207\23\40J\207\23\40J"
+ "\207\23\40J\207\23\40J\207\23\40J\207\23\40J\207\23\40J\207\23\40J\207\23"
+ "\40J\207\23\40J\207\23\40J\207\23\40J\207\23\40J\207\23\40J\207\23\40J\207"
+ "\23\40J\207\15\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'P\213\267'Q\214\275'Q\214\275"
+ "'Q\214\275'Q\214\275'Q\214\275'Q\214\275'Q\214\275'Q\214\275'Q\214\275'Q\214"
+ "\275'Q\214\275'Q\214\275'Q\214\275'Q\214\275'Q\214\275'Q\214\275'Q\214\275"
+ "'Q\214\275'Q\214\275'Q\214\275'Q\214\275'Q\214\275(R\215\275'Q\214\275'Q\214"
+ "\275'Q\214\275'Q\214\275'Q\214\275'Q\214\275'Q\214\275'Q\214\275'Q\214\275"
+ "'Q\214\275'Q\214\275'Q\214\275'Q\214\275'Q\214\275'Q\214\275'Q\214\275(R\215"
+ "\275(R\215\275(R\215\275&P\213\267\40J\207\20\0\0\0\0\0\0\0\0\40J\207+Y{\252"
+ "\377\216\253\320\377\204\243\314\377\204\243\314\377\204\243\314\377\204\243"
+ "\314\377\204\243\314\377\204\243\314\377\204\243\314\377\204\243\314\377\204"
+ "\243\314\377\204\243\314\377\204\243\314\377\204\243\314\377\204\243\314\377"
+ "\204\243\314\377\204\243\314\377\204\243\314\377\204\243\314\377\204\243\314"
+ "\377\204\243\314\377\204\243\314\377\204\243\314\377\204\243\314\377\204\243"
+ "\314\377\204\243\314\377\203\243\314\377\203\243\313\377\203\243\313\377\203"
+ "\243\313\377\203\242\313\377\202\242\313\377\202\242\313\377\202\241\313\377"
+ "\201\241\312\377\201\241\312\377\201\240\312\377\201\240\312\377\200\240\312"
+ "\377\200\240\312\377\200\240\312\377\200\240\312\377\211\247\316\377Jn\241"
+ "\377\40J\207+\0\0\0\0\0\0\0\0\40J\207+Wy\251\377]\207\275\377>o\260\377>o"
+ "\260\377>o\260\377>o\260\377>o\260\377>o\260\377>o\260\377>o\260\377>o\260"
+ "\377>o\260\377>o\260\377>o\260\377>o\260\377>o\260\377>o\260\377>o\260\377"
+ ">o\260\377>o\260\377>o\260\377>o\260\377>o\260\377>o\260\377>o\260\377>o\260"
+ "\377>o\260\377>o\260\377>o\260\377>o\260\377=o\260\377=o\260\377=o\260\377"
+ "=o\260\377=o\260\377=o\260\377=o\260\377=o\260\377=o\260\377=o\260\377=o\260"
+ "\377=o\260\377_\210\275\377Hm\241\377\40J\207+\0\0\0\0\0\0\0\0\40J\207+Tw"
+ "\251\377]\207\276\377?r\263\377?r\263\377?r\263\377?r\263\377?r\263\377?r"
+ "\263\377?r\263\377?r\263\377?r\263\377?r\263\377?r\263\377?r\263\377?r\263"
+ "\377?r\263\377?r\263\377?r\263\377?r\263\377?r\263\377?r\263\377?r\263\377"
+ "?r\263\377?r\263\377?r\263\377?r\263\377?r\263\377?r\263\377?r\263\377?r\263"
+ "\377?r\263\377?r\263\377?r\263\377?r\263\377?r\263\377?r\263\377?r\263\377"
+ "?r\263\377?r\263\377?r\263\377?r\263\377?r\263\377]\210\277\377Fl\241\377"
+ "\40J\207+\0\0\0\0\0\0\0\0\40J\207+Rv\250\377\\\210\300\377At\265\377At\265"
+ "\377At\265\377At\265\377At\265\377At\265\377At\265\377At\265\377At\265\377"
+ "At\265\377At\265\377At\265\377At\265\377At\265\377At\265\377At\265\377At\265"
+ "\377At\265\377At\265\377At\265\377At\265\377At\265\377At\265\377At\265\377"
+ "At\265\377At\265\377At\265\377At\265\377At\265\377At\265\377At\265\377At\265"
+ "\377At\265\377At\265\377At\265\377At\265\377At\265\377At\265\377At\265\377"
+ "At\265\377\\\210\300\377Dj\240\377\40J\207+\0\0\0\0\0\0\0\0\40J\207+Ot\247"
+ "\377\\\211\302\377Dw\271\377Dw\271\377Dw\271\377Dw\271\377Dw\271\377Dw\271"
+ "\377Dw\271\377Dw\271\377Dw\271\377Dw\271\377Dw\271\377Dw\271\377Dw\271\377"
+ "Dw\271\377Dw\271\377Dw\271\377Dw\271\377Dw\271\377Dw\271\377Dw\271\377Dw\271"
+ "\377Dw\271\377Dw\271\377Dw\271\377Dw\271\377Dw\271\377Dw\271\377Dw\271\377"
+ "Dw\271\377Dw\271\377Dw\271\377Dw\271\377Dw\271\377Dw\271\377Dw\271\377Dw\271"
+ "\377Dw\271\377Dw\271\377Dw\271\377Dw\271\377[\211\302\377Bi\240\377\40J\207"
+ "+\0\0\0\0\0\0\0\0\40J\207+Ls\247\377\\\211\303\377Fy\273\377Fy\273\377Fy\273"
+ "\377Fy\273\377Fy\273\377Fy\273\377Fy\273\377Fy\273\377Fy\273\377Fy\273\377"
+ "Fy\273\377Fy\273\377Fy\273\377Fy\273\377Fy\273\377Fy\273\377Fy\273\377Fy\273"
+ "\377Fy\273\377Fy\273\377Fy\273\377Fy\273\377Fy\273\377Fy\273\377Fy\273\377"
+ "Fy\273\377Fy\273\377Fy\273\377Fy\273\377Fy\273\377Fy\273\377Fy\273\377Fy\273"
+ "\377Fy\273\377Fy\273\377Fy\273\377Fy\273\377Fy\273\377Fy\273\377Fy\273\377"
+ "\\\211\303\377Ah\240\377\40J\207+\0\0\0\0\0\0\0\0\40J\207+Jq\246\377\\\212"
+ "\305\377I}\277\377I}\277\377I}\277\377I}\277\377I}\277\377I}\277\377I}\277"
+ "\377I}\277\377I}\277\377I}\277\377I}\277\377I}\277\377I}\277\377I}\277\377"
+ "I}\277\377I}\277\377I}\277\377I}\277\377I}\277\377I}\277\377I}\277\377I}\277"
+ "\377I}\277\377I}\277\377I}\277\377I}\277\377I}\277\377I}\277\377I}\277\377"
+ "I}\277\377I}\277\377I}\277\377I}\277\377I}\277\377I}\277\377I}\277\377I}\277"
+ "\377I}\277\377I}\277\377I}\277\377[\212\305\377?g\237\377\40J\207+\0\0\0\0"
+ "\0\0\0\0\40J\207+Jq\246\377\\\212\305\377I}\277\377I}\277\377I}\277\377I}"
+ "\277\377I}\277\377I}\277\377I}\277\377I}\277\377I}\277\377I}\277\377I}\277"
+ "\377I}\277\377I}\277\377I}\277\377I}\277\377I}\277\377I}\277\377I}\277\377"
+ "I}\277\377I}\277\377I}\277\377J~\300\377K\177\301\377K\177\301\377K\177\301"
+ "\377K\177\301\377K\177\301\377K\177\301\377K\177\301\377K\177\301\377K\177"
+ "\301\377K\177\301\377K\177\301\377K\177\301\377K\177\301\377K\177\301\377"
+ "K\177\301\377K\177\301\377K\177\301\377K\177\301\377Z\212\307\377=f\237\377"
+ "\40J\207+\0\0\0\0\0\0\0\0\40J\207+Ip\247\377\\\213\307\377J\177\301\377J\177"
+ "\301\377J\177\301\377J\177\301\377J\177\301\377J\177\301\377J\177\301\377"
+ "J\177\301\377J\177\301\377J\177\301\377J\177\301\377J\177\301\377J\177\301"
+ "\377J\177\301\377J\177\301\377J\177\301\377J\177\301\377J\177\301\377J\177"
+ "\301\377J\177\301\377J\177\301\377K\200\302\377K\200\302\377K\200\302\377"
+ "K\200\302\377K\200\302\377K\200\302\377K\200\302\377K\200\302\377K\200\302"
+ "\377K\200\302\377K\200\302\377K\200\302\377K\200\302\377K\200\302\377K\200"
+ "\302\377K\200\302\377K\200\302\377K\200\302\377K\200\302\377Z\212\307\377"
+ "=f\237\377\40J\207+\0\0\0\0\0\0\0\0\40J\207+Ho\247\377e\223\314\377Z\213\310"
+ "\377Z\213\310\377Z\213\310\377Z\213\310\377Z\213\307\377Z\213\307\377Z\213"
+ "\307\377Z\213\307\377Z\213\307\377Y\212\307\377Y\212\307\377Y\212\307\377"
+ "Y\212\307\377Y\212\307\377X\212\307\377X\212\307\377X\212\307\377X\211\307"
+ "\377X\212\307\377X\212\307\377X\211\307\377X\211\307\377X\211\307\377X\211"
+ "\307\377X\211\307\377X\211\307\377X\211\307\377X\211\307\377X\211\307\377"
+ "X\211\307\377W\211\307\377W\211\307\377W\211\307\377W\211\307\377W\211\307"
+ "\377W\211\307\377V\211\307\377V\211\307\377V\210\307\377V\210\307\377`\217"
+ "\312\377<e\237\377\40J\207+\0\0\0\0\0\0\0\0\40J\207+<b\231\377i\212\267\377"
+ "i\211\266\377i\211\266\377i\211\266\377i\211\266\377i\211\266\377h\211\266"
+ "\377h\211\266\377h\211\266\377h\211\266\377h\211\266\377h\211\266\377h\211"
+ "\266\377g\211\266\377g\211\266\377g\211\266\377g\211\266\377g\210\266\377"
+ "g\210\266\377g\211\266\377g\211\266\377g\210\266\377g\210\266\377g\210\265"
+ "\377g\210\265\377g\210\265\377g\210\265\377g\210\265\377g\210\265\377g\210"
+ "\265\377g\210\265\377g\210\265\377f\210\265\377f\210\264\377f\210\264\377"
+ "f\210\264\377f\210\264\377f\210\264\377f\210\264\377f\210\264\377f\207\264"
+ "\377f\207\264\3778^\226\377\40J\207+\0\0\0\0\0\0\0\0\40J\207+b\177\251\377"
+ "\340\344\351\377\337\342\350\377\337\342\350\377\336\342\350\377\336\342\347"
+ "\377\336\342\347\377\336\342\347\377\336\342\347\377\335\341\347\377\335\341"
+ "\346\377\335\341\346\377\335\341\346\377\335\341\346\377\335\341\346\377\335"
+ "\341\346\377\335\341\346\377\335\341\346\377\334\340\346\377\334\340\346\377"
+ "\335\341\346\377\335\341\346\377\334\340\346\377\334\340\346\377\334\340\345"
+ "\377\334\340\345\377\334\340\345\377\333\337\345\377\333\337\345\377\333\337"
+ "\344\377\333\337\344\377\333\337\344\377\333\336\344\377\332\336\344\377\332"
+ "\336\344\377\332\336\344\377\332\336\343\377\332\336\343\377\332\336\343\377"
+ "\332\336\343\377\332\336\343\377\332\336\343\377\333\337\345\377a}\247\377"
+ "\40J\207+\0\0\0\0\0\0\0\0\40J\207+h\204\254\377\355\355\355\377\351\351\351"
+ "\377\351\351\352\377\351\351\351\377\351\351\351\377\351\351\351\377\351\351"
+ "\350\377\351\350\350\377\351\351\351\377\350\350\350\377\350\350\350\377\350"
+ "\350\350\377\350\350\350\377\350\350\350\377\350\350\350\377\350\350\350\377"
+ "\350\347\350\377\350\347\347\377\347\347\347\377\347\347\347\377\347\347\347"
+ "\377\347\347\347\377\347\347\347\377\347\347\347\377\346\347\347\377\347\346"
+ "\347\377\347\346\347\377\346\346\347\377\346\346\346\377\346\346\346\377\346"
+ "\346\346\377\346\346\346\377\346\346\346\377\346\346\346\377\346\346\346\377"
+ "\346\345\345\377\345\345\346\377\345\346\345\377\345\346\345\377\345\346\345"
+ "\377\345\345\345\377\351\351\351\377g\202\252\377\40J\207+\0\0\0\0\0\0\0\0"
+ "\40J\207+h\203\253\377\354\354\354\377\350\350\350\377\347\350\350\377\347"
+ "\347\350\377\347\347\350\377\347\347\347\377\347\347\347\377\347\347\347\377"
+ "\347\347\347\377\347\346\347\377\347\346\346\377\347\346\346\377\347\347\347"
+ "\377\346\346\346\377\346\346\346\377\346\346\346\377\346\346\346\377\346\346"
+ "\346\377\346\346\346\377\345\345\346\377\345\345\345\377\345\345\346\377\345"
+ "\346\346\377\345\345\345\377\345\345\345\377\345\345\345\377\345\345\345\377"
+ "\345\345\345\377\345\344\344\377\345\345\344\377\345\345\345\377\345\344\344"
+ "\377\344\344\344\377\344\344\344\377\344\344\344\377\344\344\344\377\343\344"
+ "\344\377\343\344\344\377\344\344\344\377\344\344\343\377\344\343\343\377\350"
+ "\350\350\377g\202\252\377\40J\207+\0\0\0\0\0\0\0\0\40J\207+h\203\253\377\353"
+ "\352\352\377\346\346\346\377\346\345\345\377\345\346\346\377\345\346\345\377"
+ "\345\345\345\377\345\345\345\377\345\345\345\377\345\345\346\377\345\345\345"
+ "\377\345\345\345\377\345\345\345\377\345\345\345\377\345\345\345\377\345\345"
+ "\345\377\344\345\345\377\344\344\344\377\344\345\344\377\344\344\344\377\344"
+ "\344\344\377\344\344\344\377\344\344\344\377\344\344\344\377\343\343\344\377"
+ "\343\343\343\377\344\343\343\377\343\343\343\377\343\343\343\377\343\343\343"
+ "\377\343\343\343\377\343\343\343\377\342\343\343\377\342\343\343\377\343\342"
+ "\343\377\343\342\342\377\342\342\342\377\342\342\342\377\342\342\342\377\342"
+ "\342\342\377\342\342\342\377\342\342\342\377\346\346\346\377g\202\252\377"
+ "\40J\207+\0\0\0\0\0\0\0\0\40J\207+h\203\253\377\351\351\351\377\345\344\344"
+ "\377\344\344\344\377\344\344\344\377\344\344\344\377\344\344\344\377\343\344"
+ "\344\377\343\344\344\377\343\343\344\377\343\343\343\377\343\343\343\377\343"
+ "\343\343\377\343\343\343\377\343\343\343\377\343\343\343\377\343\343\343\377"
+ "\343\343\343\377\343\343\343\377\343\342\342\377\342\342\342\377\342\342\342"
+ "\377\342\342\342\377\342\342\342\377\342\341\342\377\342\342\342\377\342\342"
+ "\341\377\342\341\342\377\341\341\342\377\341\341\341\377\341\341\341\377\341"
+ "\341\341\377\341\341\341\377\341\341\341\377\341\340\341\377\341\341\341\377"
+ "\340\341\341\377\341\341\341\377\340\340\340\377\340\340\340\377\340\340\340"
+ "\377\340\340\340\377\346\345\346\377g\202\252\377\40J\207+\0\0\0\0\0\0\0\0"
+ "\40J\207+h\203\253\377\350\350\350\377\342\342\343\377\342\342\342\377\342"
+ "\342\342\377\342\342\342\377\342\342\342\377\342\342\342\377\342\342\342\377"
+ "\342\342\342\377\342\342\341\377\342\341\342\377\341\341\341\377\341\341\341"
+ "\377\341\341\341\377\341\341\341\377\341\341\341\377\341\341\341\377\341\341"
+ "\341\377\340\341\341\377\341\340\340\377\341\340\340\377\340\340\340\377\340"
+ "\340\340\377\340\340\340\377\340\340\340\377\340\340\340\377\340\340\340\377"
+ "\340\340\337\377\340\340\340\377\337\337\337\377\340\340\337\377\337\337\337"
+ "\377\337\337\337\377\337\337\337\377\337\337\337\377\337\337\337\377\337\337"
+ "\337\377\337\337\337\377\337\337\336\377\336\337\336\377\336\336\336\377\344"
+ "\344\344\377g\202\252\377\40J\207+\0\0\0\0\0\0\0\0\40J\207+h\203\253\377\347"
+ "\347\347\377\341\341\341\377\341\341\341\377\341\341\341\377\341\341\341\377"
+ "\341\341\341\377\340\341\340\377\340\340\340\377\340\340\340\377\340\340\337"
+ "\377\340\340\340\377\340\340\340\377\340\340\337\377\337\340\337\377\337\340"
+ "\337\377\337\337\340\377\337\337\337\377\337\337\337\377\337\337\337\377\337"
+ "\337\337\377\337\337\337\377\337\337\337\377\336\337\337\377\336\337\336\377"
+ "\337\336\336\377\336\336\336\377\336\336\336\377\336\336\336\377\336\336\336"
+ "\377\336\336\336\377\336\336\336\377\336\336\335\377\336\335\335\377\336\335"
+ "\335\377\336\335\335\377\335\335\335\377\335\335\335\377\335\335\335\377\335"
+ "\335\335\377\335\335\335\377\335\335\335\377\343\343\343\377g\202\252\377"
+ "\40J\207+\0\0\0\0\0\0\0\0\40J\207+h\203\253\377\345\346\346\377\337\337\340"
+ "\377\340\337\337\377\337\337\337\377\337\337\337\377\337\337\337\377\337\337"
+ "\337\377\337\337\336\377\337\336\336\377\336\336\337\377\336\336\337\377\336"
+ "\336\336\377\336\336\336\377\336\336\336\377\336\336\335\377\336\336\336\377"
+ "\336\335\336\377\336\336\336\377\335\335\336\377\335\335\335\377\335\335\335"
+ "\377\335\335\335\377\335\335\335\377\335\335\335\377\335\335\335\377\335\335"
+ "\334\377\334\335\335\377\334\334\334\377\334\334\334\377\334\334\334\377\334"
+ "\334\334\377\334\334\334\377\334\334\334\377\334\334\334\377\334\334\334\377"
+ "\334\333\334\377\333\333\333\377\333\333\333\377\333\333\333\377\333\333\333"
+ "\377\333\333\333\377\341\341\342\377f\202\252\377\40J\207+\0\0\0\0\0\0\0\0"
+ "\40J\207+h\203\253\377\345\345\345\377\336\335\336\377\336\335\335\377\335"
+ "\335\335\377\336\335\335\377\335\335\335\377\335\335\335\377\335\335\335\377"
+ "\335\334\335\377\335\335\335\377\335\335\335\377\334\335\334\377\334\334\335"
+ "\377\334\335\334\377\334\334\334\377\334\334\334\377\334\334\334\377\334\334"
+ "\334\377\334\334\334\377\334\334\334\377\334\333\334\377\334\333\333\377\333"
+ "\333\333\377\333\333\333\377\333\333\333\377\333\333\333\377\333\333\333\377"
+ "\333\332\333\377\333\333\333\377\333\333\333\377\332\333\333\377\332\333\332"
+ "\377\332\332\332\377\332\332\332\377\332\332\332\377\332\332\332\377\332\332"
+ "\332\377\332\332\332\377\331\332\331\377\331\332\331\377\331\331\332\377\341"
+ "\341\341\377f\202\252\377\40J\207+\0\0\0\0\0\0\0\0\40J\207+g\203\253\377\343"
+ "\343\344\377\334\334\334\377\334\333\334\377\334\334\334\377\333\333\334\377"
+ "\333\333\333\377\333\334\333\377\333\333\333\377\333\333\333\377\333\333\333"
+ "\377\333\333\333\377\333\333\333\377\333\332\333\377\333\332\333\377\333\332"
+ "\332\377\332\332\333\377\332\332\333\377\332\332\332\377\333\332\332\377\332"
+ "\332\332\377\332\332\332\377\331\332\332\377\331\332\331\377\331\332\331\377"
+ "\331\332\331\377\331\331\331\377\331\331\331\377\332\331\331\377\331\331\331"
+ "\377\331\331\331\377\331\331\331\377\330\330\331\377\330\330\330\377\331\330"
+ "\330\377\330\330\330\377\330\330\330\377\331\330\330\377\330\330\330\377\330"
+ "\330\330\377\330\330\330\377\330\330\330\377\337\337\337\377f\202\252\377"
+ "\40J\207+\0\0\0\0\0\0\0\0\40J\207+g\203\253\377\342\342\343\377\332\332\332"
+ "\377\332\332\332\377\332\332\332\377\332\332\332\377\331\332\332\377\331\332"
+ "\332\377\332\332\331\377\332\331\331\377\331\331\331\377\331\331\331\377\331"
+ "\331\331\377\331\331\331\377\331\331\331\377\331\331\331\377\331\331\331\377"
+ "\331\331\331\377\330\330\330\377\331\330\331\377\330\331\330\377\330\330\330"
+ "\377\330\330\330\377\330\330\330\377\330\330\327\377\327\330\330\377\330\330"
+ "\327\377\327\330\330\377\330\330\327\377\327\327\327\377\327\327\327\377\327"
+ "\327\327\377\327\327\327\377\327\327\327\377\327\327\327\377\327\327\327\377"
+ "\326\327\327\377\327\326\326\377\326\327\326\377\326\326\326\377\326\326\326"
+ "\377\326\326\326\377\336\336\337\377f\202\252\377\40J\207+\0\0\0\0\0\0\0\0"
+ "\40J\207+g\203\253\377\341\341\341\377\331\331\331\377\330\331\331\377\330"
+ "\330\330\377\330\330\330\377\330\330\330\377\330\330\330\377\330\330\330\377"
+ "\327\330\330\377\330\330\327\377\330\330\330\377\330\327\330\377\327\327\327"
+ "\377\327\330\327\377\327\330\327\377\327\327\327\377\327\327\327\377\327\327"
+ "\327\377\327\327\327\377\326\327\326\377\326\327\326\377\326\326\326\377\326"
+ "\326\326\377\326\326\326\377\326\326\326\377\326\326\326\377\326\326\326\377"
+ "\326\326\326\377\325\325\326\377\325\326\326\377\326\325\325\377\325\325\325"
+ "\377\325\326\325\377\325\325\325\377\325\325\325\377\325\325\325\377\325\325"
+ "\325\377\325\325\325\377\325\324\325\377\324\324\324\377\324\324\324\377\335"
+ "\335\335\377f\202\252\377\40J\207+\0\0\0\0\0\0\0\0\40J\207+g\203\253\377\340"
+ "\340\340\377\327\327\327\377\327\327\327\377\326\327\327\377\327\326\327\377"
+ "\326\326\326\377\326\326\327\377\326\326\326\377\326\326\326\377\326\326\326"
+ "\377\326\326\326\377\326\326\326\377\326\326\325\377\325\326\325\377\325\326"
+ "\326\377\325\325\325\377\325\325\325\377\325\325\325\377\325\325\325\377\325"
+ "\325\325\377\325\325\325\377\324\325\325\377\325\325\325\377\324\325\325\377"
+ "\324\324\324\377\325\324\324\377\324\324\324\377\324\324\324\377\324\324\324"
+ "\377\323\324\324\377\324\323\324\377\323\324\324\377\323\324\324\377\323\324"
+ "\323\377\323\323\324\377\323\323\323\377\323\323\323\377\323\323\323\377\323"
+ "\323\323\377\323\323\323\377\323\323\323\377\334\334\334\377f\202\252\377"
+ "\40J\207+\0\0\0\0\0\0\0\0\40J\207+g\203\253\377\337\336\337\377\325\325\325"
+ "\377\325\325\325\377\325\325\325\377\325\325\325\377\325\325\325\377\325\325"
+ "\325\377\325\325\324\377\325\325\325\377\324\325\324\377\324\324\324\377\324"
+ "\324\324\377\324\324\324\377\324\324\324\377\324\324\324\377\324\324\324\377"
+ "\324\324\324\377\324\323\323\377\323\323\323\377\324\323\323\377\323\323\323"
+ "\377\323\323\323\377\324\323\323\377\323\323\323\377\323\323\323\377\323\322"
+ "\323\377\323\322\322\377\322\322\322\377\322\322\322\377\322\322\322\377\322"
+ "\322\322\377\322\322\322\377\322\322\322\377\322\322\322\377\321\322\321\377"
+ "\321\321\322\377\321\321\321\377\321\321\321\377\321\321\321\377\321\321\321"
+ "\377\321\321\321\377\334\333\334\377g\202\252\377\40J\207+\0\0\0\0\0\0\0\0"
+ "\40J\207+h\203\253\377\336\336\336\377\323\324\323\377\324\323\324\377\324"
+ "\323\323\377\323\324\323\377\323\323\323\377\323\323\323\377\323\323\323\377"
+ "\323\323\323\377\323\323\323\377\323\323\323\377\322\323\323\377\323\322\323"
+ "\377\322\322\323\377\322\322\322\377\322\322\322\377\322\322\322\377\322\322"
+ "\322\377\322\322\322\377\322\322\322\377\322\322\322\377\321\321\321\377\322"
+ "\321\321\377\321\321\321\377\321\321\321\377\321\321\321\377\321\321\321\377"
+ "\321\321\320\377\321\321\321\377\321\321\321\377\321\321\321\377\320\320\320"
+ "\377\320\320\320\377\320\320\320\377\320\320\320\377\320\317\320\377\317\320"
+ "\320\377\320\320\320\377\320\320\320\377\320\320\320\377\320\317\317\377\332"
+ "\332\332\377g\202\252\377\40J\207+\0\0\0\0\0\0\0\0\40J\207+h\203\253\377\335"
+ "\335\335\377\322\322\322\377\322\322\322\377\322\322\322\377\321\322\322\377"
+ "\321\321\321\377\321\321\321\377\322\321\321\377\321\321\321\377\321\321\321"
+ "\377\321\321\321\377\321\321\321\377\321\320\321\377\320\321\320\377\320\320"
+ "\320\377\320\320\320\377\320\320\320\377\320\320\320\377\320\320\320\377\320"
+ "\320\320\377\320\320\320\377\320\320\320\377\320\320\320\377\317\320\320\377"
+ "\317\320\317\377\317\317\317\377\317\317\317\377\317\317\317\377\317\317\317"
+ "\377\317\317\317\377\316\317\317\377\317\317\317\377\317\316\316\377\316\316"
+ "\316\377\317\316\316\377\316\316\316\377\316\316\316\377\316\316\316\377\316"
+ "\316\316\377\316\316\316\377\316\316\316\377\331\331\331\377f\202\252\377"
+ "\40J\207+\0\0\0\0\0\0\0\0\40J\207+h\203\253\377\334\334\334\377\320\320\320"
+ "\377\320\320\320\377\320\320\320\377\320\320\320\377\320\320\320\377\320\320"
+ "\320\377\320\320\317\377\317\317\320\377\320\317\317\377\317\317\317\377\317"
+ "\317\317\377\317\317\317\377\317\317\317\377\317\317\317\377\317\317\317\377"
+ "\317\316\317\377\317\317\316\377\316\317\317\377\316\316\316\377\316\316\316"
+ "\377\316\316\316\377\316\316\316\377\316\316\316\377\316\316\316\377\316\315"
+ "\315\377\316\316\315\377\315\316\315\377\315\316\316\377\315\315\315\377\315"
+ "\315\315\377\315\315\315\377\315\315\315\377\315\314\315\377\315\315\315\377"
+ "\315\315\314\377\314\314\314\377\314\314\314\377\314\314\314\377\314\314\314"
+ "\377\314\314\314\377\331\330\331\377f\202\252\377\40J\207+\0\0\0\0\0\0\0\0"
+ "\40J\207+h\203\253\377\333\333\333\377\317\317\316\377\316\317\316\377\317"
+ "\316\316\377\317\316\316\377\316\317\316\377\316\316\316\377\316\316\316\377"
+ "\316\316\316\377\316\316\316\377\316\315\316\377\315\315\315\377\315\315\316"
+ "\377\315\315\315\377\315\315\315\377\315\315\315\377\315\315\315\377\315\315"
+ "\315\377\315\315\315\377\315\315\314\377\315\314\314\377\314\314\314\377\314"
+ "\315\314\377\314\314\314\377\314\314\314\377\314\314\314\377\314\314\314\377"
+ "\314\314\314\377\314\314\313\377\313\313\313\377\314\314\313\377\313\313\313"
+ "\377\313\313\313\377\313\313\313\377\313\313\313\377\313\313\313\377\313\313"
+ "\313\377\312\313\313\377\312\312\313\377\312\312\313\377\312\312\313\377\327"
+ "\327\327\377g\203\253\377\40J\207+\0\0\0\0\0\0\0\0\40J\207+h\203\253\377\332"
+ "\332\332\377\315\315\315\377\314\315\315\377\315\315\315\377\315\315\315\377"
+ "\314\315\315\377\314\314\315\377\314\314\314\377\314\314\314\377\314\314\314"
+ "\377\314\314\314\377\314\314\314\377\314\314\314\377\313\314\314\377\313\314"
+ "\313\377\313\313\313\377\313\313\313\377\313\313\313\377\313\313\313\377\313"
+ "\313\313\377\313\313\313\377\313\312\313\377\313\313\313\377\312\313\313\377"
+ "\313\313\313\377\312\312\312\377\312\312\312\377\312\312\312\377\312\312\312"
+ "\377\312\312\312\377\312\312\312\377\312\312\311\377\312\311\311\377\312\312"
+ "\311\377\311\311\311\377\311\311\311\377\311\311\311\377\311\311\311\377\311"
+ "\311\311\377\311\311\311\377\311\311\311\377\327\326\327\377g\203\253\377"
+ "\40J\207+\0\0\0\0\0\0\0\0\40J\207+g\203\253\377\331\331\331\377\313\313\313"
+ "\377\313\313\313\377\313\313\313\377\313\313\313\377\313\313\313\377\313\313"
+ "\313\377\312\312\313\377\312\313\313\377\312\312\312\377\312\312\312\377\312"
+ "\312\312\377\312\312\312\377\312\312\312\377\312\312\312\377\311\311\311\377"
+ "\312\312\312\377\311\311\311\377\311\311\311\377\311\312\311\377\311\311\311"
+ "\377\311\311\311\377\311\311\311\377\311\311\311\377\311\311\311\377\311\310"
+ "\311\377\310\310\310\377\310\310\310\377\310\310\310\377\310\310\310\377\310"
+ "\310\310\377\310\310\310\377\310\310\310\377\310\310\310\377\310\310\310\377"
+ "\307\307\310\377\310\307\310\377\307\307\307\377\307\307\307\377\307\307\307"
+ "\377\307\307\307\377\325\325\325\377g\203\253\377\40J\207+\0\0\0\0\0\0\0\0"
+ "\40J\207+h\204\254\377\330\330\331\377\312\312\311\377\312\312\311\377\312"
+ "\311\311\377\311\311\311\377\311\311\311\377\311\311\311\377\310\311\311\377"
+ "\311\311\311\377\311\311\311\377\311\310\311\377\311\310\310\377\311\311\311"
+ "\377\310\310\311\377\310\310\310\377\310\310\310\377\310\310\310\377\310\310"
+ "\310\377\310\310\310\377\307\310\307\377\310\310\310\377\307\307\307\377\307"
+ "\307\310\377\307\307\307\377\307\307\307\377\307\307\307\377\307\307\307\377"
+ "\307\307\307\377\307\307\307\377\306\306\307\377\306\307\307\377\306\306\306"
+ "\377\306\306\306\377\306\306\306\377\306\306\306\377\306\306\306\377\306\306"
+ "\306\377\305\306\306\377\305\306\305\377\305\306\306\377\305\306\306\377\325"
+ "\325\325\377g\203\253\377\40J\207+\0\0\0\0\0\0\0\0\40J\207+h\204\254\377\327"
+ "\327\327\377\310\310\310\377\310\310\310\377\310\310\310\377\310\307\307\377"
+ "\307\307\310\377\307\307\307\377\307\307\307\377\307\307\307\377\307\307\307"
+ "\377\307\307\307\377\307\307\307\377\307\307\307\377\307\306\306\377\307\306"
+ "\306\377\306\307\306\377\307\306\306\377\306\306\306\377\306\306\306\377\306"
+ "\306\306\377\306\306\306\377\306\306\306\377\306\306\306\377\305\305\305\377"
+ "\305\305\306\377\306\305\305\377\305\305\305\377\305\305\306\377\305\305\305"
+ "\377\305\305\305\377\305\305\305\377\305\304\305\377\304\304\304\377\304\304"
+ "\305\377\304\304\305\377\304\304\304\377\304\304\304\377\304\304\304\377\304"
+ "\304\304\377\304\304\304\377\304\304\304\377\324\324\324\377g\203\253\377"
+ "\40J\207+\0\0\0\0\0\0\0\0\40J\207+h\204\254\377\326\326\326\377\307\306\306"
+ "\377\306\306\306\377\306\306\306\377\306\306\306\377\306\306\306\377\306\305"
+ "\306\377\306\306\305\377\305\305\306\377\305\306\306\377\305\305\305\377\305"
+ "\305\305\377\305\305\305\377\305\305\305\377\305\305\305\377\305\305\305\377"
+ "\305\304\305\377\305\305\305\377\304\304\305\377\304\304\304\377\304\304\304"
+ "\377\304\304\304\377\304\304\304\377\304\304\304\377\304\304\304\377\304\304"
+ "\303\377\304\303\304\377\303\303\304\377\303\303\304\377\303\303\303\377\303"
+ "\303\303\377\303\302\303\377\303\303\303\377\303\303\303\377\303\302\303\377"
+ "\303\302\303\377\303\302\303\377\302\302\302\377\302\302\302\377\302\302\302"
+ "\377\302\302\302\377\323\323\323\377h\204\254\377\40J\207+\0\0\0\0\0\0\0\0"
+ "\40J\206+h\204\254\377\325\325\325\377\304\305\305\377\305\305\305\377\305"
+ "\304\304\377\304\304\305\377\304\305\305\377\304\304\304\377\305\304\305\377"
+ "\304\305\304\377\304\304\304\377\304\304\304\377\304\304\303\377\304\304\304"
+ "\377\304\304\303\377\303\303\304\377\303\303\303\377\304\303\303\377\303\303"
+ "\303\377\303\303\303\377\303\303\303\377\303\302\303\377\303\302\303\377\302"
+ "\303\303\377\302\302\303\377\302\303\303\377\302\303\302\377\302\302\302\377"
+ "\302\302\302\377\301\301\301\377\301\301\301\377\301\301\301\377\301\301\301"
+ "\377\301\301\301\377\301\301\301\377\301\301\301\377\301\301\301\377\301\301"
+ "\301\377\301\301\301\377\301\301\301\377\301\301\301\377\301\301\301\377\322"
+ "\322\322\377g\203\253\377\37H\204,\0\0\0\0\0\0\0\1\35Cy0f\202\252\377\350"
+ "\350\350\377\344\344\344\377\344\344\344\377\344\344\344\377\344\344\344\377"
+ "\344\344\344\377\344\344\344\377\344\344\344\377\344\344\344\377\344\344\344"
+ "\377\344\344\344\377\344\344\344\377\344\344\344\377\344\344\344\377\344\344"
+ "\344\377\344\344\344\377\344\344\344\377\343\343\343\377\343\343\343\377\344"
+ "\344\344\377\344\343\344\377\343\343\343\377\343\343\343\377\343\343\343\377"
+ "\343\343\343\377\343\343\343\377\343\343\343\377\343\343\343\377\342\342\342"
+ "\377\342\342\342\377\342\342\342\377\342\342\342\377\342\342\342\377\342\342"
+ "\342\377\342\342\342\377\342\342\342\377\342\342\342\377\342\342\342\377\342"
+ "\342\342\377\342\342\342\377\342\342\342\377\347\347\347\377f\202\252\377"
+ "\33=p3\0\0\0\5\0\0\0\14\27""5`<+T\216\377<d\233\377<d\233\377<d\233\377<d"
+ "\233\377<d\233\377<d\233\377<c\233\377:b\232\377-U\217\377<c\233\377;c\233"
+ "\377:c\233\377:c\233\377:c\233\377:c\233\377:c\233\377:c\232\377:c\232\377"
+ ":b\232\377:c\232\377:c\232\377:b\232\377:b\232\377:b\232\3779b\232\3779b\232"
+ "\3779b\232\3779b\232\3779b\232\3779b\232\3779b\232\3779b\232\3778b\232\377"
+ "+U\217\3778a\231\3778a\232\3778a\232\3778a\232\3778a\232\3778a\232\3778a\232"
+ "\3777`\231\377)R\216\377\25""1YA\0\0\0\23\0\0\0\25\24.UD4_\234\377R\202\277"
+ "\377R\202\277\377R\202\277\377R\202\277\377R\202\277\377R\202\277\377R\202"
+ "\277\377N\177\273\3771^\232\377R\202\277\377Q\202\277\377Q\202\277\377Q\202"
+ "\277\377Q\202\277\377Q\202\277\377Q\202\277\377Q\201\276\377Q\201\276\377"
+ "P\201\276\377Q\201\276\377Q\201\276\377P\201\276\377P\201\276\377P\201\276"
+ "\377P\201\276\377P\201\276\377P\201\276\377P\201\276\377P\201\276\377P\201"
+ "\276\377O\200\276\377O\200\276\377O\200\276\3770]\232\377N\177\274\377O\200"
+ "\276\377O\200\276\377O\200\276\377N\200\276\377N\200\276\377N\200\276\377"
+ "N\177\275\3771]\233\377\22+OI\0\0\0\34\0\0\0\33\20%C@*S\214\377<b\227\377"
+ "<a\227\377<a\227\377<a\227\377<a\227\377<a\227\377<a\227\377;_\226\377-T\216"
+ "\377;`\226\377<a\227\377<a\227\377<a\227\377<a\227\377<a\227\377<a\227\377"
+ "<a\227\377<a\227\377<a\227\377;`\226\377;`\226\377;`\226\377;`\226\377;`\226"
+ "\377;`\226\377;`\226\377;`\226\377;`\226\377;`\226\377;`\226\377;`\226\377"
+ ";`\226\377;`\226\377-T\215\377;`\226\377;`\226\377;`\226\377;`\226\377;a\226"
+ "\377;a\226\377;a\226\377<a\227\377*R\214\377\17!=G\0\0\0#\0\0\0\33\3\7\14"
+ ")\31""6bw\30""4\\}\26""0W\205\25.S\214\25.R\215\25.R\215\25.R\215\25.R\215"
+ "\25.R\215\25.R\215\25.R\215\25.R\215\25.R\215\25.R\215\25.R\215\25.R\215\25"
+ ".R\215\25.R\215\25.R\215\25.R\215\25.R\215\25.R\215\25.R\215\25.R\215\25."
+ "R\215\25.R\215\25.R\215\25.R\215\25.R\215\25.R\215\25.R\215\25.R\215\25.R"
+ "\215\25.R\215\25.R\215\25.R\215\25.R\215\25.R\215\25.R\215\25.R\215\25-Q\217"
+ "\26/T\212\27""2Y\202\30""4^{\2\5\12""1\0\0\0#\0\0\0\25\0\0\0\37\0\0\0+\0\0"
+ "\0""4\0\0\0=\0\0\0C\0\0\0D\0\0\0C\0\0\0C\0\0\0C\0\0\0C\0\0\0C\0\0\0C\0\0\0"
+ "C\0\0\0C\0\0\0C\0\0\0C\0\0\0C\0\0\0C\0\0\0C\0\0\0C\0\0\0C\0\0\0C\0\0\0C\0"
+ "\0\0C\0\0\0C\0\0\0C\0\0\0C\0\0\0C\0\0\0C\0\0\0C\0\0\0C\0\0\0C\0\0\0C\0\0\0"
+ "C\0\0\0C\0\0\0C\0\0\0C\0\0\0C\0\0\0C\0\0\0C\0\0\0D\0\0\0F\0\0\0B\0\0\0:\0"
+ "\0\0""2\0\0\0&\0\0\0\35\0\0\0\15\0\0\0\26\0\0\0\40\0\0\0'\0\0\0/\0\0\0""3"
+ "\0\0\0""3\0\0\0""2\0\0\0""2\0\0\0""2\0\0\0""2\0\0\0""2\0\0\0""2\0\0\0""2\0"
+ "\0\0""2\0\0\0""2\0\0\0""2\0\0\0""2\0\0\0""2\0\0\0""2\0\0\0""2\0\0\0""2\0\0"
+ "\0""2\0\0\0""2\0\0\0""2\0\0\0""2\0\0\0""2\0\0\0""2\0\0\0""2\0\0\0""2\0\0\0"
+ """2\0\0\0""2\0\0\0""2\0\0\0""2\0\0\0""2\0\0\0""2\0\0\0""2\0\0\0""2\0\0\0""2"
+ "\0\0\0""2\0\0\0""2\0\0\0""3\0\0\0""5\0\0\0""2\0\0\0,\0\0\0%\0\0\0\34\0\0\0"
+ "\23\0\0\0\1\0\0\0\7\0\0\0\16\0\0\0\25\0\0\0\32\0\0\0\35\0\0\0\35\0\0\0\34"
+ "\0\0\0\34\0\0\0\34\0\0\0\34\0\0\0\34\0\0\0\34\0\0\0\34\0\0\0\34\0\0\0\34\0"
+ "\0\0\34\0\0\0\34\0\0\0\34\0\0\0\34\0\0\0\34\0\0\0\34\0\0\0\34\0\0\0\34\0\0"
+ "\0\34\0\0\0\34\0\0\0\34\0\0\0\34\0\0\0\34\0\0\0\34\0\0\0\34\0\0\0\34\0\0\0"
+ "\34\0\0\0\34\0\0\0\34\0\0\0\34\0\0\0\34\0\0\0\34\0\0\0\34\0\0\0\34\0\0\0\34"
+ "\0\0\0\35\0\0\0\36\0\0\0\35\0\0\0\30\0\0\0\23\0\0\0\12\0\0\0\3\0\0\0\0\0\0"
+ "\0\0\0\0\0\2\0\0\0\5\0\0\0\10\0\0\0\13\0\0\0\14\0\0\0\13\0\0\0\13\0\0\0\13"
+ "\0\0\0\13\0\0\0\13\0\0\0\13\0\0\0\13\0\0\0\13\0\0\0\13\0\0\0\13\0\0\0\13\0"
+ "\0\0\13\0\0\0\13\0\0\0\13\0\0\0\13\0\0\0\13\0\0\0\13\0\0\0\13\0\0\0\13\0\0"
+ "\0\13\0\0\0\13\0\0\0\13\0\0\0\13\0\0\0\13\0\0\0\13\0\0\0\13\0\0\0\13\0\0\0"
+ "\13\0\0\0\13\0\0\0\13\0\0\0\13\0\0\0\13\0\0\0\13\0\0\0\13\0\0\0\14\0\0\0\14"
+ "\0\0\0\12\0\0\0\6\0\0\0\3\0\0\0\0\0\0\0\0");
+
diff --git a/obrender/image.c b/obrender/image.c
new file mode 100644
index 00000000..924504fd
--- /dev/null
+++ b/obrender/image.c
@@ -0,0 +1,516 @@
+/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
+
+ image.c for the Openbox window manager
+ Copyright (c) 2006 Mikael Magnusson
+ Copyright (c) 2003-2007 Dana Jansens
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ See the COPYING file for a copy of the GNU General Public License.
+*/
+
+#include "geom.h"
+#include "image.h"
+#include "color.h"
+#include "imagecache.h"
+
+#include <glib.h>
+
+#define FRACTION 12
+#define FLOOR(i) ((i) & (~0UL << FRACTION))
+#define AVERAGE(a, b) (((((a) ^ (b)) & 0xfefefefeL) >> 1) + ((a) & (b)))
+
+void RrImagePicInit(RrImagePic *pic, gint w, gint h, RrPixel32 *data)
+{
+ gint i;
+
+ pic->width = w;
+ pic->height = h;
+ pic->data = data;
+ pic->sum = 0;
+ for (i = w*h; i > 0; --i)
+ pic->sum += *(data++);
+}
+
+static void RrImagePicFree(RrImagePic *pic)
+{
+ if (pic) {
+ g_free(pic->data);
+ g_free(pic);
+ }
+}
+
+/*! Add a picture to an Image, that is, add another copy of the image at
+ another size. This may add it to the "originals" list or to the
+ "resized" list. */
+static void AddPicture(RrImage *self, RrImagePic ***list, gint *len,
+ RrImagePic *pic)
+{
+ gint i;
+
+ g_assert(pic->width > 0 && pic->height > 0);
+
+ g_assert(g_hash_table_lookup(self->cache->table, pic) == NULL);
+
+ /* grow the list */
+ *list = g_renew(RrImagePic*, *list, ++*len);
+
+ /* move everything else down one */
+ for (i = *len-1; i > 0; --i)
+ (*list)[i] = (*list)[i-1];
+
+ /* set the new picture up at the front of the list */
+ (*list)[0] = pic;
+
+ /* add the picture as a key to point to this image in the cache */
+ g_hash_table_insert(self->cache->table, (*list)[0], self);
+
+/*
+#ifdef DEBUG
+ g_debug("Adding %s picture to the cache:\n "
+ "Image 0x%lx, w %d h %d Hash %u",
+ (*list == self->original ? "ORIGINAL" : "RESIZED"),
+ (gulong)self, pic->width, pic->height, RrImagePicHash(pic));
+#endif
+*/
+}
+
+/*! Remove a picture from an Image. This may remove it from the "originals"
+ list or the "resized" list. */
+static void RemovePicture(RrImage *self, RrImagePic ***list,
+ gint i, gint *len)
+{
+ gint j;
+
+/*
+#ifdef DEBUG
+ g_debug("Removing %s picture from the cache:\n "
+ "Image 0x%lx, w %d h %d Hash %u",
+ (*list == self->original ? "ORIGINAL" : "RESIZED"),
+ (gulong)self, (*list)[i]->width, (*list)[i]->height,
+ RrImagePicHash((*list)[i]));
+#endif
+*/
+
+ /* remove the picture as a key in the cache */
+ g_hash_table_remove(self->cache->table, (*list)[i]);
+
+ /* free the picture */
+ RrImagePicFree((*list)[i]);
+ /* shift everything down one */
+ for (j = i; j < *len-1; ++j)
+ (*list)[j] = (*list)[j+1];
+ /* shrink the list */
+ *list = g_renew(RrImagePic*, *list, --*len);
+}
+
+/*! Given a picture in RGBA format, of a specified size, resize it to the new
+ requested size (but keep its aspect ratio). If the image does not need to
+ be resized (it is already the right size) then this returns NULL. Otherwise
+ it returns a newly allocated RrImagePic with the resized picture inside it
+*/
+static RrImagePic* ResizeImage(RrPixel32 *src,
+ gulong srcW, gulong srcH,
+ gulong dstW, gulong dstH)
+{
+ RrPixel32 *dst, *dststart;
+ RrImagePic *pic;
+ gulong dstX, dstY, srcX, srcY;
+ gulong srcX1, srcX2, srcY1, srcY2;
+ gulong ratioX, ratioY;
+ gulong aspectW, aspectH;
+
+ /* XXX should these variables be ensured to not be zero in the callers? */
+ srcW = srcW ? srcW : 1;
+ srcH = srcH ? srcH : 1;
+ dstW = dstW ? dstW : 1;
+ dstH = dstH ? dstH : 1;
+
+ /* keep the aspect ratio */
+ aspectW = dstW;
+ aspectH = (gint)(dstW * ((gdouble)srcH / srcW));
+ if (aspectH > dstH) {
+ aspectH = dstH;
+ aspectW = (gint)(dstH * ((gdouble)srcW / srcH));
+ }
+ dstW = aspectW ? aspectW : 1;
+ dstH = aspectH ? aspectH : 1;
+
+ if (srcW == dstW && srcH == dstH)
+ return NULL; /* no scaling needed! */
+
+ dststart = dst = g_new(RrPixel32, dstW * dstH);
+
+ ratioX = (srcW << FRACTION) / dstW;
+ ratioY = (srcH << FRACTION) / dstH;
+
+ srcY2 = 0;
+ for (dstY = 0; dstY < dstH; dstY++) {
+ srcY1 = srcY2;
+ srcY2 += ratioY;
+
+ srcX2 = 0;
+ for (dstX = 0; dstX < dstW; dstX++) {
+ gulong red = 0, green = 0, blue = 0, alpha = 0;
+ gulong portionX, portionY, portionXY, sumXY = 0;
+ RrPixel32 pixel;
+
+ srcX1 = srcX2;
+ srcX2 += ratioX;
+
+ for (srcY = srcY1; srcY < srcY2; srcY += (1UL << FRACTION)) {
+ if (srcY == srcY1) {
+ srcY = FLOOR(srcY);
+ portionY = (1UL << FRACTION) - (srcY1 - srcY);
+ if (portionY > srcY2 - srcY1)
+ portionY = srcY2 - srcY1;
+ }
+ else if (srcY == FLOOR(srcY2))
+ portionY = srcY2 - srcY;
+ else
+ portionY = (1UL << FRACTION);
+
+ for (srcX = srcX1; srcX < srcX2; srcX += (1UL << FRACTION)) {
+ if (srcX == srcX1) {
+ srcX = FLOOR(srcX);
+ portionX = (1UL << FRACTION) - (srcX1 - srcX);
+ if (portionX > srcX2 - srcX1)
+ portionX = srcX2 - srcX1;
+ }
+ else if (srcX == FLOOR(srcX2))
+ portionX = srcX2 - srcX;
+ else
+ portionX = (1UL << FRACTION);
+
+ portionXY = (portionX * portionY) >> FRACTION;
+ sumXY += portionXY;
+
+ pixel = *(src + (srcY >> FRACTION) * srcW
+ + (srcX >> FRACTION));
+ red += ((pixel >> RrDefaultRedOffset) & 0xFF)
+ * portionXY;
+ green += ((pixel >> RrDefaultGreenOffset) & 0xFF)
+ * portionXY;
+ blue += ((pixel >> RrDefaultBlueOffset) & 0xFF)
+ * portionXY;
+ alpha += ((pixel >> RrDefaultAlphaOffset) & 0xFF)
+ * portionXY;
+ }
+ }
+
+ g_assert(sumXY != 0);
+ red /= sumXY;
+ green /= sumXY;
+ blue /= sumXY;
+ alpha /= sumXY;
+
+ *dst++ = (red << RrDefaultRedOffset) |
+ (green << RrDefaultGreenOffset) |
+ (blue << RrDefaultBlueOffset) |
+ (alpha << RrDefaultAlphaOffset);
+ }
+ }
+
+ pic = g_new(RrImagePic, 1);
+ RrImagePicInit(pic, dstW, dstH, dststart);
+
+ return pic;
+}
+
+/*! This draws an RGBA picture into the target, within the rectangle specified
+ by the area parameter. If the area's size differs from the source's then it
+ will be centered within the rectangle */
+void DrawRGBA(RrPixel32 *target, gint target_w, gint target_h,
+ RrPixel32 *source, gint source_w, gint source_h,
+ gint alpha, RrRect *area)
+{
+ RrPixel32 *dest;
+ gint col, num_pixels;
+ gint dw, dh;
+
+ g_assert(source_w <= area->width && source_h <= area->height);
+ g_assert(area->x + area->width <= target_w);
+ g_assert(area->y + area->height <= target_h);
+
+ /* keep the aspect ratio */
+ dw = area->width;
+ dh = (gint)(dw * ((gdouble)source_h / source_w));
+ if (dh > area->height) {
+ dh = area->height;
+ dw = (gint)(dh * ((gdouble)source_w / source_h));
+ }
+
+ /* copy source -> dest, and apply the alpha channel.
+ center the image if it is smaller than the area */
+ col = 0;
+ num_pixels = dw * dh;
+ dest = target + area->x + (area->width - dw) / 2 +
+ (target_w * (area->y + (area->height - dh) / 2));
+ while (num_pixels-- > 0) {
+ guchar a, r, g, b, bgr, bgg, bgb;
+
+ /* apply the rgba's opacity as well */
+ a = ((*source >> RrDefaultAlphaOffset) * alpha) >> 8;
+ r = *source >> RrDefaultRedOffset;
+ g = *source >> RrDefaultGreenOffset;
+ b = *source >> RrDefaultBlueOffset;
+
+ /* background color */
+ bgr = *dest >> RrDefaultRedOffset;
+ bgg = *dest >> RrDefaultGreenOffset;
+ bgb = *dest >> RrDefaultBlueOffset;
+
+ r = bgr + (((r - bgr) * a) >> 8);
+ g = bgg + (((g - bgg) * a) >> 8);
+ b = bgb + (((b - bgb) * a) >> 8);
+
+ *dest = ((r << RrDefaultRedOffset) |
+ (g << RrDefaultGreenOffset) |
+ (b << RrDefaultBlueOffset));
+
+ dest++;
+ source++;
+
+ if (++col >= dw) {
+ col = 0;
+ dest += target_w - dw;
+ }
+ }
+}
+
+/*! Draw an RGBA texture into a target pixel buffer. */
+void RrImageDrawRGBA(RrPixel32 *target, RrTextureRGBA *rgba,
+ gint target_w, gint target_h,
+ RrRect *area)
+{
+ RrImagePic *scaled;
+
+ scaled = ResizeImage(rgba->data, rgba->width, rgba->height,
+ area->width, area->height);
+
+ if (scaled) {
+#ifdef DEBUG
+ g_warning("Scaling an RGBA! You should avoid this and just make "
+ "it the right size yourself!");
+#endif
+ DrawRGBA(target, target_w, target_h,
+ scaled->data, scaled->width, scaled->height,
+ rgba->alpha, area);
+ RrImagePicFree(scaled);
+ }
+ else
+ DrawRGBA(target, target_w, target_h,
+ rgba->data, rgba->width, rgba->height,
+ rgba->alpha, area);
+}
+
+/*! Create a new RrImage, which is linked to an image cache */
+RrImage* RrImageNew(RrImageCache *cache)
+{
+ RrImage *self;
+
+ g_assert(cache != NULL);
+
+ self = g_new0(RrImage, 1);
+ self->ref = 1;
+ self->cache = cache;
+ return self;
+}
+
+void RrImageRef(RrImage *self)
+{
+ ++self->ref;
+}
+
+void RrImageUnref(RrImage *self)
+{
+ if (self && --self->ref == 0) {
+/*
+#ifdef DEBUG
+ g_debug("Refcount to 0, removing ALL pictures from the cache:\n "
+ "Image 0x%lx", (gulong)self);
+#endif
+*/
+ while (self->n_original > 0)
+ RemovePicture(self, &self->original, 0, &self->n_original);
+ while (self->n_resized > 0)
+ RemovePicture(self, &self->resized, 0, &self->n_resized);
+ g_free(self);
+ }
+}
+
+/*! Add a new picture with the given RGBA pixel data and dimensions into the
+ RrImage. This adds an "original" picture to the image.
+*/
+void RrImageAddPicture(RrImage *self, RrPixel32 *data, gint w, gint h)
+{
+ gint i;
+ RrImagePic *pic;
+
+ /* make sure we don't already have this size.. */
+ for (i = 0; i < self->n_original; ++i)
+ if (self->original[i]->width == w && self->original[i]->height == h) {
+/*
+#ifdef DEBUG
+ g_debug("Found duplicate ORIGINAL image:\n "
+ "Image 0x%lx, w %d h %d", (gulong)self, w, h);
+#endif
+*/
+ return;
+ }
+
+ /* remove any resized pictures of this same size */
+ for (i = 0; i < self->n_resized; ++i)
+ if (self->resized[i]->width == w || self->resized[i]->height == h) {
+ RemovePicture(self, &self->resized, i, &self->n_resized);
+ break;
+ }
+
+ /* add the new picture */
+ pic = g_new(RrImagePic, 1);
+ RrImagePicInit(pic, w, h, g_memdup(data, w*h*sizeof(RrPixel32)));
+ AddPicture(self, &self->original, &self->n_original, pic);
+}
+
+/*! Remove the picture from the RrImage which has the given dimensions. This
+ removes an "original" picture from the image.
+*/
+void RrImageRemovePicture(RrImage *self, gint w, gint h)
+{
+ gint i;
+
+ /* remove any resized pictures of this same size */
+ for (i = 0; i < self->n_original; ++i)
+ if (self->original[i]->width == w && self->original[i]->height == h) {
+ RemovePicture(self, &self->original, i, &self->n_original);
+ break;
+ }
+}
+
+/*! Draw an RrImage texture into a target pixel buffer. If the RrImage does
+ not contain a picture of the appropriate size, then one of its "original"
+ pictures will be resized and used (and stored in the RrImage as a "resized"
+ picture).
+ */
+void RrImageDrawImage(RrPixel32 *target, RrTextureImage *img,
+ gint target_w, gint target_h,
+ RrRect *area)
+{
+ gint i, min_diff, min_i, min_aspect_diff, min_aspect_i;
+ RrImage *self;
+ RrImagePic *pic;
+ gboolean free_pic;
+
+ self = img->image;
+ pic = NULL;
+ free_pic = FALSE;
+
+ /* is there an original of this size? (only the larger of
+ w or h has to be right cuz we maintain aspect ratios) */
+ for (i = 0; i < self->n_original; ++i)
+ if ((self->original[i]->width >= self->original[i]->height &&
+ self->original[i]->width == area->width) ||
+ (self->original[i]->width <= self->original[i]->height &&
+ self->original[i]->height == area->height))
+ {
+ pic = self->original[i];
+ break;
+ }
+
+ /* is there a resize of this size? */
+ for (i = 0; i < self->n_resized; ++i)
+ if ((self->resized[i]->width >= self->resized[i]->height &&
+ self->resized[i]->width == area->width) ||
+ (self->resized[i]->width <= self->resized[i]->height &&
+ self->resized[i]->height == area->height))
+ {
+ gint j;
+ RrImagePic *saved;
+
+ /* save the selected one */
+ saved = self->resized[i];
+
+ /* shift all the others down */
+ for (j = i; j > 0; --j)
+ self->resized[j] = self->resized[j-1];
+
+ /* and move the selected one to the top of the list */
+ self->resized[0] = saved;
+
+ pic = self->resized[0];
+ break;
+ }
+
+ if (!pic) {
+ gdouble aspect;
+
+ /* find an original with a close size */
+ min_diff = min_aspect_diff = -1;
+ min_i = min_aspect_i = 0;
+ aspect = ((gdouble)area->width) / area->height;
+ for (i = 0; i < self->n_original; ++i) {
+ gint diff;
+ gint wdiff, hdiff;
+ gdouble myasp;
+
+ /* our size difference metric.. */
+ wdiff = self->original[i]->width - area->width;
+ hdiff = self->original[i]->height - area->height;
+ diff = (wdiff * wdiff) + (hdiff * hdiff);
+
+ /* find the smallest difference */
+ if (min_diff < 0 || diff < min_diff) {
+ min_diff = diff;
+ min_i = i;
+ }
+ /* and also find the smallest difference with the same aspect
+ ratio (and prefer this one) */
+ myasp = ((gdouble)self->original[i]->width) /
+ self->original[i]->height;
+ if (ABS(aspect - myasp) < 0.0000001 &&
+ (min_aspect_diff < 0 || diff < min_aspect_diff))
+ {
+ min_aspect_diff = diff;
+ min_aspect_i = i;
+ }
+ }
+
+ /* use the aspect ratio correct source if there is one */
+ if (min_aspect_i >= 0)
+ min_i = min_aspect_i;
+
+ /* resize the original to the given area */
+ pic = ResizeImage(self->original[min_i]->data,
+ self->original[min_i]->width,
+ self->original[min_i]->height,
+ area->width, area->height);
+
+ /* add the resized image to the image, as the first in the resized
+ list */
+ if (self->n_resized >= self->cache->max_resized_saved)
+ /* remove the last one (last used one) */
+ RemovePicture(self, &self->resized, self->n_resized - 1,
+ &self->n_resized);
+ if (self->cache->max_resized_saved)
+ /* add it to the top of the resized list */
+ AddPicture(self, &self->resized, &self->n_resized, pic);
+ else
+ free_pic = TRUE; /* don't leak mem! */
+ }
+
+ g_assert(pic != NULL);
+
+ DrawRGBA(target, target_w, target_h,
+ pic->data, pic->width, pic->height,
+ img->alpha, area);
+ if (free_pic)
+ RrImagePicFree(pic);
+}
diff --git a/obrender/image.h b/obrender/image.h
new file mode 100644
index 00000000..b478daf9
--- /dev/null
+++ b/obrender/image.h
@@ -0,0 +1,35 @@
+/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
+
+ image.h for the Openbox window manager
+ Copyright (c) 2003-2007 Dana Jansens
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ See the COPYING file for a copy of the GNU General Public License.
+*/
+
+#ifndef __image_h
+#define __image_h
+
+#include "render.h"
+#include "geom.h"
+
+/*! Initialize an RrImagePicture to the specified dimensions and pixel data */
+void RrImagePicInit(RrImagePic *pic, gint w, gint h, RrPixel32 *data);
+
+void RrImageDrawImage(RrPixel32 *target, RrTextureImage *img,
+ gint target_w, gint target_h,
+ RrRect *area);
+void RrImageDrawRGBA(RrPixel32 *target, RrTextureRGBA *rgba,
+ gint target_w, gint target_h,
+ RrRect *area);
+
+#endif
diff --git a/obrender/imagecache.c b/obrender/imagecache.c
new file mode 100644
index 00000000..9c605f9d
--- /dev/null
+++ b/obrender/imagecache.c
@@ -0,0 +1,144 @@
+/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
+
+ imagecache.c for the Openbox window manager
+ Copyright (c) 2008 Dana Jansens
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ See the COPYING file for a copy of the GNU General Public License.
+*/
+
+#include "render.h"
+#include "imagecache.h"
+#include "image.h"
+
+static gboolean RrImagePicEqual(const RrImagePic *p1,
+ const RrImagePic *p2);
+
+RrImageCache* RrImageCacheNew(gint max_resized_saved)
+{
+ RrImageCache *self;
+
+ g_assert(max_resized_saved >= 0);
+
+ self = g_new(RrImageCache, 1);
+ self->ref = 1;
+ self->max_resized_saved = max_resized_saved;
+ self->table = g_hash_table_new((GHashFunc)RrImagePicHash,
+ (GEqualFunc)RrImagePicEqual);
+ return self;
+}
+
+void RrImageCacheRef(RrImageCache *self)
+{
+ ++self->ref;
+}
+
+void RrImageCacheUnref(RrImageCache *self)
+{
+ if (self && --self->ref == 0) {
+ g_assert(g_hash_table_size(self->table) == 0);
+ g_hash_table_unref(self->table);
+
+ g_free(self);
+ }
+}
+
+/*! Finds an image in the cache, if it is already in there */
+RrImage* RrImageCacheFind(RrImageCache *self,
+ RrPixel32 *data, gint w, gint h)
+{
+ RrImagePic pic;
+
+ RrImagePicInit(&pic, w, h, data);
+ return g_hash_table_lookup(self->table, &pic);
+}
+
+#define hashsize(n) ((RrPixel32)1<<(n))
+#define hashmask(n) (hashsize(n)-1)
+#define rot(x,k) (((x)<<(k)) | ((x)>>(32-(k))))
+/* mix -- mix 3 32-bit values reversibly. */
+#define mix(a,b,c) \
+{ \
+ a -= c; a ^= rot(c, 4); c += b; \
+ b -= a; b ^= rot(a, 6); a += c; \
+ c -= b; c ^= rot(b, 8); b += a; \
+ a -= c; a ^= rot(c,16); c += b; \
+ b -= a; b ^= rot(a,19); a += c; \
+ c -= b; c ^= rot(b, 4); b += a; \
+}
+/* final -- final mixing of 3 32-bit values (a,b,c) into c */
+#define final(a,b,c) \
+{ \
+ c ^= b; c -= rot(b,14); \
+ a ^= c; a -= rot(c,11); \
+ b ^= a; b -= rot(a,25); \
+ c ^= b; c -= rot(b,16); \
+ a ^= c; a -= rot(c,4); \
+ b ^= a; b -= rot(a,14); \
+ c ^= b; c -= rot(b,24); \
+}
+
+/* This is a fast, reversable hash function called "lookup3", found here:
+ http://burtleburtle.net/bob/c/lookup3.c, by Bob Jenkins
+
+ This hashing algorithm is "reversible", that is, not cryptographically
+ secure at all. But we don't care about that, we just want something to
+ tell when images are the same or different relatively quickly.
+*/
+guint32 hashword(const guint32 *key, gint length, guint32 initval)
+{
+ guint32 a,b,c;
+
+ /* Set up the internal state */
+ a = b = c = 0xdeadbeef + (((guint32)length)<<2) + initval;
+
+ /* handle most of the key */
+ while (length > 3)
+ {
+ a += key[0];
+ b += key[1];
+ c += key[2];
+ mix(a,b,c);
+ length -= 3;
+ key += 3;
+ }
+
+ /* handle the last 3 guint32's */
+ switch(length) /* all the case statements fall through */
+ {
+ case 3: c+=key[2];
+ case 2: b+=key[1];
+ case 1: a+=key[0];
+ final(a,b,c);
+ case 0: /* case 0: nothing left to add */
+ break;
+ }
+ /* report the result */
+ return c;
+}
+
+/*! This is some arbitrary initial value for the hashing function. It's
+ constant so that you get the same result from the same data each time.
+*/
+#define HASH_INITVAL 0xf00d
+
+guint RrImagePicHash(const RrImagePic *p)
+{
+ return hashword(p->data, p->width * p->height, HASH_INITVAL);
+}
+
+static gboolean RrImagePicEqual(const RrImagePic *p1,
+ const RrImagePic *p2)
+{
+ return p1->width == p2->width && p1->height == p2->height &&
+ p1->sum == p2->sum;
+}
diff --git a/obrender/imagecache.h b/obrender/imagecache.h
new file mode 100644
index 00000000..4ad2deae
--- /dev/null
+++ b/obrender/imagecache.h
@@ -0,0 +1,51 @@
+/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
+
+ imagecache.h for the Openbox window manager
+ Copyright (c) 2008 Dana Jansens
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ See the COPYING file for a copy of the GNU General Public License.
+*/
+
+#ifndef __imagecache_h
+#define __imagecache_h
+
+#include <glib.h>
+
+struct _RrImagePic;
+
+guint RrImagePicHash(const struct _RrImagePic *p);
+
+/*! Create a new image cache. An image cache is basically a hash table to look
+ up RrImages. Each RrImage in the cache may contain one or more Pictures,
+ that is one or more actual copies of image data at various sizes. For eg,
+ for a window, all of its various icons are loaded into the same RrImage.
+ When an RrImage is drawn and a picture inside it needs to be resized, that
+ is also saved within the RrImage.
+
+ For each picture that an RrImage has, the picture is hashed and that is used
+ as a key to find the RrImage. So, given any picture in any RrImage in the
+ cache, if you hash it, you will find the RrImage.
+*/
+struct _RrImageCache {
+ gint ref;
+ /*! When an original picture is resized for an RrImage, the resized picture
+ is saved in the RrImage. This specifies how many pictures should be
+ saved at a time. When this is exceeded, the least recently used
+ "resized" picture is deleted.
+ */
+ gint max_resized_saved;
+
+ GHashTable *table;
+};
+
+#endif
diff --git a/obrender/instance.c b/obrender/instance.c
new file mode 100644
index 00000000..b867815c
--- /dev/null
+++ b/obrender/instance.c
@@ -0,0 +1,309 @@
+/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
+
+ instance.c for the Openbox window manager
+ Copyright (c) 2006 Mikael Magnusson
+ Copyright (c) 2003-2007 Dana Jansens
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ See the COPYING file for a copy of the GNU General Public License.
+*/
+
+#include "render.h"
+#include "instance.h"
+
+static RrInstance *definst = NULL;
+
+static void RrTrueColorSetup (RrInstance *inst);
+static void RrPseudoColorSetup (RrInstance *inst);
+
+#ifdef DEBUG
+#include "color.h"
+#endif
+static void
+dest(gpointer data)
+{
+#ifdef DEBUG
+ RrColor *c = data;
+ if (c->refcount > 0)
+ g_error("color %d (%d,%d,%d) in hash table with %d "
+ "leftover references",
+ c->id, RrColorRed(c), RrColorGreen(c), RrColorBlue(c),
+ c->refcount);
+#endif
+}
+
+#if 0
+static void f(gpointer key, gpointer value, gpointer n)
+{
+ RrColor *c = value;
+ if (c->id == *(gint*)n)
+ g_message("color %d has %d references", c->id, c->refcount);
+}
+
+void print_refs(gint id)
+{
+ g_hash_table_foreach(RrColorHash(definst), f, &id);
+}
+#endif
+
+RrInstance* RrInstanceNew (Display *display, gint screen)
+{
+ g_type_init(); /* supposedly needed for pango but seems to work without */
+
+ definst = g_new (RrInstance, 1);
+ definst->display = display;
+ definst->screen = screen;
+
+ definst->depth = DefaultDepth(display, screen);
+ definst->visual = DefaultVisual(display, screen);
+ definst->colormap = DefaultColormap(display, screen);
+ definst->pango = pango_xft_get_context(display, screen);
+
+ definst->pseudo_colors = NULL;
+
+ definst->color_hash = g_hash_table_new_full(g_int_hash, g_int_equal,
+ NULL, dest);
+
+ switch (definst->visual->class) {
+ case TrueColor:
+ RrTrueColorSetup(definst);
+ break;
+ case PseudoColor:
+ case StaticColor:
+ case GrayScale:
+ case StaticGray:
+ RrPseudoColorSetup(definst);
+ break;
+ default:
+ g_critical("Unsupported visual class");
+ g_free (definst);
+ return definst = NULL;
+ }
+ return definst;
+}
+
+static void RrTrueColorSetup (RrInstance *inst)
+{
+ gulong red_mask, green_mask, blue_mask;
+ XImage *timage = NULL;
+
+ timage = XCreateImage(inst->display, inst->visual, inst->depth,
+ ZPixmap, 0, NULL, 1, 1, 32, 0);
+ g_assert(timage != NULL);
+ /* find the offsets for each color in the visual's masks */
+ inst->red_mask = red_mask = timage->red_mask;
+ inst->green_mask = green_mask = timage->green_mask;
+ inst->blue_mask = blue_mask = timage->blue_mask;
+
+ inst->red_offset = 0;
+ inst->green_offset = 0;
+ inst->blue_offset = 0;
+
+ while (! (red_mask & 1)) { inst->red_offset++; red_mask >>= 1; }
+ while (! (green_mask & 1)) { inst->green_offset++; green_mask >>= 1; }
+ while (! (blue_mask & 1)) { inst->blue_offset++; blue_mask >>= 1; }
+
+ inst->red_shift = inst->green_shift = inst->blue_shift = 8;
+ while (red_mask) { red_mask >>= 1; inst->red_shift--; }
+ while (green_mask) { green_mask >>= 1; inst->green_shift--; }
+ while (blue_mask) { blue_mask >>= 1; inst->blue_shift--; }
+ XFree(timage);
+}
+
+#define RrPseudoNcolors(inst) (1 << (inst->pseudo_bpc * 3))
+
+static void RrPseudoColorSetup (RrInstance *inst)
+{
+ XColor icolors[256];
+ gint tr, tg, tb, n, r, g, b, i, incolors, ii;
+ gulong dev;
+ gint cpc, _ncolors;
+
+ /* determine the number of colors and the bits-per-color */
+ inst->pseudo_bpc = 2; /* XXX THIS SHOULD BE A USER OPTION */
+ g_assert(inst->pseudo_bpc >= 1);
+ _ncolors = RrPseudoNcolors(inst);
+
+ if (_ncolors > 1 << inst->depth) {
+ g_message("Invalid colormap size. Resizing.");
+ inst->pseudo_bpc = 1 << (inst->depth/3) >> 3;
+ _ncolors = 1 << (inst->pseudo_bpc * 3);
+ }
+
+ /* build a color cube */
+ inst->pseudo_colors = g_new(XColor, _ncolors);
+ cpc = 1 << inst->pseudo_bpc; /* colors per channel */
+
+ for (n = 0, r = 0; r < cpc; r++)
+ for (g = 0; g < cpc; g++)
+ for (b = 0; b < cpc; b++, n++) {
+ tr = (gint)(((gfloat)(r)/(gfloat)(cpc-1)) * 0xFF);
+ tg = (gint)(((gfloat)(g)/(gfloat)(cpc-1)) * 0xFF);
+ tb = (gint)(((gfloat)(b)/(gfloat)(cpc-1)) * 0xFF);
+ inst->pseudo_colors[n].red = tr | tr << 8;
+ inst->pseudo_colors[n].green = tg | tg << 8;
+ inst->pseudo_colors[n].blue = tb | tb << 8;
+ /* used to track allocation */
+ inst->pseudo_colors[n].flags = DoRed|DoGreen|DoBlue;
+ }
+
+ /* allocate the colors */
+ for (i = 0; i < _ncolors; i++)
+ if (!XAllocColor(inst->display, inst->colormap,
+ &inst->pseudo_colors[i]))
+ inst->pseudo_colors[i].flags = 0; /* mark it as unallocated */
+
+ /* try allocate any colors that failed allocation above */
+
+ /* get the allocated values from the X server
+ (only the first 256 XXX why!?)
+ */
+ incolors = (((1 << inst->depth) > 256) ? 256 : (1 << inst->depth));
+ for (i = 0; i < incolors; i++)
+ icolors[i].pixel = i;
+ XQueryColors(inst->display, inst->colormap, icolors, incolors);
+
+ /* try match unallocated ones */
+ for (i = 0; i < _ncolors; i++) {
+ if (!inst->pseudo_colors[i].flags) { /* if it wasn't allocated... */
+ gulong closest = 0xffffffff, close = 0;
+ for (ii = 0; ii < incolors; ii++) {
+ /* find deviations */
+ r = (inst->pseudo_colors[i].red - icolors[ii].red) & 0xff;
+ g = (inst->pseudo_colors[i].green - icolors[ii].green) & 0xff;
+ b = (inst->pseudo_colors[i].blue - icolors[ii].blue) & 0xff;
+ /* find a weighted absolute deviation */
+ dev = (r * r) + (g * g) + (b * b);
+
+ if (dev < closest) {
+ closest = dev;
+ close = ii;
+ }
+ }
+
+ inst->pseudo_colors[i].red = icolors[close].red;
+ inst->pseudo_colors[i].green = icolors[close].green;
+ inst->pseudo_colors[i].blue = icolors[close].blue;
+ inst->pseudo_colors[i].pixel = icolors[close].pixel;
+
+ /* try alloc this closest color, it had better succeed! */
+ if (XAllocColor(inst->display, inst->colormap,
+ &inst->pseudo_colors[i]))
+ /* mark as alloced */
+ inst->pseudo_colors[i].flags = DoRed|DoGreen|DoBlue;
+ else
+ /* wtf has gone wrong, its already alloced for chissake! */
+ g_assert_not_reached();
+ }
+ }
+}
+
+void RrInstanceFree (RrInstance *inst)
+{
+ if (inst) {
+ if (inst == definst) definst = NULL;
+ g_free(inst->pseudo_colors);
+ g_hash_table_destroy(inst->color_hash);
+ g_object_unref(inst->pango);
+ g_free(inst);
+ }
+}
+
+Display* RrDisplay (const RrInstance *inst)
+{
+ return (inst ? inst : definst)->display;
+}
+
+gint RrScreen (const RrInstance *inst)
+{
+ return (inst ? inst : definst)->screen;
+}
+
+Window RrRootWindow (const RrInstance *inst)
+{
+ return RootWindow (RrDisplay (inst), RrScreen (inst));
+}
+
+Visual *RrVisual (const RrInstance *inst)
+{
+ return (inst ? inst : definst)->visual;
+}
+
+gint RrDepth (const RrInstance *inst)
+{
+ return (inst ? inst : definst)->depth;
+}
+
+Colormap RrColormap (const RrInstance *inst)
+{
+ return (inst ? inst : definst)->colormap;
+}
+
+gint RrRedOffset (const RrInstance *inst)
+{
+ return (inst ? inst : definst)->red_offset;
+}
+
+gint RrGreenOffset (const RrInstance *inst)
+{
+ return (inst ? inst : definst)->green_offset;
+}
+
+gint RrBlueOffset (const RrInstance *inst)
+{
+ return (inst ? inst : definst)->blue_offset;
+}
+
+gint RrRedShift (const RrInstance *inst)
+{
+ return (inst ? inst : definst)->red_shift;
+}
+
+gint RrGreenShift (const RrInstance *inst)
+{
+ return (inst ? inst : definst)->green_shift;
+}
+
+gint RrBlueShift (const RrInstance *inst)
+{
+ return (inst ? inst : definst)->blue_shift;
+}
+
+gint RrRedMask (const RrInstance *inst)
+{
+ return (inst ? inst : definst)->red_mask;
+}
+
+gint RrGreenMask (const RrInstance *inst)
+{
+ return (inst ? inst : definst)->green_mask;
+}
+
+gint RrBlueMask (const RrInstance *inst)
+{
+ return (inst ? inst : definst)->blue_mask;
+}
+
+guint RrPseudoBPC (const RrInstance *inst)
+{
+ return (inst ? inst : definst)->pseudo_bpc;
+}
+
+XColor *RrPseudoColors (const RrInstance *inst)
+{
+ return (inst ? inst : definst)->pseudo_colors;
+}
+
+GHashTable* RrColorHash (const RrInstance *inst)
+{
+ return (inst ? inst : definst)->color_hash;
+}
diff --git a/obrender/instance.h b/obrender/instance.h
new file mode 100644
index 00000000..324f061a
--- /dev/null
+++ b/obrender/instance.h
@@ -0,0 +1,57 @@
+/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
+
+ instance.h for the Openbox window manager
+ Copyright (c) 2003-2007 Dana Jansens
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ See the COPYING file for a copy of the GNU General Public License.
+*/
+
+#ifndef __render_instance_h
+#define __render_instance_h
+
+#include <X11/Xlib.h>
+#include <glib.h>
+#include <pango/pangoxft.h>
+
+struct _RrInstance {
+ Display *display;
+ gint screen;
+
+ Visual *visual;
+ gint depth;
+ Colormap colormap;
+ PangoContext *pango;
+
+ gint red_offset;
+ gint green_offset;
+ gint blue_offset;
+
+ gint red_shift;
+ gint green_shift;
+ gint blue_shift;
+
+ gint red_mask;
+ gint green_mask;
+ gint blue_mask;
+
+ gint pseudo_bpc;
+ XColor *pseudo_colors;
+
+ GHashTable *color_hash;
+};
+
+guint RrPseudoBPC (const RrInstance *inst);
+XColor* RrPseudoColors (const RrInstance *inst);
+GHashTable* RrColorHash (const RrInstance *inst);
+
+#endif
diff --git a/obrender/mask.c b/obrender/mask.c
new file mode 100644
index 00000000..b6e3c443
--- /dev/null
+++ b/obrender/mask.c
@@ -0,0 +1,82 @@
+/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
+
+ mask.c for the Openbox window manager
+ Copyright (c) 2003-2007 Dana Jansens
+ Copyright (c) 2003 Derek Foreman
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ See the COPYING file for a copy of the GNU General Public License.
+*/
+
+#include "render.h"
+#include "color.h"
+#include "mask.h"
+
+RrPixmapMask *RrPixmapMaskNew(const RrInstance *inst,
+ gint w, gint h, const gchar *data)
+{
+ RrPixmapMask *m = g_new(RrPixmapMask, 1);
+ m->inst = inst;
+ m->width = w;
+ m->height = h;
+ /* round up to nearest byte */
+ m->data = g_memdup(data, (w + 7) / 8 * h);
+ m->mask = XCreateBitmapFromData(RrDisplay(inst), RrRootWindow(inst),
+ data, w, h);
+ return m;
+}
+
+void RrPixmapMaskFree(RrPixmapMask *m)
+{
+ if (m) {
+ XFreePixmap(RrDisplay(m->inst), m->mask);
+ g_free(m->data);
+ g_free(m);
+ }
+}
+
+void RrPixmapMaskDraw(Pixmap p, const RrTextureMask *m, const RrRect *area)
+{
+ gint x, y;
+ if (m->mask == NULL) return; /* no mask given */
+
+ /* set the clip region */
+ x = area->x + (area->width - m->mask->width) / 2;
+ y = area->y + (area->height - m->mask->height) / 2;
+
+ if (x < 0) x = 0;
+ if (y < 0) y = 0;
+
+ XSetClipMask(RrDisplay(m->mask->inst), RrColorGC(m->color), m->mask->mask);
+ XSetClipOrigin(RrDisplay(m->mask->inst), RrColorGC(m->color), x, y);
+
+ /* fill in the clipped region */
+ XFillRectangle(RrDisplay(m->mask->inst), p, RrColorGC(m->color), x, y,
+ x + m->mask->width, y + m->mask->height);
+
+ /* unset the clip region */
+ XSetClipMask(RrDisplay(m->mask->inst), RrColorGC(m->color), None);
+ XSetClipOrigin(RrDisplay(m->mask->inst), RrColorGC(m->color), 0, 0);
+}
+
+RrPixmapMask *RrPixmapMaskCopy(const RrPixmapMask *src)
+{
+ RrPixmapMask *m = g_new(RrPixmapMask, 1);
+ m->inst = src->inst;
+ m->width = src->width;
+ m->height = src->height;
+ /* round up to nearest byte */
+ m->data = g_memdup(src->data, (src->width + 7) / 8 * src->height);
+ m->mask = XCreateBitmapFromData(RrDisplay(m->inst), RrRootWindow(m->inst),
+ m->data, m->width, m->height);
+ return m;
+}
diff --git a/obrender/mask.h b/obrender/mask.h
new file mode 100644
index 00000000..4dc85031
--- /dev/null
+++ b/obrender/mask.h
@@ -0,0 +1,32 @@
+/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
+
+ mask.h for the Openbox window manager
+ Copyright (c) 2003-2007 Dana Jansens
+ Copyright (c) 2003 Derek Foreman
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ See the COPYING file for a copy of the GNU General Public License.
+*/
+
+#ifndef __mask_h
+#define __mask_h
+
+#include "render.h"
+#include "geom.h"
+
+RrPixmapMask *RrPixmapMaskNew(const RrInstance *inst,
+ gint w, gint h, const gchar *data);
+void RrPixmapMaskFree(RrPixmapMask *m);
+RrPixmapMask *RrPixmapMaskCopy(const RrPixmapMask *src);
+void RrPixmapMaskDraw(Pixmap p, const RrTextureMask *m, const RrRect *area);
+
+#endif
diff --git a/obrender/obrender-3.5.pc.in b/obrender/obrender-3.5.pc.in
new file mode 100644
index 00000000..78646e32
--- /dev/null
+++ b/obrender/obrender-3.5.pc.in
@@ -0,0 +1,14 @@
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+libdir=@libdir@
+includedir=@includedir@
+
+xcflags=@X_CFLAGS@
+xlibs=@X_LIBS@
+
+Name: ObRender
+Description: Openbox Render Library
+Version: @RR_VERSION@
+Requires: obt-3.5 glib-2.0 xft pangoxft
+Libs: -L${libdir} -lobrender ${xlibs}
+Cflags: -I${includedir}/openbox/@RR_VERSION@ ${xcflags}
diff --git a/obrender/render.c b/obrender/render.c
new file mode 100644
index 00000000..20002e32
--- /dev/null
+++ b/obrender/render.c
@@ -0,0 +1,564 @@
+/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
+
+ render.c for the Openbox window manager
+ Copyright (c) 2006 Mikael Magnusson
+ Copyright (c) 2003-2007 Dana Jansens
+ Copyright (c) 2003 Derek Foreman
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ See the COPYING file for a copy of the GNU General Public License.
+*/
+
+#include "render.h"
+#include "gradient.h"
+#include "font.h"
+#include "mask.h"
+#include "color.h"
+#include "image.h"
+#include "theme.h"
+
+#include <glib.h>
+#include <X11/Xlib.h>
+#include <X11/Xutil.h>
+#include <X11/Xft/Xft.h>
+
+#ifdef HAVE_STDLIB_H
+# include <stdlib.h>
+#endif
+
+static void pixel_data_to_pixmap(RrAppearance *l,
+ gint x, gint y, gint w, gint h);
+
+Pixmap RrPaintPixmap(RrAppearance *a, gint w, gint h)
+{
+ gint i, transferred = 0, force_transfer = 0;
+ Pixmap oldp = None;
+ RrRect tarea; /* area in which to draw textures */
+ gboolean resized;
+
+ if (w <= 0 || h <= 0) return None;
+
+ if (a->surface.parentx < 0 || a->surface.parenty < 0) {
+ /* ob_debug("Invalid parent co-ordinates\n"); */
+ return None;
+ }
+
+ if (a->surface.grad == RR_SURFACE_PARENTREL &&
+ (a->surface.parentx >= a->surface.parent->w ||
+ a->surface.parenty >= a->surface.parent->h))
+ {
+ return None;
+ }
+
+ resized = (a->w != w || a->h != h);
+
+ oldp = a->pixmap; /* save to free after changing the visible pixmap */
+ a->pixmap = XCreatePixmap(RrDisplay(a->inst),
+ RrRootWindow(a->inst),
+ w, h, RrDepth(a->inst));
+
+ g_assert(a->pixmap != None);
+ a->w = w;
+ a->h = h;
+
+ if (a->xftdraw != NULL)
+ XftDrawDestroy(a->xftdraw);
+ a->xftdraw = XftDrawCreate(RrDisplay(a->inst), a->pixmap,
+ RrVisual(a->inst), RrColormap(a->inst));
+ g_assert(a->xftdraw != NULL);
+
+ if (resized) {
+ g_free(a->surface.pixel_data);
+ a->surface.pixel_data = g_new(RrPixel32, w * h);
+ }
+
+ RrRender(a, w, h);
+
+ {
+ gint l, t, r, b;
+ RrMargins(a, &l, &t, &r, &b);
+ RECT_SET(tarea, l, t, w - l - r, h - t - b);
+ }
+
+ for (i = 0; i < a->textures; i++) {
+ switch (a->texture[i].type) {
+ case RR_TEXTURE_NONE:
+ break;
+ case RR_TEXTURE_TEXT:
+ if (!transferred) {
+ transferred = 1;
+ if ((a->surface.grad != RR_SURFACE_SOLID)
+ || (a->surface.interlaced))
+ pixel_data_to_pixmap(a, 0, 0, w, h);
+ }
+ if (a->xftdraw == NULL) {
+ a->xftdraw = XftDrawCreate(RrDisplay(a->inst), a->pixmap,
+ RrVisual(a->inst),
+ RrColormap(a->inst));
+ }
+ RrFontDraw(a->xftdraw, &a->texture[i].data.text, &tarea);
+ break;
+ case RR_TEXTURE_LINE_ART:
+ if (!transferred) {
+ transferred = 1;
+ if ((a->surface.grad != RR_SURFACE_SOLID)
+ || (a->surface.interlaced))
+ pixel_data_to_pixmap(a, 0, 0, w, h);
+ }
+ XDrawLine(RrDisplay(a->inst), a->pixmap,
+ RrColorGC(a->texture[i].data.lineart.color),
+ a->texture[i].data.lineart.x1,
+ a->texture[i].data.lineart.y1,
+ a->texture[i].data.lineart.x2,
+ a->texture[i].data.lineart.y2);
+ break;
+ case RR_TEXTURE_MASK:
+ if (!transferred) {
+ transferred = 1;
+ if ((a->surface.grad != RR_SURFACE_SOLID)
+ || (a->surface.interlaced))
+ pixel_data_to_pixmap(a, 0, 0, w, h);
+ }
+ RrPixmapMaskDraw(a->pixmap, &a->texture[i].data.mask, &tarea);
+ break;
+ case RR_TEXTURE_IMAGE:
+ g_assert(!transferred);
+ {
+ RrRect narea = tarea;
+ RrTextureImage *img = &a->texture[i].data.image;
+ narea.x += img->tx;
+ narea.width -= img->tx;
+ narea.y += img->ty;
+ narea.height -= img->ty;
+ if (img->twidth)
+ narea.width = MIN(narea.width, img->twidth);
+ if (img->theight)
+ narea.height = MIN(narea.height, img->theight);
+ RrImageDrawImage(a->surface.pixel_data,
+ &a->texture[i].data.image,
+ a->w, a->h,
+ &narea);
+ }
+ force_transfer = 1;
+ break;
+ case RR_TEXTURE_RGBA:
+ g_assert(!transferred);
+ {
+ RrRect narea = tarea;
+ RrTextureRGBA *rgb = &a->texture[i].data.rgba;
+ narea.x += rgb->tx;
+ narea.width -= rgb->tx;
+ narea.y += rgb->ty;
+ narea.height -= rgb->ty;
+ if (rgb->twidth)
+ narea.width = MIN(narea.width, rgb->twidth);
+ if (rgb->theight)
+ narea.height = MIN(narea.height, rgb->theight);
+ RrImageDrawRGBA(a->surface.pixel_data,
+ &a->texture[i].data.rgba,
+ a->w, a->h,
+ &narea);
+ }
+ force_transfer = 1;
+ break;
+ }
+ }
+
+ if (!transferred) {
+ transferred = 1;
+ if ((a->surface.grad != RR_SURFACE_SOLID) || (a->surface.interlaced) ||
+ force_transfer)
+ {
+ pixel_data_to_pixmap(a, 0, 0, w, h);
+ }
+ }
+
+ return oldp;
+}
+
+void RrPaint(RrAppearance *a, Window win, gint w, gint h)
+{
+ Pixmap oldp;
+
+ oldp = RrPaintPixmap(a, w, h);
+ XSetWindowBackgroundPixmap(RrDisplay(a->inst), win, a->pixmap);
+ XClearWindow(RrDisplay(a->inst), win);
+ /* free this after changing the visible pixmap */
+ if (oldp) XFreePixmap(RrDisplay(a->inst), oldp);
+}
+
+RrAppearance *RrAppearanceNew(const RrInstance *inst, gint numtex)
+{
+ RrAppearance *out;
+
+ out = g_new0(RrAppearance, 1);
+ out->inst = inst;
+ out->textures = numtex;
+ out->surface.bevel_light_adjust = 128;
+ out->surface.bevel_dark_adjust = 64;
+ if (numtex) out->texture = g_new0(RrTexture, numtex);
+
+ return out;
+}
+
+void RrAppearanceRemoveTextures(RrAppearance *a)
+{
+ g_free(a->texture);
+ a->textures = 0;
+}
+
+void RrAppearanceAddTextures(RrAppearance *a, gint numtex)
+{
+ g_assert(a->textures == 0);
+
+ a->textures = numtex;
+ if (numtex) a->texture = g_new0(RrTexture, numtex);
+}
+
+void RrAppearanceClearTextures(RrAppearance *a)
+{
+ memset(a->texture, 0, a->textures * sizeof(RrTexture));
+}
+
+RrAppearance *RrAppearanceCopy(RrAppearance *orig)
+{
+ RrSurface *spo, *spc;
+ RrAppearance *copy = g_new(RrAppearance, 1);
+
+ copy->inst = orig->inst;
+
+ spo = &(orig->surface);
+ spc = &(copy->surface);
+ spc->grad = spo->grad;
+ spc->relief = spo->relief;
+ spc->bevel = spo->bevel;
+ if (spo->primary != NULL)
+ spc->primary = RrColorNew(copy->inst,
+ spo->primary->r,
+ spo->primary->g,
+ spo->primary->b);
+ else spc->primary = NULL;
+
+ if (spo->secondary != NULL)
+ spc->secondary = RrColorNew(copy->inst,
+ spo->secondary->r,
+ spo->secondary->g,
+ spo->secondary->b);
+ else spc->secondary = NULL;
+
+ if (spo->border_color != NULL)
+ spc->border_color = RrColorNew(copy->inst,
+ spo->border_color->r,
+ spo->border_color->g,
+ spo->border_color->b);
+ else spc->border_color = NULL;
+
+ if (spo->interlace_color != NULL)
+ spc->interlace_color = RrColorNew(copy->inst,
+ spo->interlace_color->r,
+ spo->interlace_color->g,
+ spo->interlace_color->b);
+ else spc->interlace_color = NULL;
+
+ if (spo->bevel_dark != NULL)
+ spc->bevel_dark = RrColorNew(copy->inst,
+ spo->bevel_dark->r,
+ spo->bevel_dark->g,
+ spo->bevel_dark->b);
+ else spc->bevel_dark = NULL;
+
+ if (spo->bevel_light != NULL)
+ spc->bevel_light = RrColorNew(copy->inst,
+ spo->bevel_light->r,
+ spo->bevel_light->g,
+ spo->bevel_light->b);
+ else spc->bevel_light = NULL;
+
+ if (spo->split_primary != NULL)
+ spc->split_primary = RrColorNew(copy->inst,
+ spo->split_primary->r,
+ spo->split_primary->g,
+ spo->split_primary->b);
+ else spc->split_primary = NULL;
+
+ if (spo->split_secondary != NULL)
+ spc->split_secondary = RrColorNew(copy->inst,
+ spo->split_secondary->r,
+ spo->split_secondary->g,
+ spo->split_secondary->b);
+ else spc->split_secondary = NULL;
+
+ spc->interlaced = spo->interlaced;
+ spc->bevel_light_adjust = spo->bevel_light_adjust;
+ spc->bevel_dark_adjust = spo->bevel_dark_adjust;
+ spc->border = spo->border;
+ spc->parent = NULL;
+ spc->parentx = spc->parenty = 0;
+ spc->pixel_data = NULL;
+
+ copy->textures = orig->textures;
+ copy->texture = g_memdup(orig->texture,
+ orig->textures * sizeof(RrTexture));
+ copy->pixmap = None;
+ copy->xftdraw = NULL;
+ copy->w = copy->h = 0;
+ return copy;
+}
+
+void RrAppearanceFree(RrAppearance *a)
+{
+ if (a) {
+ RrSurface *p;
+ if (a->pixmap != None) XFreePixmap(RrDisplay(a->inst), a->pixmap);
+ if (a->xftdraw != NULL) XftDrawDestroy(a->xftdraw);
+ if (a->textures)
+ g_free(a->texture);
+ p = &a->surface;
+ RrColorFree(p->primary);
+ RrColorFree(p->secondary);
+ RrColorFree(p->border_color);
+ RrColorFree(p->interlace_color);
+ RrColorFree(p->bevel_dark);
+ RrColorFree(p->bevel_light);
+ RrColorFree(p->split_primary);
+ RrColorFree(p->split_secondary);
+ g_free(p->pixel_data);
+ p->pixel_data = NULL;
+ g_free(a);
+ }
+}
+
+static void pixel_data_to_pixmap(RrAppearance *l,
+ gint x, gint y, gint w, gint h)
+{
+ RrPixel32 *in, *scratch;
+ Pixmap out;
+ XImage *im = NULL;
+ im = XCreateImage(RrDisplay(l->inst), RrVisual(l->inst), RrDepth(l->inst),
+ ZPixmap, 0, NULL, w, h, 32, 0);
+ g_assert(im != NULL);
+
+ in = l->surface.pixel_data;
+ out = l->pixmap;
+
+/* this malloc is a complete waste of time on normal 32bpp
+ as reduce_depth just sets im->data = data and returns
+*/
+ scratch = g_new(RrPixel32, im->width * im->height);
+ im->data = (gchar*) scratch;
+ RrReduceDepth(l->inst, in, im);
+ XPutImage(RrDisplay(l->inst), out,
+ DefaultGC(RrDisplay(l->inst), RrScreen(l->inst)),
+ im, 0, 0, x, y, w, h);
+ im->data = NULL;
+ XDestroyImage(im);
+ g_free(scratch);
+}
+
+void RrMargins (RrAppearance *a, gint *l, gint *t, gint *r, gint *b)
+{
+ *l = *t = *r = *b = 0;
+
+ if (a->surface.grad != RR_SURFACE_PARENTREL) {
+ if (a->surface.relief != RR_RELIEF_FLAT) {
+ switch (a->surface.bevel) {
+ case RR_BEVEL_1:
+ *l = *t = *r = *b = 1;
+ break;
+ case RR_BEVEL_2:
+ *l = *t = *r = *b = 2;
+ break;
+ }
+ } else if (a->surface.border) {
+ *l = *t = *r = *b = 1;
+ }
+ }
+}
+
+void RrMinSize(RrAppearance *a, gint *w, gint *h)
+{
+ *w = RrMinWidth(a);
+ *h = RrMinHeight(a);
+}
+
+gint RrMinWidth(RrAppearance *a)
+{
+ gint i;
+ RrSize *m;
+ gint l, t, r, b;
+ gint w = 0;
+
+ RrMargins(a, &l, &t, &r, &b);
+
+ for (i = 0; i < a->textures; ++i) {
+ switch (a->texture[i].type) {
+ case RR_TEXTURE_NONE:
+ break;
+ case RR_TEXTURE_MASK:
+ w = MAX(w, a->texture[i].data.mask.mask->width);
+ break;
+ case RR_TEXTURE_TEXT:
+ m = RrFontMeasureString(a->texture[i].data.text.font,
+ a->texture[i].data.text.string,
+ a->texture[i].data.text.shadow_offset_x,
+ a->texture[i].data.text.shadow_offset_y,
+ a->texture[i].data.text.flow,
+ a->texture[i].data.text.maxwidth);
+ w = MAX(w, m->width);
+ g_free(m);
+ break;
+ case RR_TEXTURE_RGBA:
+ w += MAX(w, a->texture[i].data.rgba.width);
+ break;
+ case RR_TEXTURE_IMAGE:
+ /* images resize so they don't contribute anything to the min */
+ break;
+ case RR_TEXTURE_LINE_ART:
+ w = MAX(w, MAX(a->texture[i].data.lineart.x1 - l - r,
+ a->texture[i].data.lineart.x2 - l - r));
+ break;
+ }
+ }
+
+ w += l + r;
+
+ if (w < 1) w = 1;
+ return w;
+}
+
+gint RrMinHeight(RrAppearance *a)
+{
+ gint i;
+ gint l, t, r, b;
+ RrSize *m;
+ gint h = 0;
+
+ RrMargins(a, &l, &t, &r, &b);
+
+ for (i = 0; i < a->textures; ++i) {
+ switch (a->texture[i].type) {
+ case RR_TEXTURE_NONE:
+ break;
+ case RR_TEXTURE_MASK:
+ h = MAX(h, a->texture[i].data.mask.mask->height);
+ break;
+ case RR_TEXTURE_TEXT:
+ if (a->texture[i].data.text.flow) {
+ g_assert(a->texture[i].data.text.string != NULL);
+
+ m = RrFontMeasureString
+ (a->texture[i].data.text.font,
+ a->texture[i].data.text.string,
+ a->texture[i].data.text.shadow_offset_x,
+ a->texture[i].data.text.shadow_offset_y,
+ a->texture[i].data.text.flow,
+ a->texture[i].data.text.maxwidth);
+ h += MAX(h, m->height);
+ g_free(m);
+ }
+ else
+ h += MAX(h,
+ RrFontHeight
+ (a->texture[i].data.text.font,
+ a->texture[i].data.text.shadow_offset_y));
+ break;
+ case RR_TEXTURE_RGBA:
+ h += MAX(h, a->texture[i].data.rgba.height);
+ break;
+ case RR_TEXTURE_IMAGE:
+ /* images resize so they don't contribute anything to the min */
+ break;
+ case RR_TEXTURE_LINE_ART:
+ h = MAX(h, MAX(a->texture[i].data.lineart.y1 - t - b,
+ a->texture[i].data.lineart.y2 - t - b));
+ break;
+ }
+ }
+
+ h += t + b;
+
+ if (h < 1) h = 1;
+ return h;
+}
+
+static void reverse_bits(gchar *c, gint n)
+{
+ gint i;
+ for (i = 0; i < n; i++, c++)
+ *c = (((*c * 0x0802UL & 0x22110UL) |
+ (*c * 0x8020UL & 0x88440UL)) * 0x10101UL) >> 16;
+}
+
+gboolean RrPixmapToRGBA(const RrInstance *inst,
+ Pixmap pmap, Pixmap mask,
+ gint *w, gint *h, RrPixel32 **data)
+{
+ Window xr;
+ gint xx, xy;
+ guint pw, ph, mw, mh, xb, xd, i, x, y, di;
+ XImage *xi, *xm = NULL;
+
+ if (!XGetGeometry(RrDisplay(inst), pmap,
+ &xr, &xx, &xy, &pw, &ph, &xb, &xd))
+ return FALSE;
+
+ if (mask) {
+ if (!XGetGeometry(RrDisplay(inst), mask,
+ &xr, &xx, &xy, &mw, &mh, &xb, &xd))
+ return FALSE;
+ if (pw != mw || ph != mh || xd != 1)
+ return FALSE;
+ }
+
+ xi = XGetImage(RrDisplay(inst), pmap,
+ 0, 0, pw, ph, 0xffffffff, ZPixmap);
+ if (!xi)
+ return FALSE;
+
+ if (mask) {
+ xm = XGetImage(RrDisplay(inst), mask,
+ 0, 0, mw, mh, 0xffffffff, ZPixmap);
+ if (!xm) {
+ XDestroyImage(xi);
+ return FALSE;
+ }
+ if ((xm->bits_per_pixel == 1) && (xm->bitmap_bit_order != LSBFirst))
+ reverse_bits(xm->data, xm->bytes_per_line * xm->height);
+ }
+
+ if ((xi->bits_per_pixel == 1) && (xi->bitmap_bit_order != LSBFirst))
+ reverse_bits(xi->data, xi->bytes_per_line * xi->height);
+
+ *data = g_new(RrPixel32, pw * ph);
+ RrIncreaseDepth(inst, *data, xi);
+
+ if (mask) {
+ /* apply transparency from the mask */
+ di = 0;
+ for (i = 0, y = 0; y < ph; ++y) {
+ for (x = 0; x < pw; ++x, ++i) {
+ if (!((((unsigned)xm->data[di + x / 8]) >> (x % 8)) & 0x1))
+ (*data)[i] &= ~(0xff << RrDefaultAlphaOffset);
+ }
+ di += xm->bytes_per_line;
+ }
+ }
+
+ *w = pw;
+ *h = ph;
+
+ XDestroyImage(xi);
+ if (mask)
+ XDestroyImage(xm);
+
+ return TRUE;
+}
diff --git a/obrender/render.h b/obrender/render.h
new file mode 100644
index 00000000..7bea1b54
--- /dev/null
+++ b/obrender/render.h
@@ -0,0 +1,350 @@
+/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
+
+ render.h for the Openbox window manager
+ Copyright (c) 2006 Mikael Magnusson
+ Copyright (c) 2003-2007 Dana Jansens
+ Copyright (c) 2003 Derek Foreman
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ See the COPYING file for a copy of the GNU General Public License.
+*/
+
+#ifndef __render_h
+#define __render_h
+
+#include <X11/Xlib.h> /* some platforms dont include this as needed for Xft */
+#include <pango/pangoxft.h>
+#include <glib.h>
+
+G_BEGIN_DECLS
+
+#include "obrender/geom.h"
+#include "obrender/version.h"
+
+typedef union _RrTextureData RrTextureData;
+typedef struct _RrAppearance RrAppearance;
+typedef struct _RrSurface RrSurface;
+typedef struct _RrFont RrFont;
+typedef struct _RrTexture RrTexture;
+typedef struct _RrTextureMask RrTextureMask;
+typedef struct _RrTextureRGBA RrTextureRGBA;
+typedef struct _RrTextureImage RrTextureImage;
+typedef struct _RrTextureText RrTextureText;
+typedef struct _RrTextureLineArt RrTextureLineArt;
+typedef struct _RrPixmapMask RrPixmapMask;
+typedef struct _RrInstance RrInstance;
+typedef struct _RrColor RrColor;
+typedef struct _RrImage RrImage;
+typedef struct _RrImagePic RrImagePic;
+typedef struct _RrImageCache RrImageCache;
+
+typedef guint32 RrPixel32;
+typedef guint16 RrPixel16;
+typedef guchar RrPixel8;
+
+typedef enum {
+ RR_RELIEF_FLAT,
+ RR_RELIEF_RAISED,
+ RR_RELIEF_SUNKEN
+} RrReliefType;
+
+typedef enum {
+ RR_BEVEL_1,
+ RR_BEVEL_2
+} RrBevelType;
+
+typedef enum {
+ RR_SURFACE_NONE,
+ RR_SURFACE_PARENTREL,
+ RR_SURFACE_SOLID,
+ RR_SURFACE_SPLIT_VERTICAL,
+ RR_SURFACE_HORIZONTAL,
+ RR_SURFACE_VERTICAL,
+ RR_SURFACE_DIAGONAL,
+ RR_SURFACE_CROSS_DIAGONAL,
+ RR_SURFACE_PYRAMID,
+ RR_SURFACE_MIRROR_HORIZONTAL
+} RrSurfaceColorType;
+
+typedef enum {
+ RR_TEXTURE_NONE,
+ RR_TEXTURE_MASK,
+ RR_TEXTURE_TEXT,
+ RR_TEXTURE_LINE_ART,
+ RR_TEXTURE_RGBA,
+ RR_TEXTURE_IMAGE
+} RrTextureType;
+
+typedef enum {
+ RR_JUSTIFY_LEFT,
+ RR_JUSTIFY_CENTER,
+ RR_JUSTIFY_RIGHT
+} RrJustify;
+
+/* Put middle first so it's the default */
+typedef enum {
+ RR_ELLIPSIZE_MIDDLE,
+ RR_ELLIPSIZE_NONE,
+ RR_ELLIPSIZE_START,
+ RR_ELLIPSIZE_END
+} RrEllipsizeMode;
+
+typedef enum {
+ RR_FONTWEIGHT_LIGHT,
+ RR_FONTWEIGHT_NORMAL,
+ RR_FONTWEIGHT_SEMIBOLD,
+ RR_FONTWEIGHT_BOLD,
+ RR_FONTWEIGHT_ULTRABOLD
+} RrFontWeight;
+
+typedef enum {
+ RR_FONTSLANT_NORMAL,
+ RR_FONTSLANT_ITALIC,
+ RR_FONTSLANT_OBLIQUE
+} RrFontSlant;
+
+struct _RrSurface {
+ RrSurfaceColorType grad;
+ RrReliefType relief;
+ RrBevelType bevel;
+ RrColor *primary;
+ RrColor *secondary;
+ RrColor *border_color;
+ RrColor *bevel_dark;
+ RrColor *bevel_light;
+ RrColor *interlace_color;
+ gboolean interlaced;
+ gboolean border;
+ RrAppearance *parent;
+ gint parentx;
+ gint parenty;
+ RrPixel32 *pixel_data;
+ gint bevel_dark_adjust; /* 0-255, default is 64 */
+ gint bevel_light_adjust; /* 0-255, default is 128 */
+ RrColor *split_primary;
+ RrColor *split_secondary;
+};
+
+struct _RrTextureText {
+ RrFont *font;
+ RrJustify justify;
+ RrColor *color;
+ const gchar *string;
+ gint shadow_offset_x;
+ gint shadow_offset_y;
+ RrColor *shadow_color;
+ gboolean shortcut; /*!< Underline a character */
+ guint shortcut_pos; /*!< Position in bytes of the character to underline */
+ RrEllipsizeMode ellipsize;
+ gboolean flow; /* allow multiple lines. must set maxwidth below */
+ gint maxwidth;
+ guchar shadow_alpha; /* at the bottom to improve alignment */
+};
+
+struct _RrPixmapMask {
+ const RrInstance *inst;
+ Pixmap mask;
+ gint width;
+ gint height;
+ gchar *data;
+};
+
+struct _RrTextureMask {
+ RrColor *color;
+ RrPixmapMask *mask;
+};
+
+struct _RrTextureRGBA {
+ gint width;
+ gint height;
+ gint alpha;
+ RrPixel32 *data;
+ /* size and position to draw at (if these are zero, then it will be
+ drawn to fill the entire texture */
+ gint tx;
+ gint ty;
+ gint twidth;
+ gint theight;
+};
+
+struct _RrTextureImage {
+ RrImage *image;
+ gint alpha;
+ /* size and position to draw at (if these are zero, then it will be
+ drawn to fill the entire texture */
+ gint tx;
+ gint ty;
+ gint twidth;
+ gint theight;
+};
+
+struct _RrTextureLineArt {
+ RrColor *color;
+ gint x1;
+ gint y1;
+ gint x2;
+ gint y2;
+};
+
+union _RrTextureData {
+ RrTextureRGBA rgba;
+ RrTextureImage image;
+ RrTextureText text;
+ RrTextureMask mask;
+ RrTextureLineArt lineart;
+};
+
+struct _RrTexture {
+ /* If changing the type of a texture, you should DEFINITELY call
+ RrAppearanceClearTextures() first! */
+ RrTextureType type;
+ RrTextureData data;
+};
+
+struct _RrAppearance {
+ const RrInstance *inst;
+
+ RrSurface surface;
+ gint textures;
+ RrTexture *texture;
+ Pixmap pixmap;
+ XftDraw *xftdraw;
+
+ /* cached for internal use */
+ gint w, h;
+};
+
+/*! Holds a RGBA image picture */
+struct _RrImagePic {
+ gint width, height;
+ RrPixel32 *data;
+ /* The sum of all the pixels. This is used to compare pictures if their
+ hashes match. */
+ gint sum;
+};
+
+/*! An RrImage is a sort of meta-image. It can contain multiple versions of
+ an image at different sizes, which may or may not be completely different
+ pictures */
+struct _RrImage {
+ gint ref;
+ RrImageCache *cache;
+
+ /*! An array of "originals", that is of RrPictures that have been added
+ to the image in various sizes, and that have not been resized. These
+ are explicitly added to the RrImage. */
+ RrImagePic **original;
+ gint n_original;
+ /*! An array of "resized" pictures. When an "original" RrPicture
+ needs to be resized for drawing, it is saved in here so that it doesn't
+ need to be resized again. These are automatically added to the
+ RrImage. */
+ RrImagePic **resized;
+ gint n_resized;
+};
+
+/* these are the same on all endian machines because it seems to be dependant
+ on the endianness of the gfx card, not the cpu. */
+#define RrDefaultAlphaOffset 24
+#define RrDefaultRedOffset 16
+#define RrDefaultGreenOffset 8
+#define RrDefaultBlueOffset 0
+
+#define RrDefaultFontFamily "arial,sans"
+#define RrDefaultFontSize 8
+#define RrDefaultFontWeight RR_FONTWEIGHT_NORMAL
+#define RrDefaultFontSlant RR_FONTSLANT_NORMAL
+
+RrInstance* RrInstanceNew (Display *display, gint screen);
+void RrInstanceFree (RrInstance *inst);
+
+Display* RrDisplay (const RrInstance *inst);
+gint RrScreen (const RrInstance *inst);
+Window RrRootWindow (const RrInstance *inst);
+Visual* RrVisual (const RrInstance *inst);
+gint RrDepth (const RrInstance *inst);
+Colormap RrColormap (const RrInstance *inst);
+gint RrRedOffset (const RrInstance *inst);
+gint RrGreenOffset (const RrInstance *inst);
+gint RrBlueOffset (const RrInstance *inst);
+gint RrRedShift (const RrInstance *inst);
+gint RrGreenShift (const RrInstance *inst);
+gint RrBlueShift (const RrInstance *inst);
+gint RrRedMask (const RrInstance *inst);
+gint RrGreenMask (const RrInstance *inst);
+gint RrBlueMask (const RrInstance *inst);
+
+RrColor *RrColorNew (const RrInstance *inst, gint r, gint g, gint b);
+RrColor *RrColorParse (const RrInstance *inst, gchar *colorname);
+void RrColorFree (RrColor *in);
+
+gint RrColorRed (const RrColor *c);
+gint RrColorGreen (const RrColor *c);
+gint RrColorBlue (const RrColor *c);
+gulong RrColorPixel (const RrColor *c);
+GC RrColorGC (RrColor *c);
+
+RrAppearance *RrAppearanceNew (const RrInstance *inst, gint numtex);
+RrAppearance *RrAppearanceCopy (RrAppearance *a);
+void RrAppearanceFree (RrAppearance *a);
+void RrAppearanceRemoveTextures(RrAppearance *a);
+void RrAppearanceAddTextures(RrAppearance *a, gint numtex);
+/*! Always call this when changing the type of a texture in an appearance */
+void RrAppearanceClearTextures(RrAppearance *a);
+
+RrFont *RrFontOpen (const RrInstance *inst, const gchar *name,
+ gint size, RrFontWeight weight, RrFontSlant slant);
+RrFont *RrFontOpenDefault (const RrInstance *inst);
+void RrFontClose (RrFont *f);
+RrSize *RrFontMeasureString (const RrFont *f, const gchar *str,
+ gint shadow_offset_x, gint shadow_offset_y,
+ gboolean flow, gint maxwidth);
+gint RrFontHeight (const RrFont *f, gint shadow_offset_y);
+gint RrFontMaxCharWidth (const RrFont *f);
+
+/* Paint into the appearance. The old pixmap is returned (if there was one). It
+ is the responsibility of the caller to call XFreePixmap on the return when
+ it is non-null. */
+Pixmap RrPaintPixmap (RrAppearance *a, gint w, gint h);
+void RrPaint (RrAppearance *a, Window win, gint w, gint h);
+void RrMinSize (RrAppearance *a, gint *w, gint *h);
+gint RrMinWidth (RrAppearance *a);
+/* For text textures, if flow is TRUE, then the string must be set before
+ calling this, otherwise it doesn't need to be */
+gint RrMinHeight (RrAppearance *a);
+void RrMargins (RrAppearance *a, gint *l, gint *t, gint *r, gint *b);
+
+gboolean RrPixmapToRGBA(const RrInstance *inst,
+ Pixmap pmap, Pixmap mask,
+ gint *w, gint *h, RrPixel32 **data);
+
+/*! Create a new image cache for RrImages.
+ @param max_resized_saved The number of resized copies of an image to save
+*/
+RrImageCache* RrImageCacheNew(gint max_resized_saved);
+void RrImageCacheRef(RrImageCache *self);
+void RrImageCacheUnref(RrImageCache *self);
+
+/*! Finds an image in the cache, if it is already in there */
+RrImage* RrImageCacheFind(RrImageCache *self,
+ RrPixel32 *data, gint w, gint h);
+
+RrImage* RrImageNew(RrImageCache *cache);
+void RrImageRef(RrImage *im);
+void RrImageUnref(RrImage *im);
+
+void RrImageAddPicture(RrImage *im, RrPixel32 *data, gint w, gint h);
+void RrImageRemovePicture(RrImage *im, gint w, gint h);
+
+G_END_DECLS
+
+#endif /*__render_h*/
diff --git a/obrender/test.c b/obrender/test.c
new file mode 100644
index 00000000..55ab621e
--- /dev/null
+++ b/obrender/test.c
@@ -0,0 +1,115 @@
+/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
+
+ test.c for the Openbox window manager
+ Copyright (c) 2006 Mikael Magnusson
+ Copyright (c) 2003-2007 Dana Jansens
+ Copyright (c) 2003 Derek Foreman
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ See the COPYING file for a copy of the GNU General Public License.
+*/
+
+#include <stdio.h>
+#include <X11/Xlib.h>
+#include <X11/Xutil.h>
+#include <X11/extensions/shape.h>
+#include <string.h>
+#include <stdlib.h>
+#include "render.h"
+#include <glib.h>
+
+static gint x_error_handler(Display * disp, XErrorEvent * error)
+{
+ gchar buf[1024];
+ XGetErrorText(disp, error->error_code, buf, 1024);
+ printf("%s\n", buf);
+ return 0;
+}
+
+Display *ob_display;
+gint ob_screen;
+Window ob_root;
+
+gint main()
+{
+ Window win;
+ RrInstance *inst;
+ RrAppearance *look;
+ int done;
+
+ Window root;
+ XEvent report;
+ gint h = 500, w = 500;
+
+ ob_display = XOpenDisplay(NULL);
+ XSetErrorHandler(x_error_handler);
+ ob_screen = DefaultScreen(ob_display);
+ ob_root = RootWindow(ob_display, ob_screen);
+ win =
+ XCreateWindow(ob_display, RootWindow(ob_display, 0),
+ 10, 10, w, h, 10,
+ CopyFromParent, /* depth */
+ CopyFromParent, /* class */
+ CopyFromParent, /* visual */
+ 0, /* valuemask */
+ 0); /* attributes */
+ XMapWindow(ob_display, win);
+ XSelectInput(ob_display, win, ExposureMask | StructureNotifyMask);
+ root = RootWindow (ob_display, DefaultScreen (ob_display));
+ inst = RrInstanceNew(ob_display, ob_screen);
+
+ look = RrAppearanceNew(inst, 0);
+ look->surface.grad = RR_SURFACE_MIRROR_HORIZONTAL;
+ look->surface.secondary = RrColorParse(inst, "Yellow");
+ look->surface.split_secondary = RrColorParse(inst, "Red");
+ look->surface.split_primary = RrColorParse(inst, "Green");
+ look->surface.primary = RrColorParse(inst, "Blue");
+ look->surface.interlaced = FALSE;
+ if (ob_display == NULL) {
+ fprintf(stderr, "couldn't connect to X server :0\n");
+ return 0;
+ }
+
+#if BIGTEST
+ int i;
+ look->surface.pixel_data = g_new(RrPixel32, w*h);
+ for (i = 0; i < 10000; ++i) {
+ printf("\r%d", i);
+ fflush(stdout);
+ RrRender(look, w, h);
+ }
+ exit (0);
+#endif
+
+ RrPaint(look, win, w, h);
+ done = 0;
+ while (!done) {
+ XNextEvent(ob_display, &report);
+ switch (report.type) {
+ case Expose:
+ break;
+ case ConfigureNotify:
+ RrPaint(look, win,
+ report.xconfigure.width,
+ report.xconfigure.height);
+ break;
+ case UnmapNotify:
+ done = 1;
+ break;
+ }
+ }
+
+ RrAppearanceFree (look);
+ RrInstanceFree (inst);
+
+ return 1;
+}
diff --git a/obrender/theme.c b/obrender/theme.c
new file mode 100644
index 00000000..6c136bf5
--- /dev/null
+++ b/obrender/theme.c
@@ -0,0 +1,2007 @@
+/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
+
+ theme.c for the Openbox window manager
+ Copyright (c) 2006 Mikael Magnusson
+ Copyright (c) 2003-2007 Dana Jansens
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ See the COPYING file for a copy of the GNU General Public License.
+*/
+
+#include "render.h"
+#include "color.h"
+#include "font.h"
+#include "mask.h"
+#include "theme.h"
+#include "icon.h"
+#include "obt/paths.h"
+
+#include <X11/Xlib.h>
+#include <X11/Xresource.h>
+#include <ctype.h>
+#include <stdlib.h>
+#include <string.h>
+
+static XrmDatabase loaddb(const gchar *name, gchar **path);
+static gboolean read_int(XrmDatabase db, const gchar *rname, gint *value);
+static gboolean read_string(XrmDatabase db, const gchar *rname, gchar **value);
+static gboolean read_color(XrmDatabase db, const RrInstance *inst,
+ const gchar *rname, RrColor **value);
+static gboolean read_mask(const RrInstance *inst, const gchar *path,
+ RrTheme *theme, const gchar *maskname,
+ RrPixmapMask **value);
+static gboolean read_appearance(XrmDatabase db, const RrInstance *inst,
+ const gchar *rname, RrAppearance *value,
+ gboolean allow_trans);
+static int parse_inline_number(const char *p);
+static RrPixel32* read_c_image(gint width, gint height, const guint8 *data);
+static void set_default_appearance(RrAppearance *a);
+
+static RrFont *get_font(RrFont *target, RrFont **default_font,
+ const RrInstance *inst)
+{
+ if (target) {
+ RrFontRef(target);
+ return target;
+ } else {
+ /* Only load the default font once */
+ if (*default_font) {
+ RrFontRef(*default_font);
+ } else {
+ *default_font = RrFontOpenDefault(inst);
+ }
+ return *default_font;
+ }
+}
+
+RrTheme* RrThemeNew(const RrInstance *inst, const gchar *name,
+ gboolean allow_fallback,
+ RrFont *active_window_font, RrFont *inactive_window_font,
+ RrFont *menu_title_font, RrFont *menu_item_font,
+ RrFont *active_osd_font, RrFont *inactive_osd_font)
+{
+ XrmDatabase db = NULL;
+ RrJustify winjust, mtitlejust;
+ gchar *str;
+ RrTheme *theme;
+ RrFont *default_font = NULL;
+ gchar *path;
+ gboolean userdef;
+
+ if (name) {
+ db = loaddb(name, &path);
+ if (db == NULL) {
+ g_message("Unable to load the theme '%s'", name);
+ if (allow_fallback)
+ g_message("Falling back to the default theme '%s'",
+ DEFAULT_THEME);
+ /* fallback to the default theme */
+ name = NULL;
+ }
+ }
+ if (name == NULL) {
+ if (allow_fallback) {
+ db = loaddb(DEFAULT_THEME, &path);
+ if (db == NULL) {
+ g_message("Unable to load the theme '%s'", DEFAULT_THEME);
+ return NULL;
+ }
+ } else
+ return NULL;
+ }
+
+ theme = g_new0(RrTheme, 1);
+
+ theme->inst = inst;
+ theme->name = g_strdup(name ? name : DEFAULT_THEME);
+
+ theme->a_disabled_focused_max = RrAppearanceNew(inst, 1);
+ theme->a_disabled_unfocused_max = RrAppearanceNew(inst, 1);
+ theme->a_hover_focused_max = RrAppearanceNew(inst, 1);
+ theme->a_hover_unfocused_max = RrAppearanceNew(inst, 1);
+ theme->a_toggled_focused_unpressed_max = RrAppearanceNew(inst, 1);
+ theme->a_toggled_unfocused_unpressed_max = RrAppearanceNew(inst, 1);
+ theme->a_toggled_hover_focused_max = RrAppearanceNew(inst, 1);
+ theme->a_toggled_hover_unfocused_max = RrAppearanceNew(inst, 1);
+ theme->a_toggled_focused_pressed_max = RrAppearanceNew(inst, 1);
+ theme->a_toggled_unfocused_pressed_max = RrAppearanceNew(inst, 1);
+ theme->a_focused_unpressed_max = RrAppearanceNew(inst, 1);
+ theme->a_focused_pressed_max = RrAppearanceNew(inst, 1);
+ theme->a_unfocused_unpressed_max = RrAppearanceNew(inst, 1);
+ theme->a_unfocused_pressed_max = RrAppearanceNew(inst, 1);
+ theme->a_focused_grip = RrAppearanceNew(inst, 0);
+ theme->a_unfocused_grip = RrAppearanceNew(inst, 0);
+ theme->a_focused_title = RrAppearanceNew(inst, 0);
+ theme->a_unfocused_title = RrAppearanceNew(inst, 0);
+ theme->a_focused_label = RrAppearanceNew(inst, 1);
+ theme->a_unfocused_label = RrAppearanceNew(inst, 1);
+ theme->a_icon = RrAppearanceNew(inst, 1);
+ theme->a_focused_handle = RrAppearanceNew(inst, 0);
+ theme->a_unfocused_handle = RrAppearanceNew(inst, 0);
+ theme->a_menu = RrAppearanceNew(inst, 0);
+ theme->a_menu_title = RrAppearanceNew(inst, 0);
+ theme->a_menu_text_title = RrAppearanceNew(inst, 1);
+ theme->a_menu_normal = RrAppearanceNew(inst, 0);
+ theme->a_menu_selected = RrAppearanceNew(inst, 0);
+ theme->a_menu_disabled = RrAppearanceNew(inst, 0);
+ /* a_menu_disabled_selected is copied from a_menu_selected */
+ theme->a_menu_text_normal = RrAppearanceNew(inst, 1);
+ theme->a_menu_text_selected = RrAppearanceNew(inst, 1);
+ theme->a_menu_text_disabled = RrAppearanceNew(inst, 1);
+ theme->a_menu_text_disabled_selected = RrAppearanceNew(inst, 1);
+ theme->a_menu_bullet_normal = RrAppearanceNew(inst, 1);
+ theme->a_menu_bullet_selected = RrAppearanceNew(inst, 1);
+ theme->a_clear = RrAppearanceNew(inst, 0);
+ theme->a_clear_tex = RrAppearanceNew(inst, 1);
+ theme->osd_bg = RrAppearanceNew(inst, 0);
+ theme->osd_hilite_label = RrAppearanceNew(inst, 1);
+ theme->osd_hilite_bg = RrAppearanceNew(inst, 0);
+ theme->osd_unhilite_label = RrAppearanceNew(inst, 1);
+ theme->osd_unhilite_bg = RrAppearanceNew(inst, 0);
+
+ /* load the font stuff */
+ theme->win_font_focused = get_font(active_window_font,
+ &default_font, inst);
+ theme->win_font_unfocused = get_font(inactive_window_font,
+ &default_font, inst);
+
+ winjust = RR_JUSTIFY_LEFT;
+ if (read_string(db, "window.label.text.justify", &str)) {
+ if (!g_ascii_strcasecmp(str, "right"))
+ winjust = RR_JUSTIFY_RIGHT;
+ else if (!g_ascii_strcasecmp(str, "center"))
+ winjust = RR_JUSTIFY_CENTER;
+ }
+
+ theme->menu_title_font = get_font(menu_title_font, &default_font, inst);
+
+ mtitlejust = RR_JUSTIFY_LEFT;
+ if (read_string(db, "menu.title.text.justify", &str)) {
+ if (!g_ascii_strcasecmp(str, "right"))
+ mtitlejust = RR_JUSTIFY_RIGHT;
+ else if (!g_ascii_strcasecmp(str, "center"))
+ mtitlejust = RR_JUSTIFY_CENTER;
+ }
+
+ theme->menu_font = get_font(menu_item_font, &default_font, inst);
+
+ theme->osd_font_hilite = get_font(active_osd_font, &default_font, inst);
+ theme->osd_font_unhilite = get_font(inactive_osd_font, &default_font,inst);
+
+ /* load direct dimensions */
+ if ((!read_int(db, "menu.overlap.x", &theme->menu_overlap_x) &&
+ !read_int(db, "menu.overlap", &theme->menu_overlap_x)) ||
+ theme->menu_overlap_x < -100 || theme->menu_overlap_x > 100)
+ theme->menu_overlap_x = 0;
+ if ((!read_int(db, "menu.overlap.y", &theme->menu_overlap_y) &&
+ !read_int(db, "menu.overlap", &theme->menu_overlap_y)) ||
+ theme->menu_overlap_y < -100 || theme->menu_overlap_y > 100)
+ theme->menu_overlap_y = 0;
+ if (!read_int(db, "window.handle.width", &theme->handle_height) ||
+ theme->handle_height < 0 || theme->handle_height > 100)
+ theme->handle_height = 6;
+ if (!read_int(db, "padding.width", &theme->paddingx) ||
+ theme->paddingx < 0 || theme->paddingx > 100)
+ theme->paddingx = 3;
+ if (!read_int(db, "padding.height", &theme->paddingy) ||
+ theme->paddingy < 0 || theme->paddingy > 100)
+ theme->paddingy = theme->paddingx;
+ if (!read_int(db, "border.width", &theme->fbwidth) ||
+ theme->fbwidth < 0 || theme->fbwidth > 100)
+ theme->fbwidth = 1;
+ /* menu border width inherits from the frame border width */
+ if (!read_int(db, "menu.border.width", &theme->mbwidth) ||
+ theme->mbwidth < 0 || theme->mbwidth > 100)
+ theme->mbwidth = theme->fbwidth;
+ /* osd border width inherits from the frame border width */
+ if (!read_int(db, "osd.border.width", &theme->obwidth) ||
+ theme->obwidth < 0 || theme->obwidth > 100)
+ theme->obwidth = theme->fbwidth;
+ if (!read_int(db, "window.client.padding.width", &theme->cbwidthx) ||
+ theme->cbwidthx < 0 || theme->cbwidthx > 100)
+ theme->cbwidthx = theme->paddingx;
+ if (!read_int(db, "window.client.padding.height", &theme->cbwidthy) ||
+ theme->cbwidthy < 0 || theme->cbwidthy > 100)
+ theme->cbwidthy = theme->cbwidthx;
+ if (!read_int(db, "menu.separator.width", &theme->menu_sep_width) ||
+ theme->menu_sep_width < 1 || theme->menu_sep_width > 100)
+ theme->menu_sep_width = 1;
+ if (!read_int(db, "menu.separator.padding.width",
+ &theme->menu_sep_paddingx) ||
+ theme->menu_sep_paddingx < 0 || theme->menu_sep_paddingx > 100)
+ theme->menu_sep_paddingx = 6;
+ if (!read_int(db, "menu.separator.padding.height",
+ &theme->menu_sep_paddingy) ||
+ theme->menu_sep_paddingy < 0 || theme->menu_sep_paddingy > 100)
+ theme->menu_sep_paddingy = 3;
+
+ /* load colors */
+ if (!read_color(db, inst,
+ "window.active.border.color",
+ &theme->frame_focused_border_color) &&
+ !read_color(db, inst,
+ "border.color",
+ &theme->frame_focused_border_color))
+ theme->frame_focused_border_color = RrColorNew(inst, 0, 0, 0);
+ /* title separator focused color inherits from focused border color */
+ if (!read_color(db, inst,
+ "window.active.title.separator.color",
+ &theme->title_separator_focused_color))
+ theme->title_separator_focused_color =
+ RrColorNew(inst,
+ theme->frame_focused_border_color->r,
+ theme->frame_focused_border_color->g,
+ theme->frame_focused_border_color->b);
+ /* unfocused border color inherits from frame focused border color */
+ if (!read_color(db, inst,
+ "window.inactive.border.color",
+ &theme->frame_unfocused_border_color))
+ theme->frame_unfocused_border_color =
+ RrColorNew(inst, theme->frame_focused_border_color->r,
+ theme->frame_focused_border_color->g,
+ theme->frame_focused_border_color->b);
+ /* title separator unfocused color inherits from unfocused border color */
+ if (!read_color(db, inst,
+ "window.inactive.title.separator.color",
+ &theme->title_separator_unfocused_color))
+ theme->title_separator_unfocused_color =
+ RrColorNew(inst,
+ theme->frame_unfocused_border_color->r,
+ theme->frame_unfocused_border_color->g,
+ theme->frame_unfocused_border_color->b);
+
+ /* menu border color inherits from frame focused border color */
+ if (!read_color(db, inst, "menu.border.color", &theme->menu_border_color))
+ theme->menu_border_color =
+ RrColorNew(inst,
+ theme->frame_focused_border_color->r,
+ theme->frame_focused_border_color->g,
+ theme->frame_focused_border_color->b);
+ /* osd border color inherits from frame focused border color */
+ if (!read_color(db, inst, "osd.border.color", &theme->osd_border_color))
+ theme->osd_border_color =
+ RrColorNew(inst,
+ theme->frame_focused_border_color->r,
+ theme->frame_focused_border_color->g,
+ theme->frame_focused_border_color->b);
+ if (!read_color(db, inst,
+ "window.active.client.color",
+ &theme->cb_focused_color))
+ theme->cb_focused_color = RrColorNew(inst, 0xff, 0xff, 0xff);
+ if (!read_color(db, inst,
+ "window.inactive.client.color",
+ &theme->cb_unfocused_color))
+ theme->cb_unfocused_color = RrColorNew(inst, 0xff, 0xff, 0xff);
+ if (!read_color(db, inst,
+ "window.active.label.text.color",
+ &theme->title_focused_color))
+ theme->title_focused_color = RrColorNew(inst, 0x0, 0x0, 0x0);
+ if (!read_color(db, inst, "osd.active.label.text.color",
+ &theme->osd_text_active_color) &&
+ !read_color(db, inst, "osd.label.text.color",
+ &theme->osd_text_active_color))
+ theme->osd_text_active_color =
+ RrColorNew(inst,
+ theme->title_focused_color->r,
+ theme->title_focused_color->g,
+ theme->title_focused_color->b);
+ if (!read_color(db, inst,
+ "window.inactive.label.text.color",
+ &theme->title_unfocused_color))
+ theme->title_unfocused_color = RrColorNew(inst, 0xff, 0xff, 0xff);
+ if (!read_color(db, inst, "osd.inactive.label.text.color",
+ &theme->osd_text_inactive_color))
+ theme->osd_text_inactive_color =
+ RrColorNew(inst,
+ theme->title_unfocused_color->r,
+ theme->title_unfocused_color->g,
+ theme->title_unfocused_color->b);
+ if (!read_color(db, inst,
+ "window.active.button.unpressed.image.color",
+ &theme->titlebut_focused_unpressed_color))
+ theme->titlebut_focused_unpressed_color = RrColorNew(inst, 0, 0, 0);
+ if (!read_color(db, inst,
+ "window.inactive.button.unpressed.image.color",
+ &theme->titlebut_unfocused_unpressed_color))
+ theme->titlebut_unfocused_unpressed_color =
+ RrColorNew(inst, 0xff, 0xff, 0xff);
+ if (!read_color(db, inst,
+ "window.active.button.pressed.image.color",
+ &theme->titlebut_focused_pressed_color))
+ theme->titlebut_focused_pressed_color =
+ RrColorNew(inst,
+ theme->titlebut_focused_unpressed_color->r,
+ theme->titlebut_focused_unpressed_color->g,
+ theme->titlebut_focused_unpressed_color->b);
+ if (!read_color(db, inst,
+ "window.inactive.button.pressed.image.color",
+ &theme->titlebut_unfocused_pressed_color))
+ theme->titlebut_unfocused_pressed_color =
+ RrColorNew(inst,
+ theme->titlebut_unfocused_unpressed_color->r,
+ theme->titlebut_unfocused_unpressed_color->g,
+ theme->titlebut_unfocused_unpressed_color->b);
+ if (!read_color(db, inst,
+ "window.active.button.disabled.image.color",
+ &theme->titlebut_disabled_focused_color))
+ theme->titlebut_disabled_focused_color =
+ RrColorNew(inst, 0xff, 0xff, 0xff);
+ if (!read_color(db, inst,
+ "window.inactive.button.disabled.image.color",
+ &theme->titlebut_disabled_unfocused_color))
+ theme->titlebut_disabled_unfocused_color = RrColorNew(inst, 0, 0, 0);
+ if (!read_color(db, inst,
+ "window.active.button.hover.image.color",
+ &theme->titlebut_hover_focused_color))
+ theme->titlebut_hover_focused_color =
+ RrColorNew(inst,
+ theme->titlebut_focused_unpressed_color->r,
+ theme->titlebut_focused_unpressed_color->g,
+ theme->titlebut_focused_unpressed_color->b);
+ if (!read_color(db, inst,
+ "window.inactive.button.hover.image.color",
+ &theme->titlebut_hover_unfocused_color))
+ theme->titlebut_hover_unfocused_color =
+ RrColorNew(inst,
+ theme->titlebut_unfocused_unpressed_color->r,
+ theme->titlebut_unfocused_unpressed_color->g,
+ theme->titlebut_unfocused_unpressed_color->b);
+ if (!read_color(db, inst,
+ "window.active.button.toggled.unpressed.image.color",
+ &theme->titlebut_toggled_focused_unpressed_color) &&
+ !read_color(db, inst,
+ "window.active.button.toggled.image.color",
+ &theme->titlebut_toggled_focused_unpressed_color))
+ theme->titlebut_toggled_focused_unpressed_color =
+ RrColorNew(inst,
+ theme->titlebut_focused_pressed_color->r,
+ theme->titlebut_focused_pressed_color->g,
+ theme->titlebut_focused_pressed_color->b);
+ if (!read_color(db, inst,
+ "window.inactive.button.toggled.unpressed.image.color",
+ &theme->titlebut_toggled_unfocused_unpressed_color) &&
+ !read_color(db, inst,
+ "window.inactive.button.toggled.image.color",
+ &theme->titlebut_toggled_unfocused_unpressed_color))
+ theme->titlebut_toggled_unfocused_unpressed_color =
+ RrColorNew(inst,
+ theme->titlebut_unfocused_pressed_color->r,
+ theme->titlebut_unfocused_pressed_color->g,
+ theme->titlebut_unfocused_pressed_color->b);
+ if (!read_color(db, inst,
+ "window.active.button.toggled.hover.image.color",
+ &theme->titlebut_toggled_hover_focused_color))
+ theme->titlebut_toggled_hover_focused_color =
+ RrColorNew(inst,
+ theme->titlebut_toggled_focused_unpressed_color->r,
+ theme->titlebut_toggled_focused_unpressed_color->g,
+ theme->titlebut_toggled_focused_unpressed_color->b);
+ if (!read_color(db, inst,
+ "window.inactive.button.toggled.hover.image.color",
+ &theme->titlebut_toggled_hover_unfocused_color))
+ theme->titlebut_toggled_hover_unfocused_color =
+ RrColorNew(inst,
+ theme->titlebut_toggled_unfocused_unpressed_color->r,
+ theme->titlebut_toggled_unfocused_unpressed_color->g,
+ theme->titlebut_toggled_unfocused_unpressed_color->b);
+ if (!read_color(db, inst,
+ "window.active.button.toggled.pressed.image.color",
+ &theme->titlebut_toggled_focused_pressed_color))
+ theme->titlebut_toggled_focused_pressed_color =
+ RrColorNew(inst,
+ theme->titlebut_focused_pressed_color->r,
+ theme->titlebut_focused_pressed_color->g,
+ theme->titlebut_focused_pressed_color->b);
+ if (!read_color(db, inst,
+ "window.inactive.button.toggled.pressed.image.color",
+ &theme->titlebut_toggled_unfocused_pressed_color))
+ theme->titlebut_toggled_unfocused_pressed_color =
+ RrColorNew(inst,
+ theme->titlebut_unfocused_pressed_color->r,
+ theme->titlebut_unfocused_pressed_color->g,
+ theme->titlebut_unfocused_pressed_color->b);
+ if (!read_color(db, inst,
+ "menu.title.text.color", &theme->menu_title_color))
+ theme->menu_title_color = RrColorNew(inst, 0, 0, 0);
+ if (!read_color(db, inst,
+ "menu.items.text.color", &theme->menu_color))
+ theme->menu_color = RrColorNew(inst, 0xff, 0xff, 0xff);
+ if (!read_color(db, inst,
+ "menu.items.disabled.text.color",
+ &theme->menu_disabled_color))
+ theme->menu_disabled_color = RrColorNew(inst, 0, 0, 0);
+ if (!read_color(db, inst,
+ "menu.items.active.disabled.text.color",
+ &theme->menu_disabled_selected_color))
+ theme->menu_disabled_selected_color =
+ RrColorNew(inst,
+ theme->menu_disabled_color->r,
+ theme->menu_disabled_color->g,
+ theme->menu_disabled_color->b);
+ if (!read_color(db, inst,
+ "menu.items.active.text.color",
+ &theme->menu_selected_color))
+ theme->menu_selected_color = RrColorNew(inst, 0, 0, 0);
+ if (!read_color(db, inst,
+ "menu.separator.color", &theme->menu_sep_color))
+ theme->menu_sep_color = RrColorNew(inst,
+ theme->menu_color->r,
+ theme->menu_color->g,
+ theme->menu_color->b);
+
+ /* load the image masks */
+
+ /* maximize button masks */
+ userdef = TRUE;
+ if (!read_mask(inst, path, theme, "max.xbm", &theme->max_mask)) {
+ guchar data[] = { 0x3f, 0x3f, 0x21, 0x21, 0x21, 0x3f };
+ theme->max_mask = RrPixmapMaskNew(inst, 6, 6, (gchar*)data);
+ userdef = FALSE;
+ }
+ if (!read_mask(inst, path, theme, "max_toggled.xbm",
+ &theme->max_toggled_mask))
+ {
+ if (userdef)
+ theme->max_toggled_mask = RrPixmapMaskCopy(theme->max_mask);
+ else {
+ guchar data[] = { 0x3e, 0x22, 0x2f, 0x29, 0x39, 0x0f };
+ theme->max_toggled_mask = RrPixmapMaskNew(inst, 6, 6,(gchar*)data);
+ }
+ }
+ if (!read_mask(inst, path, theme, "max_pressed.xbm",
+ &theme->max_pressed_mask))
+ theme->max_pressed_mask = RrPixmapMaskCopy(theme->max_mask);
+ if (!read_mask(inst,path,theme,"max_disabled.xbm",
+ &theme->max_disabled_mask))
+ theme->max_disabled_mask = RrPixmapMaskCopy(theme->max_mask);
+ if (!read_mask(inst, path, theme, "max_hover.xbm", &theme->max_hover_mask))
+ theme->max_hover_mask = RrPixmapMaskCopy(theme->max_mask);
+ if (!read_mask(inst, path, theme, "max_toggled_pressed.xbm",
+ &theme->max_toggled_pressed_mask))
+ theme->max_toggled_pressed_mask =
+ RrPixmapMaskCopy(theme->max_toggled_mask);
+ if (!read_mask(inst, path, theme, "max_toggled_hover.xbm",
+ &theme->max_toggled_hover_mask))
+ theme->max_toggled_hover_mask =
+ RrPixmapMaskCopy(theme->max_toggled_mask);
+
+ /* iconify button masks */
+ if (!read_mask(inst, path, theme, "iconify.xbm", &theme->iconify_mask)) {
+ guchar data[] = { 0x00, 0x00, 0x00, 0x00, 0x3f, 0x3f };
+ theme->iconify_mask = RrPixmapMaskNew(inst, 6, 6, (gchar*)data);
+ }
+ if (!read_mask(inst, path, theme, "iconify_pressed.xbm",
+ &theme->iconify_pressed_mask))
+ theme->iconify_pressed_mask = RrPixmapMaskCopy(theme->iconify_mask);
+ if (!read_mask(inst, path, theme, "iconify_disabled.xbm",
+ &theme->iconify_disabled_mask))
+ theme->iconify_disabled_mask = RrPixmapMaskCopy(theme->iconify_mask);
+ if (!read_mask(inst, path, theme, "iconify_hover.xbm",
+ &theme->iconify_hover_mask))
+ theme->iconify_hover_mask = RrPixmapMaskCopy(theme->iconify_mask);
+
+ /* all desktops button masks */
+ userdef = TRUE;
+ if (!read_mask(inst, path, theme, "desk.xbm", &theme->desk_mask)) {
+ guchar data[] = { 0x33, 0x33, 0x00, 0x00, 0x33, 0x33 };
+ theme->desk_mask = RrPixmapMaskNew(inst, 6, 6, (gchar*)data);
+ userdef = FALSE;
+ }
+ if (!read_mask(inst, path, theme, "desk_toggled.xbm",
+ &theme->desk_toggled_mask)) {
+ if (userdef)
+ theme->desk_toggled_mask = RrPixmapMaskCopy(theme->desk_mask);
+ else {
+ guchar data[] = { 0x00, 0x1e, 0x1a, 0x16, 0x1e, 0x00 };
+ theme->desk_toggled_mask =
+ RrPixmapMaskNew(inst, 6, 6, (gchar*)data);
+ }
+ }
+ if (!read_mask(inst, path, theme, "desk_pressed.xbm",
+ &theme->desk_pressed_mask))
+ theme->desk_pressed_mask = RrPixmapMaskCopy(theme->desk_mask);
+ if (!read_mask(inst, path, theme, "desk_disabled.xbm",
+ &theme->desk_disabled_mask))
+ theme->desk_disabled_mask = RrPixmapMaskCopy(theme->desk_mask);
+ if (!read_mask(inst, path, theme, "desk_hover.xbm",
+ &theme->desk_hover_mask))
+ theme->desk_hover_mask = RrPixmapMaskCopy(theme->desk_mask);
+ if (!read_mask(inst, path, theme, "desk_toggled_pressed.xbm",
+ &theme->desk_toggled_pressed_mask))
+ theme->desk_toggled_pressed_mask =
+ RrPixmapMaskCopy(theme->desk_toggled_mask);
+ if (!read_mask(inst, path, theme, "desk_toggled_hover.xbm",
+ &theme->desk_toggled_hover_mask))
+ theme->desk_toggled_hover_mask =
+ RrPixmapMaskCopy(theme->desk_toggled_mask);
+
+ /* shade button masks */
+ if (!read_mask(inst, path, theme, "shade.xbm", &theme->shade_mask)) {
+ guchar data[] = { 0x3f, 0x3f, 0x00, 0x00, 0x00, 0x00 };
+ theme->shade_mask = RrPixmapMaskNew(inst, 6, 6, (gchar*)data);
+ }
+ if (!read_mask(inst, path, theme, "shade_toggled.xbm",
+ &theme->shade_toggled_mask))
+ theme->shade_toggled_mask = RrPixmapMaskCopy(theme->shade_mask);
+ if (!read_mask(inst, path, theme, "shade_pressed.xbm",
+ &theme->shade_pressed_mask))
+ theme->shade_pressed_mask = RrPixmapMaskCopy(theme->shade_mask);
+ if (!read_mask(inst, path, theme, "shade_disabled.xbm",
+ &theme->shade_disabled_mask))
+ theme->shade_disabled_mask = RrPixmapMaskCopy(theme->shade_mask);
+ if (!read_mask(inst, path, theme, "shade_hover.xbm",
+ &theme->shade_hover_mask))
+ theme->shade_hover_mask = RrPixmapMaskCopy(theme->shade_mask);
+ if (!read_mask(inst, path, theme, "shade_toggled_pressed.xbm",
+ &theme->shade_toggled_pressed_mask))
+ theme->shade_toggled_pressed_mask =
+ RrPixmapMaskCopy(theme->shade_toggled_mask);
+ if (!read_mask(inst, path, theme, "shade_toggled_hover.xbm",
+ &theme->shade_toggled_hover_mask))
+ theme->shade_toggled_hover_mask =
+ RrPixmapMaskCopy(theme->shade_toggled_mask);
+
+ /* close button masks */
+ if (!read_mask(inst, path, theme, "close.xbm", &theme->close_mask)) {
+ guchar data[] = { 0x33, 0x3f, 0x1e, 0x1e, 0x3f, 0x33 };
+ theme->close_mask = RrPixmapMaskNew(inst, 6, 6, (gchar*)data);
+ }
+ if (!read_mask(inst, path, theme, "close_pressed.xbm",
+ &theme->close_pressed_mask))
+ theme->close_pressed_mask = RrPixmapMaskCopy(theme->close_mask);
+ if (!read_mask(inst, path, theme, "close_disabled.xbm",
+ &theme->close_disabled_mask))
+ theme->close_disabled_mask = RrPixmapMaskCopy(theme->close_mask);
+ if (!read_mask(inst, path, theme, "close_hover.xbm",
+ &theme->close_hover_mask))
+ theme->close_hover_mask = RrPixmapMaskCopy(theme->close_mask);
+
+ /* submenu bullet mask */
+ if (!read_mask(inst, path, theme, "bullet.xbm", &theme->menu_bullet_mask))
+ {
+ guchar data[] = { 0x01, 0x03, 0x07, 0x0f, 0x07, 0x03, 0x01 };
+ theme->menu_bullet_mask = RrPixmapMaskNew(inst, 4, 7, (gchar*)data);
+ }
+
+ /* up and down arrows */
+ {
+ guchar data[] = { 0xfe, 0x00, 0x7c, 0x00, 0x38, 0x00, 0x10, 0x00 };
+ theme->down_arrow_mask = RrPixmapMaskNew(inst, 9, 4, (gchar*)data);
+ }
+ {
+ guchar data[] = { 0x10, 0x00, 0x38, 0x00, 0x7c, 0x00, 0xfe, 0x00 };
+ theme->up_arrow_mask = RrPixmapMaskNew(inst, 9, 4, (gchar*)data);
+ }
+
+ /* setup the default window icon */
+ theme->def_win_icon = read_c_image(OB_DEFAULT_ICON_WIDTH,
+ OB_DEFAULT_ICON_HEIGHT,
+ OB_DEFAULT_ICON_pixel_data);
+ theme->def_win_icon_w = OB_DEFAULT_ICON_WIDTH;
+ theme->def_win_icon_h = OB_DEFAULT_ICON_HEIGHT;
+
+ /* read the decoration textures */
+ if (!read_appearance(db, inst,
+ "window.active.title.bg", theme->a_focused_title,
+ FALSE))
+ set_default_appearance(theme->a_focused_title);
+ if (!read_appearance(db, inst,
+ "window.inactive.title.bg", theme->a_unfocused_title,
+ FALSE))
+ set_default_appearance(theme->a_unfocused_title);
+ if (!read_appearance(db, inst,
+ "window.active.label.bg", theme->a_focused_label,
+ TRUE))
+ set_default_appearance(theme->a_focused_label);
+ if (!read_appearance(db, inst,
+ "window.inactive.label.bg", theme->a_unfocused_label,
+ TRUE))
+ set_default_appearance(theme->a_unfocused_label);
+ if (!read_appearance(db, inst,
+ "window.active.handle.bg", theme->a_focused_handle,
+ FALSE))
+ set_default_appearance(theme->a_focused_handle);
+ if (!read_appearance(db, inst,
+ "window.inactive.handle.bg",theme->a_unfocused_handle,
+ FALSE))
+ set_default_appearance(theme->a_unfocused_handle);
+ if (!read_appearance(db, inst,
+ "window.active.grip.bg", theme->a_focused_grip,
+ TRUE))
+ set_default_appearance(theme->a_focused_grip);
+ if (!read_appearance(db, inst,
+ "window.inactive.grip.bg", theme->a_unfocused_grip,
+ TRUE))
+ set_default_appearance(theme->a_unfocused_grip);
+ if (!read_appearance(db, inst,
+ "menu.items.bg", theme->a_menu,
+ FALSE))
+ set_default_appearance(theme->a_menu);
+ if (!read_appearance(db, inst,
+ "menu.title.bg", theme->a_menu_title,
+ TRUE))
+ set_default_appearance(theme->a_menu_title);
+ if (!read_appearance(db, inst,
+ "menu.items.active.bg", theme->a_menu_selected,
+ TRUE))
+ set_default_appearance(theme->a_menu_selected);
+ theme->a_menu_disabled_selected =
+ RrAppearanceCopy(theme->a_menu_selected);
+
+ /* read appearances for non-decorations (on-screen-display) */
+ if (!read_appearance(db, inst, "osd.bg", theme->osd_bg, FALSE)) {
+ RrAppearanceFree(theme->osd_bg);
+ theme->osd_bg = RrAppearanceCopy(theme->a_focused_title);
+ }
+ if (!read_appearance(db, inst, "osd.active.label.bg",
+ theme->osd_hilite_label, TRUE) &&
+ !read_appearance(db, inst, "osd.label.bg",
+ theme->osd_hilite_label, TRUE)) {
+ RrAppearanceFree(theme->osd_hilite_label);
+ theme->osd_hilite_label = RrAppearanceCopy(theme->a_focused_label);
+ }
+ if (!read_appearance(db, inst, "osd.inactive.label.bg",
+ theme->osd_unhilite_label, TRUE)) {
+ RrAppearanceFree(theme->osd_unhilite_label);
+ theme->osd_unhilite_label = RrAppearanceCopy(theme->a_unfocused_label);
+ }
+ /* osd_hilite_fg can't be parentrel */
+ if (!read_appearance(db, inst, "osd.hilight.bg",
+ theme->osd_hilite_bg, FALSE)) {
+ RrAppearanceFree(theme->osd_hilite_bg);
+ if (theme->a_focused_label->surface.grad != RR_SURFACE_PARENTREL)
+ theme->osd_hilite_bg = RrAppearanceCopy(theme->a_focused_label);
+ else
+ theme->osd_hilite_bg = RrAppearanceCopy(theme->a_focused_title);
+ }
+ /* osd_unhilite_fg can't be parentrel either */
+ if (!read_appearance(db, inst, "osd.unhilight.bg",
+ theme->osd_unhilite_bg, FALSE)) {
+ RrAppearanceFree(theme->osd_unhilite_bg);
+ if (theme->a_unfocused_label->surface.grad != RR_SURFACE_PARENTREL)
+ theme->osd_unhilite_bg=RrAppearanceCopy(theme->a_unfocused_label);
+ else
+ theme->osd_unhilite_bg=RrAppearanceCopy(theme->a_unfocused_title);
+ }
+
+ /* read buttons textures */
+ if (!read_appearance(db, inst,
+ "window.active.button.disabled.bg",
+ theme->a_disabled_focused_max,
+ TRUE))
+ set_default_appearance(theme->a_disabled_focused_max);
+ if (!read_appearance(db, inst,
+ "window.inactive.button.disabled.bg",
+ theme->a_disabled_unfocused_max,
+ TRUE))
+ set_default_appearance(theme->a_disabled_unfocused_max);
+ if (!read_appearance(db, inst,
+ "window.active.button.pressed.bg",
+ theme->a_focused_pressed_max,
+ TRUE))
+ set_default_appearance(theme->a_focused_pressed_max);
+ if (!read_appearance(db, inst,
+ "window.inactive.button.pressed.bg",
+ theme->a_unfocused_pressed_max,
+ TRUE))
+ set_default_appearance(theme->a_unfocused_pressed_max);
+ if (!read_appearance(db, inst,
+ "window.active.button.toggled.unpressed.bg",
+ theme->a_toggled_focused_unpressed_max,
+ TRUE) &&
+ !read_appearance(db, inst,
+ "window.active.button.toggled.bg",
+ theme->a_toggled_focused_unpressed_max,
+ TRUE))
+ {
+ RrAppearanceFree(theme->a_toggled_focused_unpressed_max);
+ theme->a_toggled_focused_unpressed_max =
+ RrAppearanceCopy(theme->a_focused_pressed_max);
+ }
+ if (!read_appearance(db, inst,
+ "window.inactive.button.toggled.unpressed.bg",
+ theme->a_toggled_unfocused_unpressed_max,
+ TRUE) &&
+ !read_appearance(db, inst,
+ "window.inactive.button.toggled.bg",
+ theme->a_toggled_unfocused_unpressed_max,
+ TRUE))
+ {
+ RrAppearanceFree(theme->a_toggled_unfocused_unpressed_max);
+ theme->a_toggled_unfocused_unpressed_max =
+ RrAppearanceCopy(theme->a_unfocused_pressed_max);
+ }
+ if (!read_appearance(db, inst,
+ "window.active.button.toggled.hover.bg",
+ theme->a_toggled_hover_focused_max,
+ TRUE))
+ {
+ RrAppearanceFree(theme->a_toggled_hover_focused_max);
+ theme->a_toggled_hover_focused_max =
+ RrAppearanceCopy(theme->a_toggled_focused_unpressed_max);
+ }
+ if (!read_appearance(db, inst,
+ "window.inactive.button.toggled.hover.bg",
+ theme->a_toggled_hover_unfocused_max,
+ TRUE))
+ {
+ RrAppearanceFree(theme->a_toggled_hover_unfocused_max);
+ theme->a_toggled_hover_unfocused_max =
+ RrAppearanceCopy(theme->a_toggled_unfocused_unpressed_max);
+ }
+ if (!read_appearance(db, inst,
+ "window.active.button.toggled.pressed.bg",
+ theme->a_toggled_focused_pressed_max,
+ TRUE))
+ {
+ RrAppearanceFree(theme->a_toggled_focused_pressed_max);
+ theme->a_toggled_focused_pressed_max =
+ RrAppearanceCopy(theme->a_focused_pressed_max);
+ }
+ if (!read_appearance(db, inst,
+ "window.inactive.button.toggled.pressed.bg",
+ theme->a_toggled_unfocused_pressed_max,
+ TRUE))
+ {
+ RrAppearanceFree(theme->a_toggled_unfocused_pressed_max);
+ theme->a_toggled_unfocused_pressed_max =
+ RrAppearanceCopy(theme->a_unfocused_pressed_max);
+ }
+ if (!read_appearance(db, inst,
+ "window.active.button.unpressed.bg",
+ theme->a_focused_unpressed_max,
+ TRUE))
+ set_default_appearance(theme->a_focused_unpressed_max);
+ if (!read_appearance(db, inst,
+ "window.inactive.button.unpressed.bg",
+ theme->a_unfocused_unpressed_max,
+ TRUE))
+ set_default_appearance(theme->a_unfocused_unpressed_max);
+ if (!read_appearance(db, inst,
+ "window.active.button.hover.bg",
+ theme->a_hover_focused_max,
+ TRUE))
+ {
+ RrAppearanceFree(theme->a_hover_focused_max);
+ theme->a_hover_focused_max =
+ RrAppearanceCopy(theme->a_focused_unpressed_max);
+ }
+ if (!read_appearance(db, inst,
+ "window.inactive.button.hover.bg",
+ theme->a_hover_unfocused_max,
+ TRUE))
+ {
+ RrAppearanceFree(theme->a_hover_unfocused_max);
+ theme->a_hover_unfocused_max =
+ RrAppearanceCopy(theme->a_unfocused_unpressed_max);
+ }
+
+ theme->a_disabled_focused_close =
+ RrAppearanceCopy(theme->a_disabled_focused_max);
+ theme->a_disabled_unfocused_close =
+ RrAppearanceCopy(theme->a_disabled_unfocused_max);
+ theme->a_hover_focused_close =
+ RrAppearanceCopy(theme->a_hover_focused_max);
+ theme->a_hover_unfocused_close =
+ RrAppearanceCopy(theme->a_hover_unfocused_max);
+ theme->a_unfocused_unpressed_close =
+ RrAppearanceCopy(theme->a_unfocused_unpressed_max);
+ theme->a_unfocused_pressed_close =
+ RrAppearanceCopy(theme->a_unfocused_pressed_max);
+ theme->a_focused_unpressed_close =
+ RrAppearanceCopy(theme->a_focused_unpressed_max);
+ theme->a_focused_pressed_close =
+ RrAppearanceCopy(theme->a_focused_pressed_max);
+ theme->a_disabled_focused_desk =
+ RrAppearanceCopy(theme->a_disabled_focused_max);
+ theme->a_disabled_unfocused_desk =
+ RrAppearanceCopy(theme->a_disabled_unfocused_max);
+ theme->a_hover_focused_desk =
+ RrAppearanceCopy(theme->a_hover_focused_max);
+ theme->a_hover_unfocused_desk =
+ RrAppearanceCopy(theme->a_hover_unfocused_max);
+ theme->a_toggled_hover_focused_desk =
+ RrAppearanceCopy(theme->a_toggled_hover_focused_max);
+ theme->a_toggled_hover_unfocused_desk =
+ RrAppearanceCopy(theme->a_toggled_hover_unfocused_max);
+ theme->a_toggled_focused_unpressed_desk =
+ RrAppearanceCopy(theme->a_toggled_focused_unpressed_max);
+ theme->a_toggled_unfocused_unpressed_desk =
+ RrAppearanceCopy(theme->a_toggled_unfocused_unpressed_max);
+ theme->a_toggled_focused_pressed_desk =
+ RrAppearanceCopy(theme->a_toggled_focused_pressed_max);
+ theme->a_toggled_unfocused_pressed_desk =
+ RrAppearanceCopy(theme->a_toggled_unfocused_pressed_max);
+ theme->a_unfocused_unpressed_desk =
+ RrAppearanceCopy(theme->a_unfocused_unpressed_max);
+ theme->a_unfocused_pressed_desk =
+ RrAppearanceCopy(theme->a_unfocused_pressed_max);
+ theme->a_focused_unpressed_desk =
+ RrAppearanceCopy(theme->a_focused_unpressed_max);
+ theme->a_focused_pressed_desk =
+ RrAppearanceCopy(theme->a_focused_pressed_max);
+ theme->a_disabled_focused_shade =
+ RrAppearanceCopy(theme->a_disabled_focused_max);
+ theme->a_disabled_unfocused_shade =
+ RrAppearanceCopy(theme->a_disabled_unfocused_max);
+ theme->a_hover_focused_shade =
+ RrAppearanceCopy(theme->a_hover_focused_max);
+ theme->a_hover_unfocused_shade =
+ RrAppearanceCopy(theme->a_hover_unfocused_max);
+ theme->a_toggled_hover_focused_shade =
+ RrAppearanceCopy(theme->a_toggled_hover_focused_max);
+ theme->a_toggled_hover_unfocused_shade =
+ RrAppearanceCopy(theme->a_toggled_hover_unfocused_max);
+ theme->a_toggled_focused_unpressed_shade =
+ RrAppearanceCopy(theme->a_toggled_focused_unpressed_max);
+ theme->a_toggled_unfocused_unpressed_shade =
+ RrAppearanceCopy(theme->a_toggled_unfocused_unpressed_max);
+ theme->a_toggled_focused_pressed_shade =
+ RrAppearanceCopy(theme->a_toggled_focused_pressed_max);
+ theme->a_toggled_unfocused_pressed_shade =
+ RrAppearanceCopy(theme->a_toggled_unfocused_pressed_max);
+ theme->a_unfocused_unpressed_shade =
+ RrAppearanceCopy(theme->a_unfocused_unpressed_max);
+ theme->a_unfocused_pressed_shade =
+ RrAppearanceCopy(theme->a_unfocused_pressed_max);
+ theme->a_focused_unpressed_shade =
+ RrAppearanceCopy(theme->a_focused_unpressed_max);
+ theme->a_focused_pressed_shade =
+ RrAppearanceCopy(theme->a_focused_pressed_max);
+ theme->a_disabled_focused_iconify =
+ RrAppearanceCopy(theme->a_disabled_focused_max);
+ theme->a_disabled_unfocused_iconify =
+ RrAppearanceCopy(theme->a_disabled_focused_max);
+ theme->a_hover_focused_iconify =
+ RrAppearanceCopy(theme->a_hover_focused_max);
+ theme->a_hover_unfocused_iconify =
+ RrAppearanceCopy(theme->a_hover_unfocused_max);
+ theme->a_unfocused_unpressed_iconify =
+ RrAppearanceCopy(theme->a_unfocused_unpressed_max);
+ theme->a_unfocused_pressed_iconify =
+ RrAppearanceCopy(theme->a_unfocused_pressed_max);
+ theme->a_focused_unpressed_iconify =
+ RrAppearanceCopy(theme->a_focused_unpressed_max);
+ theme->a_focused_pressed_iconify =
+ RrAppearanceCopy(theme->a_focused_pressed_max);
+
+ theme->a_icon->surface.grad =
+ theme->a_clear->surface.grad =
+ theme->a_clear_tex->surface.grad =
+ theme->a_menu_text_title->surface.grad =
+ theme->a_menu_normal->surface.grad =
+ theme->a_menu_disabled->surface.grad =
+ theme->a_menu_text_normal->surface.grad =
+ theme->a_menu_text_selected->surface.grad =
+ theme->a_menu_text_disabled->surface.grad =
+ theme->a_menu_text_disabled_selected->surface.grad =
+ theme->a_menu_bullet_normal->surface.grad =
+ theme->a_menu_bullet_selected->surface.grad = RR_SURFACE_PARENTREL;
+
+ /* set up the textures */
+ theme->a_focused_label->texture[0].type = RR_TEXTURE_TEXT;
+ theme->a_focused_label->texture[0].data.text.justify = winjust;
+ theme->a_focused_label->texture[0].data.text.font=theme->win_font_focused;
+ theme->a_focused_label->texture[0].data.text.color =
+ theme->title_focused_color;
+
+ if (read_string(db, "window.active.label.text.font", &str)) {
+ char *p;
+ gint i = 0;
+ gint j;
+ if (strstr(str, "shadow=y")) {
+ if ((p = strstr(str, "shadowoffset=")))
+ i = parse_inline_number(p + strlen("shadowoffset="));
+ else
+ i = 1;
+ theme->a_focused_label->texture[0].data.text.shadow_offset_x = i;
+ theme->a_focused_label->texture[0].data.text.shadow_offset_y = i;
+ }
+ if ((p = strstr(str, "shadowtint=")))
+ {
+ i = parse_inline_number(p + strlen("shadowtint="));
+ j = (i > 0 ? 0 : 255);
+ i = ABS(i*255/100);
+
+ theme->title_focused_shadow_color = RrColorNew(inst, j, j, j);
+ theme->title_focused_shadow_alpha = i;
+ } else {
+ theme->title_focused_shadow_color = RrColorNew(inst, 0, 0, 0);
+ theme->title_focused_shadow_alpha = 50;
+ }
+ }
+
+ theme->a_focused_label->texture[0].data.text.shadow_color =
+ theme->title_focused_shadow_color;
+ theme->a_focused_label->texture[0].data.text.shadow_alpha =
+ theme->title_focused_shadow_alpha;
+
+ theme->osd_hilite_label->texture[0].type = RR_TEXTURE_TEXT;
+ theme->osd_hilite_label->texture[0].data.text.justify = RR_JUSTIFY_LEFT;
+ theme->osd_hilite_label->texture[0].data.text.font =
+ theme->osd_font_hilite;
+ theme->osd_hilite_label->texture[0].data.text.color =
+ theme->osd_text_active_color;
+
+ theme->osd_unhilite_label->texture[0].type = RR_TEXTURE_TEXT;
+ theme->osd_unhilite_label->texture[0].data.text.justify = RR_JUSTIFY_LEFT;
+ theme->osd_unhilite_label->texture[0].data.text.font =
+ theme->osd_font_unhilite;
+ theme->osd_unhilite_label->texture[0].data.text.color =
+ theme->osd_text_inactive_color;
+
+ if (read_string(db, "osd.active.label.text.font", &str) ||
+ read_string(db, "osd.label.text.font", &str))
+ {
+ char *p;
+ gint i = 0;
+ gint j;
+ if (strstr(str, "shadow=y")) {
+ if ((p = strstr(str, "shadowoffset=")))
+ i = parse_inline_number(p + strlen("shadowoffset="));
+ else
+ i = 1;
+ theme->osd_hilite_label->texture[0].data.text.shadow_offset_x = i;
+ theme->osd_hilite_label->texture[0].data.text.shadow_offset_y = i;
+ }
+ if ((p = strstr(str, "shadowtint=")))
+ {
+ i = parse_inline_number(p + strlen("shadowtint="));
+ j = (i > 0 ? 0 : 255);
+ i = ABS(i*255/100);
+
+ theme->osd_text_active_shadow_color = RrColorNew(inst, j, j, j);
+ theme->osd_text_active_shadow_alpha = i;
+ } else {
+ theme->osd_text_active_shadow_color = RrColorNew(inst, 0, 0, 0);
+ theme->osd_text_active_shadow_alpha = 50;
+ }
+ } else {
+ /* inherit the font settings from the focused label */
+ theme->osd_hilite_label->texture[0].data.text.shadow_offset_x =
+ theme->a_focused_label->texture[0].data.text.shadow_offset_x;
+ theme->osd_hilite_label->texture[0].data.text.shadow_offset_y =
+ theme->a_focused_label->texture[0].data.text.shadow_offset_y;
+ if (theme->title_focused_shadow_color)
+ theme->osd_text_active_shadow_color =
+ RrColorNew(inst,
+ theme->title_focused_shadow_color->r,
+ theme->title_focused_shadow_color->g,
+ theme->title_focused_shadow_color->b);
+ else
+ theme->osd_text_active_shadow_color = RrColorNew(inst, 0, 0, 0);
+ theme->osd_text_active_shadow_alpha =
+ theme->title_focused_shadow_alpha;
+ }
+
+ theme->osd_hilite_label->texture[0].data.text.shadow_color =
+ theme->osd_text_active_shadow_color;
+ theme->osd_hilite_label->texture[0].data.text.shadow_alpha =
+ theme->osd_text_active_shadow_alpha;
+
+ if (read_string(db, "osd.inactive.label.text.font", &str))
+ {
+ char *p;
+ gint i = 0;
+ gint j;
+ if (strstr(str, "shadow=y")) {
+ if ((p = strstr(str, "shadowoffset=")))
+ i = parse_inline_number(p + strlen("shadowoffset="));
+ else
+ i = 1;
+ theme->osd_unhilite_label->texture[0].data.text.shadow_offset_x=i;
+ theme->osd_unhilite_label->texture[0].data.text.shadow_offset_y=i;
+ }
+ if ((p = strstr(str, "shadowtint=")))
+ {
+ i = parse_inline_number(p + strlen("shadowtint="));
+ j = (i > 0 ? 0 : 255);
+ i = ABS(i*255/100);
+
+ theme->osd_text_inactive_shadow_color = RrColorNew(inst, j, j, j);
+ theme->osd_text_inactive_shadow_alpha = i;
+ } else {
+ theme->osd_text_inactive_shadow_color = RrColorNew(inst, 0, 0, 0);
+ theme->osd_text_inactive_shadow_alpha = 50;
+ }
+ } else {
+ /* inherit the font settings from the focused label */
+ theme->osd_unhilite_label->texture[0].data.text.shadow_offset_x =
+ theme->a_unfocused_label->texture[0].data.text.shadow_offset_x;
+ theme->osd_unhilite_label->texture[0].data.text.shadow_offset_y =
+ theme->a_unfocused_label->texture[0].data.text.shadow_offset_y;
+ if (theme->title_unfocused_shadow_color)
+ theme->osd_text_inactive_shadow_color =
+ RrColorNew(inst,
+ theme->title_unfocused_shadow_color->r,
+ theme->title_unfocused_shadow_color->g,
+ theme->title_unfocused_shadow_color->b);
+ else
+ theme->osd_text_inactive_shadow_color = RrColorNew(inst, 0, 0, 0);
+ theme->osd_text_inactive_shadow_alpha =
+ theme->title_unfocused_shadow_alpha;
+ }
+
+ theme->osd_unhilite_label->texture[0].data.text.shadow_color =
+ theme->osd_text_inactive_shadow_color;
+ theme->osd_unhilite_label->texture[0].data.text.shadow_alpha =
+ theme->osd_text_inactive_shadow_alpha;
+
+ theme->a_unfocused_label->texture[0].type = RR_TEXTURE_TEXT;
+ theme->a_unfocused_label->texture[0].data.text.justify = winjust;
+ theme->a_unfocused_label->texture[0].data.text.font =
+ theme->win_font_unfocused;
+ theme->a_unfocused_label->texture[0].data.text.color =
+ theme->title_unfocused_color;
+
+ if (read_string(db, "window.inactive.label.text.font", &str)) {
+ char *p;
+ gint i = 0;
+ gint j;
+ if (strstr(str, "shadow=y")) {
+ if ((p = strstr(str, "shadowoffset=")))
+ i = parse_inline_number(p + strlen("shadowoffset="));
+ else
+ i = 1;
+ theme->a_unfocused_label->texture[0].data.text.shadow_offset_x = i;
+ theme->a_unfocused_label->texture[0].data.text.shadow_offset_y = i;
+ }
+ if ((p = strstr(str, "shadowtint=")))
+ {
+ i = parse_inline_number(p + strlen("shadowtint="));
+ j = (i > 0 ? 0 : 255);
+ i = ABS(i*255/100);
+
+ theme->title_unfocused_shadow_color = RrColorNew(inst, j, j, j);
+ theme->title_unfocused_shadow_alpha = i;
+ } else {
+ theme->title_unfocused_shadow_color = RrColorNew(inst, 0, 0, 0);
+ theme->title_unfocused_shadow_alpha = 50;
+ }
+ }
+
+ theme->a_unfocused_label->texture[0].data.text.shadow_color =
+ theme->title_unfocused_shadow_color;
+ theme->a_unfocused_label->texture[0].data.text.shadow_alpha =
+ theme->title_unfocused_shadow_alpha;
+
+ theme->a_menu_text_title->texture[0].type = RR_TEXTURE_TEXT;
+ theme->a_menu_text_title->texture[0].data.text.justify = mtitlejust;
+ theme->a_menu_text_title->texture[0].data.text.font =
+ theme->menu_title_font;
+ theme->a_menu_text_title->texture[0].data.text.color =
+ theme->menu_title_color;
+
+ if (read_string(db, "menu.title.text.font", &str)) {
+ char *p;
+ gint i = 0;
+ gint j;
+ if (strstr(str, "shadow=y")) {
+ if ((p = strstr(str, "shadowoffset=")))
+ i = parse_inline_number(p + strlen("shadowoffset="));
+ else
+ i = 1;
+ theme->a_menu_text_title->texture[0].data.text.shadow_offset_x = i;
+ theme->a_menu_text_title->texture[0].data.text.shadow_offset_y = i;
+ }
+ if ((p = strstr(str, "shadowtint=")))
+ {
+ i = parse_inline_number(p + strlen("shadowtint="));
+ j = (i > 0 ? 0 : 255);
+ i = ABS(i*255/100);
+
+ theme->menu_title_shadow_color = RrColorNew(inst, j, j, j);
+ theme->menu_title_shadow_alpha = i;
+ } else {
+ theme->menu_title_shadow_color = RrColorNew(inst, 0, 0, 0);
+ theme->menu_title_shadow_alpha = 50;
+ }
+ }
+
+ theme->a_menu_text_title->texture[0].data.text.shadow_color =
+ theme->menu_title_shadow_color;
+ theme->a_menu_text_title->texture[0].data.text.shadow_alpha =
+ theme->menu_title_shadow_alpha;
+
+ theme->a_menu_text_normal->texture[0].type =
+ theme->a_menu_text_selected->texture[0].type =
+ theme->a_menu_text_disabled->texture[0].type =
+ theme->a_menu_text_disabled_selected->texture[0].type =
+ RR_TEXTURE_TEXT;
+ theme->a_menu_text_normal->texture[0].data.text.justify =
+ theme->a_menu_text_selected->texture[0].data.text.justify =
+ theme->a_menu_text_disabled->texture[0].data.text.justify =
+ theme->a_menu_text_disabled_selected->texture[0].data.text.justify =
+ RR_JUSTIFY_LEFT;
+ theme->a_menu_text_normal->texture[0].data.text.font =
+ theme->a_menu_text_selected->texture[0].data.text.font =
+ theme->a_menu_text_disabled->texture[0].data.text.font =
+ theme->a_menu_text_disabled_selected->texture[0].data.text.font =
+ theme->menu_font;
+ theme->a_menu_text_normal->texture[0].data.text.color = theme->menu_color;
+ theme->a_menu_text_selected->texture[0].data.text.color =
+ theme->menu_selected_color;
+ theme->a_menu_text_disabled->texture[0].data.text.color =
+ theme->menu_disabled_color;
+ theme->a_menu_text_disabled_selected->texture[0].data.text.color =
+ theme->menu_disabled_selected_color;
+
+ if (read_string(db, "menu.items.font", &str)) {
+ char *p;
+ gint i = 0;
+ gint j;
+ if (strstr(str, "shadow=y")) {
+ if ((p = strstr(str, "shadowoffset=")))
+ i = parse_inline_number(p + strlen("shadowoffset="));
+ else
+ i = 1;
+ theme->a_menu_text_normal->
+ texture[0].data.text.shadow_offset_x = i;
+ theme->a_menu_text_normal->
+ texture[0].data.text.shadow_offset_y = i;
+ theme->a_menu_text_selected->
+ texture[0].data.text.shadow_offset_x = i;
+ theme->a_menu_text_selected->
+ texture[0].data.text.shadow_offset_y = i;
+ theme->a_menu_text_disabled->
+ texture[0].data.text.shadow_offset_x = i;
+ theme->a_menu_text_disabled->
+ texture[0].data.text.shadow_offset_y = i;
+ theme->a_menu_text_disabled_selected->
+ texture[0].data.text.shadow_offset_x = i;
+ theme->a_menu_text_disabled_selected->
+ texture[0].data.text.shadow_offset_y = i;
+ }
+ if ((p = strstr(str, "shadowtint=")))
+ {
+ i = parse_inline_number(p + strlen("shadowtint="));
+ j = (i > 0 ? 0 : 255);
+ i = ABS(i*255/100);
+
+ theme->menu_text_normal_shadow_color = RrColorNew(inst, j, j, j);
+ theme->menu_text_selected_shadow_color = RrColorNew(inst, j, j, j);
+ theme->menu_text_disabled_shadow_color = RrColorNew(inst, j, j, j);
+ theme->menu_text_normal_shadow_alpha = i;
+ theme->menu_text_selected_shadow_alpha = i;
+ theme->menu_text_disabled_shadow_alpha = i;
+ theme->menu_text_disabled_selected_shadow_alpha = i;
+ } else {
+ theme->menu_text_normal_shadow_color = RrColorNew(inst, 0, 0, 0);
+ theme->menu_text_selected_shadow_color = RrColorNew(inst, 0, 0, 0);
+ theme->menu_text_disabled_shadow_color = RrColorNew(inst, 0, 0, 0);
+ theme->menu_text_normal_shadow_alpha = 50;
+ theme->menu_text_selected_shadow_alpha = 50;
+ theme->menu_text_disabled_selected_shadow_alpha = 50;
+ }
+ }
+
+ theme->a_menu_text_normal->texture[0].data.text.shadow_color =
+ theme->menu_text_normal_shadow_color;
+ theme->a_menu_text_normal->texture[0].data.text.shadow_alpha =
+ theme->menu_text_normal_shadow_alpha;
+ theme->a_menu_text_selected->texture[0].data.text.shadow_color =
+ theme->menu_text_selected_shadow_color;
+ theme->a_menu_text_selected->texture[0].data.text.shadow_alpha =
+ theme->menu_text_selected_shadow_alpha;
+ theme->a_menu_text_disabled->texture[0].data.text.shadow_color =
+ theme->menu_text_disabled_shadow_color;
+ theme->a_menu_text_disabled->texture[0].data.text.shadow_alpha =
+ theme->menu_text_disabled_shadow_alpha;
+ theme->a_menu_text_disabled_selected->texture[0].data.text.shadow_color =
+ theme->menu_text_disabled_shadow_color;
+ theme->a_menu_text_disabled_selected->texture[0].data.text.shadow_alpha =
+ theme->menu_text_disabled_shadow_alpha;
+
+ theme->a_disabled_focused_max->texture[0].type =
+ theme->a_disabled_unfocused_max->texture[0].type =
+ theme->a_hover_focused_max->texture[0].type =
+ theme->a_hover_unfocused_max->texture[0].type =
+ theme->a_toggled_hover_focused_max->texture[0].type =
+ theme->a_toggled_hover_unfocused_max->texture[0].type =
+ theme->a_toggled_focused_unpressed_max->texture[0].type =
+ theme->a_toggled_unfocused_unpressed_max->texture[0].type =
+ theme->a_toggled_focused_pressed_max->texture[0].type =
+ theme->a_toggled_unfocused_pressed_max->texture[0].type =
+ theme->a_focused_unpressed_max->texture[0].type =
+ theme->a_focused_pressed_max->texture[0].type =
+ theme->a_unfocused_unpressed_max->texture[0].type =
+ theme->a_unfocused_pressed_max->texture[0].type =
+ theme->a_disabled_focused_close->texture[0].type =
+ theme->a_disabled_unfocused_close->texture[0].type =
+ theme->a_hover_focused_close->texture[0].type =
+ theme->a_hover_unfocused_close->texture[0].type =
+ theme->a_focused_unpressed_close->texture[0].type =
+ theme->a_focused_pressed_close->texture[0].type =
+ theme->a_unfocused_unpressed_close->texture[0].type =
+ theme->a_unfocused_pressed_close->texture[0].type =
+ theme->a_disabled_focused_desk->texture[0].type =
+ theme->a_disabled_unfocused_desk->texture[0].type =
+ theme->a_hover_focused_desk->texture[0].type =
+ theme->a_hover_unfocused_desk->texture[0].type =
+ theme->a_toggled_hover_focused_desk->texture[0].type =
+ theme->a_toggled_hover_unfocused_desk->texture[0].type =
+ theme->a_toggled_focused_unpressed_desk->texture[0].type =
+ theme->a_toggled_unfocused_unpressed_desk->texture[0].type =
+ theme->a_toggled_focused_pressed_desk->texture[0].type =
+ theme->a_toggled_unfocused_pressed_desk->texture[0].type =
+ theme->a_focused_unpressed_desk->texture[0].type =
+ theme->a_focused_pressed_desk->texture[0].type =
+ theme->a_unfocused_unpressed_desk->texture[0].type =
+ theme->a_unfocused_pressed_desk->texture[0].type =
+ theme->a_disabled_focused_shade->texture[0].type =
+ theme->a_disabled_unfocused_shade->texture[0].type =
+ theme->a_hover_focused_shade->texture[0].type =
+ theme->a_hover_unfocused_shade->texture[0].type =
+ theme->a_toggled_hover_focused_shade->texture[0].type =
+ theme->a_toggled_hover_unfocused_shade->texture[0].type =
+ theme->a_toggled_focused_unpressed_shade->texture[0].type =
+ theme->a_toggled_unfocused_unpressed_shade->texture[0].type =
+ theme->a_toggled_focused_pressed_shade->texture[0].type =
+ theme->a_toggled_unfocused_pressed_shade->texture[0].type =
+ theme->a_focused_unpressed_shade->texture[0].type =
+ theme->a_focused_pressed_shade->texture[0].type =
+ theme->a_unfocused_unpressed_shade->texture[0].type =
+ theme->a_unfocused_pressed_shade->texture[0].type =
+ theme->a_disabled_focused_iconify->texture[0].type =
+ theme->a_disabled_unfocused_iconify->texture[0].type =
+ theme->a_hover_focused_iconify->texture[0].type =
+ theme->a_hover_unfocused_iconify->texture[0].type =
+ theme->a_focused_unpressed_iconify->texture[0].type =
+ theme->a_focused_pressed_iconify->texture[0].type =
+ theme->a_unfocused_unpressed_iconify->texture[0].type =
+ theme->a_unfocused_pressed_iconify->texture[0].type =
+ theme->a_menu_bullet_normal->texture[0].type =
+ theme->a_menu_bullet_selected->texture[0].type = RR_TEXTURE_MASK;
+
+ theme->a_disabled_focused_max->texture[0].data.mask.mask =
+ theme->a_disabled_unfocused_max->texture[0].data.mask.mask =
+ theme->max_disabled_mask;
+ theme->a_hover_focused_max->texture[0].data.mask.mask =
+ theme->a_hover_unfocused_max->texture[0].data.mask.mask =
+ theme->max_hover_mask;
+ theme->a_focused_pressed_max->texture[0].data.mask.mask =
+ theme->a_unfocused_pressed_max->texture[0].data.mask.mask =
+ theme->max_pressed_mask;
+ theme->a_focused_unpressed_max->texture[0].data.mask.mask =
+ theme->a_unfocused_unpressed_max->texture[0].data.mask.mask =
+ theme->max_mask;
+ theme->a_toggled_hover_focused_max->texture[0].data.mask.mask =
+ theme->a_toggled_hover_unfocused_max->texture[0].data.mask.mask =
+ theme->max_toggled_hover_mask;
+ theme->a_toggled_focused_unpressed_max->texture[0].data.mask.mask =
+ theme->a_toggled_unfocused_unpressed_max->texture[0].data.mask.mask =
+ theme->max_toggled_mask;
+ theme->a_toggled_focused_pressed_max->texture[0].data.mask.mask =
+ theme->a_toggled_unfocused_pressed_max->texture[0].data.mask.mask =
+ theme->max_toggled_pressed_mask;
+ theme->a_disabled_focused_close->texture[0].data.mask.mask =
+ theme->a_disabled_unfocused_close->texture[0].data.mask.mask =
+ theme->close_disabled_mask;
+ theme->a_hover_focused_close->texture[0].data.mask.mask =
+ theme->a_hover_unfocused_close->texture[0].data.mask.mask =
+ theme->close_hover_mask;
+ theme->a_focused_pressed_close->texture[0].data.mask.mask =
+ theme->a_unfocused_pressed_close->texture[0].data.mask.mask =
+ theme->close_pressed_mask;
+ theme->a_focused_unpressed_close->texture[0].data.mask.mask =
+ theme->a_unfocused_unpressed_close->texture[0].data.mask.mask =
+ theme->close_mask;
+ theme->a_disabled_focused_desk->texture[0].data.mask.mask =
+ theme->a_disabled_unfocused_desk->texture[0].data.mask.mask =
+ theme->desk_disabled_mask;
+ theme->a_hover_focused_desk->texture[0].data.mask.mask =
+ theme->a_hover_unfocused_desk->texture[0].data.mask.mask =
+ theme->desk_hover_mask;
+ theme->a_focused_pressed_desk->texture[0].data.mask.mask =
+ theme->a_unfocused_pressed_desk->texture[0].data.mask.mask =
+ theme->desk_pressed_mask;
+ theme->a_focused_unpressed_desk->texture[0].data.mask.mask =
+ theme->a_unfocused_unpressed_desk->texture[0].data.mask.mask =
+ theme->desk_mask;
+ theme->a_toggled_hover_focused_desk->texture[0].data.mask.mask =
+ theme->a_toggled_hover_unfocused_desk->texture[0].data.mask.mask =
+ theme->desk_toggled_hover_mask;
+ theme->a_toggled_focused_unpressed_desk->texture[0].data.mask.mask =
+ theme->a_toggled_unfocused_unpressed_desk->texture[0].data.mask.mask =
+ theme->desk_toggled_mask;
+ theme->a_toggled_focused_pressed_desk->texture[0].data.mask.mask =
+ theme->a_toggled_unfocused_pressed_desk->texture[0].data.mask.mask =
+ theme->desk_toggled_pressed_mask;
+ theme->a_disabled_focused_shade->texture[0].data.mask.mask =
+ theme->a_disabled_unfocused_shade->texture[0].data.mask.mask =
+ theme->shade_disabled_mask;
+ theme->a_hover_focused_shade->texture[0].data.mask.mask =
+ theme->a_hover_unfocused_shade->texture[0].data.mask.mask =
+ theme->shade_hover_mask;
+ theme->a_focused_pressed_shade->texture[0].data.mask.mask =
+ theme->a_unfocused_pressed_shade->texture[0].data.mask.mask =
+ theme->shade_pressed_mask;
+ theme->a_focused_unpressed_shade->texture[0].data.mask.mask =
+ theme->a_unfocused_unpressed_shade->texture[0].data.mask.mask =
+ theme->shade_mask;
+ theme->a_toggled_hover_focused_shade->texture[0].data.mask.mask =
+ theme->a_toggled_hover_unfocused_shade->texture[0].data.mask.mask =
+ theme->shade_toggled_hover_mask;
+ theme->a_toggled_focused_unpressed_shade->texture[0].data.mask.mask =
+ theme->a_toggled_unfocused_unpressed_shade->texture[0].data.mask.mask =
+ theme->shade_toggled_mask;
+ theme->a_toggled_focused_pressed_shade->texture[0].data.mask.mask =
+ theme->a_toggled_unfocused_pressed_shade->texture[0].data.mask.mask =
+ theme->shade_toggled_pressed_mask;
+ theme->a_disabled_focused_iconify->texture[0].data.mask.mask =
+ theme->a_disabled_unfocused_iconify->texture[0].data.mask.mask =
+ theme->iconify_disabled_mask;
+ theme->a_hover_focused_iconify->texture[0].data.mask.mask =
+ theme->a_hover_unfocused_iconify->texture[0].data.mask.mask =
+ theme->iconify_hover_mask;
+ theme->a_focused_pressed_iconify->texture[0].data.mask.mask =
+ theme->a_unfocused_pressed_iconify->texture[0].data.mask.mask =
+ theme->iconify_pressed_mask;
+ theme->a_focused_unpressed_iconify->texture[0].data.mask.mask =
+ theme->a_unfocused_unpressed_iconify->texture[0].data.mask.mask =
+ theme->iconify_mask;
+ theme->a_menu_bullet_normal->texture[0].data.mask.mask =
+ theme->a_menu_bullet_selected->texture[0].data.mask.mask =
+ theme->menu_bullet_mask;
+ theme->a_disabled_focused_max->texture[0].data.mask.color =
+ theme->a_disabled_focused_close->texture[0].data.mask.color =
+ theme->a_disabled_focused_desk->texture[0].data.mask.color =
+ theme->a_disabled_focused_shade->texture[0].data.mask.color =
+ theme->a_disabled_focused_iconify->texture[0].data.mask.color =
+ theme->titlebut_disabled_focused_color;
+ theme->a_disabled_unfocused_max->texture[0].data.mask.color =
+ theme->a_disabled_unfocused_close->texture[0].data.mask.color =
+ theme->a_disabled_unfocused_desk->texture[0].data.mask.color =
+ theme->a_disabled_unfocused_shade->texture[0].data.mask.color =
+ theme->a_disabled_unfocused_iconify->texture[0].data.mask.color =
+ theme->titlebut_disabled_unfocused_color;
+ theme->a_hover_focused_max->texture[0].data.mask.color =
+ theme->a_hover_focused_close->texture[0].data.mask.color =
+ theme->a_hover_focused_desk->texture[0].data.mask.color =
+ theme->a_hover_focused_shade->texture[0].data.mask.color =
+ theme->a_hover_focused_iconify->texture[0].data.mask.color =
+ theme->titlebut_hover_focused_color;
+ theme->a_hover_unfocused_max->texture[0].data.mask.color =
+ theme->a_hover_unfocused_close->texture[0].data.mask.color =
+ theme->a_hover_unfocused_desk->texture[0].data.mask.color =
+ theme->a_hover_unfocused_shade->texture[0].data.mask.color =
+ theme->a_hover_unfocused_iconify->texture[0].data.mask.color =
+ theme->titlebut_hover_unfocused_color;
+ theme->a_toggled_hover_focused_max->texture[0].data.mask.color =
+ theme->a_toggled_hover_focused_desk->texture[0].data.mask.color =
+ theme->a_toggled_hover_focused_shade->texture[0].data.mask.color =
+ theme->titlebut_toggled_hover_focused_color;
+ theme->a_toggled_hover_unfocused_max->texture[0].data.mask.color =
+ theme->a_toggled_hover_unfocused_desk->texture[0].data.mask.color =
+ theme->a_toggled_hover_unfocused_shade->texture[0].data.mask.color =
+ theme->titlebut_toggled_hover_unfocused_color;
+ theme->a_toggled_focused_unpressed_max->texture[0].data.mask.color =
+ theme->a_toggled_focused_unpressed_desk->texture[0].data.mask.color =
+ theme->a_toggled_focused_unpressed_shade->texture[0].data.mask.color =
+ theme->titlebut_toggled_focused_unpressed_color;
+ theme->a_toggled_unfocused_unpressed_max->texture[0].data.mask.color =
+ theme->a_toggled_unfocused_unpressed_desk->texture[0].data.mask.color =
+ theme->a_toggled_unfocused_unpressed_shade->texture[0].data.mask.color=
+ theme->titlebut_toggled_unfocused_unpressed_color;
+ theme->a_toggled_focused_pressed_max->texture[0].data.mask.color =
+ theme->a_toggled_focused_pressed_desk->texture[0].data.mask.color =
+ theme->a_toggled_focused_pressed_shade->texture[0].data.mask.color =
+ theme->titlebut_toggled_focused_pressed_color;
+ theme->a_toggled_unfocused_pressed_max->texture[0].data.mask.color =
+ theme->a_toggled_unfocused_pressed_desk->texture[0].data.mask.color =
+ theme->a_toggled_unfocused_pressed_shade->texture[0].data.mask.color =
+ theme->titlebut_toggled_unfocused_pressed_color;
+ theme->a_focused_unpressed_max->texture[0].data.mask.color =
+ theme->a_focused_unpressed_close->texture[0].data.mask.color =
+ theme->a_focused_unpressed_desk->texture[0].data.mask.color =
+ theme->a_focused_unpressed_shade->texture[0].data.mask.color =
+ theme->a_focused_unpressed_iconify->texture[0].data.mask.color =
+ theme->titlebut_focused_unpressed_color;
+ theme->a_focused_pressed_max->texture[0].data.mask.color =
+ theme->a_focused_pressed_close->texture[0].data.mask.color =
+ theme->a_focused_pressed_desk->texture[0].data.mask.color =
+ theme->a_focused_pressed_shade->texture[0].data.mask.color =
+ theme->a_focused_pressed_iconify->texture[0].data.mask.color =
+ theme->titlebut_focused_pressed_color;
+ theme->a_unfocused_unpressed_max->texture[0].data.mask.color =
+ theme->a_unfocused_unpressed_close->texture[0].data.mask.color =
+ theme->a_unfocused_unpressed_desk->texture[0].data.mask.color =
+ theme->a_unfocused_unpressed_shade->texture[0].data.mask.color =
+ theme->a_unfocused_unpressed_iconify->texture[0].data.mask.color =
+ theme->titlebut_unfocused_unpressed_color;
+ theme->a_unfocused_pressed_max->texture[0].data.mask.color =
+ theme->a_unfocused_pressed_close->texture[0].data.mask.color =
+ theme->a_unfocused_pressed_desk->texture[0].data.mask.color =
+ theme->a_unfocused_pressed_shade->texture[0].data.mask.color =
+ theme->a_unfocused_pressed_iconify->texture[0].data.mask.color =
+ theme->titlebut_unfocused_pressed_color;
+ theme->a_menu_bullet_normal->texture[0].data.mask.color =
+ theme->menu_color;
+ theme->a_menu_bullet_selected->texture[0].data.mask.color =
+ theme->menu_selected_color;
+
+ g_free(path);
+ XrmDestroyDatabase(db);
+
+ /* set the font heights */
+ theme->win_font_height = RrFontHeight
+ (theme->win_font_focused,
+ theme->a_focused_label->texture[0].data.text.shadow_offset_y);
+ theme->win_font_height =
+ MAX(theme->win_font_height,
+ RrFontHeight
+ (theme->win_font_focused,
+ theme->a_unfocused_label->texture[0].data.text.shadow_offset_y));
+ theme->menu_title_font_height = RrFontHeight
+ (theme->menu_title_font,
+ theme->a_menu_text_title->texture[0].data.text.shadow_offset_y);
+ theme->menu_font_height = RrFontHeight
+ (theme->menu_font,
+ theme->a_menu_text_normal->texture[0].data.text.shadow_offset_y);
+
+ /* calculate some last extents */
+ {
+ gint ft, fb, fl, fr, ut, ub, ul, ur;
+
+ RrMargins(theme->a_focused_label, &fl, &ft, &fr, &fb);
+ RrMargins(theme->a_unfocused_label, &ul, &ut, &ur, &ub);
+ theme->label_height = theme->win_font_height + MAX(ft + fb, ut + ub);
+ theme->label_height += theme->label_height % 2;
+
+ /* this would be nice I think, since padding.width can now be 0,
+ but it breaks frame.c horribly and I don't feel like fixing that
+ right now, so if anyone complains, here is how to keep text from
+ going over the title's bevel/border with a padding.width of 0 and a
+ bevelless/borderless label
+ RrMargins(theme->a_focused_title, &fl, &ft, &fr, &fb);
+ RrMargins(theme->a_unfocused_title, &ul, &ut, &ur, &ub);
+ theme->title_height = theme->label_height +
+ MAX(MAX(theme->padding * 2, ft + fb),
+ MAX(theme->padding * 2, ut + ub));
+ */
+ theme->title_height = theme->label_height + theme->paddingy * 2;
+
+ RrMargins(theme->a_menu_title, &ul, &ut, &ur, &ub);
+ theme->menu_title_label_height = theme->menu_title_font_height+ut+ub;
+ theme->menu_title_height = theme->menu_title_label_height +
+ theme->paddingy * 2;
+ }
+ theme->button_size = theme->label_height - 2;
+ theme->grip_width = 25;
+
+ return theme;
+}
+
+void RrThemeFree(RrTheme *theme)
+{
+ if (theme) {
+ g_free(theme->name);
+
+ RrColorFree(theme->menu_border_color);
+ RrColorFree(theme->osd_border_color);
+ RrColorFree(theme->frame_focused_border_color);
+ RrColorFree(theme->frame_unfocused_border_color);
+ RrColorFree(theme->title_separator_focused_color);
+ RrColorFree(theme->title_separator_unfocused_color);
+ RrColorFree(theme->cb_unfocused_color);
+ RrColorFree(theme->cb_focused_color);
+ RrColorFree(theme->title_focused_color);
+ RrColorFree(theme->title_unfocused_color);
+ RrColorFree(theme->titlebut_disabled_focused_color);
+ RrColorFree(theme->titlebut_disabled_unfocused_color);
+ RrColorFree(theme->titlebut_hover_focused_color);
+ RrColorFree(theme->titlebut_hover_unfocused_color);
+ RrColorFree(theme->titlebut_toggled_hover_focused_color);
+ RrColorFree(theme->titlebut_toggled_hover_unfocused_color);
+ RrColorFree(theme->titlebut_toggled_focused_pressed_color);
+ RrColorFree(theme->titlebut_toggled_unfocused_pressed_color);
+ RrColorFree(theme->titlebut_toggled_focused_unpressed_color);
+ RrColorFree(theme->titlebut_toggled_unfocused_unpressed_color);
+ RrColorFree(theme->titlebut_focused_pressed_color);
+ RrColorFree(theme->titlebut_unfocused_pressed_color);
+ RrColorFree(theme->titlebut_focused_unpressed_color);
+ RrColorFree(theme->titlebut_unfocused_unpressed_color);
+ RrColorFree(theme->menu_title_color);
+ RrColorFree(theme->menu_sep_color);
+ RrColorFree(theme->menu_color);
+ RrColorFree(theme->menu_selected_color);
+ RrColorFree(theme->menu_disabled_color);
+ RrColorFree(theme->menu_disabled_selected_color);
+ RrColorFree(theme->title_focused_shadow_color);
+ RrColorFree(theme->title_unfocused_shadow_color);
+ RrColorFree(theme->osd_text_active_color);
+ RrColorFree(theme->osd_text_inactive_color);
+ RrColorFree(theme->osd_text_active_shadow_color);
+ RrColorFree(theme->osd_text_inactive_shadow_color);
+ RrColorFree(theme->menu_title_shadow_color);
+ RrColorFree(theme->menu_text_normal_shadow_color);
+ RrColorFree(theme->menu_text_selected_shadow_color);
+ RrColorFree(theme->menu_text_disabled_shadow_color);
+ RrColorFree(theme->menu_text_disabled_selected_shadow_color);
+
+ g_free(theme->def_win_icon);
+
+ RrPixmapMaskFree(theme->max_mask);
+ RrPixmapMaskFree(theme->max_toggled_mask);
+ RrPixmapMaskFree(theme->max_toggled_hover_mask);
+ RrPixmapMaskFree(theme->max_toggled_pressed_mask);
+ RrPixmapMaskFree(theme->max_disabled_mask);
+ RrPixmapMaskFree(theme->max_hover_mask);
+ RrPixmapMaskFree(theme->max_pressed_mask);
+ RrPixmapMaskFree(theme->desk_mask);
+ RrPixmapMaskFree(theme->desk_toggled_mask);
+ RrPixmapMaskFree(theme->desk_toggled_hover_mask);
+ RrPixmapMaskFree(theme->desk_toggled_pressed_mask);
+ RrPixmapMaskFree(theme->desk_disabled_mask);
+ RrPixmapMaskFree(theme->desk_hover_mask);
+ RrPixmapMaskFree(theme->desk_pressed_mask);
+ RrPixmapMaskFree(theme->shade_mask);
+ RrPixmapMaskFree(theme->shade_toggled_mask);
+ RrPixmapMaskFree(theme->shade_toggled_hover_mask);
+ RrPixmapMaskFree(theme->shade_toggled_pressed_mask);
+ RrPixmapMaskFree(theme->shade_disabled_mask);
+ RrPixmapMaskFree(theme->shade_hover_mask);
+ RrPixmapMaskFree(theme->shade_pressed_mask);
+ RrPixmapMaskFree(theme->iconify_mask);
+ RrPixmapMaskFree(theme->iconify_disabled_mask);
+ RrPixmapMaskFree(theme->iconify_hover_mask);
+ RrPixmapMaskFree(theme->iconify_pressed_mask);
+ RrPixmapMaskFree(theme->close_mask);
+ RrPixmapMaskFree(theme->close_disabled_mask);
+ RrPixmapMaskFree(theme->close_hover_mask);
+ RrPixmapMaskFree(theme->close_pressed_mask);
+ RrPixmapMaskFree(theme->menu_bullet_mask);
+ RrPixmapMaskFree(theme->down_arrow_mask);
+ RrPixmapMaskFree(theme->up_arrow_mask);
+
+ RrFontClose(theme->win_font_focused);
+ RrFontClose(theme->win_font_unfocused);
+ RrFontClose(theme->menu_title_font);
+ RrFontClose(theme->menu_font);
+ RrFontClose(theme->osd_font_hilite);
+ RrFontClose(theme->osd_font_unhilite);
+
+ RrAppearanceFree(theme->a_disabled_focused_max);
+ RrAppearanceFree(theme->a_disabled_unfocused_max);
+ RrAppearanceFree(theme->a_hover_focused_max);
+ RrAppearanceFree(theme->a_hover_unfocused_max);
+ RrAppearanceFree(theme->a_toggled_hover_focused_max);
+ RrAppearanceFree(theme->a_toggled_hover_unfocused_max);
+ RrAppearanceFree(theme->a_toggled_focused_unpressed_max);
+ RrAppearanceFree(theme->a_toggled_focused_pressed_max);
+ RrAppearanceFree(theme->a_toggled_unfocused_unpressed_max);
+ RrAppearanceFree(theme->a_toggled_unfocused_pressed_max);
+ RrAppearanceFree(theme->a_focused_unpressed_max);
+ RrAppearanceFree(theme->a_focused_pressed_max);
+ RrAppearanceFree(theme->a_unfocused_unpressed_max);
+ RrAppearanceFree(theme->a_unfocused_pressed_max);
+ RrAppearanceFree(theme->a_disabled_focused_close);
+ RrAppearanceFree(theme->a_disabled_unfocused_close);
+ RrAppearanceFree(theme->a_hover_focused_close);
+ RrAppearanceFree(theme->a_hover_unfocused_close);
+ RrAppearanceFree(theme->a_focused_unpressed_close);
+ RrAppearanceFree(theme->a_focused_pressed_close);
+ RrAppearanceFree(theme->a_unfocused_unpressed_close);
+ RrAppearanceFree(theme->a_unfocused_pressed_close);
+ RrAppearanceFree(theme->a_disabled_focused_desk);
+ RrAppearanceFree(theme->a_disabled_unfocused_desk);
+ RrAppearanceFree(theme->a_hover_focused_desk);
+ RrAppearanceFree(theme->a_hover_unfocused_desk);
+ RrAppearanceFree(theme->a_toggled_hover_focused_desk);
+ RrAppearanceFree(theme->a_toggled_hover_unfocused_desk);
+ RrAppearanceFree(theme->a_toggled_focused_unpressed_desk);
+ RrAppearanceFree(theme->a_toggled_focused_pressed_desk);
+ RrAppearanceFree(theme->a_toggled_unfocused_unpressed_desk);
+ RrAppearanceFree(theme->a_toggled_unfocused_pressed_desk);
+ RrAppearanceFree(theme->a_focused_unpressed_desk);
+ RrAppearanceFree(theme->a_focused_pressed_desk);
+ RrAppearanceFree(theme->a_unfocused_unpressed_desk);
+ RrAppearanceFree(theme->a_unfocused_pressed_desk);
+ RrAppearanceFree(theme->a_disabled_focused_shade);
+ RrAppearanceFree(theme->a_disabled_unfocused_shade);
+ RrAppearanceFree(theme->a_hover_focused_shade);
+ RrAppearanceFree(theme->a_hover_unfocused_shade);
+ RrAppearanceFree(theme->a_toggled_hover_focused_shade);
+ RrAppearanceFree(theme->a_toggled_hover_unfocused_shade);
+ RrAppearanceFree(theme->a_toggled_focused_unpressed_shade);
+ RrAppearanceFree(theme->a_toggled_focused_pressed_shade);
+ RrAppearanceFree(theme->a_toggled_unfocused_unpressed_shade);
+ RrAppearanceFree(theme->a_toggled_unfocused_pressed_shade);
+ RrAppearanceFree(theme->a_focused_unpressed_shade);
+ RrAppearanceFree(theme->a_focused_pressed_shade);
+ RrAppearanceFree(theme->a_unfocused_unpressed_shade);
+ RrAppearanceFree(theme->a_unfocused_pressed_shade);
+ RrAppearanceFree(theme->a_disabled_focused_iconify);
+ RrAppearanceFree(theme->a_disabled_unfocused_iconify);
+ RrAppearanceFree(theme->a_hover_focused_iconify);
+ RrAppearanceFree(theme->a_hover_unfocused_iconify);
+ RrAppearanceFree(theme->a_focused_unpressed_iconify);
+ RrAppearanceFree(theme->a_focused_pressed_iconify);
+ RrAppearanceFree(theme->a_unfocused_unpressed_iconify);
+ RrAppearanceFree(theme->a_unfocused_pressed_iconify);
+ RrAppearanceFree(theme->a_focused_grip);
+ RrAppearanceFree(theme->a_unfocused_grip);
+ RrAppearanceFree(theme->a_focused_title);
+ RrAppearanceFree(theme->a_unfocused_title);
+ RrAppearanceFree(theme->a_focused_label);
+ RrAppearanceFree(theme->a_unfocused_label);
+ RrAppearanceFree(theme->a_icon);
+ RrAppearanceFree(theme->a_focused_handle);
+ RrAppearanceFree(theme->a_unfocused_handle);
+ RrAppearanceFree(theme->a_menu);
+ RrAppearanceFree(theme->a_menu_title);
+ RrAppearanceFree(theme->a_menu_text_title);
+ RrAppearanceFree(theme->a_menu_normal);
+ RrAppearanceFree(theme->a_menu_selected);
+ RrAppearanceFree(theme->a_menu_disabled);
+ RrAppearanceFree(theme->a_menu_disabled_selected);
+ RrAppearanceFree(theme->a_menu_text_normal);
+ RrAppearanceFree(theme->a_menu_text_selected);
+ RrAppearanceFree(theme->a_menu_text_disabled);
+ RrAppearanceFree(theme->a_menu_text_disabled_selected);
+ RrAppearanceFree(theme->a_menu_bullet_normal);
+ RrAppearanceFree(theme->a_menu_bullet_selected);
+ RrAppearanceFree(theme->a_clear);
+ RrAppearanceFree(theme->a_clear_tex);
+ RrAppearanceFree(theme->osd_bg);
+ RrAppearanceFree(theme->osd_hilite_bg);
+ RrAppearanceFree(theme->osd_hilite_label);
+ RrAppearanceFree(theme->osd_unhilite_bg);
+ RrAppearanceFree(theme->osd_unhilite_label);
+
+ g_free(theme);
+ }
+}
+
+static XrmDatabase loaddb(const gchar *name, gchar **path)
+{
+ GSList *it;
+ XrmDatabase db = NULL;
+ gchar *s;
+
+ if (name[0] == '/') {
+ s = g_build_filename(name, "openbox-3", "themerc", NULL);
+ if ((db = XrmGetFileDatabase(s)))
+ *path = g_path_get_dirname(s);
+ g_free(s);
+ } else {
+ ObtPaths *p;
+
+ p = obt_paths_new();
+
+ /* XXX backwards compatibility, remove me sometime later */
+ s = g_build_filename(g_get_home_dir(), ".themes", name,
+ "openbox-3", "themerc", NULL);
+ if ((db = XrmGetFileDatabase(s)))
+ *path = g_path_get_dirname(s);
+ g_free(s);
+
+ for (it = obt_paths_data_dirs(p); !db && it; it = g_slist_next(it))
+ {
+ s = g_build_filename(it->data, "themes", name,
+ "openbox-3", "themerc", NULL);
+ if ((db = XrmGetFileDatabase(s)))
+ *path = g_path_get_dirname(s);
+ g_free(s);
+ }
+
+ obt_paths_unref(p);
+ }
+
+ if (db == NULL) {
+ s = g_build_filename(name, "themerc", NULL);
+ if ((db = XrmGetFileDatabase(s)))
+ *path = g_path_get_dirname(s);
+ g_free(s);
+ }
+
+ return db;
+}
+
+static gchar *create_class_name(const gchar *rname)
+{
+ gchar *rclass = g_strdup(rname);
+ gchar *p = rclass;
+
+ while (TRUE) {
+ *p = toupper(*p);
+ p = strchr(p+1, '.');
+ if (p == NULL) break;
+ ++p;
+ if (*p == '\0') break;
+ }
+ return rclass;
+}
+
+static gboolean read_int(XrmDatabase db, const gchar *rname, gint *value)
+{
+ gboolean ret = FALSE;
+ gchar *rclass = create_class_name(rname);
+ gchar *rettype, *end;
+ XrmValue retvalue;
+
+ if (XrmGetResource(db, rname, rclass, &rettype, &retvalue) &&
+ retvalue.addr != NULL) {
+ *value = (gint)strtol(retvalue.addr, &end, 10);
+ if (end != retvalue.addr)
+ ret = TRUE;
+ }
+
+ g_free(rclass);
+ return ret;
+}
+
+static gboolean read_string(XrmDatabase db, const gchar *rname, gchar **value)
+{
+ gboolean ret = FALSE;
+ gchar *rclass = create_class_name(rname);
+ gchar *rettype;
+ XrmValue retvalue;
+
+ if (XrmGetResource(db, rname, rclass, &rettype, &retvalue) &&
+ retvalue.addr != NULL) {
+ *value = retvalue.addr;
+ ret = TRUE;
+ }
+
+ g_free(rclass);
+ return ret;
+}
+
+static gboolean read_color(XrmDatabase db, const RrInstance *inst,
+ const gchar *rname, RrColor **value)
+{
+ gboolean ret = FALSE;
+ gchar *rclass = create_class_name(rname);
+ gchar *rettype;
+ XrmValue retvalue;
+
+ if (XrmGetResource(db, rname, rclass, &rettype, &retvalue) &&
+ retvalue.addr != NULL) {
+ RrColor *c = RrColorParse(inst, retvalue.addr);
+ if (c != NULL) {
+ *value = c;
+ ret = TRUE;
+ }
+ }
+
+ g_free(rclass);
+ return ret;
+}
+
+static gboolean read_mask(const RrInstance *inst, const gchar *path,
+ RrTheme *theme, const gchar *maskname,
+ RrPixmapMask **value)
+{
+ gboolean ret = FALSE;
+ gchar *s;
+ gint hx, hy; /* ignored */
+ guint w, h;
+ guchar *b;
+
+ s = g_build_filename(path, maskname, NULL);
+ if (XReadBitmapFileData(s, &w, &h, &b, &hx, &hy) == BitmapSuccess) {
+ ret = TRUE;
+ *value = RrPixmapMaskNew(inst, w, h, (gchar*)b);
+ XFree(b);
+ }
+ g_free(s);
+
+ return ret;
+}
+
+static void parse_appearance(gchar *tex, RrSurfaceColorType *grad,
+ RrReliefType *relief, RrBevelType *bevel,
+ gboolean *interlaced, gboolean *border,
+ gboolean allow_trans)
+{
+ gchar *t;
+
+ /* convert to all lowercase */
+ for (t = tex; *t != '\0'; ++t)
+ *t = g_ascii_tolower(*t);
+
+ if (allow_trans && strstr(tex, "parentrelative") != NULL) {
+ *grad = RR_SURFACE_PARENTREL;
+ } else {
+ if (strstr(tex, "gradient") != NULL) {
+ if (strstr(tex, "crossdiagonal") != NULL)
+ *grad = RR_SURFACE_CROSS_DIAGONAL;
+ else if (strstr(tex, "pyramid") != NULL)
+ *grad = RR_SURFACE_PYRAMID;
+ else if (strstr(tex, "mirrorhorizontal") != NULL)
+ *grad = RR_SURFACE_MIRROR_HORIZONTAL;
+ else if (strstr(tex, "horizontal") != NULL)
+ *grad = RR_SURFACE_HORIZONTAL;
+ else if (strstr(tex, "splitvertical") != NULL)
+ *grad = RR_SURFACE_SPLIT_VERTICAL;
+ else if (strstr(tex, "vertical") != NULL)
+ *grad = RR_SURFACE_VERTICAL;
+ else
+ *grad = RR_SURFACE_DIAGONAL;
+ } else {
+ *grad = RR_SURFACE_SOLID;
+ }
+ }
+
+ if (strstr(tex, "sunken") != NULL)
+ *relief = RR_RELIEF_SUNKEN;
+ else if (strstr(tex, "flat") != NULL)
+ *relief = RR_RELIEF_FLAT;
+ else if (strstr(tex, "raised") != NULL)
+ *relief = RR_RELIEF_RAISED;
+ else
+ *relief = (*grad == RR_SURFACE_PARENTREL) ?
+ RR_RELIEF_FLAT : RR_RELIEF_RAISED;
+
+ *border = FALSE;
+ if (*relief == RR_RELIEF_FLAT) {
+ if (strstr(tex, "border") != NULL)
+ *border = TRUE;
+ } else {
+ if (strstr(tex, "bevel2") != NULL)
+ *bevel = RR_BEVEL_2;
+ else
+ *bevel = RR_BEVEL_1;
+ }
+
+ if (strstr(tex, "interlaced") != NULL)
+ *interlaced = TRUE;
+ else
+ *interlaced = FALSE;
+}
+
+static gboolean read_appearance(XrmDatabase db, const RrInstance *inst,
+ const gchar *rname, RrAppearance *value,
+ gboolean allow_trans)
+{
+ gboolean ret = FALSE;
+ gchar *rclass = create_class_name(rname);
+ gchar *cname, *ctoname, *bcname, *icname, *hname, *sname;
+ gchar *csplitname, *ctosplitname;
+ gchar *rettype;
+ XrmValue retvalue;
+ gint i;
+
+ cname = g_strconcat(rname, ".color", NULL);
+ ctoname = g_strconcat(rname, ".colorTo", NULL);
+ bcname = g_strconcat(rname, ".border.color", NULL);
+ icname = g_strconcat(rname, ".interlace.color", NULL);
+ hname = g_strconcat(rname, ".highlight", NULL);
+ sname = g_strconcat(rname, ".shadow", NULL);
+ csplitname = g_strconcat(rname, ".color.splitTo", NULL);
+ ctosplitname = g_strconcat(rname, ".colorTo.splitTo", NULL);
+
+ if (XrmGetResource(db, rname, rclass, &rettype, &retvalue) &&
+ retvalue.addr != NULL) {
+ parse_appearance(retvalue.addr,
+ &value->surface.grad,
+ &value->surface.relief,
+ &value->surface.bevel,
+ &value->surface.interlaced,
+ &value->surface.border,
+ allow_trans);
+ if (!read_color(db, inst, cname, &value->surface.primary))
+ value->surface.primary = RrColorNew(inst, 0, 0, 0);
+ if (!read_color(db, inst, ctoname, &value->surface.secondary))
+ value->surface.secondary = RrColorNew(inst, 0, 0, 0);
+ if (value->surface.border)
+ if (!read_color(db, inst, bcname,
+ &value->surface.border_color))
+ value->surface.border_color = RrColorNew(inst, 0, 0, 0);
+ if (value->surface.interlaced)
+ if (!read_color(db, inst, icname,
+ &value->surface.interlace_color))
+ value->surface.interlace_color = RrColorNew(inst, 0, 0, 0);
+ if (read_int(db, hname, &i) && i >= 0)
+ value->surface.bevel_light_adjust = i;
+ if (read_int(db, sname, &i) && i >= 0 && i <= 256)
+ value->surface.bevel_dark_adjust = i;
+
+ if (value->surface.grad == RR_SURFACE_SPLIT_VERTICAL) {
+ gint r, g, b;
+
+ if (!read_color(db, inst, csplitname,
+ &value->surface.split_primary))
+ {
+ r = value->surface.primary->r;
+ r += r >> 2;
+ g = value->surface.primary->g;
+ g += g >> 2;
+ b = value->surface.primary->b;
+ b += b >> 2;
+ if (r > 0xFF) r = 0xFF;
+ if (g > 0xFF) g = 0xFF;
+ if (b > 0xFF) b = 0xFF;
+ value->surface.split_primary = RrColorNew(inst, r, g, b);
+ }
+
+ if (!read_color(db, inst, ctosplitname,
+ &value->surface.split_secondary))
+ {
+ r = value->surface.secondary->r;
+ r += r >> 4;
+ g = value->surface.secondary->g;
+ g += g >> 4;
+ b = value->surface.secondary->b;
+ b += b >> 4;
+ if (r > 0xFF) r = 0xFF;
+ if (g > 0xFF) g = 0xFF;
+ if (b > 0xFF) b = 0xFF;
+ value->surface.split_secondary = RrColorNew(inst, r, g, b);
+ }
+ }
+
+ ret = TRUE;
+ }
+
+ g_free(ctosplitname);
+ g_free(csplitname);
+ g_free(sname);
+ g_free(hname);
+ g_free(icname);
+ g_free(bcname);
+ g_free(ctoname);
+ g_free(cname);
+ g_free(rclass);
+ return ret;
+}
+
+static int parse_inline_number(const char *p)
+{
+ int neg = 1;
+ int res = 0;
+ if (*p == '-') {
+ neg = -1;
+ ++p;
+ }
+ for (; isdigit(*p); ++p)
+ res = res * 10 + *p - '0';
+ res *= neg;
+ return res;
+}
+
+static void set_default_appearance(RrAppearance *a)
+{
+ a->surface.grad = RR_SURFACE_SOLID;
+ a->surface.relief = RR_RELIEF_FLAT;
+ a->surface.bevel = RR_BEVEL_1;
+ a->surface.interlaced = FALSE;
+ a->surface.border = FALSE;
+ a->surface.primary = RrColorNew(a->inst, 0, 0, 0);
+ a->surface.secondary = RrColorNew(a->inst, 0, 0, 0);
+}
+
+/* Reads the output from gimp's C-Source file format into valid RGBA data for
+ an RrTextureRGBA. */
+static RrPixel32* read_c_image(gint width, gint height, const guint8 *data)
+{
+ RrPixel32 *im, *p;
+ gint i;
+
+ p = im = g_memdup(data, width * height * sizeof(RrPixel32));
+
+ for (i = 0; i < width * height; ++i) {
+ guchar a = ((*p >> 24) & 0xff);
+ guchar b = ((*p >> 16) & 0xff);
+ guchar g = ((*p >> 8) & 0xff);
+ guchar r = ((*p >> 0) & 0xff);
+
+ *p = ((r << RrDefaultRedOffset) +
+ (g << RrDefaultGreenOffset) +
+ (b << RrDefaultBlueOffset) +
+ (a << RrDefaultAlphaOffset));
+ p++;
+ }
+
+ return im;
+}
diff --git a/obrender/theme.h b/obrender/theme.h
new file mode 100644
index 00000000..3f9063e6
--- /dev/null
+++ b/obrender/theme.h
@@ -0,0 +1,266 @@
+/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
+
+ theme.h for the Openbox window manager
+ Copyright (c) 2006 Mikael Magnusson
+ Copyright (c) 2003-2007 Dana Jansens
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ See the COPYING file for a copy of the GNU General Public License.
+*/
+
+#ifndef __theme_h
+#define __theme_h
+
+#include "render.h"
+
+G_BEGIN_DECLS
+
+typedef struct _RrTheme RrTheme;
+
+struct _RrTheme {
+ const RrInstance *inst;
+
+ /* style settings - fonts */
+ RrFont *win_font_focused;
+ RrFont *win_font_unfocused;
+ RrFont *menu_title_font;
+ RrFont *menu_font;
+ RrFont *osd_font_hilite;
+ RrFont *osd_font_unhilite;
+
+ /* style settings - geometry */
+ gint paddingx;
+ gint paddingy;
+ gint handle_height;
+ gint fbwidth; /*!< frame border width */
+ gint mbwidth; /*!< menu border width */
+ gint obwidth; /*!< osd border width */
+ gint cbwidthx;
+ gint cbwidthy;
+ gint menu_overlap_x;
+ gint menu_overlap_y;
+ gint menu_sep_width;
+ gint menu_sep_paddingx;
+ gint menu_sep_paddingy;
+ /* these ones are calculated, not set directly by the theme file */
+ gint win_font_height;
+ gint menu_title_font_height;
+ gint menu_font_height;
+ gint label_height;
+ gint title_height;
+ gint button_size;
+ gint grip_width;
+ gint menu_title_label_height;
+ gint menu_title_height;
+
+ /* style settings - colors */
+ RrColor *menu_border_color;
+ RrColor *osd_border_color;
+ RrColor *frame_focused_border_color;
+ RrColor *frame_unfocused_border_color;
+ RrColor *title_separator_focused_color;
+ RrColor *title_separator_unfocused_color;
+ RrColor *cb_focused_color;
+ RrColor *cb_unfocused_color;
+ RrColor *title_focused_color;
+ RrColor *title_unfocused_color;
+ RrColor *titlebut_disabled_focused_color;
+ RrColor *titlebut_disabled_unfocused_color;
+ RrColor *titlebut_hover_focused_color;
+ RrColor *titlebut_hover_unfocused_color;
+ RrColor *titlebut_toggled_hover_focused_color;
+ RrColor *titlebut_toggled_hover_unfocused_color;
+ RrColor *titlebut_toggled_focused_pressed_color;
+ RrColor *titlebut_toggled_unfocused_pressed_color;
+ RrColor *titlebut_toggled_focused_unpressed_color;
+ RrColor *titlebut_toggled_unfocused_unpressed_color;
+ RrColor *titlebut_focused_pressed_color;
+ RrColor *titlebut_unfocused_pressed_color;
+ RrColor *titlebut_focused_unpressed_color;
+ RrColor *titlebut_unfocused_unpressed_color;
+ RrColor *menu_title_color;
+ RrColor *menu_sep_color;
+ RrColor *menu_color;
+ RrColor *menu_selected_color;
+ RrColor *menu_disabled_color;
+ RrColor *menu_disabled_selected_color;
+ RrColor *title_focused_shadow_color;
+ gchar title_focused_shadow_alpha;
+ RrColor *title_unfocused_shadow_color;
+ gchar title_unfocused_shadow_alpha;
+ RrColor *osd_text_active_color;
+ RrColor *osd_text_inactive_color;
+ RrColor *osd_text_active_shadow_color;
+ RrColor *osd_text_inactive_shadow_color;
+ gchar osd_text_active_shadow_alpha;
+ gchar osd_text_inactive_shadow_alpha;
+ RrColor *menu_title_shadow_color;
+ gchar menu_title_shadow_alpha;
+ RrColor *menu_text_normal_shadow_color;
+ gchar menu_text_normal_shadow_alpha;
+ RrColor *menu_text_selected_shadow_color;
+ gchar menu_text_selected_shadow_alpha;
+ RrColor *menu_text_disabled_shadow_color;
+ gchar menu_text_disabled_shadow_alpha;
+ RrColor *menu_text_disabled_selected_shadow_color;
+ gchar menu_text_disabled_selected_shadow_alpha;
+
+ /* style settings - pics */
+ RrPixel32 *def_win_icon; /* RGBA */
+ gint def_win_icon_w;
+ gint def_win_icon_h;
+
+ /* style settings - masks */
+ RrPixmapMask *max_mask;
+ RrPixmapMask *max_hover_mask;
+ RrPixmapMask *max_pressed_mask;
+ RrPixmapMask *max_toggled_mask;
+ RrPixmapMask *max_toggled_hover_mask;
+ RrPixmapMask *max_toggled_pressed_mask;
+ RrPixmapMask *max_disabled_mask;
+ RrPixmapMask *iconify_mask;
+ RrPixmapMask *iconify_hover_mask;
+ RrPixmapMask *iconify_pressed_mask;
+ RrPixmapMask *iconify_disabled_mask;
+ RrPixmapMask *desk_mask;
+ RrPixmapMask *desk_hover_mask;
+ RrPixmapMask *desk_pressed_mask;
+ RrPixmapMask *desk_toggled_mask;
+ RrPixmapMask *desk_toggled_hover_mask;
+ RrPixmapMask *desk_toggled_pressed_mask;
+ RrPixmapMask *desk_disabled_mask;
+ RrPixmapMask *shade_mask;
+ RrPixmapMask *shade_hover_mask;
+ RrPixmapMask *shade_pressed_mask;
+ RrPixmapMask *shade_toggled_mask;
+ RrPixmapMask *shade_toggled_hover_mask;
+ RrPixmapMask *shade_toggled_pressed_mask;
+ RrPixmapMask *shade_disabled_mask;
+ RrPixmapMask *close_mask;
+ RrPixmapMask *close_hover_mask;
+ RrPixmapMask *close_disabled_mask;
+ RrPixmapMask *close_pressed_mask;
+
+ RrPixmapMask *menu_bullet_mask; /* submenu pointer */
+#if 0
+ RrPixmapMask *menu_toggle_mask; /* menu boolean */
+#endif
+
+ RrPixmapMask *down_arrow_mask;
+ RrPixmapMask *up_arrow_mask;
+
+ /* global appearances */
+ RrAppearance *a_disabled_focused_max;
+ RrAppearance *a_disabled_unfocused_max;
+ RrAppearance *a_hover_focused_max;
+ RrAppearance *a_hover_unfocused_max;
+ RrAppearance *a_focused_unpressed_max;
+ RrAppearance *a_focused_pressed_max;
+ RrAppearance *a_unfocused_unpressed_max;
+ RrAppearance *a_unfocused_pressed_max;
+ RrAppearance *a_toggled_hover_focused_max;
+ RrAppearance *a_toggled_hover_unfocused_max;
+ RrAppearance *a_toggled_focused_unpressed_max;
+ RrAppearance *a_toggled_focused_pressed_max;
+ RrAppearance *a_toggled_unfocused_unpressed_max;
+ RrAppearance *a_toggled_unfocused_pressed_max;
+ RrAppearance *a_disabled_focused_close;
+ RrAppearance *a_disabled_unfocused_close;
+ RrAppearance *a_hover_focused_close;
+ RrAppearance *a_hover_unfocused_close;
+ RrAppearance *a_focused_unpressed_close;
+ RrAppearance *a_focused_pressed_close;
+ RrAppearance *a_unfocused_unpressed_close;
+ RrAppearance *a_unfocused_pressed_close;
+ RrAppearance *a_disabled_focused_desk;
+ RrAppearance *a_disabled_unfocused_desk;
+ RrAppearance *a_hover_focused_desk;
+ RrAppearance *a_hover_unfocused_desk;
+ RrAppearance *a_focused_unpressed_desk;
+ RrAppearance *a_focused_pressed_desk;
+ RrAppearance *a_unfocused_unpressed_desk;
+ RrAppearance *a_unfocused_pressed_desk;
+ RrAppearance *a_toggled_hover_focused_desk;
+ RrAppearance *a_toggled_hover_unfocused_desk;
+ RrAppearance *a_toggled_focused_unpressed_desk;
+ RrAppearance *a_toggled_focused_pressed_desk;
+ RrAppearance *a_toggled_unfocused_unpressed_desk;
+ RrAppearance *a_toggled_unfocused_pressed_desk;
+ RrAppearance *a_disabled_focused_shade;
+ RrAppearance *a_disabled_unfocused_shade;
+ RrAppearance *a_hover_focused_shade;
+ RrAppearance *a_hover_unfocused_shade;
+ RrAppearance *a_focused_unpressed_shade;
+ RrAppearance *a_focused_pressed_shade;
+ RrAppearance *a_unfocused_unpressed_shade;
+ RrAppearance *a_unfocused_pressed_shade;
+ RrAppearance *a_toggled_hover_focused_shade;
+ RrAppearance *a_toggled_hover_unfocused_shade;
+ RrAppearance *a_toggled_focused_unpressed_shade;
+ RrAppearance *a_toggled_focused_pressed_shade;
+ RrAppearance *a_toggled_unfocused_unpressed_shade;
+ RrAppearance *a_toggled_unfocused_pressed_shade;
+ RrAppearance *a_disabled_focused_iconify;
+ RrAppearance *a_disabled_unfocused_iconify;
+ RrAppearance *a_hover_focused_iconify;
+ RrAppearance *a_hover_unfocused_iconify;
+ RrAppearance *a_focused_unpressed_iconify;
+ RrAppearance *a_focused_pressed_iconify;
+ RrAppearance *a_unfocused_unpressed_iconify;
+ RrAppearance *a_unfocused_pressed_iconify;
+ RrAppearance *a_focused_grip;
+ RrAppearance *a_unfocused_grip;
+ RrAppearance *a_focused_title;
+ RrAppearance *a_unfocused_title;
+ RrAppearance *a_focused_label;
+ RrAppearance *a_unfocused_label;
+ /* always parentrelative, so no focused/unfocused */
+ RrAppearance *a_icon;
+ RrAppearance *a_focused_handle;
+ RrAppearance *a_unfocused_handle;
+ RrAppearance *a_menu_text_title;
+ RrAppearance *a_menu_title;
+ RrAppearance *a_menu;
+ RrAppearance *a_menu_normal;
+ RrAppearance *a_menu_selected;
+ RrAppearance *a_menu_disabled;
+ RrAppearance *a_menu_disabled_selected;
+ RrAppearance *a_menu_text_normal;
+ RrAppearance *a_menu_text_disabled;
+ RrAppearance *a_menu_text_disabled_selected;
+ RrAppearance *a_menu_text_selected;
+ RrAppearance *a_menu_bullet_normal;
+ RrAppearance *a_menu_bullet_selected;
+ RrAppearance *a_clear; /* clear with no texture */
+ RrAppearance *a_clear_tex; /* clear with a texture */
+
+ RrAppearance *osd_bg; /* can never be parent relative */
+ RrAppearance *osd_hilite_bg; /* can never be parent relative */
+ RrAppearance *osd_hilite_label; /* can be parent relative */
+ RrAppearance *osd_unhilite_bg; /* can never be parent relative */
+ RrAppearance *osd_unhilite_label; /* can be parent relative */
+
+ gchar *name;
+};
+
+/*! The font values are all optional. If a NULL is used for any of them, then
+ the default font will be used. */
+RrTheme* RrThemeNew(const RrInstance *inst, const gchar *theme,
+ gboolean allow_fallback,
+ RrFont *active_window_font, RrFont *inactive_window_font,
+ RrFont *menu_title_font, RrFont *menu_item_font,
+ RrFont *active_osd_font, RrFont *inactive_osd_font);
+void RrThemeFree(RrTheme *theme);
+
+G_END_DECLS
+
+#endif
diff --git a/obrender/version.h.in b/obrender/version.h.in
new file mode 100644
index 00000000..0ff30b57
--- /dev/null
+++ b/obrender/version.h.in
@@ -0,0 +1,15 @@
+#ifndef rr__version_h
+#define rr__version_h
+
+#define RR_MAJOR_VERSION @RR_MAJOR_VERSION@
+#define RR_MINOR_VERSION @RR_MINOR_VERSION@
+#define RR_MICRO_VERSION @RR_MICRO_VERSION@
+#define RR_VERSION RR_MAJOR_VERSION.RR_MINOR_VERSION.RR_MICRO_VERSION
+
+#define RR_CHECK_VERSION(major,minor,micro) \
+ (RR_MAJOR_VERSION > (major) || \
+ (RR_MAJOR_VERSION == (major) && RR_MINOR_VERSION > (minor)) || \
+ (RR_MAJOR_VERSION == (major) && RR_MINOR_VERSION == (minor) && \
+ RR_MICRO_VERSION >= (micro)))
+
+#endif