Complete.Org:
Mailing Lists:
Archives:
freeciv-dev:
December 2005: [Freeciv-Dev] Re: (PR#14639) Patch for SDL client |
[Freeciv-Dev] Re: (PR#14639) Patch for SDL client[Date Prev][Date Next][Thread Prev][Thread Next][Date Index] [Thread Index]
<URL: http://bugs.freeciv.org/Ticket/Display.html?id=14639 > This transaction appears to have no content Here's the finished theme handling patch. Themes for the SDL client are expected in "themes/gui-sdl" within any of the freeciv data directories and they are recognized by the "theme.themespec" file (example: "/~/.freeciv/themes/gui-sdl/deluxe/theme.themespec"). The repackaged "deluxe" theme can be found here: http://web.inf.tu-dresden.de/~s3564543/freeciv-sdl/deluxe_theme.tar.bz2 <http://web.inf.tu-dresden.de/%7Es3564543/freeciv-sdl/deluxe_theme.tar.bz2> Using some of aneglus' graphics seems a good idea. The diplomacy icons, if we have to replace them, should maybe look more like the ones currently used. Here's a screenshot showing the diplomacy dialog in action: http://rcswww.urz.tu-dresden.de/~s3564543/freeciv-sdl/diplodlg.png<http://rcswww.urz.tu-dresden.de/%7Es3564543/freeciv-sdl/diplodlg.png> Red seals fit perfectly, don't they?Here's the finished theme handling patch. Themes for the SDL client are expected in "themes/gui-sdl" within any of the freeciv data directories and they are recognized by the "theme.themespec" file (example: "/~/.freeciv/themes/gui-sdl/deluxe/theme.themespec"). The repackaged "deluxe" theme can be found here: http://web.inf.tu-dresden.de/~s3564543/freeciv-sdl/deluxe_theme.tar.bz2 Using some of aneglus' graphics seems a good idea. The diplomacy icons, if we have to replace them, should maybe look more like the ones currently used. Here's a screenshot showing the diplomacy dialog in action: http://rcswww.urz.tu-dresden.de/~s3564543/freeciv-sdl/diplodlg.png Red seals fit perfectly, don't they? diff -urN -X devel/diff_ignore devel_distclean/client/gui-sdl/graphics.c devel/client/gui-sdl/graphics.c --- devel_distclean/client/gui-sdl/graphics.c 2005-12-04 05:59:27.000000000 +0100 +++ devel/client/gui-sdl/graphics.c 2005-12-09 17:25:19.000000000 +0100 @@ -52,7 +52,7 @@ #include "mapview_g.h" #include "options.h" -#include "tilespec.h" +#include "themespec.h" #include "gui_mem.h" #include "graphics.h" #include "gui_zoom.h" @@ -3415,13 +3415,15 @@ SDL_Surface * get_logo_gfx(void) { SDL_Surface *pLogo; - SDL_Surface *pLogo_Surf = IMG_Load(tileset_mini_intro_filename(tileset)); + + SDL_Surface *pLogo_Surf = adj_surf(GET_SURF(themeset_lookup_sprite_tag_alt( + themeset, "theme.logo", "", TRUE, "", ""))); assert(pLogo_Surf != NULL); pLogo = SDL_CreateRGBSurface(SDL_SWSURFACE, pLogo_Surf->w, pLogo_Surf->h, 32, 0x00FF0000, 0x0000FF00, 0x000000FF, 0x0); SDL_BlitSurface(pLogo_Surf, NULL, pLogo, NULL); - FREESURFACE(pLogo_Surf); + return pLogo; } diff -urN -X devel/diff_ignore devel_distclean/client/gui-sdl/gui_main.c devel/client/gui-sdl/gui_main.c --- devel_distclean/client/gui-sdl/gui_main.c 2005-12-04 05:59:27.000000000 +0100 +++ devel/client/gui-sdl/gui_main.c 2005-12-09 23:58:06.000000000 +0100 @@ -84,6 +84,7 @@ #include "spaceshipdlg.h" #include "resources.h" #include "tilespec.h" +#include "themespec.h" #include "gui_tilespec.h" #include "messagewin.h" #include "citydlg.h" @@ -938,12 +939,8 @@ CF_DRAW_PLAYERS_PEACE_STATUS| CF_DRAW_PLAYERS_ALLIANCE_STATUS); - tilespec_setup_theme(); - tilespec_setup_anim(); - tilespec_setup_city_gfx(); - tilespec_setup_city_icons(); - tileset_load_tiles(tileset); + tileset_use_prefered_theme(tileset); load_cursors(); @@ -1015,11 +1012,14 @@ #endif free_auxiliary_tech_icons(); - - tilespec_free_anim(); unload_cursors(); - free_font_system(); +/* FIXME: the font system cannot be freed yet, because it is still + * needed in civclient.c for message window output */ +#if 0 + free_font_system(); +#endif + quit_sdl(); } diff -urN -X devel/diff_ignore devel_distclean/client/gui-sdl/gui_tilespec.c devel/client/gui-sdl/gui_tilespec.c --- devel_distclean/client/gui-sdl/gui_tilespec.c 2005-12-04 05:59:27.000000000 +0100 +++ devel/client/gui-sdl/gui_tilespec.c 2005-12-09 17:25:19.000000000 +0100 @@ -34,6 +34,7 @@ #include "log.h" #include "tilespec.h" +#include "themespec.h" #include "gui_mem.h" #include "gui_main.h" @@ -43,15 +44,10 @@ #include "gui_tilespec.h" -struct sprite* lookup_sprite_tag_alt(struct tileset *t, - const char *tag, const char *alt, - bool required, const char *what, - const char *name); - #ifdef SMALL_SCREEN #define load_GUI_surface(pSpr, pStruct, pSurf, tag) \ do { \ - pSpr = lookup_sprite_tag_alt(tileset, tag, "", TRUE, "", ""); \ + pSpr = themeset_lookup_sprite_tag_alt(themeset, tag, "", TRUE, "", ""); \ pStruct->pSurf = (pSpr ? GET_SURF(pSpr) : NULL); \ assert(pStruct->pSurf != NULL); \ pStruct->pSurf = ZoomSurface(pStruct->pSurf, 0.5, 0.5, 0); \ @@ -60,7 +56,7 @@ #else #define load_GUI_surface(pSpr, pStruct, pSurf, tag) \ do { \ - pSpr = lookup_sprite_tag_alt(tileset, tag, "", TRUE, "", ""); \ + pSpr = themeset_lookup_sprite_tag_alt(themeset, tag, "", TRUE, "", ""); \ pStruct->pSurf = (pSpr ? GET_SURF(pSpr) : NULL); \ assert(pStruct->pSurf != NULL); \ pSpr->psurface = NULL; \ @@ -211,12 +207,10 @@ ... **************************************************************************/ void tilespec_setup_city_gfx(void) { - struct sprite *pSpr = lookup_sprite_tag_alt(tileset, "theme.city", "", TRUE, "", ""); - pCity_Surf = (pSpr ? GET_SURF(pSpr) : NULL); - - #ifdef SMALL_SCREEN - pCity_Surf = ZoomSurface(pCity_Surf, 0.5, 0.5, 0); - #endif + struct sprite *pSpr = themeset_lookup_sprite_tag_alt( + themeset, "theme.city", "", TRUE, "", ""); + + pCity_Surf = (pSpr ? adj_surf(GET_SURF(pSpr)) : NULL); assert(pCity_Surf != NULL); } @@ -343,7 +337,7 @@ pTheme = MALLOC(sizeof(struct Theme)); - if(!lookup_sprite_tag_alt(tileset, "theme.tech_tree", "", FALSE, "", "")) { + if(!themeset_lookup_sprite_tag_alt(themeset, "theme.tech_tree", "", FALSE, "", "")) { freelog(LOG_FATAL, "Your current tileset don't contains ""all"" GUI theme graphic\n" "Please use other tileset with ""full"" GUI graphic pack (use -t tileset options)\n" "If you don't have any tileset with SDLClient GUI theme then go to freeciv\n" @@ -491,7 +485,7 @@ do { \ iter = 0; \ my_snprintf(cBuf , sizeof(cBuf), "%s_%d", Tag, iter); \ - while(lookup_sprite_tag_alt(tileset, cBuf, "", FALSE, "", "")) { \ + while(themeset_lookup_sprite_tag_alt(themeset, cBuf, "", FALSE, "", "")) { \ iter++; \ my_snprintf(cBuf , sizeof(cBuf), "%s_%d", Tag, iter); \ } \ @@ -500,7 +494,7 @@ pAnim->Cursors.Type = CALLOC(num + 1, sizeof(SDL_Cursor *)); \ for( iter=0; iter<num; iter++) { \ my_snprintf(cBuf,sizeof(cBuf), "%s_%d", Tag, iter); \ - pSpr = lookup_sprite_tag_alt(tileset, cBuf, "", FALSE, "", ""); \ + pSpr = themeset_lookup_sprite_tag_alt(themeset, cBuf, "", FALSE, "", ""); \ image = (pSpr ? GET_SURF(pSpr) : NULL); \ assert(image != NULL); \ if (center) { \ @@ -568,7 +562,7 @@ /* * Free memmory */ -void tilespec_unload_theme(void) +void tilespec_free_theme(void) { FREESURFACE(pTheme->Button); FREESURFACE(pTheme->Edit); diff -urN -X devel/diff_ignore devel_distclean/client/gui-sdl/gui_tilespec.h devel/client/gui-sdl/gui_tilespec.h --- devel_distclean/client/gui-sdl/gui_tilespec.h 2005-12-04 05:59:27.000000000 +0100 +++ devel/client/gui-sdl/gui_tilespec.h 2005-12-09 17:25:19.000000000 +0100 @@ -22,6 +22,8 @@ #ifndef FC__GUI_TILESPEC_H #define FC__GUI_TILESPEC_H +#include <SDL/SDL.h> + #include "citydlg_common.h" /* struct citizen_type */ struct Theme { @@ -128,7 +130,7 @@ } *pTheme; void tilespec_setup_theme(void); -void tilespec_unload_theme(void); +void tilespec_free_theme(void); enum DirScrolling { SCROLL_NORTH = 0, @@ -202,8 +204,10 @@ SDL_Surface *pCity_Surf; void tilespec_setup_city_gfx(void); + void tilespec_setup_city_icons(void); void tilespec_free_city_icons(void); + void reload_citizens_icons(int style); SDL_Surface * get_city_gfx(void); SDL_Surface * get_citizen_surface(enum citizen_category type, diff -urN -X devel/diff_ignore devel_distclean/client/gui-sdl/Makefile.am devel/client/gui-sdl/Makefile.am --- devel_distclean/client/gui-sdl/Makefile.am 2005-11-27 01:35:27.000000000 +0100 +++ devel/client/gui-sdl/Makefile.am 2005-12-09 17:25:19.000000000 +0100 @@ -82,6 +82,7 @@ spaceshipdlg.c \ spaceshipdlg.h \ themes.c \ + themespec.c \ wldlg.c \ wldlg.h \ gui_string.c \ diff -urN -X devel/diff_ignore devel_distclean/client/gui-sdl/themespec.c devel/client/gui-sdl/themespec.c --- devel_distclean/client/gui-sdl/themespec.c 1970-01-01 01:00:00.000000000 +0100 +++ devel/client/gui-sdl/themespec.c 2005-12-09 17:25:19.000000000 +0100 @@ -0,0 +1,1054 @@ +/********************************************************************** + Freeciv - Copyright (C) 1996 - A Kjeldberg, L Gregersen, P Unold + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. +***********************************************************************/ + +/********************************************************************** + Functions for handling the themespec files which describe + the files and contents of themesets. + original author: David Pfitzner <dwp@xxxxxxxxxxxxxx> +***********************************************************************/ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <assert.h> +#include <stdio.h> +#include <stdlib.h> /* exit */ +#include <string.h> + +#include "astring.h" +#include "capability.h" +#include "fcintl.h" +#include "government.h" +#include "hash.h" +#include "log.h" +#include "map.h" +#include "mem.h" +#include "nation.h" +#include "player.h" +#include "registry.h" +#include "shared.h" +#include "support.h" +#include "unit.h" + +#include "dialogs_g.h" +#include "graphics_g.h" +#include "gui_main_g.h" +#include "mapview_g.h" /* for update_map_canvas_visible */ +#include "themes_g.h" + +#include "civclient.h" /* for get_client_state() */ +#include "themes_common.h" + +#include "themespec.h" + +#include "gui_tilespec.h" + +#define THEMESPEC_SUFFIX ".themespec" + +#if 0 +/* TODO: may be useful for theme sprites, too */ +struct named_sprites { + .... +}; +#endif + +struct specfile { + struct sprite *big_sprite; + char *file_name; +}; + +#define SPECLIST_TAG specfile +#define SPECLIST_TYPE struct specfile +#include "speclist.h" + +#define specfile_list_iterate(list, pitem) \ + TYPED_LIST_ITERATE(struct specfile, list, pitem) +#define specfile_list_iterate_end LIST_ITERATE_END + +/* + * Information about an individual sprite. All fields except 'sprite' are + * filled at the time of the scan of the specfile. 'Sprite' is + * set/cleared on demand in load_sprite/unload_sprite. + */ +struct small_sprite { + int ref_count; + + /* The sprite is in this file. */ + char *file; + + /* Or, the sprite is in this file at the location. */ + struct specfile *sf; + int x, y, width, height; + + /* A little more (optional) data. */ + int hot_x, hot_y; + + struct sprite *sprite; +}; + +#define SPECLIST_TAG small_sprite +#define SPECLIST_TYPE struct small_sprite +#include "speclist.h" + +#define small_sprite_list_iterate(list, pitem) \ + TYPED_LIST_ITERATE(struct small_sprite, list, pitem) +#define small_sprite_list_iterate_end LIST_ITERATE_END + +struct themeset { + char name[512]; + int priority; + + char *main_intro_filename; + char *minimap_intro_filename; + + struct specfile_list *specfiles; + struct small_sprite_list *small_sprites; + + /* + * This hash table maps themespec tags to struct small_sprites. + */ + struct hash_table *sprite_hash; + +/* struct named_sprites sprites;*/ +}; + +struct themeset *themeset; + +#define THEMESPEC_CAPSTR "+themespec3 duplicates_ok" +/* + * Themespec capabilities acceptable to this program: + * + * +themespec3 - basic format; required + * + * duplicates_ok - we can handle existence of duplicate tags + * (lattermost tag which appears is used; themesets which + * have duplicates should specify "+duplicates_ok") + */ + +#define SPEC_CAPSTR "+spec3" +/* + * Individual spec file capabilities acceptable to this program: + * +spec3 - basic format, required + */ + + +/**************************************************************************** + Return the name of the given themeset. +****************************************************************************/ +const char *themeset_get_name(const struct themeset *t) +{ + return t->name; +} + +/**************************************************************************** + Return the path within the data directories where the main intro graphics + file can be found. (It is left up to the GUI code to load and unload this + file.) +****************************************************************************/ +const char *themeset_main_intro_filename(const struct themeset *t) +{ + return t->main_intro_filename; +} + +/**************************************************************************** + Return the path within the data directories where the mini intro graphics + file can be found. (It is left up to the GUI code to load and unload this + file.) +****************************************************************************/ +const char *themeset_mini_intro_filename(const struct themeset *t) +{ + return t->minimap_intro_filename; +} + +/************************************************************************** + Initialize. +**************************************************************************/ +static struct themeset *themeset_new(void) +{ + struct themeset *t = fc_calloc(1, sizeof(*t)); + + t->specfiles = specfile_list_new(); + t->small_sprites = small_sprite_list_new(); + return t; +} + +/********************************************************************** + Returns a static list of themesets available on the system by + searching all data directories for files matching THEMESPEC_SUFFIX. + The list is NULL-terminated. +***********************************************************************/ +const char **get_themeset_list(void) +{ + static const char **themesets = NULL; + + if (!themesets) { + /* Note: this means you must restart the client after installing a new + themeset. */ + char **list = datafilelist(THEMESPEC_SUFFIX); + int i, count = 0; + + for (i = 0; list[i]; i++) { + struct themeset *t = themeset_read_toplevel(list[i]); + + if (t) { + themesets = fc_realloc(themesets, (count + 1) * sizeof(*themesets)); + themesets[count] = list[i]; + count++; + themeset_free(t); + } else { + free(list[i]); + } + } + + themesets = fc_realloc(themesets, (count + 1) * sizeof(*themesets)); + themesets[count] = NULL; + } + + return themesets; +} + +/********************************************************************** + Gets full filename for themespec file, based on input name. + Returned data is allocated, and freed by user as required. + Input name may be null, in which case uses default. + Falls back to default if can't find specified name; + dies if can't find default. +***********************************************************************/ +static char *themespec_fullname(const char *themeset_name) +{ + if (themeset_name) { + char fname[strlen(themeset_name) + strlen(THEMESPEC_SUFFIX) + 1], *dname; + + my_snprintf(fname, sizeof(fname), + "%s%s", themeset_name, THEMESPEC_SUFFIX); + +/* dname = datafilename(fname);*/ + dname = fname; + + if (dname) { + return mystrdup(dname); + } + } + + return NULL; +} + +/********************************************************************** + Checks options in filename match what we require and support. + Die if not. + 'which' should be "themespec" or "spec". +***********************************************************************/ +static bool check_themespec_capabilities(struct section_file *file, + const char *which, + const char *us_capstr, + const char *filename) +{ + char *file_capstr = secfile_lookup_str(file, "%s.options", which); + + if (!has_capabilities(us_capstr, file_capstr)) { + freelog(LOG_DEBUG, _("%s file appears incompatible:\n" + "file: \"%s\"\n" + "file options: %s\n" + "supported options: %s"), + which, filename, file_capstr, us_capstr); + return FALSE; + } + if (!has_capabilities(file_capstr, us_capstr)) { + freelog(LOG_DEBUG, _("%s file claims required option(s)" + " which we don't support:\n" + "file: \"%s\"\n" + "file options: %s\n" + "supported options: %s"), + which, filename, file_capstr, us_capstr); + return FALSE; + } + + return TRUE; +} + +/********************************************************************** + Frees the themespec toplevel data, in preparation for re-reading it. + + See themespec_read_toplevel(). +***********************************************************************/ +static void themeset_free_toplevel(struct themeset *t) +{ + if (t->main_intro_filename) { + free(t->main_intro_filename); + t->main_intro_filename = NULL; + } + if (t->minimap_intro_filename) { + free(t->minimap_intro_filename); + t->minimap_intro_filename = NULL; + } +} + +/************************************************************************** + Clean up. +**************************************************************************/ +void themeset_free(struct themeset *t) +{ + if (t) { + themeset_free_tiles(t); + themeset_free_toplevel(t); + specfile_list_free(t->specfiles); + small_sprite_list_free(t->small_sprites); + free(t); + t = NULL; + } +} + +/********************************************************************** + Read a new themespec in when first starting the game. + + Call this function with the (guessed) name of the themeset, when + starting the client. +***********************************************************************/ +void themespec_try_read(const char *themeset_name) +{ + if (!(themeset = themeset_read_toplevel(themeset_name))) { + char **list = datafilelist(THEMESPEC_SUFFIX); + int i; + + for (i = 0; list[i]; i++) { + struct themeset *t = themeset_read_toplevel(list[i]); + + if (t) { + if (!themeset || t->priority > themeset->priority) { + themeset = t; + } else { + themeset_free(t); + } + } + free(list[i]); + } + free(list); + + if (!themeset) { + freelog(LOG_FATAL, _("No usable default themeset found, aborting!")); + exit(EXIT_FAILURE); + } + + freelog(LOG_NORMAL, _("Trying \"%s\" themeset."), themeset->name); + } +/* sz_strlcpy(default_themeset_name, themeset_get_name(themeset));*/ +} + +/********************************************************************** + Read a new themespec in from scratch. + + Unlike the initial reading code, which reads pieces one at a time, + this gets rid of the old data and reads in the new all at once. If the + new themeset fails to load the old themeset may be reloaded; otherwise the + client will exit. If a NULL name is given the current themeset will be + reread. + + It will also call the necessary functions to redraw the graphics. +***********************************************************************/ +void themespec_reread(const char *new_themeset_name) +{ + struct tile *center_tile; + enum client_states state = get_client_state(); + const char *name = new_themeset_name ? new_themeset_name : themeset->name; + char themeset_name[strlen(name) + 1], old_name[strlen(themeset->name) + 1]; + + /* Make local copies since these values may be freed down below */ + sz_strlcpy(themeset_name, name); + sz_strlcpy(old_name, themeset->name); + + freelog(LOG_NORMAL, "Loading themeset %s.", themeset_name); + + /* Step 0: Record old data. + * + * We record the current mapcanvas center, etc. + */ + center_tile = get_center_tile_mapcanvas(); + + /* Step 1: Cleanup. + * + * We free all old data in preparation for re-reading it. + */ + themeset_free_tiles(themeset); + themeset_free_toplevel(themeset); + + /* Step 2: Read. + * + * We read in the new themeset. This should be pretty straightforward. + */ + if (!(themeset = themeset_read_toplevel(themeset_name))) { + if (!(themeset = themeset_read_toplevel(old_name))) { + die("Failed to re-read the currently loaded themeset."); + } + } +/* sz_strlcpy(default_themeset_name, themeset->name);*/ + themeset_load_tiles(themeset); + + /* Step 3: Setup + * + * This is a seriously sticky problem. On startup, we build a hash + * from all the sprite data. Then, when we connect to a server, the + * server sends us ruleset data a piece at a time and we use this data + * to assemble the sprite structures. But if we change while connected + * we have to reassemble all of these. This should just involve + * calling themespec_setup_*** on everything. But how do we tell what + * "everything" is? + * + * The below code just does things straightforwardly, by setting up + * each possible sprite again. Hopefully it catches everything, and + * doesn't mess up too badly if we change themesets while not connected + * to a server. + */ + if (state < CLIENT_GAME_RUNNING_STATE) { + /* The ruleset data is not sent until this point. */ + return; + } + + /* Step 4: Draw. + * + * Do any necessary redraws. + */ + if (state < CLIENT_GAME_RUNNING_STATE) { + /* Unless the client state is playing a game or in gameover, + we don't want/need to redraw. */ + return; + } + popdown_all_game_dialogs(); + generate_citydlg_dimensions(); +/* themeset_changed();*/ + can_slide = FALSE; + center_tile_mapcanvas(center_tile); + /* update_map_cavnas_visible forces a full redraw. Otherwise with fast + * drawing we might not get one. Of course this is slower. */ + update_map_canvas_visible(); + can_slide = TRUE; +} + +/************************************************************************** + This is merely a wrapper for themespec_reread (above) for use in + options.c and the client local options dialog. +**************************************************************************/ +void themespec_reread_callback(struct client_option *option) +{ + assert(option->p_string_value && *option->p_string_value != '\0'); + themespec_reread(option->p_string_value); +} + +/************************************************************************** + Loads the given graphics file (found in the data path) into a newly + allocated sprite. +**************************************************************************/ +static struct sprite *load_gfx_file(const char *gfx_filename) +{ + const char **gfx_fileexts = gfx_fileextensions(), *gfx_fileext; + struct sprite *s; + + /* Try out all supported file extensions to find one that works. */ + while ((gfx_fileext = *gfx_fileexts++)) { + char *real_full_name; + char full_name[strlen(gfx_filename) + strlen(gfx_fileext) + 2]; + + sprintf(full_name, "%s.%s", gfx_filename, gfx_fileext); + if ((real_full_name = datafilename(full_name))) { + freelog(LOG_DEBUG, "trying to load gfx file %s", real_full_name); + s = load_gfxfile(real_full_name); + if (s) { + return s; + } + } + } + + freelog(LOG_VERBOSE, "Could not load gfx file %s.", gfx_filename); + return NULL; +} + +/************************************************************************** + Ensure that the big sprite of the given spec file is loaded. +**************************************************************************/ +static void ensure_big_sprite(struct specfile *sf) +{ + struct section_file the_file, *file = &the_file; + const char *gfx_filename; + + if (sf->big_sprite) { + /* Looks like it's already loaded. */ + return; + } + + /* Otherwise load it. The big sprite will sometimes be freed and will have + * to be reloaded, but most of the time it's just loaded once, the small + * sprites are extracted, and then it's freed. */ + if (!section_file_load(file, sf->file_name)) { + freelog(LOG_FATAL, _("Could not open \"%s\"."), sf->file_name); + exit(EXIT_FAILURE); + } + + if (!check_themespec_capabilities(file, "spec", + SPEC_CAPSTR, sf->file_name)) { + exit(EXIT_FAILURE); + } + + gfx_filename = secfile_lookup_str(file, "file.gfx"); + + sf->big_sprite = load_gfx_file(gfx_filename); + + if (!sf->big_sprite) { + freelog(LOG_FATAL, _("Couldn't load gfx file for the spec file %s"), + sf->file_name); + exit(EXIT_FAILURE); + } + section_file_free(file); +} + +/************************************************************************** + Scan all sprites declared in the given specfile. This means that the + positions of the sprites in the big_sprite are saved in the + small_sprite structs. +**************************************************************************/ +static void scan_specfile(struct themeset *t, struct specfile *sf, + bool duplicates_ok) +{ + struct section_file the_file, *file = &the_file; + char **gridnames; + int num_grids, i; + + if (!section_file_load(file, sf->file_name)) { + freelog(LOG_FATAL, _("Could not open \"%s\"."), sf->file_name); + exit(EXIT_FAILURE); + } + if (!check_themespec_capabilities(file, "spec", + SPEC_CAPSTR, sf->file_name)) { + exit(EXIT_FAILURE); + } + + /* currently unused */ + (void) section_file_lookup(file, "info.artists"); + + gridnames = secfile_get_secnames_prefix(file, "grid_", &num_grids); + + for (i = 0; i < num_grids; i++) { + int j, k; + int x_top_left, y_top_left, dx, dy; + int pixel_border; + + pixel_border = secfile_lookup_int_default(file, 0, "%s.pixel_border", + gridnames[i]); + + x_top_left = secfile_lookup_int(file, "%s.x_top_left", gridnames[i]); + y_top_left = secfile_lookup_int(file, "%s.y_top_left", gridnames[i]); + dx = secfile_lookup_int(file, "%s.dx", gridnames[i]); + dy = secfile_lookup_int(file, "%s.dy", gridnames[i]); + + j = -1; + while (section_file_lookup(file, "%s.tiles%d.tag", gridnames[i], ++j)) { + struct small_sprite *ss = fc_malloc(sizeof(*ss)); + int row, column; + int x1, y1; + char **tags; + int num_tags; + int hot_x, hot_y; + + row = secfile_lookup_int(file, "%s.tiles%d.row", gridnames[i], j); + column = secfile_lookup_int(file, "%s.tiles%d.column", gridnames[i], j); + tags = secfile_lookup_str_vec(file, &num_tags, "%s.tiles%d.tag", + gridnames[i], j); + hot_x = secfile_lookup_int_default(file, 0, "%s.tiles%d.hot_x", + gridnames[i], j); + hot_y = secfile_lookup_int_default(file, 0, "%s.tiles%d.hot_y", + gridnames[i], j); + + /* there must be at least 1 because of the while(): */ + assert(num_tags > 0); + + x1 = x_top_left + (dx + pixel_border) * column; + y1 = y_top_left + (dy + pixel_border) * row; + + ss->ref_count = 0; + ss->file = NULL; + ss->x = x1; + ss->y = y1; + ss->width = dx; + ss->height = dy; + ss->sf = sf; + ss->sprite = NULL; + ss->hot_x = hot_x; + ss->hot_y = hot_y; + + small_sprite_list_prepend(t->small_sprites, ss); + + if (!duplicates_ok) { + for (k = 0; k < num_tags; k++) { + if (!hash_insert(t->sprite_hash, mystrdup(tags[k]), ss)) { + freelog(LOG_ERROR, "warning: already have a sprite for %s", tags[k]); + } + } + } else { + for (k = 0; k < num_tags; k++) { + (void) hash_replace(t->sprite_hash, mystrdup(tags[k]), ss); + } + } + + free(tags); + tags = NULL; + } + } + free(gridnames); + gridnames = NULL; + + /* Load "extra" sprites. Each sprite is one file. */ + i = -1; + while (secfile_lookup_str_default(file, NULL, "extra.sprites%d.tag", ++i)) { + struct small_sprite *ss = fc_malloc(sizeof(*ss)); + char **tags; + char *filename; + int num_tags, k; + int hot_x, hot_y; + + tags + = secfile_lookup_str_vec(file, &num_tags, "extra.sprites%d.tag", i); + filename = secfile_lookup_str(file, "extra.sprites%d.file", i); + + hot_x = secfile_lookup_int_default(file, 0, "extra.sprites%d.hot_x", i); + hot_y = secfile_lookup_int_default(file, 0, "extra.sprites%d.hot_y", i); + + ss->ref_count = 0; + ss->file = mystrdup(filename); + ss->sf = NULL; + ss->sprite = NULL; + ss->hot_x = hot_x; + ss->hot_y = hot_y; + + small_sprite_list_prepend(t->small_sprites, ss); + + if (!duplicates_ok) { + for (k = 0; k < num_tags; k++) { + if (!hash_insert(t->sprite_hash, mystrdup(tags[k]), ss)) { + freelog(LOG_ERROR, "warning: already have a sprite for %s", tags[k]); + } + } + } else { + for (k = 0; k < num_tags; k++) { + (void) hash_replace(t->sprite_hash, mystrdup(tags[k]), ss); + } + } + free(tags); + } + + section_file_check_unused(file, sf->file_name); + section_file_free(file); +} + +/********************************************************************** + Returns the correct name of the gfx file (with path and extension) + Must be free'd when no longer used +***********************************************************************/ +static char *themespec_gfx_filename(const char *gfx_filename) +{ + const char *gfx_current_fileext; + const char **gfx_fileexts = gfx_fileextensions(); + + while((gfx_current_fileext = *gfx_fileexts++)) + { + char *full_name = + fc_malloc(strlen(gfx_filename) + strlen(gfx_current_fileext) + 2); + char *real_full_name; + + sprintf(full_name,"%s.%s",gfx_filename,gfx_current_fileext); + + real_full_name = datafilename(full_name); + free(full_name); + if (real_full_name) { + return mystrdup(real_full_name); + } + } + + freelog(LOG_FATAL, _("Couldn't find a supported gfx file extension for %s"), + gfx_filename); + exit(EXIT_FAILURE); + return NULL; +} + +/********************************************************************** + Finds and reads the toplevel themespec file based on given name. + Sets global variables, including tile sizes and full names for + intro files. +***********************************************************************/ +struct themeset *themeset_read_toplevel(const char *themeset_name) +{ + struct section_file the_file, *file = &the_file; + char *fname, *c; + int i; + int num_spec_files; + char **spec_filenames; + char *file_capstr; + bool duplicates_ok; + struct themeset *t = themeset_new(); + + fname = themespec_fullname(themeset_name); + if (!fname) { + themeset_free(t); + return NULL; + } + freelog(LOG_VERBOSE, "themespec file is %s", fname); + + if (!section_file_load(file, fname)) { + freelog(LOG_ERROR, _("Could not open \"%s\"."), fname); + section_file_free(file); + free(fname); + themeset_free(t); + return NULL; + } + + if (!check_themespec_capabilities(file, "themespec", + THEMESPEC_CAPSTR, fname)) { + section_file_free(file); + free(fname); + themeset_free(t); + return NULL; + } + + file_capstr = secfile_lookup_str(file, "%s.options", "themespec"); + duplicates_ok = has_capabilities("+duplicates_ok", file_capstr); + + (void) section_file_lookup(file, "themespec.name"); /* currently unused */ + + sz_strlcpy(t->name, themeset_name); + t->priority = secfile_lookup_int(file, "themespec.priority"); + + c = secfile_lookup_str(file, "themespec.main_intro_file"); + t->main_intro_filename = themespec_gfx_filename(c); + freelog(LOG_DEBUG, "theme intro file %s", t->main_intro_filename); + + c = secfile_lookup_str(file, "themespec.minimap_intro_file"); + t->minimap_intro_filename = themespec_gfx_filename(c); + freelog(LOG_DEBUG, "theme radar file %s", t->minimap_intro_filename); + + spec_filenames = secfile_lookup_str_vec(file, &num_spec_files, + "themespec.files"); + if (num_spec_files == 0) { + freelog(LOG_ERROR, "No theme graphics files specified in \"%s\"", fname); + section_file_free(file); + free(fname); + themeset_free(t); + return NULL; + } + + assert(t->sprite_hash == NULL); + t->sprite_hash = hash_new(hash_fval_string, hash_fcmp_string); + for (i = 0; i < num_spec_files; i++) { + struct specfile *sf = fc_malloc(sizeof(*sf)); + char *dname; + + freelog(LOG_DEBUG, "spec file %s", spec_filenames[i]); + + sf->big_sprite = NULL; + dname = datafilename(spec_filenames[i]); + if (!dname) { + section_file_free(file); + free(fname); + themeset_free(t); + return NULL; + } + sf->file_name = mystrdup(dname); + scan_specfile(t, sf, duplicates_ok); + + specfile_list_prepend(t->specfiles, sf); + } + free(spec_filenames); + + section_file_check_unused(file, fname); + + section_file_free(file); + freelog(LOG_VERBOSE, "finished reading %s", fname); + free(fname); + + return t; +} + + +/************************************************************************** + Loads the sprite. If the sprite is already loaded a reference + counter is increased. Can return NULL if the sprite couldn't be + loaded. +**************************************************************************/ +static struct sprite *load_sprite(struct themeset *t, const char *tag_name) +{ + /* Lookup information about where the sprite is found. */ + struct small_sprite *ss = hash_lookup_data(t->sprite_hash, tag_name); + + freelog(LOG_DEBUG, "load_sprite(tag='%s')", tag_name); + if (!ss) { + return NULL; + } + + assert(ss->ref_count >= 0); + + if (!ss->sprite) { + /* If the sprite hasn't been loaded already, then load it. */ + assert(ss->ref_count == 0); + if (ss->file) { + ss->sprite = load_gfx_file(ss->file); + if (!ss->sprite) { + freelog(LOG_FATAL, _("Couldn't load gfx file %s for sprite %s"), + ss->file, tag_name); + exit(EXIT_FAILURE); + } + } else { + int sf_w, sf_h; + + ensure_big_sprite(ss->sf); + get_sprite_dimensions(ss->sf->big_sprite, &sf_w, &sf_h); + if (ss->x < 0 || ss->x + ss->width > sf_w + || ss->y < 0 || ss->y + ss->height > sf_h) { + freelog(LOG_ERROR, + "Sprite '%s' in file '%s' isn't within the image!", + tag_name, ss->sf->file_name); + return NULL; + } + ss->sprite = + crop_sprite(ss->sf->big_sprite, ss->x, ss->y, ss->width, ss->height, + NULL, -1, -1); + } + } + + /* Track the reference count so we know when to free the sprite. */ + ss->ref_count++; + + return ss->sprite; +} + +/************************************************************************** + Unloads the sprite. Decrease the reference counter. If the last + reference is removed the sprite is freed. +**************************************************************************/ +static void unload_sprite(struct themeset *t, const char *tag_name) +{ + struct small_sprite *ss = hash_lookup_data(t->sprite_hash, tag_name); + + assert(ss); + assert(ss->ref_count >= 1); + assert(ss->sprite); + + ss->ref_count--; + + if (ss->ref_count == 0) { + /* Nobody's using the sprite anymore, so we should free it. We know + * where to find it if we need it again. */ + freelog(LOG_DEBUG, "freeing sprite '%s'", tag_name); + free_sprite(ss->sprite); + ss->sprite = NULL; + } +} + +/* Not very safe, but convenient: */ +#define SET_SPRITE(field, tag) \ + do { \ + t->sprites.field = load_sprite(t, tag); \ + if (!t->sprites.field) { \ + die("Sprite tag %s missing.", tag); \ + } \ + } while(FALSE) + +/* Sets sprites.field to tag or (if tag isn't available) to alt */ +#define SET_SPRITE_ALT(field, tag, alt) \ + do { \ + t->sprites.field = load_sprite(t, tag); \ + if (!t->sprites.field) { \ + t->sprites.field = load_sprite(t, alt); \ + } \ + if (!t->sprites.field) { \ + die("Sprite tag %s and alternate %s are both missing.", tag, alt); \ + } \ + } while(FALSE) + +/* Sets sprites.field to tag, or NULL if not available */ +#define SET_SPRITE_OPT(field, tag) \ + t->sprites.field = load_sprite(t, tag) + +#define SET_SPRITE_ALT_OPT(field, tag, alt) \ + do { \ + t->sprites.field = themeset_lookup_sprite_tag_alt(t, tag, alt, FALSE, \ + "sprite", #field); \ + } while (FALSE) + +/********************************************************************** + Initialize 'sprites' structure based on hardwired tags which the + client always requires. +***********************************************************************/ +static void themeset_lookup_sprite_tags(struct themeset *t) +{ + /* the 'sprites' structure is currently not used, for now we call some + * functions in gui_tilespec.c instead */ + + tilespec_setup_theme(); + tilespec_setup_anim(); + tilespec_setup_city_gfx(); + tilespec_setup_city_icons(); +} + +/************************************************************************** + Frees any internal buffers which are created by load_sprite. Should + be called after the last (for a given period of time) load_sprite + call. This saves a fair amount of memory, but it will take extra time + the next time we start loading sprites again. +**************************************************************************/ +static void finish_loading_sprites(struct themeset *t) +{ + specfile_list_iterate(t->specfiles, sf) { + if (sf->big_sprite) { + free_sprite(sf->big_sprite); + sf->big_sprite = NULL; + } + } specfile_list_iterate_end; +} +/********************************************************************** + Load the tiles; requires themespec_read_toplevel() called previously. + Leads to tile_sprites being allocated and filled with pointers + to sprites. Also sets up and populates sprite_hash, and calls func + to initialize 'sprites' structure. +***********************************************************************/ +void themeset_load_tiles(struct themeset *t) +{ + themeset_lookup_sprite_tags(t); + finish_loading_sprites(t); +} + +/********************************************************************** + Lookup sprite to match tag, or else to match alt if don't find, + or else return NULL, and emit log message. +***********************************************************************/ +struct sprite* themeset_lookup_sprite_tag_alt(struct themeset *t, + const char *tag, const char *alt, + bool required, const char *what, + const char *name) +{ + struct sprite *sp; + + /* (should get sprite_hash before connection) */ + if (!t->sprite_hash) { + die("attempt to lookup for %s %s before sprite_hash setup", what, name); + } + + sp = load_sprite(t, tag); + if (sp) return sp; + + sp = load_sprite(t, alt); + if (sp) { + freelog(LOG_VERBOSE, + "Using alternate graphic %s (instead of %s) for %s %s", + alt, tag, what, name); + return sp; + } + + freelog(required ? LOG_FATAL : LOG_VERBOSE, + _("Don't have graphics tags %s or %s for %s %s"), + tag, alt, what, name); + if (required) { + exit(EXIT_FAILURE); + } + return NULL; +} + +#define FULL_TILE_X_OFFSET ((t->normal_tile_width - t->full_tile_width) / 2) +#define FULL_TILE_Y_OFFSET (t->normal_tile_height - t->full_tile_height) + +#define ADD_SPRITE(s, draw_fog, x_offset, y_offset) \ + (assert(s != NULL), \ + sprs->sprite = s, \ + sprs->foggable = (draw_fog && t->fogstyle == FOG_AUTO), \ + sprs->offset_x = x_offset, \ + sprs->offset_y = y_offset, \ + sprs++) +#define ADD_SPRITE_SIMPLE(s) ADD_SPRITE(s, TRUE, 0, 0) +#define ADD_SPRITE_FULL(s) \ + ADD_SPRITE(s, TRUE, FULL_TILE_X_OFFSET, FULL_TILE_Y_OFFSET) + +/**************************************************************************** + This patch unloads all sprites from the sprite hash (the hash itself + is left intact). +****************************************************************************/ +static void unload_all_sprites(struct themeset *t) +{ + if (t->sprite_hash) { + int i, entries = hash_num_entries(t->sprite_hash); + + for (i = 0; i < entries; i++) { + const char *tag_name = hash_key_by_number(t->sprite_hash, i); + struct small_sprite *ss = hash_lookup_data(t->sprite_hash, tag_name); + + while (ss->ref_count > 0) { + unload_sprite(t, tag_name); + } + } + } +} + +/********************************************************************** +... +***********************************************************************/ +void themeset_free_tiles(struct themeset *t) +{ + int i; + + freelog(LOG_DEBUG, "themespec_free_tiles"); + + unload_all_sprites(t); + + if (t->sprite_hash) { + const int entries = hash_num_entries(t->sprite_hash); + + for (i = 0; i < entries; i++) { + const char *key = hash_key_by_number(t->sprite_hash, 0); + + hash_delete_entry(t->sprite_hash, key); + free((void *) key); + } + + hash_free(t->sprite_hash); + t->sprite_hash = NULL; + } + + small_sprite_list_iterate(t->small_sprites, ss) { + small_sprite_list_unlink(t->small_sprites, ss); + if (ss->file) { + free(ss->file); + } + assert(ss->sprite == NULL); + free(ss); + } small_sprite_list_iterate_end; + + specfile_list_iterate(t->specfiles, sf) { + specfile_list_unlink(t->specfiles, sf); + free(sf->file_name); + if (sf->big_sprite) { + free_sprite(sf->big_sprite); + sf->big_sprite = NULL; + } + free(sf); + } specfile_list_iterate_end; + +#if 0 + /* TODO: may be useful for theme sprites, too */ + sprite_vector_iterate(&t->sprites.****, psprite) { + free_sprite(*psprite); + } sprite_vector_iterate_end; + sprite_vector_free(&t->sprites.****); +#endif + + /* the 'sprites' structure is currently not used, for now we call some + * functions in gui_tilespec.c instead */ + + tilespec_free_theme(); + tilespec_free_anim(); + tilespec_free_city_icons(); +} diff -urN -X devel/diff_ignore devel_distclean/client/gui-sdl/themespec.h devel/client/gui-sdl/themespec.h --- devel_distclean/client/gui-sdl/themespec.h 1970-01-01 01:00:00.000000000 +0100 +++ devel/client/gui-sdl/themespec.h 2005-12-09 17:25:19.000000000 +0100 @@ -0,0 +1,52 @@ +/********************************************************************** + Freeciv - Copyright (C) 1996 - A Kjeldberg, L Gregersen, P Unold + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. +***********************************************************************/ + +/********************************************************************** + Reading and using the themespec files, which describe + the files and contents of themesets. +***********************************************************************/ +#ifndef FC__THEMESPEC_H +#define FC__THEMESPEC_H + +#include "fc_types.h" + +#include "options.h" + +struct sprite; /* opaque; gui-dep */ + +struct themeset; + +extern struct themeset *themeset; + +const char **get_themeset_list(void); + +struct themeset *themeset_read_toplevel(const char *themeset_name); +void themeset_free(struct themeset *themeset); +void themeset_load_tiles(struct themeset *t); +void themeset_free_tiles(struct themeset *t); + +void themespec_try_read(const char *themeset_name); +void themespec_reread(const char *themeset_name); +void themespec_reread_callback(struct client_option *option); + +struct sprite* themeset_lookup_sprite_tag_alt(struct themeset *t, + const char *tag, const char *alt, + bool required, const char *what, + const char *name); + +/* Themeset accessor functions. */ +const char *themeset_get_name(const struct themeset *t); +const char *themeset_main_intro_filename(const struct themeset *t); +const char *themeset_mini_intro_filename(const struct themeset *t); + +#endif /* FC__THEMESPEC_H */ diff -urN -X devel/diff_ignore devel_distclean/utility/shared.c devel/utility/shared.c --- devel_distclean/utility/shared.c 2005-11-27 00:54:02.000000000 +0100 +++ devel/utility/shared.c 2005-12-10 01:51:14.000000000 +0100 @@ -833,7 +833,7 @@ num_dirs, if not NULL, will be set to the number of entries in the list. ***************************************************************************/ -static const char **get_data_dirs(int *num_dirs) +const char **get_data_dirs(int *num_dirs) { const char *path; char *path2, *tok; diff -urN -X devel/diff_ignore devel_distclean/utility/shared.h devel/utility/shared.h --- devel_distclean/utility/shared.h 2005-11-27 01:35:10.000000000 +0100 +++ devel/utility/shared.h 2005-12-10 01:51:07.000000000 +0100 @@ -186,6 +186,9 @@ char *user_home_dir(void); char *user_username(char *buf, size_t bufsz); + +const char **get_data_dirs(int *num_dirs); + char **datafilelist(const char *suffix); struct datafile_list *datafilelist_infix(const char *subpath, const char *infix, bool nodups); @@ -232,4 +235,3 @@ char scanin(char **buf, char *delimiters, char *dest, int size); #endif /* FC__SHARED_H */ - diff -urN -X devel/diff_ignore devel_distclean/client/gui-sdl/themes.c devel/client/gui-sdl/themes.c --- devel_distclean/client/gui-sdl/themes.c 2005-12-04 05:59:27.000000000 +0100 +++ devel/client/gui-sdl/themes.c 2005-12-10 02:42:19.000000000 +0100 @@ -14,15 +14,32 @@ #include <config.h> #endif +#include <dirent.h> +#include <sys/stat.h> + +#include "log.h" +#include "shared.h" +#include "themespec.h" + #include "themes_common.h" #include "themes_g.h" +#define GUI_SDL_DEFAULT_THEME "deluxe" + /***************************************************************************** - Loads a gtk theme directory/theme_name + Loads a gui-sdl theme directory/theme_name *****************************************************************************/ void gui_load_theme(const char *directory, const char *theme_name) { - /* Nothing */ + char buf[strlen(directory) + strlen("/") + strlen(theme_name) + strlen("/theme") + 1]; + + /* free previous loaded theme, if any */ + themeset_free(themeset); + + my_snprintf(buf, sizeof(buf), "%s/%s/theme", directory, theme_name); + + themespec_try_read(buf); + themeset_load_tiles(themeset); } /***************************************************************************** @@ -30,7 +47,8 @@ *****************************************************************************/ void gui_clear_theme(void) { - /* Nothing */ + themeset_free(themeset); + load_theme(GUI_SDL_DEFAULT_THEME); } /***************************************************************************** @@ -41,19 +59,76 @@ *****************************************************************************/ char **get_gui_specific_themes_directories(int *count) { - *count = 0; - - return fc_malloc(sizeof(char*) * 0); + int i; + + const char **data_directories = get_data_dirs(count); + + char **directories = fc_malloc(sizeof(char *) * *count); + + for (i = 0; i < *count; i++) { + char buf[strlen(data_directories[i]) + strlen("/themes/gui-sdl") + 1]; + + my_snprintf(buf, sizeof(buf), "%s/themes/gui-sdl", data_directories[i]); + + directories[i] = mystrdup(buf); + } + + return directories; } /***************************************************************************** Return an array of names of usable themes in the given directory. Array size is stored in count. - Useable theme for gtk+ is a directory which contains file gtk-2.0/gtkrc. + Useable theme for gui-sdl is a directory which contains file theme.themespec. The caller is responsible for freeing the array and the names *****************************************************************************/ char **get_useable_themes_in_directory(const char *directory, int *count) { + DIR *dir; + struct dirent *entry; + + char **theme_names = fc_malloc(sizeof(char *) * 2); + /* Allocated memory size */ + int t_size = 2; + *count = 0; - return fc_malloc(sizeof(char*) * 0); + + dir = opendir(directory); + if (!dir) { + /* This isn't directory or we can't list it */ + return theme_names; + } + + while ((entry = readdir(dir))) { + char buf[strlen(directory) + strlen(entry->d_name) + 32]; + struct stat stat_result; + + my_snprintf(buf, sizeof(buf), + "%s/%s/theme.themespec", directory, entry->d_name); + + if (stat(buf, &stat_result) != 0) { + /* File doesn't exist */ + continue; + } + + if (!S_ISREG(stat_result.st_mode)) { + /* Not a regular file */ + continue; + } + + /* Otherwise it's ok */ + + /* Increase array size if needed */ + if (*count == t_size) { + theme_names = fc_realloc(theme_names, t_size * 2 * sizeof(char *)); + t_size *= 2; + } + + theme_names[*count] = mystrdup(entry->d_name); + (*count)++; + } + + closedir(dir); + + return theme_names; }
|