$(XML_CFLAGS) \
$(PANGO_CFLAGS) \
$(IMLIB2_CFLAGS) \
+ $(LIBRSVG_CFLAGS) \
-DG_LOG_DOMAIN=\"ObRender\" \
-DDEFAULT_THEME=\"$(theme)\"
obrender_libobrender_la_LDFLAGS = \
$(PANGO_LIBS) \
$(GLIB_LIBS) \
$(IMLIB2_LIBS) \
+ $(LIBRSVG_LIBS) \
$(XML_LIBS)
obrender_libobrender_la_SOURCES = \
gettext.h \
AM_CONDITIONAL(USE_IMLIB2, [test $imlib2_found = yes])
+AC_ARG_ENABLE(librsvg,
+ AC_HELP_STRING(
+ [--disable-librsvg],
+ [disable use of SVG image files for loading icons. [default=enabled]]
+ ),
+ [enable_librsvg=$enableval],
+ [enable_librsvg=yes]
+)
+
+if test "$enable_librsvg" = yes; then
+PKG_CHECK_MODULES(LIBRSVG, [librsvg-2.0],
+ [
+ AC_DEFINE(USE_LIBRSVG, [1], [Use SVG image files])
+ AC_SUBST(LIBRSVG_CFLAGS)
+ AC_SUBST(LIBRSVG_LIBS)
+ # export it for the pkg-config file
+ PKG_CONFIG_LIBRSVG=librsvg-2.0
+ AC_SUBST(PKG_CONFIG_LIBRSVG)
+ librsvg_found=yes
+ ],
+ [
+ librsvg_found=no
+ ]
+)
+else
+ librsvg_found=no
+fi
+
+AM_CONDITIONAL(USE_LIBRSVG, [test $librsvg_found = yes])
+
dnl Check for session management
X11_SM
Startup Notification... $sn_found
X Cursor Library... $xcursor_found
Session Management... $SM
- Imlib2 library... $imlib2_found
+ Imlib2 Library... $imlib2_found
+ SVG Support (librsvg)... $librsvg_found
])
AC_MSG_RESULT([configure complete, now type "make"])
#ifdef USE_IMLIB2
#include <Imlib2.h>
#endif
+#ifdef USE_LIBRSVG
+#include <librsvg/rsvg.h>
+#endif
#include <glib.h>
return self;
}
+#if defined(USE_IMLIB2)
+typedef struct _ImlibLoader ImlibLoader;
+
+struct _ImlibLoader
+{
+ Imlib_Image img;
+};
+
+void DestroyImlibLoader(ImlibLoader *loader)
+{
+ if (!loader)
+ return;
+
+ imlib_free_image();
+ g_slice_free(ImlibLoader, loader);
+}
+
+ImlibLoader* LoadWithImlib(gchar *path,
+ RrPixel32 **pixel_data,
+ gint *width,
+ gint *height)
+{
+ ImlibLoader *loader = g_slice_new0(ImlibLoader);
+ if (!(loader->img = imlib_load_image(path))) {
+ DestroyImlibLoader(loader);
+ return NULL;
+ }
+
+ /* Get data and dimensions of the image.
+
+ WARNING: This stuff is NOT threadsafe !!
+ */
+ imlib_context_set_image(loader->img);
+ *pixel_data = imlib_image_get_data_for_reading_only();
+ *width = imlib_image_get_width();
+ *height = imlib_image_get_height();
+
+ return loader;
+}
+#endif /* USE_IMLIB2 */
+
+#if defined(USE_LIBRSVG)
+typedef struct _RsvgLoader RsvgLoader;
+
+struct _RsvgLoader
+{
+ RsvgHandle *handle;
+ cairo_surface_t *surface;
+ RrPixel32 *pixel_data;
+};
+
+void DestroyRsvgLoader(RsvgLoader *loader)
+{
+ if (!loader)
+ return;
+
+ if (loader->pixel_data)
+ g_free(loader->pixel_data);
+ if (loader->surface)
+ cairo_surface_destroy(loader->surface);
+ if (loader->handle)
+ g_object_unref(loader->handle);
+ g_slice_free(RsvgLoader, loader);
+}
+
+RsvgLoader* LoadWithRsvg(gchar *path,
+ RrPixel32 **pixel_data,
+ gint *width,
+ gint *height)
+{
+ RsvgLoader *loader = g_slice_new0(RsvgLoader);
+
+ if (!(loader->handle = rsvg_handle_new_from_file(path, NULL))) {
+ DestroyRsvgLoader(loader);
+ return NULL;
+ }
+
+ if (!rsvg_handle_close(loader->handle, NULL)) {
+ DestroyRsvgLoader(loader);
+ return NULL;
+ }
+
+ RsvgDimensionData dimension_data;
+ rsvg_handle_get_dimensions(loader->handle, &dimension_data);
+ *width = dimension_data.width;
+ *height = dimension_data.height;
+
+ loader->surface = cairo_image_surface_create(
+ CAIRO_FORMAT_ARGB32, *width, *height);
+
+ cairo_t* context = cairo_create(loader->surface);
+ gboolean success = rsvg_handle_render_cairo(loader->handle, context);
+ cairo_destroy(context);
+
+ if (!success) {
+ DestroyRsvgLoader(loader);
+ return NULL;
+ }
+
+ loader->pixel_data = g_new(guint32, *width * *height);
+
+ /*
+ Cairo has its data in ARGB with premultiplied alpha, but RrPixel32
+ non-premultipled, so convert that. Also, RrPixel32 doesn't allow
+ strides not equal to the width of the image.
+ */
+
+ /* Verify that RrPixel32 has the same ordering as cairo. */
+ g_assert(RrDefaultAlphaOffset == 24);
+ g_assert(RrDefaultRedOffset == 16);
+ g_assert(RrDefaultGreenOffset == 8);
+ g_assert(RrDefaultBlueOffset == 0);
+
+ guint32 *out_row = loader->pixel_data;
+
+ guint32 *in_row =
+ (guint32*)cairo_image_surface_get_data(loader->surface);
+ gint in_stride = cairo_image_surface_get_stride(loader->surface);
+
+ gint y;
+ for (y = 0; y < *height; ++y) {
+ gint x;
+ for (x = 0; x < *width; ++x) {
+ guchar a = in_row[x] >> 24;
+ guchar r = (in_row[x] >> 16) & 0xff;
+ guchar g = (in_row[x] >> 8) & 0xff;
+ guchar b = in_row[x] & 0xff;
+ out_row[x] =
+ ((r * 256 / (a + 1)) << RrDefaultRedOffset) +
+ ((g * 256 / (a + 1)) << RrDefaultGreenOffset) +
+ ((b * 256 / (a + 1)) << RrDefaultBlueOffset) +
+ (a << RrDefaultAlphaOffset);
+ }
+ in_row += in_stride / 4;
+ out_row += *width;
+ }
+
+ *pixel_data = loader->pixel_data;
+
+ return loader;
+}
+#endif /* USE_LIBRSVG */
+
RrImage* RrImageNewFromName(RrImageCache *cache, const gchar *name)
{
-#ifndef USE_IMLIB2
- return NULL;
-#else
RrImage *self;
RrImageSet *set;
- Imlib_Image img;
gint w, h;
RrPixel32 *data;
gchar *path;
+ gboolean loaded;
+
+#if defined(USE_IMLIB2)
+ ImlibLoader *imlib_loader = NULL;
+#endif
+#if defined(USE_LIBRSVG)
+ RsvgLoader *rsvg_loader = NULL;
+#endif
g_return_val_if_fail(cache != NULL, NULL);
g_return_val_if_fail(name != NULL, NULL);
/* XXX find the path via freedesktop icon spec (use obt) ! */
path = g_strdup(name);
- if (!(img = imlib_load_image(path)))
- g_message("Cannot load image \"%s\" from file \"%s\"", name, path);
+ loaded = FALSE;
+#if defined(USE_LIBRSVG)
+ if (!loaded) {
+ rsvg_loader = LoadWithRsvg(path, &data, &w, &h);
+ loaded = !!rsvg_loader;
+ }
+#endif
+#if defined(USE_IMLIB2)
+ if (!loaded) {
+ imlib_loader = LoadWithImlib(path, &data, &w, &h);
+ loaded = !!imlib_loader;
+ }
+#endif
+
g_free(path);
- if (!img)
+ if (!loaded) {
+ g_message("Cannot load image \"%s\" from file \"%s\"", name, path);
+#if defined(USE_LIBRSVG)
+ DestroyRsvgLoader(rsvg_loader);
+#endif
+#if defined(USE_IMLIB2)
+ DestroyImlibLoader(imlib_loader);
+#endif
return NULL;
-
- /* Get data and dimensions of the image.
-
- WARNING: This stuff is NOT threadsafe !!
- */
- imlib_context_set_image(img);
- data = imlib_image_get_data_for_reading_only();
- w = imlib_image_get_width();
- h = imlib_image_get_height();
+ }
/* get an RrImage that contains an RrImageSet with this picture in it.
the RrImage might be new, or reused if the picture was already in the
self = RrImageNewFromData(cache, data, w, h);
RrImageSetAddName(self->set, name);
- imlib_free_image();
- return self;
+#if defined(USE_LIBRSVG)
+ DestroyRsvgLoader(rsvg_loader);
+#endif
+#if defined(USE_IMLIB2)
+ DestroyImlibLoader(imlib_loader);
#endif
+
+ return self;
}
/************************************************************************
Name: ObRender
Description: Openbox Render Library
Version: @RR_VERSION@
-Requires: obt-3.5 glib-2.0 xft pangoxft @PKG_CONFIG_IMLIB@
+Requires: obt-3.5 glib-2.0 xft pangoxft @PKG_CONFIG_IMLIB@ @PKG_CONFIG_LIBRSVG@
Libs: -L${libdir} -lobrender ${xlibs}
Cflags: -I${includedir}/openbox/@RR_VERSION@ ${xcflags}
typedef struct _RrImageCache RrImageCache;
typedef struct _RrButton RrButton;
-typedef guint32 RrPixel32; /* RGBA format */
+typedef guint32 RrPixel32; /* ARGB format, not premultiplied alpha */
typedef guint16 RrPixel16;
typedef guchar RrPixel8;
xmlDocPtr doc;
xmlNodePtr root;
gchar *path;
+ gchar *last_error_file;
+ gint last_error_line;
+ gchar *last_error_message;
};
+static void obt_xml_save_last_error(ObtXmlInst* inst);
+
static void destfunc(struct Callback *c)
{
g_free(c->tag);
i->doc = NULL;
i->root = NULL;
i->path = NULL;
+ i->last_error_file = NULL;
+ i->last_error_line = -1;
+ i->last_error_message = NULL;
return i;
}
if (i && --i->ref == 0) {
obt_paths_unref(i->xdg_paths);
g_hash_table_destroy(i->callbacks);
+ g_free(i->last_error_file);
+ g_free(i->last_error_message);
g_slice_free(ObtXmlInst, i);
}
}
g_assert(i->doc == NULL); /* another doc isn't open already? */
+ xmlResetLastError();
+
for (it = paths; !r && it; it = g_slist_next(it)) {
gchar *path;
struct stat s;
g_free(path);
}
+ obt_xml_save_last_error(i);
+
return r;
}
g_assert(i->doc == NULL); /* another doc isn't open already? */
+ xmlResetLastError();
+
i->doc = xmlParseMemory(data, len);
if (i) {
i->root = xmlDocGetRootElement(i->doc);
else
r = TRUE; /* ok ! */
}
+
+ obt_xml_save_last_error(i);
+
return r;
}
+static void obt_xml_save_last_error(ObtXmlInst* inst)
+{
+ xmlErrorPtr error = xmlGetLastError();
+ if (error) {
+ inst->last_error_file = g_strdup(error->file);
+ inst->last_error_line = error->line;
+ inst->last_error_message = g_strdup(error->message);
+ xmlResetError(error);
+ }
+}
+
+gboolean obt_xml_last_error(ObtXmlInst *inst)
+{
+ return inst->last_error_file &&
+ inst->last_error_line >= 0 &&
+ inst->last_error_message;
+}
+
+gchar* obt_xml_last_error_file(ObtXmlInst *inst)
+{
+ if (!obt_xml_last_error(inst))
+ return NULL;
+ return inst->last_error_file;
+}
+
+gint obt_xml_last_error_line(ObtXmlInst *inst)
+{
+ if (!obt_xml_last_error(inst))
+ return -1;
+ return inst->last_error_line;
+}
+
+gchar* obt_xml_last_error_message(ObtXmlInst *inst)
+{
+ if (!obt_xml_last_error(inst))
+ return NULL;
+ return inst->last_error_message;
+}
+
gboolean obt_xml_save_file(ObtXmlInst *inst,
const gchar *path,
gboolean pretty)
gboolean obt_xml_load_mem(ObtXmlInst *inst,
gpointer data, guint len, const gchar *root_node);
+/* Returns true if an error is present. */
+gboolean obt_xml_last_error(ObtXmlInst *inst);
+gchar* obt_xml_last_error_file(ObtXmlInst *inst);
+gint obt_xml_last_error_line(ObtXmlInst *inst);
+gchar* obt_xml_last_error_message(ObtXmlInst *inst);
+
gboolean obt_xml_save_file(ObtXmlInst *inst,
const gchar *path,
gboolean pretty);
event_reset_time();
do {
+ gchar *xml_error_string = NULL;
ObPrompt *xmlprompt = NULL;
if (reconfigure) obt_keyboard_reload();
else
OBT_PROP_ERASE(obt_root(ob_screen), OB_CONFIG_FILE);
+ if (obt_xml_last_error(i)) {
+ xml_error_string = g_strdup_printf(
+ _("One or more XML syntax errors were found while parsing the Openbox configuration files. See stdout for more information. The last error seen was in file \"%s\" line %d, with message: %s"),
+ obt_xml_last_error_file(i),
+ obt_xml_last_error_line(i),
+ obt_xml_last_error_message(i));
+ }
+
/* we're done with parsing now, kill it */
obt_xml_instance_unref(i);
}
reconfigure = FALSE;
/* look for parsing errors */
- {
- xmlErrorPtr e = xmlGetLastError();
- if (e) {
- gchar *m;
-
- m = g_strdup_printf(_("One or more XML syntax errors were found while parsing the Openbox configuration files. See stdout for more information. The last error seen was in file \"%s\" line %d, with message: %s"), e->file, e->line, e->message);
- xmlprompt =
- prompt_show_message(m, _("Openbox Syntax Error"), _("Close"));
- g_free(m);
- xmlResetError(e);
- }
+ if (xml_error_string) {
+ xmlprompt = prompt_show_message(xml_error_string,
+ _("Openbox Syntax Error"),
+ _("Close"));
+ g_free(xml_error_string);
+ xml_error_string = NULL;
}
g_main_loop_run(ob_main_loop);
error "make (with --disable-imlib2) failed"
make clean >/dev/null || error "make clean failed"
+echo Check compile with librsvg disabled
+./configure -C --disable-imlib2 >/dev/null || \
+ error "configure failed"
+make >/dev/null 2>/dev/null || \
+ error "make (with --disable-librsvg) failed"
+make clean >/dev/null || error "make clean failed"
+
echo Check compile with session management disabled
./configure -C --disable-session-management >/dev/null || \
error "configure failed"