Web   ·   Wiki   ·   Activities   ·   Blog   ·   Lists   ·   Chat   ·   Meeting   ·   Bugs   ·   Git   ·   Translate   ·   Archive   ·   People   ·   Donate
summaryrefslogtreecommitdiffstats
path: root/cursor
diff options
context:
space:
mode:
authorMarco Pesenti Gritti <marco@localhost.localdomain>2007-04-19 17:16:09 (GMT)
committer Marco Pesenti Gritti <marco@localhost.localdomain>2007-04-19 17:16:09 (GMT)
commit855f8560b1398e6587ef315b1f361c223965b0f7 (patch)
treec21c37b826a31e0e9090c595d65885eb2218bcfc /cursor
parent2ebd40e9ab0d2d74bb29d0b8b3b23064653e295a (diff)
Cleanup directory structure and rename olpc to sugar
Diffstat (limited to 'cursor')
-rw-r--r--cursor/Makefile.am1
-rw-r--r--cursor/cursorthemegen/ChangeLog48
-rw-r--r--cursor/cursorthemegen/Makefile.am11
-rw-r--r--cursor/cursorthemegen/README186
-rw-r--r--cursor/cursorthemegen/cursortheme.h92
-rw-r--r--cursor/cursorthemegen/main.c615
-rw-r--r--cursor/cursorthemegen/sample.cursortheme148
-rw-r--r--cursor/cursorthemegen/themefile.c609
-rw-r--r--cursor/sugar/ChangeLog9
-rw-r--r--cursor/sugar/Makefile.am40
-rw-r--r--cursor/sugar/olpc-hotspots.pngbin0 -> 504 bytes
-rw-r--r--cursor/sugar/olpc.cursortheme142
-rw-r--r--cursor/sugar/sugar-0.pngbin0 -> 12667 bytes
-rw-r--r--cursor/sugar/sugar-1.pngbin0 -> 12623 bytes
-rw-r--r--cursor/sugar/sugar-10.pngbin0 -> 12703 bytes
-rw-r--r--cursor/sugar/sugar-11.pngbin0 -> 12643 bytes
-rw-r--r--cursor/sugar/sugar-2.pngbin0 -> 12686 bytes
-rw-r--r--cursor/sugar/sugar-3.pngbin0 -> 12649 bytes
-rw-r--r--cursor/sugar/sugar-4.pngbin0 -> 12622 bytes
-rw-r--r--cursor/sugar/sugar-5.pngbin0 -> 12576 bytes
-rw-r--r--cursor/sugar/sugar-6.pngbin0 -> 12639 bytes
-rw-r--r--cursor/sugar/sugar-7.pngbin0 -> 12626 bytes
-rw-r--r--cursor/sugar/sugar-8.pngbin0 -> 12553 bytes
-rw-r--r--cursor/sugar/sugar-9.pngbin0 -> 12663 bytes
24 files changed, 1901 insertions, 0 deletions
diff --git a/cursor/Makefile.am b/cursor/Makefile.am
new file mode 100644
index 0000000..5f0148f
--- /dev/null
+++ b/cursor/Makefile.am
@@ -0,0 +1 @@
+SUBDIRS = cursorthemegen sugar
diff --git a/cursor/cursorthemegen/ChangeLog b/cursor/cursorthemegen/ChangeLog
new file mode 100644
index 0000000..40bcc7e
--- /dev/null
+++ b/cursor/cursorthemegen/ChangeLog
@@ -0,0 +1,48 @@
+2003-08-06 Alexander Larsson <alexl@redhat.com>
+
+ * main.c (cursor_get_image):
+ Define variables at top of function.
+
+2003-02-22 Matt Wilson <msw@redhat.com>
+
+ * main.c (cursor_source_find_image): use tmp_list =
+ tmp_list->next;, not tmp_list = tmp_list++;
+ (cursor_add_source): convert missing frames into warnings
+ (cursor_theme_write_cursor): skip frames of size -1 (they're
+ missing cursor files)
+
+Tue Feb 4 14:52:27 2003 Owen Taylor <otaylor@redhat.com>
+
+ * main.c (cursor_theme_check_alias): Add a warning if
+ the alias is defined but missing in the image. (#83252)
+
+2003-01-17 Havoc Pennington <hp@redhat.com>
+
+ * Makefile.am (cursorthemegen_SOURCES): fix distcheck
+
+Thu Jan 16 15:25:47 2003 Owen Taylor <otaylor@redhat.com>
+
+ * README: Add, add some docs.
+
+ * main.c themefile.c cursortheme.h: Put under MIT-style
+ license.
+
+ * Makefile.am generic.cursortheme sample.cursortheme:
+ Rename generic.cursortheme => sample.cursortheme
+
+Wed Jan 15 23:48:39 2003 Owen Taylor <otaylor@redhat.com>
+
+ * main.c themefile.c: Use a hash table instead of a
+ list of cursors.
+
+ * themefile.c generic.cursortheme: Remove <sources> element,
+ put <source> elements directly under <theme> node.
+
+ * themefile.c generic.cursortheme: Rename <rows> element to
+ <layout>.
+
+Wed Jan 15 18:25:24 2003 Owen Taylor <otaylor@redhat.com>
+
+ * Import sources into CVS, start ChangeLog
+
+
diff --git a/cursor/cursorthemegen/Makefile.am b/cursor/cursorthemegen/Makefile.am
new file mode 100644
index 0000000..3ef188d
--- /dev/null
+++ b/cursor/cursorthemegen/Makefile.am
@@ -0,0 +1,11 @@
+INCLUDES = $(GDK_PIXBUF2_CFLAGS)
+
+noinst_PROGRAMS = cursorthemegen
+
+cursorthemegen_LDADD = $(GDK_PIXBUF2_LIBS)
+cursorthemegen_SOURCES = \
+ cursortheme.h \
+ themefile.c \
+ main.c
+
+EXTRA_DIST = sample.cursortheme
diff --git a/cursor/cursorthemegen/README b/cursor/cursorthemegen/README
new file mode 100644
index 0000000..e43a7c1
--- /dev/null
+++ b/cursor/cursorthemegen/README
@@ -0,0 +1,186 @@
+Introduction
+============
+
+cursorthemegen is a utility for generating libXcursor
+cursor themes.
+
+The inputs to cursorthemegen are conceptually:
+
+ A) A set of multi-layer images, one for each
+ size.
+ B) A XML theme description file
+
+Each image contains all the cursors arranged in
+a grid; the layers are:
+
+ - A layer with a dot for the hotspot of each cursor
+ - The main image or first animation frame for multi-frame
+ animated cursors
+ - The second animation frame for multi-frame animated cursors
+ - ...
+
+In practice, since loading of multilayer images is not
+supported by standard image libraries, each layer is
+input as a separate image file.
+
+The cursor theme file contains information about the
+source images to read, the location of each named
+cursor within the grid, and a set of aliases from
+names to other names.
+
+
+Invocation
+==========
+
+cursorthemegen takes two arguments:
+
+ cursorthemegen THEMEFILE OUTPUTDIR
+
+cursorthemegen reads in THEMEFILE, compiles a cursor
+theme from it, creates the OUTPUTDIR directory and
+writes the result in it. The OUTPUTDIR directory must
+not already exist.
+
+
+Theme file format
+=================
+
+The tags in the Theme file format are:
+
+<theme>: The toplevel element. There must be
+ exactly one of these.
+
+ Attributes:
+ name: The name of the theme. Required.
+
+ Child elements:
+ <alias>, <layout>, <source>
+
+<alias>: The alias element defines an alias of
+ one cursor name to another cursor.
+
+ Attributes:
+ name: The name of the alias. Required.
+ target: The name cursor (or alias) to which the
+ alias points. Required.
+
+ Child elements:
+ None
+
+<source>: Defines a multilayer source
+
+ Attributes:
+ size: The nominal size of the cursors in the
+ this source. This is used when selecting a
+ cursor. Required.
+
+ gridsize: The size of the grid used for this
+ source. Defaults to the same as 'size'.
+ Optional.
+
+ Child elements:
+ <image>
+
+<image>: One layer in a multilayer source
+
+ Attributes:
+ file: The filename to get the image from. Required.
+ use: Either an integer indicating what animation
+ frame this is (0, 1, ...) or "hotspot" to
+ indicate that this frame contains hotspots. Required.
+
+ Child elements:
+ None
+
+<layout>: Defines the layout of cursors within the grid.
+
+ Attributes:
+ None
+
+ Child elements:
+ <row>
+
+<row>: Defines one row of cursors within the grid
+
+ Attributes:
+ None
+
+ Child elements:
+ <cursor>
+
+<cursor>: Defines a single cursor. If there are no
+ child <frame> elements, it is assumed to be a non-animated
+ cursor with a single frame in it. Otherwise, one
+ or more frames is specified with child <frame> elements.
+
+ Attributes:
+ name: The name of the cursor. Required.
+
+ Child elements:
+ <frame>
+
+<frame>: Defines one frame of a multiframe cursor.
+
+ Attributes:
+ delay: The delay before the next frame is shown in
+ milliseconds. Optional. (If you don't specify the delays
+ for a multiframe animation, the result is undefined.)
+
+ Child elements:
+ None
+
+Note that cursorthemegen doesn't actually use a full
+XML parser, but the XML-subset GMarkup parser from
+GLib, so some less common XML constructs may not work.
+
+
+Makefile example
+================
+
+Makefile rules for generating and installing a theme
+look like:
+
+bluecurve_images = \
+ Bluecurve-1-24.png \
+ Bluecurve-2-24.png \
+ Bluecurve-hotspot-24.png
+
+Bluecurve.stamp: $(bluecurve_images) Bluecurve.cursortheme
+ rm -rf Bluecurve && \
+ $(THEMEGEN) Bluecurve.cursortheme Bluecurve && \
+ touch Bluecurve.stamp
+
+install:
+ rm -rf $(datadir)/icons/Bluecurve/cursors
+ mkdir -p $(datadir)/icons/Bluecurve/cursors
+ for i in `cd Bluecurve && echo *` ; do \
+ if test -L Bluecurve/$$i ; then \
+ cp -d Bluecurve/$$i $(datadir)/icons/Bluecurve/cursors/$$i ; \
+ else \
+ install -m 0644 Bluecurve/$$i $(datadir)/icons/Bluecurve/cursors/$$i ; \
+ fi ; \
+ done
+
+
+License
+=======
+
+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.
+
diff --git a/cursor/cursorthemegen/cursortheme.h b/cursor/cursorthemegen/cursortheme.h
new file mode 100644
index 0000000..78f14fc
--- /dev/null
+++ b/cursor/cursorthemegen/cursortheme.h
@@ -0,0 +1,92 @@
+/*
+ * 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.
+ */
+#ifndef __CURSORTHEME_H__
+#define __CURSORTHEME_H__
+
+#include <gdk-pixbuf/gdk-pixbuf.h>
+
+G_BEGIN_DECLS
+
+typedef struct _Cursor Cursor;
+typedef struct _CursorAlias CursorAlias;
+typedef struct _CursorFrame CursorFrame;
+typedef struct _CursorFrameConfig CursorFrameConfig;
+typedef struct _CursorImage CursorImage;
+typedef struct _CursorSource CursorSource;
+typedef struct _CursorTheme CursorTheme;
+
+struct _Cursor
+{
+ char *name;
+ int row;
+ int column;
+ GSList *frame_configs;
+ GSList *frames;
+};
+
+struct _CursorAlias
+{
+ char *name;
+ char *target;
+};
+
+struct _CursorFrameConfig
+{
+ int delay;
+};
+
+struct _CursorFrame
+{
+ int size;
+ int hot_x;
+ int hot_y;
+ int delay;
+ GdkPixbuf *image;
+};
+
+struct _CursorImage
+{
+ int use; /* Frame number or -1 for hotspot */
+ GdkPixbuf *image;
+};
+
+struct _CursorSource
+{
+ int size;
+ int gridsize;
+ GSList *images;
+};
+
+struct _CursorTheme
+{
+ char *name;
+ GSList *sources;
+ GHashTable *cursors;
+ GHashTable *aliases;
+};
+
+CursorTheme *cursor_theme_read (const char *filename);
+
+G_END_DECLS
+
+#endif /* __CURSORTHEME_H__ */
diff --git a/cursor/cursorthemegen/main.c b/cursor/cursorthemegen/main.c
new file mode 100644
index 0000000..fa1d3d1
--- /dev/null
+++ b/cursor/cursorthemegen/main.c
@@ -0,0 +1,615 @@
+/*
+ * 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 <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#include "cursortheme.h"
+
+static CursorImage *
+cursor_source_find_image (CursorSource *source,
+ int use)
+{
+ GSList *tmp_list;
+
+ for (tmp_list = source->images; tmp_list; tmp_list = tmp_list->next)
+ {
+ CursorImage *image = tmp_list->data;
+
+ if (image->use == use)
+ return image;
+ }
+
+ return NULL;
+}
+
+static gboolean
+cursor_find_hotspot (Cursor *cursor,
+ CursorSource *source,
+ int *hot_x,
+ int *hot_y)
+{
+ CursorImage *image;
+ int x = cursor->column * source->gridsize;
+ int y = cursor->row * source->gridsize;
+
+ *hot_x = -1;
+ *hot_y = -1;
+
+ image = cursor_source_find_image (source, -1);
+ if (!image)
+ {
+ g_printerr ("Source at size %d doesn't have a hotspot image\n",
+ source->size);
+ return FALSE;
+ }
+
+ if (!gdk_pixbuf_get_has_alpha (image->image) ||
+ gdk_pixbuf_get_colorspace (image->image) != GDK_COLORSPACE_RGB)
+ {
+ g_printerr ("Invalid format for hotspot file\n");
+ return FALSE;
+ }
+
+ if (x + source->gridsize <= gdk_pixbuf_get_width (image->image) &&
+ y + source->gridsize <= gdk_pixbuf_get_height (image->image))
+ {
+ int rowstride;
+ guchar *pixels;
+ int i, j;
+
+ rowstride = gdk_pixbuf_get_rowstride (image->image);
+ pixels = gdk_pixbuf_get_pixels (image->image) + y * rowstride + x * 4;
+ for (j = 0; j < source->gridsize; j++)
+ {
+ for (i = 0; i < source->gridsize; i++)
+ {
+ if (pixels[4*i + 3] > 0x80)
+ {
+ if (*hot_x >=0 || *hot_y >= 0)
+ {
+ g_printerr ("Multiple hotspots for cursor %s at size %d\n",
+ cursor->name, source->size);
+ return FALSE;
+ }
+ *hot_x = i;
+ *hot_y = j;
+ }
+ }
+ pixels += rowstride;
+ }
+ }
+
+ if (*hot_x == -1 || *hot_y == -1)
+ {
+ g_printerr ("Cannot find hotspot for cursor %s at size %d\n",
+ cursor->name, source->size);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static gboolean
+cursor_is_present (Cursor *cursor,
+ CursorSource *source,
+ int frame)
+{
+ CursorImage *image = cursor_source_find_image (source, frame);
+ int x = cursor->column * source->gridsize;
+ int y = cursor->row * source->gridsize;
+ int i, j;
+
+ const guchar *pixels;
+ int rowstride;
+ int n_channels;
+
+ if (!image)
+ return FALSE;
+
+ if (gdk_pixbuf_get_colorspace (image->image) != GDK_COLORSPACE_RGB)
+ {
+ g_printerr ("Invalid format for image file\n");
+ return FALSE;
+ }
+
+ if (x + source->gridsize > gdk_pixbuf_get_width (image->image) ||
+ y + source->gridsize > gdk_pixbuf_get_height (image->image))
+ return FALSE;
+
+ rowstride = gdk_pixbuf_get_rowstride (image->image);
+ n_channels = gdk_pixbuf_get_n_channels (image->image);
+
+ if (n_channels == 3)
+ return TRUE;
+
+ pixels = gdk_pixbuf_get_pixels (image->image) + y * rowstride + x * 4;
+
+ for (j = 0; j < source->gridsize; j++)
+ {
+ for (i = 0; i < source->gridsize; i++)
+ {
+ if (pixels[4*i + 3] != 0)
+ return TRUE;
+ }
+ pixels += rowstride;
+ }
+
+ return FALSE;
+}
+
+static void
+cursor_frame_find_bounds (Cursor *cursor,
+ CursorFrame *frame,
+ CursorSource *source,
+ int frame_index,
+ int *x,
+ int *y,
+ int *width,
+ int *height)
+{
+ CursorImage *image = cursor_source_find_image (source, frame_index);
+ int start_x = cursor->column * source->gridsize;
+ int start_y = cursor->row * source->gridsize;
+ int i, j;
+
+ const guchar *pixels;
+ int rowstride;
+ int n_channels;
+
+ rowstride = gdk_pixbuf_get_rowstride (image->image);
+ n_channels = gdk_pixbuf_get_n_channels (image->image);
+
+ if (n_channels == 3)
+ {
+ *x = start_x;
+ *y = start_x;
+ *width = source->gridsize;
+ *height = source->gridsize;
+
+ return;
+ }
+ else
+ {
+ int min_x = start_x + frame->hot_x;
+ int max_x = start_x + frame->hot_x + 1;
+ int min_y = start_y + frame->hot_y;
+ int max_y = start_y + frame->hot_y + 1;
+
+ pixels = gdk_pixbuf_get_pixels (image->image) + start_y * rowstride + start_x * 4;
+
+ for (j = 0; j < source->gridsize; j++)
+ {
+ for (i = 0; i < source->gridsize; i++)
+ {
+ if (pixels[4*i + 3] != 0)
+ {
+ if (start_x + i < min_x)
+ min_x = start_x + i;
+ if (start_x + i >= max_x)
+ max_x = start_x + i + 1;
+ if (start_y + j < min_y)
+ min_y = start_y + j;
+ if (start_y + j >= max_y)
+ max_y = start_y + j + 1;
+ }
+ }
+
+ pixels += rowstride;
+ }
+
+ *x = min_x;
+ *y = min_y;
+ *width = max_x - min_x;
+ *height = max_y - min_y;
+ }
+}
+
+static void
+cursor_get_image (Cursor *cursor,
+ CursorFrame *frame,
+ CursorSource *source,
+ int frame_index,
+ int x,
+ int y,
+ int width,
+ int height)
+{
+ CursorImage *image = cursor_source_find_image (source, frame_index);
+ GdkPixbuf *tmp_pixbuf;
+
+ /*cursor_frame_find_bounds (cursor, frame, source, frame_index,
+ &x, &y, &width, &height);
+*/
+ tmp_pixbuf = gdk_pixbuf_new_subpixbuf (image->image,
+ x, y, width, height);
+
+ frame->hot_x -= x - cursor->column * source->gridsize;
+ frame->hot_y -= y - cursor->row * source->gridsize;
+
+ frame->image = gdk_pixbuf_copy (tmp_pixbuf);
+ g_object_unref (tmp_pixbuf);
+}
+
+static gboolean
+cursor_add_source (Cursor *cursor,
+ CursorSource *source)
+{
+ GSList *tmp_list;
+ int hot_x, hot_y;
+ int i;
+ int x, y, x2, y2, height, width;
+
+ /* If the first frame is missing we silently treat
+ * it as OK
+ */
+ if (!cursor_is_present (cursor, source, 0))
+ return TRUE;
+
+ if (!cursor_find_hotspot (cursor, source, &hot_x, &hot_y))
+ return FALSE;
+
+ /* generate a single hight width for all cursors in a
+ given animation*/
+
+ for (tmp_list = cursor->frame_configs, i = 0;
+ tmp_list;
+ tmp_list = tmp_list->next, i++)
+ {
+ CursorFrameConfig *frame_config = tmp_list->data;
+ CursorFrame *frame;
+ int tx, ty, tx2, ty2, twidth, theight;
+
+ if (i != 0 && !cursor_is_present (cursor, source, i))
+ continue;
+
+ frame = g_new0 (CursorFrame, 1);
+ frame->size = source->size;
+ frame->hot_x = hot_x;
+ frame->hot_y = hot_y;
+ frame->delay = frame_config->delay;
+
+ cursor_frame_find_bounds (cursor, frame, source, i,
+ &tx, &ty, &twidth, &theight);
+
+ tx2 = tx + twidth;
+ ty2 = ty + theight;
+
+ if (i == 0)
+ {
+ x = tx;
+ y = ty;
+ x2 = tx2;
+ y2 = ty2;
+ }
+ else
+ {
+ if (tx < x)
+ x = tx;
+
+ if (ty < y)
+ y = ty;
+
+ if (tx2 > x2)
+ x2 = tx2;
+
+ if (ty2 > y2)
+ y2 = ty2;
+ }
+
+ g_free (frame);
+ }
+
+ width = x2 - x;
+ height = y2 - y;
+
+ for (tmp_list = cursor->frame_configs, i = 0;
+ tmp_list;
+ tmp_list = tmp_list->next, i++)
+ {
+ CursorFrameConfig *frame_config = tmp_list->data;
+ CursorFrame *frame;
+
+ if (i != 0 && !cursor_is_present (cursor, source, i))
+ {
+ g_printerr ("Frame %d missing for cursor '%s' at size %d\n",
+ i, cursor->name, source->size);
+ frame = g_new0 (CursorFrame, 1);
+ frame->size = -1;
+ cursor->frames = g_slist_append (cursor->frames, frame);
+ continue;
+ }
+
+ frame = g_new0 (CursorFrame, 1);
+ frame->size = source->size;
+ frame->hot_x = hot_x;
+ frame->hot_y = hot_y;
+ frame->delay = frame_config->delay;
+
+ cursor_get_image (cursor, frame, source, i, x, y, width, height);
+
+ cursor->frames = g_slist_append (cursor->frames, frame);
+ }
+
+ return TRUE;
+}
+
+static gboolean
+cursor_theme_read_cursor (CursorTheme *theme,
+ Cursor *cursor)
+{
+ GSList *tmp_list;
+
+ for (tmp_list = theme->sources; tmp_list; tmp_list = tmp_list->next)
+ {
+ if (!cursor_add_source (cursor, tmp_list->data))
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static void
+cursor_theme_write_cursor (CursorTheme *theme,
+ Cursor *cursor)
+{
+ GSList *tmp_list;
+ char *config_filename;
+ char *command;
+ FILE *config_file;
+ GError *error = NULL;
+ int status;
+ int i;
+
+ if (g_hash_table_lookup (theme->aliases, cursor->name))
+ {
+ g_printerr ("Warning: cursor '%s' overridden by alias\n", cursor->name);
+ return;
+ }
+
+ if (!cursor->frames)
+ return;
+
+ config_filename = g_strconcat (cursor->name, ".cfg", NULL);
+ config_file = fopen (config_filename, "w");
+
+ if (!config_file)
+ {
+ g_printerr ("Cannot open config file '%s'\n", config_filename);
+ return;
+ }
+
+ for (tmp_list = cursor->frames, i = 0; tmp_list; tmp_list = tmp_list->next, i++)
+ {
+ CursorFrame *frame = tmp_list->data;
+ char *filename;
+
+ if (frame->size == -1)
+ continue;
+ filename = g_strdup_printf ("%s-%d.png", cursor->name, i);
+ if (gdk_pixbuf_save (frame->image, filename, "png", &error, NULL))
+ {
+ if (frame->delay > 0)
+ fprintf (config_file, "%d %d %d %s %d\n",
+ frame->size, frame->hot_x, frame->hot_y, filename, frame->delay);
+ else
+ fprintf (config_file, "%d %d %d %s\n",
+ frame->size, frame->hot_x, frame->hot_y, filename);
+ }
+ else
+ {
+ g_printerr ("Error saving image file: %s\n", error->message);
+ g_error_free (error);
+ }
+ g_free (filename);
+ }
+
+ fclose (config_file);
+
+ command = g_strdup_printf ("sh -c 'xcursorgen %s > %s'\n",
+ config_filename, cursor->name);
+ if (!g_spawn_command_line_sync (command, NULL, NULL, &status, &error))
+ {
+ g_printerr ("Error running xcursorgen for %s: %s\n",
+ cursor->name, error->message);
+ g_error_free (error);
+ }
+ else if (status)
+ {
+ g_printerr ("Error running xcursorgen for %s\n",
+ cursor->name);
+ }
+ else
+ {
+ /* Only delete temporary files if no error occurred
+ */
+ unlink (config_filename);
+ g_free (config_filename);
+
+ for (tmp_list = cursor->frames, i = 0; tmp_list; tmp_list = tmp_list->next, i++)
+ {
+ char *filename;
+
+ filename = g_strdup_printf ("%s-%d.png", cursor->name, i);
+ unlink (filename);
+ g_free (filename);
+ }
+ }
+}
+
+static const char *
+cursor_theme_check_alias (CursorTheme *theme,
+ CursorAlias *alias)
+{
+ /* Dereference, using tortoise-and-hare checkign for circular aliases
+ */
+ CursorAlias *tortoise = alias;
+ CursorAlias *hare = alias;
+ Cursor *target;
+
+ while (TRUE)
+ {
+ CursorAlias *next;
+
+ next = g_hash_table_lookup (theme->aliases, hare->target);
+ if (!next)
+ break;
+ hare = next;
+
+ if (hare == tortoise)
+ goto found_loop;
+
+ next = g_hash_table_lookup (theme->aliases, hare->target);
+ if (!next)
+ break;
+ hare = next;
+
+ if (hare == tortoise)
+ goto found_loop;
+
+ tortoise = g_hash_table_lookup (theme->aliases, tortoise->target);
+ }
+
+ target = g_hash_table_lookup (theme->cursors, hare->target);
+
+ if (!target || !target->frames)
+ {
+ g_printerr ("Cursor '%s', which is the target of alias '%s', is not in theme\n",
+ hare->target, alias->name);
+ return NULL;
+ }
+
+ return hare->target;
+
+ found_loop:
+ g_printerr ("Circular looop detected when dereferencing alias '%s'\n",
+ alias->name);
+ return NULL;
+}
+
+static void
+cursor_theme_write_alias (CursorTheme *theme,
+ CursorAlias *alias)
+{
+ const char *target = cursor_theme_check_alias (theme, alias);
+ if (!target)
+ return;
+
+ if (symlink (target, alias->name) < 0)
+ {
+ g_printerr ("Error creating symlink for alias '%s' to '%s': %s\n",
+ alias->name, target, g_strerror (errno));
+ }
+}
+
+static void
+write_cursor_foreach (gpointer key,
+ gpointer value,
+ gpointer data)
+{
+ cursor_theme_write_cursor (data, value);
+}
+
+static void
+write_alias_foreach (gpointer key,
+ gpointer value,
+ gpointer data)
+{
+ cursor_theme_write_alias (data, value);
+}
+
+static gboolean
+cursor_theme_write (CursorTheme *theme,
+ const char *output_dir)
+{
+ char *curdir;
+
+ if (mkdir (output_dir, 0755) < 0 && errno != EEXIST)
+ {
+ g_printerr ("Error creating output directory '%s'\n: %s",
+ output_dir, g_strerror (errno));
+ return FALSE;
+ }
+
+ curdir = g_get_current_dir ();
+ if (chdir (output_dir) < 0)
+ {
+ g_printerr ("Could not change to output directory '%s'\n", output_dir);
+ return FALSE;
+ }
+
+ g_hash_table_foreach (theme->cursors,
+ write_cursor_foreach,
+ theme);
+
+ g_hash_table_foreach (theme->aliases,
+ write_alias_foreach,
+ theme);
+
+ chdir (curdir);
+
+ return TRUE;
+}
+
+void
+usage (void)
+{
+ g_printerr ("Usage: cursorthemegen CONFIG_FILE OUTPUT_DIR\n");
+ exit (1);
+}
+
+static void
+read_cursor_foreach (gpointer key,
+ gpointer value,
+ gpointer data)
+{
+ if (!cursor_theme_read_cursor (data, value))
+ exit (1);
+}
+
+int
+main (int argc, char **argv)
+{
+ CursorTheme *theme;
+
+ g_type_init ();
+
+ if (argc != 3)
+ usage ();
+
+ theme = cursor_theme_read (argv[1]);
+ if (!theme)
+ exit (1);
+
+ g_hash_table_foreach (theme->cursors,
+ read_cursor_foreach,
+ theme);
+
+ if (!cursor_theme_write (theme, argv[2]))
+ exit (1);
+
+ return 0;
+}
diff --git a/cursor/cursorthemegen/sample.cursortheme b/cursor/cursorthemegen/sample.cursortheme
new file mode 100644
index 0000000..e288b42
--- /dev/null
+++ b/cursor/cursorthemegen/sample.cursortheme
@@ -0,0 +1,148 @@
+<theme name="Bluecurve">
+ <source size="48">
+ <image file="Bluecurve-1-48.png" use="0"/>
+ <image file="Bluecurve-2-48.png" use="1"/ >
+ <image file="Bluecurve-hotspot-48.png" use="hotspot"/>
+ </source>
+ <layout>
+ <row>
+ <cursor name="X_cursor"/>
+ <cursor name="arrow"/>
+ <cursor name="based_arrow_down"/>
+ <cursor name="based_arrow_up"/>
+ <cursor name="boat"/>
+ <cursor name="bogosity"/>
+ <cursor name="bottom_left_corner"/>
+ <cursor name="bottom_right_corner"/>
+ </row><row>
+ <cursor name="bottom_side"/>
+ <cursor name="bottom_tee"/>
+ <cursor name="box_spiral"/>
+ <cursor name="center_ptr"/>
+ <cursor name="circle"/>
+ <cursor name="clock"/>
+ <cursor name="coffee_mug"/>
+ <cursor name="cross"/>
+ </row><row>
+ <cursor name="cross_reverse"/>
+ <cursor name="crosshair"/>
+ <cursor name="diamond_cross"/>
+ <cursor name="dot"/>
+ <cursor name="dotbox"/>
+ <cursor name="double_arrow"/>
+ <cursor name="draft_large"/>
+ <cursor name="draft_small"/>
+ </row><row>
+ <cursor name="draped_box"/>
+ <cursor name="exchange"/>
+ <cursor name="fleur"/>
+ <cursor name="gobbler"/>
+ <cursor name="gumby"/>
+ <cursor name="hand1"/>
+ <cursor name="hand2"/>
+ <cursor name="heart"/>
+ </row><row>
+ <cursor name="icon"/>
+ <cursor name="iron_cross"/>
+ <cursor name="left_ptr"/>
+ <cursor name="left_side"/>
+ <cursor name="left_tee"/>
+ <cursor name="leftbutton"/>
+ <cursor name="ll_angle"/>
+ <cursor name="lr_angle"/>
+ </row><row>
+ <cursor name="man"/>
+ <cursor name="middlebutton"/>
+ <cursor name="mouse"/>
+ <cursor name="pencil"/>
+ <cursor name="pirate"/>
+ <cursor name="plus"/>
+ <cursor name="question_arrow"/>
+ <cursor name="right_ptr"/>
+ </row><row>
+ <cursor name="right_side"/>
+ <cursor name="right_tee"/>
+ <cursor name="rightbutton"/>
+ <cursor name="rtl_logo"/>
+ <cursor name="sailboat"/>
+ <cursor name="sb_down_arrow"/>
+ <cursor name="sb_h_double_arrow"/>
+ <cursor name="sb_left_arrow"/>
+ </row><row>
+ <cursor name="sb_right_arrow"/>
+ <cursor name="sb_up_arrow"/>
+ <cursor name="sb_v_double_arrow"/>
+ <cursor name="shuttle"/>
+ <cursor name="sizing"/>
+ <cursor name="spider"/>
+ <cursor name="spraycan"/>
+ <cursor name="star"/>
+ </row><row>
+ <cursor name="target"/>
+ <cursor name="tcross"/>
+ <cursor name="top_left_arrow"/>
+ <cursor name="top_left_corner"/>
+ <cursor name="top_right_corner"/>
+ <cursor name="top_side"/>
+ <cursor name="top_tee"/>
+ <cursor name="trek"/>
+ </row><row>
+ <cursor name="ul_angle"/>
+ <cursor name="umbella"/>
+ <cursor name="ur_angle"/>
+ <cursor name="watch"/>
+ <cursor name="xterm"/>
+ <cursor name="left_ptr_watch">
+ <frame delay="500"/>
+ <frame delay="500"/>
+ </cursor>
+ </row>
+ </layout>
+<!--
+ The number of cursors in the WhiteGlass theme distributed with XFree86
+ are reduced using the following aliases:
+
+ <alias name="arrow" target="right_ptr"/>
+ <alias name="cross_reverse" target="cross"/>
+ <alias name="crosshair" target="cross"/>
+ <alias name="draft_large" target="right_ptr"/>
+ <alias name="draft_small" target="right_ptr"/>
+ <alias name="plus" target="cross"/>
+ <alias name="tcross" target="cross"/>
+ <alias name="top_left_arrow" target="top_left_arrow"/>
+ -->
+
+<!--
+ Aliases can also be used to provide cursors to be used in place
+ of particular legacy cursor shapes; whenever libXcursor is
+ asked to create a bitmap cursor, it creates a hash value
+ from the cursor shape, and tries using that as a cursor
+ name. Setting the environment variable XCURSOR_DISCOVER will
+ log this process, which is useful for finding out the hash values.
+
+ The following maps the mozilla pointer-while-waiting cursor to
+ left_ptr_watch.
+-->
+ <!-- moz_spinning (left_ptr with watch) -->
+ <alias name="08e8e1c95fe2fc01f976f1e063a24ccd" target="left_ptr_watch"/>
+
+<!--
+ More mozilla hash values that could be aliased:
+
+ moz_alias 0876e1c15ff2fc01f906f1c363074c0f (left_ptr with small arrow)
+ moz_copy 08ffe1cb5fe6fc01f906f1c063814ccf (left_ptr with plus)
+ moz_hand_grabbing 208530c400c041818281048008011002 (closed hand)
+ moz_hand_grab 5aca4d189052212118709018842178c0 (open hand)
+ moz_menu 08ffe1e65f80fcfdf9fff11263e74c48 (left_ptr with menu) */
+ moz_question_arrow 5c6cd98b3f3ebcb1f9c7f1c204630408 (left_ptr with ?)
+
+ GTK+ toolkit cursors:
+
+ DND copy 1081e37283d90000800003c07f3ef6bf
+ DND move 9081237383d90e509aa00f00170e968f
+ DND link 3085a0e285430894940527032f8b26df
+ DND no drop 1001208387f90000800003000700f6ff
+
+ Eye dropper 90b3018312820b127e5a0b7c8034a301
+-->
+</theme>
diff --git a/cursor/cursorthemegen/themefile.c b/cursor/cursorthemegen/themefile.c
new file mode 100644
index 0000000..a8a703f
--- /dev/null
+++ b/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;
+ }
+}
diff --git a/cursor/sugar/ChangeLog b/cursor/sugar/ChangeLog
new file mode 100644
index 0000000..ac10780
--- /dev/null
+++ b/cursor/sugar/ChangeLog
@@ -0,0 +1,9 @@
+2003-01-17 Havoc Pennington <hp@redhat.com>
+
+ * Makefile.am (EXTRA_DIST): fix distcheck
+
+Thu Jan 16 14:56:26 2003 Owen Taylor <otaylor@redhat.com>
+
+ * Bluecurve.cursortheme: Conform to new syntax,
+ add aliases.
+
diff --git a/cursor/sugar/Makefile.am b/cursor/sugar/Makefile.am
new file mode 100644
index 0000000..fb86ae2
--- /dev/null
+++ b/cursor/sugar/Makefile.am
@@ -0,0 +1,40 @@
+THEMEGEN = $(top_builddir)/art/cursor/cursorthemegen/cursorthemegen
+THEME_DIR = $(top_builddir)/art/cursor/sugar/theme
+
+all-local: sugar.stamp
+
+sugar_images = \
+ sugar-0.png \
+ sugar-1.png \
+ sugar-2.png \
+ sugar-3.png \
+ sugar-4.png \
+ sugar-5.png \
+ sugar-6.png \
+ sugar-7.png \
+ sugar-8.png \
+ sugar-9.png \
+ sugar-10.png \
+ sugar-11.png \
+ sugar-hotspots.png
+
+sugar.stamp: $(sugar_images) $(THEMEGEN) sugar.cursortheme
+ rm -rf sugar && \
+ BDIR=`pwd` && cd $(srcdir) && \
+ $$BDIR/$(THEMEGEN) sugar.cursortheme $$BDIR/theme && \
+ cd $$BDIR && touch sugar.stamp
+
+clean-local:
+ rm -rf $(THEME_DIR)
+ rm -rf $(top_builddir)/art/cursor/sugar/sugar.stamp
+
+install-data-local:
+ $(mkinstalldirs) $(DESTDIR)$(datadir)/icons/sugar/cursors/
+ for i in `cd $(THEME_DIR) && echo *` ; do \
+ $(INSTALL) $(THEME_DIR)/$$i $(DESTDIR)$(datadir)/icons/sugar/cursors/$$i ; \
+ done
+
+uninstall-local:
+ rm -rf $(DESTDIR)$(datadir)/icons/sugar/cursors
+
+EXTRA_DIST=$(sugar_images) sugar.cursortheme
diff --git a/cursor/sugar/olpc-hotspots.png b/cursor/sugar/olpc-hotspots.png
new file mode 100644
index 0000000..6fefa39
--- /dev/null
+++ b/cursor/sugar/olpc-hotspots.png
Binary files differ
diff --git a/cursor/sugar/olpc.cursortheme b/cursor/sugar/olpc.cursortheme
new file mode 100644
index 0000000..402ba6e
--- /dev/null
+++ b/cursor/sugar/olpc.cursortheme
@@ -0,0 +1,142 @@
+<theme name="OLPC">
+ <source size="48" gridsize="48">
+ <image file="olpc-0.png" use="0"/>
+ <image file="olpc-1.png" use="1"/>
+ <image file="olpc-2.png" use="2"/>
+ <image file="olpc-3.png" use="3"/>
+ <image file="olpc-4.png" use="4"/>
+ <image file="olpc-5.png" use="5"/>
+ <image file="olpc-6.png" use="6"/>
+ <image file="olpc-7.png" use="7"/>
+ <image file="olpc-8.png" use="8"/>
+ <image file="olpc-9.png" use="9"/>
+ <image file="olpc-10.png" use="10"/>
+ <image file="olpc-11.png" use="11"/>
+
+ <image file="olpc-hotspots.png" use="hotspot"/>
+ </source>
+
+ <alias name="top_left_arrow" target="left_ptr"/>
+
+ <alias name="top_right_arrow" target="right_ptr"/>
+ <alias name="arrow" target="right_ptr"/>
+ <alias name="draft_small" target="right_ptr"/>
+ <alias name="draft_large" target="right_ptr"/>
+
+ <alias name="close" target="pirate"/>
+ <alias name="destroy" target="pirate"/>
+
+ <alias name="right_side" target="left_side"/>
+ <alias name="bottom_side" target="top_side"/>
+ <alias name="double_arrow" target="top_side"/>
+ <alias name="top_right_corner" target="bottom_left_corner"/>
+ <alias name="top_left_corner" target="bottom_right_corner"/>
+
+ <layout>
+ <row>
+ <cursor name="left_ptr"/>
+ <cursor name="center_ptr"/>
+ <cursor name="right_ptr"/>
+ <cursor name="exchange"/>
+ <cursor name="plus"/>
+ <cursor name="minus"/>
+ </row><row>
+ <cursor name="question_arrow"/>
+ <cursor name="link"/>
+ <cursor name="unavailable"/>
+ <cursor name="pirate"/>
+ <cursor name="left_ptr_watch">
+ <frame delay="60"/>
+ <frame delay="60"/>
+ <frame delay="60"/>
+ <frame delay="60"/>
+ <frame delay="60"/>
+ <frame delay="60"/>
+ <frame delay="60"/>
+ <frame delay="60"/>
+ <frame delay="60"/>
+ <frame delay="60"/>
+ <frame delay="60"/>
+ <frame delay="60"/>
+ </cursor>
+ <cursor name="left_ptr_modify"/>
+ </row><row>
+ <cursor name="busy">
+ <frame delay="60"/>
+ <frame delay="60"/>
+ <frame delay="60"/>
+ <frame delay="60"/>
+ <frame delay="60"/>
+ <frame delay="60"/>
+ <frame delay="60"/>
+ <frame delay="60"/>
+ <frame delay="60"/>
+ <frame delay="60"/>
+ <frame delay="60"/>
+ <frame delay="60"/>
+ </cursor>
+ <cursor name="circle"/>
+ <cursor name="hand1"/>
+ <cursor name="hand2"/>
+ <cursor name="hand_open"/>
+ <cursor name="hand_closed"/>
+ </row><row>
+ <cursor name="sb_left_arrow"/>
+ <cursor name="sb_right_arrow"/>
+ <cursor name="sb_up_arrow"/>
+ <cursor name="sb_down_arrow"/>
+ <cursor name="sb_v_double_arrow"/>
+ <cursor name="sb_h_double_arrow"/>
+ </row><row>
+ <cursor name="bottom_right_corner"/>
+ <cursor name="bottom_left_corner"/>
+ <cursor name="top_side"/>
+ <cursor name="right_side"/>
+ <cursor name="fleur"/>
+ <cursor name="xterm"/>
+ </row><row>
+ <cursor name="crosshair"/>
+ <cursor name="pencil"/>
+ </row>
+ </layout>
+
+<!--
+ Aliases can also be used to provide cursors to be used in place
+ of particular legacy cursor shapes; whenever libXcursor is
+ asked to create a bitmap cursor, it creates a hash value
+ from the cursor shape, and tries using that as a cursor
+ name. Setting the environment variable XCURSOR_DISCOVER will
+ log this process, which is useful for finding out the hash values.
+
+ The following maps the mozilla pointer-while-waiting cursor to
+ left_ptr_watch.
+-->
+
+ <!-- moz_spinning (left_ptr with watch) -->
+ <alias name="08e8e1c95fe2fc01f976f1e063a24ccd" target="left_ptr_watch"/>
+
+ <!-- moz_question_arrow (left_ptr with question) -->
+ <alias name="5c6cd98b3f3ebcb1f9c7f1c204630408" target="question_arrow"/>
+
+<!--
+ More mozilla hash values that could be aliased:
+
+ moz_alias 0876e1c15ff2fc01f906f1c363074c0f (left_ptr with small arrow)
+ moz_copy 08ffe1cb5fe6fc01f906f1c063814ccf (left_ptr with plus)
+ moz_hand_grabbing 208530c400c041818281048008011002 (closed hand)
+ moz_hand_grab 5aca4d189052212118709018842178c0 (open hand)
+ moz_menu 08ffe1e65f80fcfdf9fff11263e74c48 (left_ptr with menu) */
+ moz_question_arrow 5c6cd98b3f3ebcb1f9c7f1c204630408 (left_ptr with ?)
+
+ GTK+ toolkit cursors:
+-->
+
+ <alias name="1081e37283d90000800003c07f3ef6bf" target="dnd_copy"/>
+ <alias name="9081237383d90e509aa00f00170e968f" target="dnd_move"/>
+ <alias name="3085a0e285430894940527032f8b26df" target="dnd_link"/>
+ <alias name="1001208387f90000800003000700f6ff" target="dnd_none"/>
+
+ <alias name="90b3018312820b127e5a0b7c8034a301" target="color-picker"/>
+
+ <alias name="watch" target="left_ptr_watch"/>
+</theme>
diff --git a/cursor/sugar/sugar-0.png b/cursor/sugar/sugar-0.png
new file mode 100644
index 0000000..5e56564
--- /dev/null
+++ b/cursor/sugar/sugar-0.png
Binary files differ
diff --git a/cursor/sugar/sugar-1.png b/cursor/sugar/sugar-1.png
new file mode 100644
index 0000000..340bbf7
--- /dev/null
+++ b/cursor/sugar/sugar-1.png
Binary files differ
diff --git a/cursor/sugar/sugar-10.png b/cursor/sugar/sugar-10.png
new file mode 100644
index 0000000..1d2991b
--- /dev/null
+++ b/cursor/sugar/sugar-10.png
Binary files differ
diff --git a/cursor/sugar/sugar-11.png b/cursor/sugar/sugar-11.png
new file mode 100644
index 0000000..02719c1
--- /dev/null
+++ b/cursor/sugar/sugar-11.png
Binary files differ
diff --git a/cursor/sugar/sugar-2.png b/cursor/sugar/sugar-2.png
new file mode 100644
index 0000000..9e69567
--- /dev/null
+++ b/cursor/sugar/sugar-2.png
Binary files differ
diff --git a/cursor/sugar/sugar-3.png b/cursor/sugar/sugar-3.png
new file mode 100644
index 0000000..bedc3b1
--- /dev/null
+++ b/cursor/sugar/sugar-3.png
Binary files differ
diff --git a/cursor/sugar/sugar-4.png b/cursor/sugar/sugar-4.png
new file mode 100644
index 0000000..1f65d44
--- /dev/null
+++ b/cursor/sugar/sugar-4.png
Binary files differ
diff --git a/cursor/sugar/sugar-5.png b/cursor/sugar/sugar-5.png
new file mode 100644
index 0000000..22b6eba
--- /dev/null
+++ b/cursor/sugar/sugar-5.png
Binary files differ
diff --git a/cursor/sugar/sugar-6.png b/cursor/sugar/sugar-6.png
new file mode 100644
index 0000000..7f5d49e
--- /dev/null
+++ b/cursor/sugar/sugar-6.png
Binary files differ
diff --git a/cursor/sugar/sugar-7.png b/cursor/sugar/sugar-7.png
new file mode 100644
index 0000000..32ec0e0
--- /dev/null
+++ b/cursor/sugar/sugar-7.png
Binary files differ
diff --git a/cursor/sugar/sugar-8.png b/cursor/sugar/sugar-8.png
new file mode 100644
index 0000000..1dd0eea
--- /dev/null
+++ b/cursor/sugar/sugar-8.png
Binary files differ
diff --git a/cursor/sugar/sugar-9.png b/cursor/sugar/sugar-9.png
new file mode 100644
index 0000000..1e57153
--- /dev/null
+++ b/cursor/sugar/sugar-9.png
Binary files differ