Web   ·   Wiki   ·   Activities   ·   Blog   ·   Lists   ·   Chat   ·   Meeting   ·   Bugs   ·   Git   ·   Translate   ·   Archive   ·   People   ·   Donate
summaryrefslogtreecommitdiffstats
path: root/art/cursor/cursorthemegen/themefile.c
diff options
context:
space:
mode:
Diffstat (limited to 'art/cursor/cursorthemegen/themefile.c')
-rw-r--r--art/cursor/cursorthemegen/themefile.c609
1 files changed, 609 insertions, 0 deletions
diff --git a/art/cursor/cursorthemegen/themefile.c b/art/cursor/cursorthemegen/themefile.c
new file mode 100644
index 0000000..a8a703f
--- /dev/null
+++ b/art/cursor/cursorthemegen/themefile.c
@@ -0,0 +1,609 @@
+/*
+ * Copyright © 2003 Red Hat, Inc.
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and its
+ * documentation for any purpose is hereby granted without fee, provided that
+ * the above copyright notice appear in all copies and that both that
+ * copyright notice and this permission notice appear in supporting
+ * documentation, and that the name of Red Hat not be used in advertising or
+ * publicity pertaining to distribution of the software without specific,
+ * written prior permission. Red Hat makes no representations about the
+ * suitability of this software for any purpose. It is provided "as is"
+ * without express or implied warranty.
+ *
+ * RED HAT DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL RED HAT
+ * BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Author: Owen Taylor, Red Hat, Inc.
+ */
+#include <stdarg.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "cursortheme.h"
+
+typedef struct _ParseData ParseData;
+
+typedef enum {
+ OUTSIDE,
+ IN_THEME,
+ IN_SOURCE,
+ IN_IMAGE,
+ IN_ALIAS,
+ IN_LAYOUT,
+ IN_ROW,
+ IN_CURSOR,
+ IN_FRAME
+} ParseState;
+
+struct _ParseData {
+ CursorTheme *theme;
+ ParseState state;
+ gboolean seen_theme;
+ gboolean seen_layout;
+ int row;
+ int column;
+ Cursor *current_cursor;
+};
+
+static gboolean
+expect_tag (GMarkupParseContext *context,
+ const gchar *element_name,
+ const gchar *expected_name, /* Null for expected empty */
+ GError **error)
+{
+ if (!expected_name || strcmp (element_name, expected_name) != 0)
+ {
+ g_set_error (error,
+ G_MARKUP_ERROR,
+ G_MARKUP_ERROR_INVALID_CONTENT,
+ "Unexpected tag '%s'",
+ element_name);
+ return FALSE;
+ }
+ return TRUE;
+}
+
+static gboolean
+extract_attrs (GMarkupParseContext *context,
+ const gchar **attribute_names,
+ const gchar **attribute_values,
+ GError **error,
+ ...)
+{
+ va_list vap;
+ const char *name;
+ gboolean *attr_map;
+ gboolean nattrs = 0;
+ int i;
+
+ for (i = 0; attribute_names[i]; i++)
+ nattrs++;
+
+ attr_map = g_new0 (gboolean, nattrs);
+
+ va_start (vap, error);
+ name = va_arg (vap, const char *);
+ while (name)
+ {
+ gboolean mandatory = va_arg (vap, gboolean);
+ const char **loc = va_arg (vap, const char **);
+ gboolean found = FALSE;
+
+ for (i = 0; attribute_names[i]; i++)
+ {
+ if (!attr_map[i] && strcmp (attribute_names[i], name) == 0)
+ {
+ if (found)
+ {
+ g_set_error (error,
+ G_MARKUP_ERROR,
+ G_MARKUP_ERROR_INVALID_CONTENT,
+ "Duplicate attribute '%s'", name);
+ return FALSE;
+ }
+
+ *loc = attribute_values[i];
+ found = TRUE;
+ attr_map[i] = TRUE;
+ }
+ }
+
+ if (!found && mandatory)
+ {
+ g_set_error (error,
+ G_MARKUP_ERROR,
+ G_MARKUP_ERROR_INVALID_CONTENT,
+ "Missing attribute '%s'", name);
+ return FALSE;
+ }
+
+ name = va_arg (vap, const char *);
+ }
+
+ for (i = 0; i < nattrs; i++)
+ if (!attr_map[i])
+ {
+ g_set_error (error,
+ G_MARKUP_ERROR,
+ G_MARKUP_ERROR_UNKNOWN_ATTRIBUTE,
+ "Unknown attribute '%s'", attribute_names[i]);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static gboolean
+get_int (const char *str,
+ int *result)
+{
+ long val;
+ char *p;
+
+ val = strtol (str, &p, 0);
+ if (*str == '\0' || *p != '\0' ||
+ val < G_MININT || val > G_MAXINT)
+ return FALSE;
+
+ *result = val;
+
+ return TRUE;
+}
+
+static void
+add_source (ParseData *parse_data,
+ int size,
+ int gridsize)
+{
+ CursorSource *source = g_new0 (CursorSource, 1);
+ source->size = size;
+ source->gridsize = gridsize;
+ parse_data->theme->sources = g_slist_prepend (parse_data->theme->sources, source);
+}
+
+static void
+add_alias (ParseData *parse_data,
+ const char *name,
+ const char *target)
+{
+ CursorAlias *alias = g_new0 (CursorAlias, 1);
+ alias->name = g_strdup (name);
+ alias->target = g_strdup (target);
+
+ g_hash_table_insert (parse_data->theme->aliases, alias->name, alias);
+}
+
+static gboolean
+add_image (ParseData *parse_data,
+ const char *file,
+ int use,
+ GError **error)
+{
+ GdkPixbuf *pixbuf;
+ CursorSource *source;
+ CursorImage *image;
+
+ pixbuf = gdk_pixbuf_new_from_file (file, error);
+ if (!pixbuf)
+ return FALSE;
+
+ source = parse_data->theme->sources->data;
+ image = g_new0 (CursorImage, 1);
+ image->image = pixbuf;
+ image->use = use;
+ source->images = g_slist_prepend (source->images, image);
+
+ return TRUE;
+}
+
+static void
+add_cursor (ParseData *parse_data,
+ const char *name)
+{
+ Cursor *cursor = g_new0 (Cursor, 1);
+ cursor->name = g_strdup (name);
+ cursor->row = parse_data->row;
+ cursor->column = parse_data->column;
+ parse_data->current_cursor = cursor;
+ g_hash_table_insert (parse_data->theme->cursors, cursor->name, cursor);
+}
+
+static void
+add_frame_config (ParseData *parse_data,
+ int delay)
+{
+ Cursor *cursor = parse_data->current_cursor;
+ CursorFrameConfig *frame_config = g_new0 (CursorFrameConfig, 1);
+ frame_config->delay = delay;
+ cursor->frame_configs = g_slist_prepend (cursor->frame_configs, frame_config);
+}
+
+/* Called for open tags <foo bar="baz"> */
+static void
+cursor_theme_start_element (GMarkupParseContext *context,
+ const gchar *element_name,
+ const gchar **attribute_names,
+ const gchar **attribute_values,
+ gpointer user_data,
+ GError **error)
+{
+ ParseData *parse_data = user_data;
+
+ switch (parse_data->state)
+ {
+ case OUTSIDE:
+ {
+ const char *name;
+
+ if (!expect_tag (context, element_name, "theme", error))
+ return;
+ if (parse_data->seen_theme)
+ {
+ g_set_error (error,
+ G_MARKUP_ERROR,
+ G_MARKUP_ERROR_INVALID_CONTENT,
+ "Multiple occurrences of <theme>");
+ return;
+ }
+ parse_data->seen_theme = TRUE;
+ parse_data->state = IN_THEME;
+
+ if (!extract_attrs (context, attribute_names, attribute_values, error,
+ "name", TRUE, &name,
+ NULL))
+ return;
+
+ parse_data->theme->name = g_strdup (name);
+ }
+ break;
+ case IN_THEME:
+ if (strcmp (element_name, "source") == 0)
+ {
+ const char *size_str;
+ const char *gridsize_str;
+ int size, gridsize;
+
+ parse_data->state = IN_SOURCE;
+
+ if (!extract_attrs (context, attribute_names, attribute_values, error,
+ "size", TRUE, &size_str,
+ "gridsize", FALSE, &gridsize_str,
+ NULL))
+ return;
+
+ if (!get_int (size_str, &size) || size < 0)
+ {
+ g_set_error (error,
+ G_MARKUP_ERROR,
+ G_MARKUP_ERROR_INVALID_CONTENT,
+ "Invalid size %s", size_str);
+ return;
+ }
+
+ if (gridsize_str)
+ {
+ if (!get_int (gridsize_str, &gridsize) || size < 0)
+ {
+ g_set_error (error,
+ G_MARKUP_ERROR,
+ G_MARKUP_ERROR_INVALID_CONTENT,
+ "Invalid size %s", size_str);
+ return;
+ }
+ }
+ else
+ gridsize = size;
+
+ add_source (parse_data, size, gridsize);
+ }
+ else if (strcmp (element_name, "alias") == 0)
+ {
+ const char *name;
+ const char *target;
+
+ parse_data->state = IN_ALIAS;
+
+ if (!extract_attrs (context, attribute_names, attribute_values, error,
+ "name", TRUE, &name,
+ "target", FALSE, &target,
+ NULL))
+ return;
+
+ add_alias (parse_data, name, target);
+ }
+ else if (strcmp (element_name, "layout") == 0)
+ {
+ if (parse_data->seen_layout)
+ {
+ g_set_error (error,
+ G_MARKUP_ERROR,
+ G_MARKUP_ERROR_INVALID_CONTENT,
+ "Multiple occurrences of <layout>");
+ return;
+ }
+ parse_data->seen_layout = TRUE;
+ parse_data->state = IN_LAYOUT;
+
+ if (!extract_attrs (context, attribute_names, attribute_values, error,
+ NULL))
+ return;
+ }
+ else
+ expect_tag (context, element_name, NULL, error);
+ break;
+ case IN_SOURCE:
+ {
+ const char *file;
+ const char *use_str;
+ int use;
+
+ if (!expect_tag (context, element_name, "image", error))
+ return;
+ parse_data->state = IN_IMAGE;
+
+ if (!extract_attrs (context, attribute_names, attribute_values, error,
+ "file", TRUE, &file,
+ "use", TRUE, &use_str,
+ NULL))
+ return;
+
+ if (strcmp (use_str, "hotspot") == 0)
+ use = -1;
+ else
+ {
+ if (!get_int (use_str, &use) || use < 0)
+ {
+ g_set_error (error,
+ G_MARKUP_ERROR,
+ G_MARKUP_ERROR_INVALID_CONTENT,
+ "Invalid use value %s", use_str);
+ return;
+ }
+ }
+
+ add_image (parse_data, file, use, error);
+ }
+
+ break;
+ case IN_LAYOUT:
+ if (!expect_tag (context, element_name, "row", error))
+ return;
+ parse_data->state = IN_ROW;
+ parse_data->column = 0;
+ break;
+ case IN_ROW:
+ {
+ const char *name;
+
+ if (!expect_tag (context, element_name, "cursor", error))
+ return;
+ parse_data->state = IN_CURSOR;
+
+ if (!extract_attrs (context, attribute_names, attribute_values, error,
+ "name", TRUE, &name,
+ NULL))
+ return;
+
+ add_cursor (parse_data, name);
+ }
+ break;
+ case IN_CURSOR:
+ {
+ const char *delay_str = NULL;
+ int delay = -1;
+
+ if (!expect_tag (context, element_name, "frame", error))
+ return;
+ parse_data->state = IN_FRAME;
+
+ if (!extract_attrs (context, attribute_names, attribute_values, error,
+ "delay", FALSE, &delay_str,
+ NULL))
+ return;
+
+ if (delay_str)
+ {
+ if (!get_int (delay_str, &delay) || delay < 0)
+ {
+ g_set_error (error,
+ G_MARKUP_ERROR,
+ G_MARKUP_ERROR_INVALID_CONTENT,
+ "Invalid delay value '%s'", delay_str);
+ return;
+ }
+ }
+
+ add_frame_config (parse_data, delay);
+ }
+ break;
+ case IN_ALIAS:
+ case IN_IMAGE:
+ case IN_FRAME:
+ expect_tag (context, element_name, NULL, error);
+ break;
+ }
+}
+
+/* Called for close tags </foo> */
+static void
+cursor_theme_end_element (GMarkupParseContext *context,
+ const gchar *element_name,
+ gpointer user_data,
+ GError **error)
+{
+ ParseData *parse_data = user_data;
+
+ switch (parse_data->state)
+ {
+ case OUTSIDE:
+ g_assert_not_reached ();
+ break;
+ case IN_THEME:
+ parse_data->state = OUTSIDE;
+ parse_data->theme->sources = g_slist_reverse (parse_data->theme->sources);
+ break;
+ case IN_SOURCE:
+ {
+ CursorSource *source = parse_data->theme->sources->data;
+ source->images = g_slist_reverse (source->images);
+ parse_data->state = IN_THEME;
+ }
+ break;
+ case IN_ALIAS:
+ parse_data->state = IN_THEME;
+ break;
+ case IN_IMAGE:
+ parse_data->state = IN_SOURCE;
+ break;
+ case IN_LAYOUT:
+ parse_data->state = IN_THEME;
+ break;
+ case IN_ROW:
+ parse_data->state = IN_LAYOUT;
+ parse_data->row++;
+ break;
+ case IN_CURSOR:
+ {
+ Cursor *cursor = parse_data->current_cursor;
+ if (!cursor->frame_configs)
+ add_frame_config (parse_data, -1);
+ cursor->frame_configs = g_slist_reverse (cursor->frame_configs);
+ parse_data->state = IN_ROW;
+ parse_data->column++;
+ parse_data->current_cursor = NULL;
+ }
+ break;
+ case IN_FRAME:
+ parse_data->state = IN_CURSOR;
+ break;
+ }
+}
+
+/* Called for character data */
+/* text is not nul-terminated */
+static void
+cursor_theme_text (GMarkupParseContext *context,
+ const gchar *text,
+ gsize text_len,
+ gpointer user_data,
+ GError **error)
+{
+ int i;
+
+ for (i = 0; i < text_len; i++)
+ if (!g_ascii_isspace (text[i]))
+ {
+ g_set_error (error,
+ G_MARKUP_ERROR,
+ G_MARKUP_ERROR_INVALID_CONTENT,
+ "Unexpected text in theme file");
+ return;
+ }
+}
+
+/* Called for strings that should be re-saved verbatim in this same
+ * position, but are not otherwise interpretable. At the moment
+ * this includes comments and processing instructions.
+ */
+/* text is not nul-terminated. */
+static void
+cursor_theme_passthrough (GMarkupParseContext *context,
+ const gchar *passthrough_text,
+ gsize text_len,
+ gpointer user_data,
+ GError **error)
+{
+ /* do nothing */
+}
+
+/* Called on error, including one set by other
+ * methods in the vtable. The GError should not be freed.
+ */
+static void
+cursor_theme_error (GMarkupParseContext *context,
+ GError *error,
+ gpointer user_data)
+{
+}
+
+static GMarkupParser cursor_theme_parse = {
+ cursor_theme_start_element,
+ cursor_theme_end_element,
+ cursor_theme_text,
+ cursor_theme_passthrough,
+ cursor_theme_error
+};
+
+static void
+cursor_theme_free (CursorTheme *theme)
+{
+}
+
+CursorTheme *
+cursor_theme_read (const char *filename)
+{
+ ParseData parse_data;
+ GMarkupParseContext *context;
+ GError *error = NULL;
+ char *text;
+ gboolean have_error = FALSE;
+ size_t len;
+
+ if (!g_file_get_contents (filename, &text, &len, &error))
+ {
+ g_printerr ("Cannot read theme definition file: %s\n", error->message);
+ g_error_free (error);
+ return NULL;
+ }
+
+ parse_data.theme = g_new0 (CursorTheme, 1);
+ parse_data.theme->cursors = g_hash_table_new (g_str_hash, g_str_equal);
+ parse_data.theme->aliases = g_hash_table_new (g_str_hash, g_str_equal);
+
+ parse_data.state = OUTSIDE;
+ parse_data.seen_theme = FALSE;
+ parse_data.seen_layout = FALSE;
+ parse_data.row = 0;
+ parse_data.column = 0;
+
+ context = g_markup_parse_context_new (&cursor_theme_parse, 0,
+ &parse_data, NULL);
+
+ if (!g_markup_parse_context_parse (context, text, len, &error) ||
+ !g_markup_parse_context_end_parse (context, &error))
+ {
+ g_printerr ("Error parsing theme definition file: %s\n", error->message);
+ have_error = TRUE;
+ g_error_free (error);
+ }
+ else if (!parse_data.seen_theme)
+ {
+ g_printerr ("Did not find <theme> element in theme file\n");
+ have_error = TRUE;
+ }
+ else if (!parse_data.seen_layout)
+ {
+ g_printerr ("Did not find <layout> element in theme file\n");
+ have_error = TRUE;
+ }
+ else if (!parse_data.theme->sources)
+ {
+ g_printerr ("No <source> element in theme file\n");
+ have_error = TRUE;
+ }
+
+ g_markup_parse_context_free (context);
+
+ if (!have_error)
+ return parse_data.theme;
+ else
+ {
+ cursor_theme_free (parse_data.theme);
+ return NULL;
+ }
+}