Add support for loading SVG icons using librsvg.
This adds a configure option --disable-librsvg, but defaults to using the library if it is present during configure. When enabled, Openbox will attempt to load svg image files using the library, similar to how Imlib2 is used for other image formats. Since librsvg uses the libXml2 library, their errors end up in the same global namespace as Openbox config file parsing. To avoid this, we reset the libXml current error whenever we start loading a file, and save the last error that occurred when we are finished, by storing the error in the ObtXmlInst.
This commit is contained in:
parent
a5eb363f48
commit
4e6c0086a6
9 changed files with 306 additions and 32 deletions
|
@ -75,6 +75,7 @@ obrender_libobrender_la_CPPFLAGS = \
|
|||
$(XML_CFLAGS) \
|
||||
$(PANGO_CFLAGS) \
|
||||
$(IMLIB2_CFLAGS) \
|
||||
$(LIBRSVG_CFLAGS) \
|
||||
-DG_LOG_DOMAIN=\"ObRender\" \
|
||||
-DDEFAULT_THEME=\"$(theme)\"
|
||||
obrender_libobrender_la_LDFLAGS = \
|
||||
|
@ -85,6 +86,7 @@ obrender_libobrender_la_LIBADD = \
|
|||
$(PANGO_LIBS) \
|
||||
$(GLIB_LIBS) \
|
||||
$(IMLIB2_LIBS) \
|
||||
$(LIBRSVG_LIBS) \
|
||||
$(XML_LIBS)
|
||||
obrender_libobrender_la_SOURCES = \
|
||||
gettext.h \
|
||||
|
|
33
configure.ac
33
configure.ac
|
@ -195,6 +195,36 @@ fi
|
|||
|
||||
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
|
||||
|
||||
|
@ -233,6 +263,7 @@ AC_MSG_RESULT([Compiling with these options:
|
|||
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"])
|
||||
|
|
202
obrender/image.c
202
obrender/image.c
|
@ -24,6 +24,9 @@
|
|||
#ifdef USE_IMLIB2
|
||||
#include <Imlib2.h>
|
||||
#endif
|
||||
#ifdef USE_LIBRSVG
|
||||
#include <librsvg/rsvg.h>
|
||||
#endif
|
||||
|
||||
#include <glib.h>
|
||||
|
||||
|
@ -463,17 +466,164 @@ RrImage* RrImageNewFromData(RrImageCache *cache, RrPixel32 *data,
|
|||
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);
|
||||
|
@ -488,21 +638,32 @@ RrImage* RrImageNewFromName(RrImageCache *cache, const gchar *name)
|
|||
/* 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
|
||||
|
@ -517,9 +678,14 @@ RrImage* RrImageNewFromName(RrImageCache *cache, const gchar *name)
|
|||
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;
|
||||
}
|
||||
|
||||
/************************************************************************
|
||||
|
|
|
@ -9,6 +9,6 @@ xlibs=@X_LIBS@
|
|||
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}
|
||||
|
|
|
@ -49,7 +49,7 @@ typedef struct _RrImagePic RrImagePic;
|
|||
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;
|
||||
|
||||
|
|
58
obt/xml.c
58
obt/xml.c
|
@ -48,8 +48,13 @@ struct _ObtXmlInst {
|
|||
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);
|
||||
|
@ -66,6 +71,9 @@ ObtXmlInst* obt_xml_instance_new(void)
|
|||
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;
|
||||
}
|
||||
|
||||
|
@ -79,6 +87,8 @@ void obt_xml_instance_unref(ObtXmlInst *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);
|
||||
}
|
||||
}
|
||||
|
@ -128,6 +138,8 @@ static gboolean load_file(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;
|
||||
|
@ -169,6 +181,8 @@ static gboolean load_file(ObtXmlInst *i,
|
|||
g_free(path);
|
||||
}
|
||||
|
||||
obt_xml_save_last_error(i);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
|
@ -264,6 +278,8 @@ gboolean obt_xml_load_mem(ObtXmlInst *i,
|
|||
|
||||
g_assert(i->doc == NULL); /* another doc isn't open already? */
|
||||
|
||||
xmlResetLastError();
|
||||
|
||||
i->doc = xmlParseMemory(data, len);
|
||||
if (i) {
|
||||
i->root = xmlDocGetRootElement(i->doc);
|
||||
|
@ -282,9 +298,51 @@ gboolean obt_xml_load_mem(ObtXmlInst *i,
|
|||
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)
|
||||
|
|
|
@ -51,6 +51,12 @@ gboolean obt_xml_load_theme_file(ObtXmlInst *inst,
|
|||
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);
|
||||
|
|
|
@ -224,6 +224,7 @@ gint main(gint argc, gchar **argv)
|
|||
event_reset_time();
|
||||
|
||||
do {
|
||||
gchar *xml_error_string = NULL;
|
||||
ObPrompt *xmlprompt = NULL;
|
||||
|
||||
if (reconfigure) obt_keyboard_reload();
|
||||
|
@ -264,6 +265,14 @@ gint main(gint argc, gchar **argv)
|
|||
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);
|
||||
}
|
||||
|
@ -362,17 +371,12 @@ gint main(gint argc, gchar **argv)
|
|||
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);
|
||||
|
|
|
@ -78,6 +78,13 @@ make >/dev/null 2>/dev/null || \
|
|||
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"
|
||||
|
|
Loading…
Reference in a new issue