diff -u icon-slicer-0.3/debian/compat icon-slicer-0.3/debian/compat --- icon-slicer-0.3/debian/compat +++ icon-slicer-0.3/debian/compat @@ -1,0 +2 @@ + diff -u icon-slicer-0.3/debian/control icon-slicer-0.3/debian/control --- icon-slicer-0.3/debian/control +++ icon-slicer-0.3/debian/control @@ -1,9 +1,10 @@ Source: icon-slicer Section: devel Priority: optional -Maintainer: Debian OLPC +Maintainer: Ubuntu MOTU Developers +XSBC-Original-Maintainer: Debian OLPC Uploaders: Jonas Smedegaard -Build-Depends: cdbs (>= 0.4.39), devscripts (>= 2.10.7), debhelper (>= 6), dh-buildinfo, libgtk2.0-dev (>= 2.0.0), libpopt-dev, help2man +Build-Depends: cdbs (>= 0.4.39), devscripts (>= 2.10.7), debhelper (>= 6), dh-buildinfo, libgtk2.0-dev (>= 2.0.0), libpopt-dev, help2man, quilt Standards-Version: 3.8.1 Vcs-Git: git://git.debian.org/git/collab-maint/icon-slicer.git Vcs-Browser: http://git.debian.org/?p=collab-maint/icon-slicer.git;a=summary diff -u icon-slicer-0.3/debian/changelog icon-slicer-0.3/debian/changelog --- icon-slicer-0.3/debian/changelog +++ icon-slicer-0.3/debian/changelog @@ -1,3 +1,11 @@ +icon-slicer (0.3-2ubuntu1) karmic; urgency=low + + * Merge from debian unstable, remaining changes: + - the src/main.c change that fixes an issue where hotspot was created in + the wrong place is still needed. + + -- Charlie Smotherman Thu, 11 jun 2009 03:02:00 -0500 + icon-slicer (0.3-2) unstable; urgency=low * Remove TopGit noise (from merged-in cdbs-skel). @@ -8,5 +16,38 @@ +icon-slicer (0.3-1ubuntu3) jaunty; urgency=low + + * Bug fix for creating cursors using a separate hotspot + image file. Fixes issue where hotspot was created in the wrong place. + (LP: #339044) + + -- Tim Swast Thur, 12 Mar 2009 23:16:00 -0500 + +icon-slicer (0.3-1ubuntu2) hardy; urgency=low + + * debian/control: change Section from "unknown" to + "x11" (LP: #181624). + + -- Siegfried-Angel Gevatter Pujals (RainCT) Wed, 09 Jan 2008 23:37:41 +0100 + +icon-slicer (0.3-1ubuntu1) hardy; urgency=low + + * New revision (LP: #176202) + * Add a working watch file, and remove the watch.ex template. + * debian/control: + - Bump standards version to 3.7.3. + - Remove package name from its description + - Add Homepage field + - Bump debhelper version to be able to bumb compat version + - Modify Maintainer value to match the DebianMaintainerField + specification. + * debian/copyright: Copy the real license text, as it is indicated in + the header of all source files, there. + * debian/compat: Bump to debhelper compat version 5 + + -- Siegfried-Angel Gevatter Pujals (RainCT) Thu, 13 Dec 2007 22:10:40 +0100 + icon-slicer (0.3-1) unstable; urgency=low * Initial release. Closes: bug#519102. -- Jonas Smedegaard Wed, 08 Apr 2009 01:18:23 +0200 + only in patch2: unchanged: --- icon-slicer-0.3.orig/src/main.c +++ icon-slicer-0.3/src/main.c @@ -103,7 +103,7 @@ if (n_channels == 3) { out->x = start_x; - out->y = start_x; + out->y = start_y; out->width = source->gridsize; out->height = source->gridsize; @@ -137,8 +137,8 @@ { min_x = start_x + i; max_x = start_x + i + 1; - min_y = start_y + i; - max_y = start_y + i + 1; + min_y = start_y + j; + max_y = start_y + j + 1; found = TRUE; } only in patch2: unchanged: --- icon-slicer-0.3.orig/debian/patches/01_main.c_fix +++ icon-slicer-0.3/debian/patches/01_main.c_fix @@ -0,0 +1,24 @@ +Index: icon-slicer-0.3/src/main.c +=================================================================== +--- icon-slicer-0.3.orig/src/main.c 2009-06-11 03:17:04.000000000 -0500 ++++ icon-slicer-0.3/src/main.c 2009-06-11 03:19:33.000000000 -0500 +@@ -103,7 +103,7 @@ + if (n_channels == 3) + { + out->x = start_x; +- out->y = start_x; ++ out->y = start_y; + out->width = source->gridsize; + out->height = source->gridsize; + +@@ -137,8 +137,8 @@ + { + min_x = start_x + i; + max_x = start_x + i + 1; +- min_y = start_y + i; +- max_y = start_y + i + 1; ++ min_y = start_y + j; ++ max_y = start_y + j + 1; + + found = TRUE; + } only in patch2: unchanged: --- icon-slicer-0.3.orig/debian/patches/series +++ icon-slicer-0.3/debian/patches/series @@ -0,0 +1 @@ +01_main.c_fix only in patch2: unchanged: --- icon-slicer-0.3.orig/.pc/applied-patches +++ icon-slicer-0.3/.pc/applied-patches @@ -0,0 +1 @@ +01_main.c_fix only in patch2: unchanged: --- icon-slicer-0.3.orig/.pc/.version +++ icon-slicer-0.3/.pc/.version @@ -0,0 +1 @@ +2 only in patch2: unchanged: --- icon-slicer-0.3.orig/.pc/01_main.c_fix/src/main.c +++ icon-slicer-0.3/.pc/01_main.c_fix/src/main.c @@ -0,0 +1,1142 @@ +/* + * 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 "config.h" + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "themefile.h" + +static void +theme_rectangle_union (ThemeRectangle *src1, + ThemeRectangle *src2, + ThemeRectangle *dest) +{ + int dest_x, dest_y; + + dest_x = MIN (src1->x, src2->x); + dest_y = MIN (src1->y, src2->y); + dest->width = MAX (src1->x + src1->width, src2->x + src2->width) - dest_x; + dest->height = MAX (src1->y + src1->height, src2->y + src2->height) - dest_y; + dest->x = dest_x; + dest->y = dest_y; +} + +static ThemeImage * +theme_source_find_image (ThemeSource *source, + int use) +{ + GSList *tmp_list; + + for (tmp_list = source->images; tmp_list; tmp_list = tmp_list->next) + { + ThemeImage *image = tmp_list->data; + + if (image->use == use) + return image; + } + + return NULL; +} + +static void +theme_source_location_start (ThemeSource *source, + ThemeLocation *location, + int *start_x, + int *start_y) +{ + *start_x = (source->margin + + location->column * (source->gridsize + source->spacing)); + + *start_y = (source->margin + + location->row * (source->gridsize + source->spacing)); +} + +static gboolean +theme_source_location_bounds (ThemeSource *source, + ThemeLocation *location, + int frame_index, + int threshold, + gboolean location_relative, + ThemeRectangle *out) +{ + ThemeImage *image = theme_source_find_image (source, frame_index); + int start_x, start_y; + int i, j; + + const guchar *pixels; + int rowstride; + int n_channels; + + theme_source_location_start (source, location, &start_x, &start_y); + + rowstride = gdk_pixbuf_get_rowstride (image->image); + n_channels = gdk_pixbuf_get_n_channels (image->image); + + if (n_channels == 3) + { + out->x = start_x; + out->y = start_x; + out->width = source->gridsize; + out->height = source->gridsize; + + return TRUE; + } + else + { + int min_x = 0, max_x = 0, min_y = 0, max_y = 0; + gboolean found = FALSE; + + 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] > threshold) + { + if (found) + { + 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; + } + else + { + min_x = start_x + i; + max_x = start_x + i + 1; + min_y = start_y + i; + max_y = start_y + i + 1; + + found = TRUE; + } + } + } + + pixels += rowstride; + } + + if (found) + { + if (out) + { + out->x = location_relative ? min_x - start_x : min_x; + out->y = location_relative ? min_y - start_y : min_y; + out->width = max_x - min_x; + out->height = max_y - min_y; + } + + return TRUE; + } + else + return FALSE; + } +} + +static GdkPixbuf * +theme_source_get_pixbuf (ThemeSource *source, + ThemeLocation *location, + int frame_index, + ThemeRectangle *bounds) +{ + ThemeImage *image = theme_source_find_image (source, frame_index); + GdkPixbuf *result, *tmp_pixbuf; + + tmp_pixbuf = gdk_pixbuf_new_subpixbuf (image->image, + bounds->x, + bounds->y, + bounds->width, + bounds->height); + + result = gdk_pixbuf_copy (tmp_pixbuf); + g_object_unref (tmp_pixbuf); + + return result; +} + +static gboolean +theme_source_has_location (ThemeSource *source, + ThemeLocation *location, + int frame) +{ + return theme_source_location_bounds (source, location, frame, 0, FALSE, NULL); +} + +static gboolean +cursor_find_hotspot (ThemeCursor *cursor, + ThemeSource *source, + int *hot_x, + int *hot_y) +{ + ThemeRectangle bounds; + ThemeImage *image; + + /* Some debugging checks */ + image = theme_source_find_image (source, THEME_SOURCE_USE_HOTSPOT); + 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 (theme_source_location_bounds (source, &cursor->location, + THEME_SOURCE_USE_HOTSPOT, + 0x80, TRUE, &bounds)) + { + if (bounds.width > 1 || bounds.height > 1) + { + g_printerr ("Multiple hotspots for cursor %s at size %d\n", + cursor->name, source->size); + return FALSE; + } + + *hot_x = bounds.x; + *hot_y = bounds.y; + + return TRUE; + } + else + { + g_printerr ("Cannot find hotspot for cursor %s at size %d\n", + cursor->name, source->size); + return FALSE; + } + + return TRUE; +} + +static void +cursor_fetch_pixbuf (ThemeCursor *cursor, + ThemeFrame *frame, + ThemeSource *source, + int frame_index) +{ + int start_x, start_y; + ThemeRectangle bounds_rect; + ThemeRectangle hotspot_rect; + + theme_source_location_start (source, &cursor->location, &start_x, &start_y); + theme_source_location_bounds (source, &cursor->location, frame_index, + 0, FALSE, &bounds_rect); + + hotspot_rect.x = start_x + frame->hot_x; + hotspot_rect.y = start_y + frame->hot_y; + hotspot_rect.width = 1; + hotspot_rect.height = 1; + theme_rectangle_union (&bounds_rect, &hotspot_rect, &bounds_rect); + + frame->image = theme_source_get_pixbuf (source, &cursor->location, + frame_index, &bounds_rect); + + frame->hot_x -= bounds_rect.x - start_x; + frame->hot_y -= bounds_rect.y - start_y; +} + +static gboolean +cursor_add_source (ThemeCursor *cursor, + ThemeSource *source) +{ + GSList *tmp_list; + int hot_x, hot_y; + int i; + + /* If the first frame is missing we silently treat + * it as OK + */ + if (!theme_source_has_location (source, &cursor->location, 0)) + return TRUE; + + if (!cursor_find_hotspot (cursor, source, &hot_x, &hot_y)) + return FALSE; + + for (tmp_list = cursor->frame_configs, i = 0; + tmp_list; + tmp_list = tmp_list->next, i++) + { + ThemeFrameConfig *frame_config = tmp_list->data; + ThemeFrame *frame; + + if (i != 0 && !theme_source_has_location (source, &cursor->location, i)) + { + g_printerr ("Frame %d missing for cursor '%s' at size %d\n", + i, cursor->name, source->size); + frame = g_new0 (ThemeFrame, 1); + frame->size = -1; + cursor->frames = g_slist_append (cursor->frames, frame); + continue; + } + + frame = g_new0 (ThemeFrame, 1); + frame->size = source->size; + frame->hot_x = hot_x; + frame->hot_y = hot_y; + frame->delay = frame_config->delay; + + cursor_fetch_pixbuf (cursor, frame, source, i); + + cursor->frames = g_slist_append (cursor->frames, frame); + } + + return TRUE; +} + +static void +icon_fetch_pixbuf (ThemeIcon *icon, + ThemeIconInstance *instance, + ThemeSource *source) +{ + ThemeRectangle bounds_rect; + + theme_source_location_start (source, &icon->location, + &bounds_rect.x, &bounds_rect.y); + bounds_rect.width = source->gridsize; + bounds_rect.height = source->gridsize; + + instance->image = theme_source_get_pixbuf (source, &icon->location, + 0, &bounds_rect); +} + +static void +icon_fetch_embedded_rect (ThemeIcon *icon, + ThemeIconInstance *instance, + ThemeSource *source) +{ + ThemeRectangle bounds_rect; + ThemeImage *image; + + image = theme_source_find_image (source, THEME_SOURCE_USE_EMBEDDED_RECT); + if (!image) + return; + + if (!gdk_pixbuf_get_has_alpha (image->image) || + gdk_pixbuf_get_colorspace (image->image) != GDK_COLORSPACE_RGB) + { + g_printerr ("Invalid format for embedded rectangle file\n"); + exit (1); + } + + if (theme_source_location_bounds (source, &icon->location, + THEME_SOURCE_USE_EMBEDDED_RECT, + 0x80, TRUE, &bounds_rect)) + { + instance->embedded_rect = g_memdup (&bounds_rect, sizeof (bounds_rect)); + } +} + +static void +icon_fetch_attach_points (ThemeIcon *icon, + ThemeIconInstance *instance, + ThemeSource *source) +{ + ThemeImage *image; + + int start_x, start_y; + int i, j; + + const guchar *pixels; + int rowstride; + + theme_source_location_start (source, &icon->location, &start_x, &start_y); + image = theme_source_find_image (source, THEME_SOURCE_USE_ATTACH_POINTS); + if (!image) + return; + + if (!gdk_pixbuf_get_has_alpha (image->image) || + gdk_pixbuf_get_colorspace (image->image) != GDK_COLORSPACE_RGB) + { + g_printerr ("Invalid format for embedded rectangle file\n"); + exit (1); + } + + rowstride = gdk_pixbuf_get_rowstride (image->image); + 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] > 0x80) + { + ThemePoint *attach_point = g_new (ThemePoint, 1); + attach_point->x = i; + attach_point->y = j; + + instance->attach_points = g_slist_append (instance->attach_points, attach_point); + } + } + + pixels += rowstride; + } +} + +static gboolean +icon_add_source (ThemeIcon *icon, + ThemeSource *source) +{ + ThemeIconInstance *instance; + + /* If the image is missing, we silently omit it + */ + if (!theme_source_has_location (source, &icon->location, 0)) + return TRUE; + + instance = g_new0 (ThemeIconInstance, 1); + instance->source = source; + + icon_fetch_pixbuf (icon, instance, source); + icon_fetch_embedded_rect (icon, instance, source); + icon_fetch_attach_points (icon, instance, source); + + icon->instances = g_slist_append (icon->instances, instance); + + return TRUE; +} + +static gboolean +theme_read_cursor (ThemeFile *theme, + ThemeCursor *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 gboolean +theme_read_icon (ThemeFile *theme, + ThemeIcon *icon) +{ + GSList *tmp_list; + + for (tmp_list = theme->sources; tmp_list; tmp_list = tmp_list->next) + { + if (!icon_add_source (icon, tmp_list->data)) + return FALSE; + } + + return TRUE; +} + +static gboolean +ensure_directory (const char *directory) +{ + if (strchr (directory, '/') != NULL) + { + char *dirname = g_path_get_dirname (directory); + gboolean success = ensure_directory (dirname); + g_free (dirname); + + if (!success) + return FALSE; + } + + if (!g_file_test (directory, G_FILE_TEST_IS_DIR) && + mkdir (directory, 0755) < 0) + { + g_printerr ("Error creating output directory '%s': %s\n", + directory, g_strerror (errno)); + + return FALSE; + } + + return TRUE; +} + +static void +theme_write_cursor (ThemeFile *theme, + ThemeCursor *cursor) +{ + GSList *tmp_list; + char *curdir; + char *config_filename; + char *command; + FILE *config_file; + GError *error = NULL; + int status; + int i; + + if (!ensure_directory ("cursors")) + return; + + curdir = g_get_current_dir (); + if (chdir ("cursors") < 0) + { + g_printerr ("Could not change to cursor directory: %s\n", + g_strerror (errno)); + return; + } + + if (g_hash_table_lookup (theme->aliases, cursor->name)) + { + g_printerr ("Warning: cursor '%s' overridden by alias\n", cursor->name); + goto out; + } + + if (!cursor->frames) + goto out; + + 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); + g_free (config_filename); + goto out; + } + + for (tmp_list = cursor->frames, i = 0; tmp_list; tmp_list = tmp_list->next, i++) + { + ThemeFrame *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); + } + } + + out: + chdir (curdir); +} + +static char * +theme_icon_output_dir (ThemeFile *theme, + ThemeSource *source, + const char *typedir_override, + const char *icon_or_alias_name) +{ + const char *typedir = typedir_override ? typedir_override : theme->typedir; + + if (!source->sizedir && !typedir) + { + g_printerr ("Both typedir and sizedir are empty for '%s'\n", + icon_or_alias_name); + return NULL; + } + + if (source->sizedir) + return g_build_filename (source->sizedir, typedir, NULL); + else + return g_strdup (typedir); +} + +static void +theme_write_icon_data_file (ThemeFile *theme, + const char *output_dir, + const char *name, + ThemeIconInstance *instance, + GSList *display_names) +{ + char *data_filename; + char *data_pathname; + FILE *data_file; + GSList *tmp_list; + + data_filename = g_strconcat (name, ".icon", NULL); + data_pathname = g_build_filename (output_dir, data_filename, NULL); + + data_file = fopen (data_pathname, "w"); + if (!data_file) + { + g_printerr ("Cannot open icon data file '%s'\n", + data_pathname); + exit (1); + } + + fprintf (data_file, "# Generated by icon-slicer, do not edit\n"); + fprintf (data_file, "[Icon Data]\n"); + if (instance->embedded_rect) + { + fprintf (data_file, "EmbeddedTextRectangle=%d,%d,%d,%d\n", + instance->embedded_rect->x, + instance->embedded_rect->y, + instance->embedded_rect->x + instance->embedded_rect->width, + instance->embedded_rect->y + instance->embedded_rect->height); + } + if (instance->attach_points) + { + GString *attach_point_string = g_string_new (NULL); + + for (tmp_list = instance->attach_points; tmp_list; tmp_list = tmp_list->next) + { + ThemePoint *attach_point = tmp_list->data; + g_string_append_printf (attach_point_string, "%d,%d", + attach_point->x, attach_point->y); + if (tmp_list->next) + g_string_append (attach_point_string, "|"); + } + + fprintf (data_file, "AttachPoints=%s\n", + attach_point_string->str); + + g_string_free (attach_point_string, TRUE); + } + for (tmp_list = display_names; tmp_list; tmp_list = tmp_list->next) + { + ThemeDisplayName *display_name = tmp_list->data; + + if (display_name->lang) + fprintf (data_file, "DisplayName[%s]=%s\n", + display_name->lang, display_name->str); + else + fprintf (data_file, "DisplayName=%s\n", + display_name->str); + } + + fclose (data_file); + + g_free (data_filename); + g_free (data_pathname); +} + +static void +theme_write_icon (ThemeFile *theme, + ThemeIcon *icon) +{ + GSList *tmp_list; + GError *error = NULL; + int i; + + if (g_hash_table_lookup (theme->aliases, icon->name)) + { + g_printerr ("Warning: icon '%s' overridden by alias\n", icon->name); + return; + } + + if (!icon->instances) + return; + + for (tmp_list = icon->instances, i = 0; tmp_list; tmp_list = tmp_list->next, i++) + { + ThemeIconInstance *instance = tmp_list->data; + char *output_dir, *filename, *pathname; + + output_dir = theme_icon_output_dir (theme, instance->source, + icon->typedir, icon->name); + if (!output_dir) + exit (1); + + if (!ensure_directory (output_dir)) + exit (1); + + filename = g_strconcat (icon->name, ".png", NULL); + pathname = g_build_filename (output_dir, filename, NULL); + if (!gdk_pixbuf_save (instance->image, pathname, "png", &error, NULL)) + { + g_printerr ("Error saving image file: %s\n", error->message); + g_error_free (error); + exit (1); + } + g_free (filename); + g_free (pathname); + + if (instance->embedded_rect || instance->attach_points || icon->display_names) + { + theme_write_icon_data_file (theme, output_dir, icon->name, + instance, icon->display_names); + } + + g_free (output_dir); + } +} + +static const char * +theme_check_alias (ThemeFile *theme, + ThemeAlias *alias, + ThemeAliasType *type, + const char **target_typedir) +{ + ThemeAlias *tortoise = alias; + ThemeAlias *hare = alias; + ThemeCursor *cursor_target; + ThemeIcon *icon_target; + + /* Dereference, using tortoise-and-hare checking for circular aliases + */ + while (TRUE) + { + ThemeAlias *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); + } + + /* Now check that the actual target exists and is sensible. + */ + cursor_target = g_hash_table_lookup (theme->cursors, hare->target); + icon_target = g_hash_table_lookup (theme->icons, hare->target); + + if (cursor_target && icon_target) + { + g_printerr ("Alias '%s' points to '%s', which is both a cursor and an icon\n", + alias->name, hare->target); + return NULL; + } + + if (!cursor_target && !icon_target) + { + g_printerr ("Alias '%s' points to '%s', which is not in the theme\n", + alias->name, hare->target); + return NULL; + } + + if (cursor_target) + { + if (!cursor_target->frames) + { + g_printerr ("Alias '%s' points to cursor '%s', which is not present in any source\n", + alias->name, hare->target); + return NULL; + } + + *type = THEME_ALIAS_CURSOR; + *target_typedir = NULL; + } + + if (icon_target) + { + if (!icon_target->instances) + { + g_printerr ("Alias '%s' points to icon '%s', which is not present in any source\n", + alias->name, hare->target); + return NULL; + } + + *type = THEME_ALIAS_ICON; + *target_typedir = icon_target->typedir; + } + + return hare->target; + + found_loop: + g_printerr ("Circular looop detected when dereferencing alias '%s'\n", + alias->name); + return NULL; +} + +static char * +relative_path (const char *directory, + const char *base) +{ + char **dir_comps = g_strsplit (directory, "/", -1); + char **base_comps = g_strsplit (base, "/", -1); + char **dirp, **basep; + GString *result = g_string_new (NULL); + + dirp = dir_comps; + basep = base_comps; + + while (*dirp && *basep && strcmp (*dirp, *basep) == 0) + { + dirp++; + basep++; + } + + while (*basep) + { + if (result->len) + g_string_append_c (result, '/'); + + g_string_append (result, ".."); + basep++; + } + + while (*dirp) + { + if (result->len) + g_string_append_c (result, '/'); + + g_string_append (result, *dirp); + dirp++; + } + + g_strfreev (dir_comps); + g_strfreev (base_comps); + + if (result->len) + return g_string_free (result, FALSE); + else + { + g_string_free (result, TRUE); + return NULL; + } +} + +static gboolean +make_symlink (const char *directory, + const char *alias, + const char *target_directory, + const char *target) +{ + char *filename; + char *target_filename; + gboolean result = FALSE; + char *relative_target_directory; + + if (!ensure_directory (directory)) + return FALSE; + + relative_target_directory = relative_path (target_directory, directory); + + filename = g_build_filename (directory, alias, NULL); + if (relative_target_directory) + target_filename = g_build_filename (relative_target_directory, target, NULL); + else + target_filename = g_strdup (target); + + unlink (filename); + if (symlink (target_filename, filename) < 0) + g_printerr ("Error creating alias symlink from '%s' to '%s': %s\n", + filename, target_filename, g_strerror (errno)); + else + result = TRUE; + + g_free (filename); + g_free (relative_target_directory); + g_free (target_filename); + + return result; +} + +static void +theme_write_alias (ThemeFile *theme, + ThemeAlias *alias) +{ + ThemeAliasType type; + const char *target_typedir; + const char *target = theme_check_alias (theme, alias, &type, &target_typedir); + if (!target) + exit (1); + + if (type == THEME_ALIAS_CURSOR) + { + if (!make_symlink ("cursors", alias->name, "cursors", target)) + exit (1); + } + else + { + ThemeIcon *icon = g_hash_table_lookup (theme->icons, target); + GSList *tmp_list; + char *pngalias = g_strdup_printf ("%s.png", alias->name); + char *pngtarget = g_strdup_printf ("%s.png", target); + char *dataalias = g_strdup_printf ("%s.icon", alias->name); + char *datatarget = g_strdup_printf ("%s.icon", target); + + for (tmp_list = icon->instances; tmp_list; tmp_list = tmp_list->next) + { + ThemeIconInstance *instance = tmp_list->data; + char *output_dir; + char *target_output_dir; + + output_dir = theme_icon_output_dir (theme, instance->source, + alias->typedir, alias->name); + if (!output_dir) + exit (1); + + target_output_dir = theme_icon_output_dir (theme, instance->source, + target_typedir, target); + if (!output_dir) + exit (1); + + if (!ensure_directory (output_dir)) + exit (1); + + if (!make_symlink (output_dir, pngalias, + target_output_dir, pngtarget)) + exit (1); + + if (alias->display_names) + theme_write_icon_data_file (theme, output_dir, alias->name, + instance, alias->display_names); + else if ((instance->embedded_rect || instance->attach_points || icon->display_names)) + { + if (!make_symlink (output_dir, dataalias, + target_output_dir, datatarget)) + exit (1); + } + + g_free (output_dir); + g_free (target_output_dir); + } + + g_free (pngalias); + g_free (pngtarget); + g_free (dataalias); + g_free (datatarget); + } +} + +static void +write_cursor_foreach (gpointer key, + gpointer value, + gpointer data) +{ + theme_write_cursor (data, value); +} + +static void +write_icon_foreach (gpointer key, + gpointer value, + gpointer data) +{ + theme_write_icon (data, value); +} + +static void +write_alias_foreach (gpointer key, + gpointer value, + gpointer data) +{ + theme_write_alias (data, value); +} + +static gboolean +theme_write (ThemeFile *theme, + const char *output_dir) +{ + char *curdir; + + if (!g_file_test (output_dir, G_FILE_TEST_IS_DIR)) + { + g_printerr ("Output directory '%s' does not exist\n", output_dir); + 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->icons, + write_icon_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 (!theme_read_cursor (data, value)) + exit (1); +} + +static void +read_icon_foreach (gpointer key, + gpointer value, + gpointer data) +{ + if (!theme_read_icon (data, value)) + exit (1); +} + +static int want_my_version = FALSE; +static const char *output_dir = NULL; +static const char *image_dir = NULL; + +static const struct poptOption options_table[] = { + { "version", 0, POPT_ARG_NONE, &want_my_version, 0, + "output version of icon-slicer" }, + { "output-dir", 0, POPT_ARG_STRING, &output_dir, 0, + "directory into which to write output", "DIRECTORY" }, + { "image-dir", 0, POPT_ARG_STRING, &image_dir, 0, + "directory in which to find source images", "DIRECTORY" }, + POPT_AUTOHELP + { NULL, 0, 0, NULL, 0 } +}; + +int +main (int argc, char **argv) +{ + poptContext opt_context; + const char *arg; + int result; + + g_type_init (); + + opt_context = poptGetContext ("xsri", argc, (const char **)argv, + options_table, 0); + + result = poptGetNextOpt (opt_context); + if (result != -1) + { + g_printerr ("xsri: %s: %s\n", + poptBadOption(opt_context, POPT_BADOPTION_NOALIAS), + poptStrerror(result)); + return 1; + } + + if (want_my_version) + { + printf ("icon-slicer version %s\n", PACKAGE_VERSION); + return 0; + } + + if (!output_dir) + { + g_printerr ("Output directory must be given with --output-dir\n"); + poptPrintUsage (opt_context, stderr, 0); + return 1; + } + + arg = poptGetArg (opt_context); + if (!arg) + { + poptPrintUsage (opt_context, stderr, 0); + return 1; + } + + while (arg) + { + ThemeFile *theme; + + theme = theme_file_read (arg, image_dir); + if (!theme) + return 1; + + g_hash_table_foreach (theme->cursors, + read_cursor_foreach, + theme); + + g_hash_table_foreach (theme->icons, + read_icon_foreach, + theme); + + if (!theme_write (theme, output_dir)) + return 1; + + theme_file_free (theme); + + arg = poptGetArg (opt_context); + } + + return 0; +}