[Tux4kids-commits] r1306 - branches/commonification/tux4kids-common/trunk/src

Bolesław Kulbabiński bolekk-guest at alioth.debian.org
Fri Jul 31 14:17:51 UTC 2009


Author: bolekk-guest
Date: 2009-07-31 14:17:50 +0000 (Fri, 31 Jul 2009)
New Revision: 1306

Added:
   branches/commonification/tux4kids-common/trunk/src/t4k-audio.c
   branches/commonification/tux4kids-common/trunk/src/t4k-loaders.c
   branches/commonification/tux4kids-common/trunk/src/t4k-menu.c
Modified:
   branches/commonification/tux4kids-common/trunk/src/t4k-globals.h
   branches/commonification/tux4kids-common/trunk/src/t4k-main.c
   branches/commonification/tux4kids-common/trunk/src/tux4kids-common.h
Log:
work on adding menu, loaders and audio to common

Added: branches/commonification/tux4kids-common/trunk/src/t4k-audio.c
===================================================================
--- branches/commonification/tux4kids-common/trunk/src/t4k-audio.c	                        (rev 0)
+++ branches/commonification/tux4kids-common/trunk/src/t4k-audio.c	2009-07-31 14:17:50 UTC (rev 1306)
@@ -0,0 +1,65 @@
+/***************************************************************************
+ -  file: audio.c
+ -  description: this file contains audio related functions
+                            -------------------
+    begin                : Jan 22, 2003
+    copyright            : Sam Hart, Jesse Andrews (C) 2003
+    email                : tuxtype-dev at tux4kids.net
+
+    Modified for use in tuxmath by David Bruce - 2006.
+    email                : <dbruce at tampabay.rr.com>
+                           <tuxmath-devel at lists.sourceforge.net>
+***************************************************************************/
+
+/***************************************************************************
+*                                                                         *
+*   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 of the License, or     *
+*   (at your option) any later version.                                   *
+*                                                                         *
+***************************************************************************/
+
+
+#include "tux4kids-common.h"
+#include "t4k-globals.h"
+
+void playsound(Mix_Chunk* sound)
+{
+ Mix_PlayChannel(-1, sound, 0);
+}
+
+Mix_Music *defaultMusic = NULL; // holds music for audioMusicLoad/unload
+
+/* audioMusicLoad attempts to load and play the music file 
+ * Note: loops == -1 means forever
+ */
+void audioMusicLoad(Mix_Music* music, int loops)
+{
+  audioMusicUnload(); // make sure defaultMusic is clear
+  defaultMusic = music;
+  Mix_PlayMusic(defaultMusic, loops);
+}
+
+
+/* audioMusicUnload attempts to unload any music data that was
+ * loaded using the audioMusicLoad function
+ */
+void audioMusicUnload( void ) {
+
+  if ( defaultMusic )
+    Mix_FreeMusic( defaultMusic );
+
+  defaultMusic=NULL;
+}
+
+/* audioMusicPlay attempts to play the passed music data. 
+ * if a music file was loaded using the audioMusicLoad
+ * it will be stopped and unloaded
+ * Note: loops == -1 means forever
+ */
+void audioMusicPlay( Mix_Music *musicData, int loops ) { 
+
+  audioMusicUnload();        
+  Mix_PlayMusic( musicData, loops );
+}

Modified: branches/commonification/tux4kids-common/trunk/src/t4k-globals.h
===================================================================
--- branches/commonification/tux4kids-common/trunk/src/t4k-globals.h	2009-07-31 13:57:14 UTC (rev 1305)
+++ branches/commonification/tux4kids-common/trunk/src/t4k-globals.h	2009-07-31 14:17:50 UTC (rev 1306)
@@ -15,13 +15,21 @@
 #define min(a,b) (((a) < (b)) ? (a) : (b))
 #define max(a,b) (((a) > (b)) ? (a) : (b))
 
+#define REG_RGBA 16,16,96,96
+#define SEL_RGBA 16,16,128,128
+
+#define MAX_FPS 30
+
 extern int dbg_status;
 
+extern const int dbg_loaders;
 extern const int dbg_menu;
 extern const int dbg_menu_parser;
 extern const int dbg_sdl;
 extern const int dbg_all;
 
+extern char* data_prefix;
+
 /* debug macros */
 #define DEBUGCODE(mask) if((mask) & dbg_status)
 #define DEBUGMSG(mask, ...) if((mask) & dbg_status){ fprintf(stderr, __VA_ARGS__); fflush(stderr); }

Added: branches/commonification/tux4kids-common/trunk/src/t4k-loaders.c
===================================================================
--- branches/commonification/tux4kids-common/trunk/src/t4k-loaders.c	                        (rev 0)
+++ branches/commonification/tux4kids-common/trunk/src/t4k-loaders.c	2009-07-31 14:17:50 UTC (rev 1306)
@@ -0,0 +1,635 @@
+/*
+  loaders.c
+
+  Functions responsible for loading multimedia.
+
+  begin                : Thu May 4 2000
+  copyright            : (C) 2000 by Sam Hart
+                       : (C) 2003 by Jesse Andrews
+  email                : tuxtype-dev at tux4kids.net
+
+  Modified for use in tuxmath by David Bruce - 2006.
+  email                : <dbruce at tampabay.rr.com>
+                         <tuxmath-devel at lists.sourceforge.net>
+
+  Modified to support SVG by Boleslaw Kulbabinski - 2009
+  email                : <bkulbabinski at gmail.com>
+
+  Part of "Tux4Kids" Project
+  http://www.tux4kids.com/
+
+  Copyright: See COPYING file that comes with this distribution.
+*/
+
+#include "tux4kids-common.h"
+#include "t4k-globals.h"
+
+#ifdef HAVE_RSVG
+#include<librsvg/rsvg.h>
+#include<librsvg/rsvg-cairo.h>
+#endif
+
+#define PATH_MAX 1024
+
+/* local functions */
+int             check_file(const char* file);
+
+#ifdef HAVE_RSVG
+SDL_Surface*    load_svg(const char* file_name, int width, int height, const char* layer_name);
+sprite*         load_svg_sprite(const char* file_name, int width, int height);
+SDL_Surface*    render_svg_from_handle(RsvgHandle* file_handle, int width, int height, const char* layer_name);
+void            get_svg_dimensions(const char* file_name, int* width, int* height);
+#endif
+
+SDL_Surface*    load_image(const char* file_name, int mode, int w, int h, bool proportional);
+void            fit_in_rectangle(int* width, int* height, int max_width, int max_height);
+SDL_Surface*    set_format(SDL_Surface* img, int mode);
+sprite*         load_sprite(const char* name, int mode, int w, int h, bool proportional);
+
+
+
+/* check to see if file exists, if so return true */
+// int checkFile( const char *file ) {
+//         static struct stat fileStats;
+//         fileStats.st_mode = 0;
+//         stat( file, &fileStats );
+//         return (S_IFREG & fileStats.st_mode);
+// }
+
+
+int check_file(const char* file)
+{
+  FILE* fp = NULL;
+
+  if (!file)
+  {
+    DEBUGMSG(dbg_loaders, "check_file(): invalid char* argument!\n");
+    return 0;
+  }
+
+  DEBUGMSG(dbg_loaders, "check_file(): checking: %s\n", file);
+
+  fp = fopen(file, "r");
+  if (fp)
+  {
+    DEBUGMSG(dbg_loaders, "check_file(): Opened successfully as FILE\n");
+    fclose(fp);
+    return 1;
+  }
+
+  DEBUGMSG(dbg_loaders, "check_file(): Unable to open '%s' as either FILE or DIR\n", file);
+  return 0;
+}
+
+
+#ifdef HAVE_RSVG
+
+/* Load a layer of SVG file and resize it to given dimensions.
+   If width or height is negative no resizing is applied.
+   If layer = NULL then the whole image is loaded.
+   layer_name must be preceded with a '#' symbol.
+   Return NULL on failure.
+   (partly based on TuxPaint's SVG loading function) */
+SDL_Surface* load_svg(const char* file_name, int width, int height, const char* layer_name)
+{
+  SDL_Surface* dest;
+  RsvgHandle* file_handle;
+
+  DEBUGMSG(dbg_loaders, "load_svg(): loading %s\n", file_name);
+
+  rsvg_init();
+
+  file_handle = rsvg_handle_new_from_file(file_name, NULL);
+  if(NULL == file_handle)
+  {
+    DEBUGMSG(dbg_loaders, "load_svg(): file %s not found\n", file_name);
+    rsvg_term();
+    return NULL;
+  }
+
+  dest = render_svg_from_handle(file_handle, width, height, layer_name);
+
+  g_object_unref(file_handle);
+  rsvg_term();
+
+  return dest;
+}
+
+sprite* load_svg_sprite(const char* file_name, int width, int height)
+{
+  RsvgHandle* file_handle;
+  sprite* new_sprite;
+  char lay_name[20];
+  int i;
+
+  DEBUGMSG(dbg_loaders, "load_svg_sprite(): loading sprite from %s\n", file_name);
+
+  rsvg_init();
+
+  file_handle = rsvg_handle_new_from_file(file_name, NULL);
+  if(NULL == file_handle)
+  {
+    DEBUGMSG(dbg_loaders, "load_svg_sprite(): file %s not found\n", file_name);
+    rsvg_term();
+    return NULL;
+  }
+
+  new_sprite = malloc(sizeof(sprite));
+  new_sprite->default_img = render_svg_from_handle(file_handle, width, height, "#default");
+
+  /* get number of frames from description */
+  sscanf(rsvg_handle_get_desc(file_handle), "%d", &new_sprite->num_frames);
+  DEBUGMSG(dbg_loaders, "load_svg_sprite(): loading %d frames\n", new_sprite->num_frames);
+
+  for(i = 0; i < new_sprite->num_frames; i++)
+  {
+    sprintf(lay_name, "#frame%d", i);
+    new_sprite->frame[i] = render_svg_from_handle(file_handle, width, height, lay_name);
+  }
+
+  g_object_unref(file_handle);
+  rsvg_term();
+
+  return new_sprite;
+}
+
+/* render a layer of SVG file and resize it to given dimensions.
+   If width or height is negative no resizing is applied. */
+SDL_Surface* render_svg_from_handle(RsvgHandle* file_handle, int width, int height, const char* layer_name)
+{
+  RsvgDimensionData dimensions;
+  cairo_surface_t* temp_surf;
+  cairo_t* context;
+  SDL_Surface* dest;
+  float scale_x, scale_y;
+  Uint32 Rmask, Gmask, Bmask, Amask;
+
+  rsvg_handle_get_dimensions(file_handle, &dimensions);
+
+  /* set scale_x and scale_y */
+  if(width < 0 || height < 0)
+  {
+    width = dimensions.width;
+    height = dimensions.height;
+    scale_x = 1.0;
+    scale_y = 1.0;
+  }
+  else
+  {
+    scale_x = (float)width / dimensions.width;
+    scale_y = (float)height / dimensions.height;
+  }
+
+  /* set color masks */
+  Rmask = screen->format->Rmask;
+  Gmask = screen->format->Gmask;
+  Bmask = screen->format->Bmask;
+  if(screen->format->Amask == 0)
+    /* find a free byte to use for Amask */
+    Amask = ~(Rmask | Gmask | Bmask);
+  else
+    Amask = screen->format->Amask;
+
+  DEBUGMSG(dbg_loaders, "render_svg_from_handle(): color masks: R=%u, G=%u, B=%u, A=%u\n",
+        Rmask, Gmask, Bmask, Amask);
+
+  dest = SDL_CreateRGBSurface(SDL_SWSURFACE | SDL_SRCALPHA,
+        width, height, screen->format->BitsPerPixel, Rmask, Gmask, Bmask, Amask);
+
+  SDL_LockSurface(dest);
+  temp_surf = cairo_image_surface_create_for_data(dest->pixels,
+        CAIRO_FORMAT_ARGB32, dest->w, dest->h, dest->pitch);
+
+  context = cairo_create(temp_surf);
+  if(cairo_status(context) != CAIRO_STATUS_SUCCESS)
+  {
+    DEBUGMSG(dbg_loaders, "render_svg_from_handle(): error rendering SVG\n");
+    cairo_surface_destroy(temp_surf);
+    return NULL;
+  }
+
+  cairo_scale(context, scale_x, scale_y);
+
+  /* render appropriate layer */
+  rsvg_handle_render_cairo_sub(file_handle, context, layer_name);
+
+  SDL_UnlockSurface(dest);
+  cairo_surface_destroy(temp_surf);
+  cairo_destroy(context);
+
+  return dest;
+}
+
+void get_svg_dimensions(const char* file_name, int* width, int* height)
+{
+  RsvgHandle* file_handle;
+  RsvgDimensionData dimensions;
+
+  rsvg_init();
+
+  file_handle = rsvg_handle_new_from_file(file_name, NULL);
+  if(file_handle == NULL)
+  {
+    DEBUGMSG(dbg_loaders, "get_svg_dimensions(): file %s not found\n", file_name);
+    rsvg_term();
+    return;
+  }
+
+  rsvg_handle_get_dimensions(file_handle, &dimensions);
+
+  *width = dimensions.width;
+  *height = dimensions.height;
+
+  g_object_unref(file_handle);
+  rsvg_term();
+}
+
+#endif /* HAVE_RSVG */
+
+/* Load an image without resizing it */
+SDL_Surface* LoadImage(const char* file_name, int mode)
+{
+  return LoadScaledImage(file_name, mode, -1, -1);
+}
+
+/* LoadScaledImage : Load an image and resize it to given dimensions.
+   If width or height is negative no resizing is applied.
+   The loader (load_svg() or IMG_Load()) is chosen depending on file extension,
+   If an SVG file is not found try to load its PNG equivalent
+   (unless IMG_NO_PNG_FALLBACK is set) */
+SDL_Surface* LoadScaledImage(const char* file_name, int mode, int width, int height)
+{
+  return load_image(file_name, mode, width, height, false);
+}
+
+/* LoadImageOfBoundingBox : Same as LoadScaledImage but preserve image proportions
+   and fit it into max_width x max_height rectangle.
+   Returned surface is not necessarily max_width x max_height ! */
+SDL_Surface* LoadImageOfBoundingBox(const char* file_name, int mode, int max_width, int max_height)
+{
+  return load_image(file_name, mode, max_width, max_height, true);
+}
+
+
+/* load_image : helper function used by LoadScaledImage and LoadImageOfBoundingBox */
+SDL_Surface* load_image(const char* file_name, int mode, int w, int h, bool proportional)
+{
+  SDL_Surface* loaded_pic = NULL;
+  SDL_Surface* final_pic = NULL;
+  char fn[PATH_MAX];
+  int fn_len;
+  int width = -1, height = -1;
+  bool is_svg = true;
+
+  if(NULL == file_name)
+  {
+    DEBUGMSG(dbg_loaders, "load_image(): file_name is NULL, exiting.\n");
+    return NULL;
+  }
+
+  /* run loader depending on file extension */
+
+  /* add path prefix */
+  snprintf(fn, PATH_MAX, "%s/images/%s", data_prefix, file_name);
+  fn_len = strlen(fn);
+
+  if(strcmp(fn + fn_len - 4, ".svg"))
+  {
+    DEBUGMSG(dbg_loaders, "load_image(): %s is not an SVG, loading using IMG_Load()\n", fn);
+    loaded_pic = IMG_Load(fn);
+    is_svg = false;
+    if (NULL == loaded_pic)
+    {
+      is_svg = true;
+      DEBUGMSG(dbg_loaders, "load_image(): Trying to load SVG equivalent of %s\n", fn);
+      sprintf(strrchr(fn, '.'), ".svg");
+    }
+  }
+  if (is_svg)
+  {
+#ifdef HAVE_RSVG
+    DEBUGMSG(dbg_loaders, "load_image(): trying to load %s as SVG.\n", fn);
+    if(proportional)
+    {
+      get_svg_dimensions(fn, &width, &height);
+      if(width > 0 && height > 0)
+        fit_in_rectangle(&width, &height, w, h);
+    }
+    else
+    {
+      width = w;
+      height = h;
+    }
+    loaded_pic = load_svg(fn, width, height, NULL);
+#endif
+
+    if(loaded_pic == NULL)
+    {
+#ifdef HAVE_RSVG
+      DEBUGMSG(dbg_loaders, "load_image(): failed to load %s as SVG.\n", fn);
+#else
+      DEBUGMSG(dbg_loaders, "load_image(): SVG support not available.\n");
+#endif
+      if(mode & IMG_NO_PNG_FALLBACK)
+      {
+        DEBUGMSG(dbg_loaders, "load_image(): %s : IMG_NO_PNG_FALLBACK is set.\n", fn);
+      }
+      else
+      {
+        DEBUGMSG(dbg_loaders, "load_image(): Trying to load PNG equivalent of %s\n", fn);
+        strcpy(fn + fn_len - 3, "png");
+
+        loaded_pic = IMG_Load(fn);
+        is_svg = false;
+      }
+    }
+  }
+
+  if (NULL == loaded_pic) /* Could not load image: */
+  {
+    if (mode & IMG_NOT_REQUIRED)
+    {
+      DEBUGMSG(dbg_loaders, "load_image(): Warning: could not load optional graphics file %s\n", file_name);
+      return NULL;  /* Allow program to continue */
+    }
+    /* If image was required, exit from program: */
+    fprintf(stderr, "load_image(): ERROR could not load required graphics file %s\n", file_name);
+    fprintf(stderr, "%s", SDL_GetError() );
+    return NULL;
+  }
+  else if(!is_svg && w > 0 && h > 0)
+  {
+    if(proportional)
+    {
+      width = loaded_pic->w;
+      height = loaded_pic->h;
+      fit_in_rectangle(&width, &height, w, h);
+    }
+    else
+    {
+      width = w;
+      height = h;
+    }
+    final_pic = zoom(loaded_pic, width, height);
+    SDL_FreeSurface(loaded_pic);
+    loaded_pic = final_pic;
+    final_pic = NULL;
+  }
+
+  final_pic = set_format(loaded_pic, mode);
+  SDL_FreeSurface(loaded_pic);
+  DEBUGMSG(dbg_loaders, "Leaving load_image()\n\n");
+
+  return final_pic;
+}
+
+/* adjust width and height to fit in max_width x max_height rectangle
+   but preserve their proportion */
+void fit_in_rectangle(int* width, int* height, int max_width, int max_height)
+{
+  float scale_w, scale_h;
+
+  if(width != 0 && height != 0)
+  {
+    scale_w = (float) max_width / (*width);
+    scale_h = (float) max_height / (*height);
+    *width *= min(scale_w, scale_h);
+    *height *= min(scale_w, scale_h);
+  }
+}
+
+SDL_Surface* set_format(SDL_Surface* img, int mode)
+{
+  switch (mode & IMG_MODES)
+  {
+    case IMG_REGULAR:
+    {
+      DEBUGMSG(dbg_loaders, "set_format(): handling IMG_REGULAR mode.\n");
+      return SDL_DisplayFormat(img);
+    }
+
+    case IMG_ALPHA:
+    {
+      DEBUGMSG(dbg_loaders, "set_format(): handling IMG_ALPHA mode.\n");
+      return SDL_DisplayFormatAlpha(img);
+    }
+
+    case IMG_COLORKEY:
+    {
+      DEBUGMSG(dbg_loaders, "set_format(): handling IMG_COLORKEY mode.\n");
+      SDL_LockSurface(img);
+      SDL_SetColorKey(img, (SDL_SRCCOLORKEY | SDL_RLEACCEL),
+                      SDL_MapRGB(img->format, 255, 255, 0));
+      return SDL_DisplayFormat(img);
+    }
+
+    default:
+    {
+      DEBUGMSG(dbg_loaders, "set_format(): Image mode not recognized\n");
+    }
+  }
+
+  return NULL;
+}
+
+
+/* LoadBkgd() : a wrapper for LoadImage() that optimizes
+   the format of background image */
+SDL_Surface* LoadBkgd(const char* file_name, int width, int height)
+{
+  SDL_Surface* orig = NULL;
+  SDL_Surface* final_pic = NULL;
+
+  orig = LoadScaledImage(file_name, IMG_REGULAR, width, height);
+
+  if (!orig)
+  {
+    DEBUGMSG(dbg_loaders, "In LoadBkgd(), LoadImage() returned NULL on %s\n",
+             file_name);
+    return NULL;
+  }
+
+  /* turn off transparency, since it's the background */
+  SDL_SetAlpha(orig, SDL_RLEACCEL, SDL_ALPHA_OPAQUE);
+  final_pic = SDL_DisplayFormat(orig); /* optimize the format */
+  SDL_FreeSurface(orig);
+
+  return final_pic;
+}
+
+
+sprite* LoadSprite(const char* name, int mode)
+{
+  return LoadScaledSprite(name, mode, -1, -1);
+}
+
+sprite* LoadScaledSprite(const char* name, int mode, int width, int height)
+{
+  return load_sprite(name, mode, width, height, false);
+}
+
+sprite* LoadSpriteOfBoundingBox(const char* name, int mode, int max_width, int max_height)
+{
+  return load_sprite(name, mode, max_width, max_height, true);
+}
+
+sprite* load_sprite(const char* name, int mode, int w, int h, bool proportional)
+{
+  sprite *new_sprite = NULL;
+  char fn[PATH_MAX];
+  int i, width, height;
+
+#ifdef HAVE_RSVG
+  /* check if SVG sprite file is present */
+  sprintf(fn, "%s/images/%s.svg", data_prefix, name);
+  if(1 == check_file(fn))
+  {
+    if(proportional)
+    {
+      get_svg_dimensions(fn, &width, &height);
+      if(width > 0 && height > 0)
+        fit_in_rectangle(&width, &height, w, h);
+    }
+    else
+    {
+      width = w;
+      height = h;
+    }
+
+    new_sprite = load_svg_sprite(fn, width, height);
+
+    if(new_sprite)
+    {
+      set_format(new_sprite->default_img, mode);
+      for(i = 0; i < new_sprite->num_frames; i++)
+        set_format(new_sprite->frame[i], mode);
+      new_sprite->cur = 0;
+    }
+  }
+#endif
+
+  if(!new_sprite)
+  {
+    /* SVG sprite was not loaded, try to load it frame by frame from PNG files */
+    new_sprite = malloc(sizeof(sprite));
+
+    sprintf(fn, "%sd.png", name);  // The 'd' means the default image
+    if(proportional)
+      new_sprite->default_img = LoadImageOfBoundingBox(fn, mode | IMG_NOT_REQUIRED, w, h);
+    else
+      new_sprite->default_img = LoadScaledImage(fn, mode | IMG_NOT_REQUIRED, w, h);
+
+    if(!new_sprite->default_img)
+      DEBUGMSG(dbg_loaders, "load_sprite(): failed to load default image for %s\n", name);
+
+    for(i = 0; i < MAX_SPRITE_FRAMES; i++)
+    {
+      sprintf(fn, "%s%d.png", name, i);
+      if(proportional)
+        new_sprite->frame[i] = LoadImageOfBoundingBox(fn, mode | IMG_NOT_REQUIRED, w, h);
+      else
+        new_sprite->frame[i] = LoadScaledImage(fn, mode | IMG_NOT_REQUIRED, w, h);
+
+      if(new_sprite->frame[i] == NULL)
+      {
+        new_sprite->cur = 0;
+        new_sprite->num_frames = i;
+        break;
+      }
+      else
+        DEBUGMSG(dbg_loaders, "load_sprite(): loaded frame %d of %s\n", i, name);
+    }
+  }
+
+  return new_sprite;
+}
+
+sprite* FlipSprite(sprite* in, int X, int Y)
+{
+  sprite *out;
+
+  out = malloc(sizeof(sprite));
+  if (in->default_img != NULL)
+    out->default_img = Flip( in->default_img, X, Y );
+  else
+    out->default_img = NULL;
+  for( out->num_frames=0; out->num_frames<in->num_frames; out->num_frames++ )
+    out->frame[out->num_frames] = Flip( in->frame[out->num_frames], X, Y );
+  out->cur = 0;
+  return out;
+}
+
+void FreeSprite(sprite* gfx)
+{
+  int x;
+  if (!gfx)
+    return;
+
+  DEBUGMSG(dbg_loaders, "Freeing image at %p", gfx);
+  for (x = 0; x < gfx->num_frames; x++)
+  {
+    DEBUGMSG(dbg_loaders, ".");
+    if (gfx->frame[x])
+    {
+      SDL_FreeSurface(gfx->frame[x]);
+      gfx->frame[x] = NULL;
+    }
+  }
+
+  if (gfx->default_img)
+  {
+    SDL_FreeSurface(gfx->default_img);
+    gfx->default_img = NULL;
+  }
+
+  DEBUGMSG(dbg_loaders, "FreeSprite() - done\n");
+  free(gfx);
+}
+
+void NextFrame(sprite* s)
+{
+  if (s && s->num_frames)
+    s->cur = (s->cur + 1) % s->num_frames;
+}
+
+
+
+/* LoadSound : Load a sound/music patch from a file. */
+Mix_Chunk* LoadSound( char *datafile )
+{
+  Mix_Chunk* tempChunk = NULL;
+  char fn[PATH_MAX];
+
+//    sprintf(fn , "%s/sounds/%s", realPath[i], datafile);
+  sprintf(fn , "%s/sounds/%s", data_prefix, datafile);
+  tempChunk = Mix_LoadWAV(fn);
+  if (!tempChunk)
+  {
+    fprintf(stderr, "LoadSound(): %s not found\n\n", fn);
+  }
+  return tempChunk;
+}
+
+/* LoadMusic : Load music from a datafile */
+Mix_Music* LoadMusic(char *datafile )
+{
+  char fn[PATH_MAX];
+  Mix_Music* tempMusic = NULL;
+
+  sprintf( fn , "%s/sounds/%s", data_prefix, datafile );
+  if (1 != check_file(fn))
+  {
+    fprintf(stderr, "LoadMusic(): %s not found\n\n", fn);
+    return NULL;
+  }
+
+  tempMusic = Mix_LoadMUS(fn);
+
+  if (!tempMusic)
+  {
+    fprintf(stderr, "LoadMusic(): %s not loaded successfully\n", fn);
+    printf("Error was: %s\n\n", Mix_GetError());
+  }
+  return tempMusic;
+}
+

Modified: branches/commonification/tux4kids-common/trunk/src/t4k-main.c
===================================================================
--- branches/commonification/tux4kids-common/trunk/src/t4k-main.c	2009-07-31 13:57:14 UTC (rev 1305)
+++ branches/commonification/tux4kids-common/trunk/src/t4k-main.c	2009-07-31 14:17:50 UTC (rev 1306)
@@ -16,7 +16,10 @@
 
 int dbg_status;
 
+char* data_prefix;
+
 /* these values have to match those used in games */
+const int dbg_loaders       = 1 << 2;
 const int dbg_menu          = 1 << 4;
 const int dbg_menu_parser   = 1 << 5;
 const int dbg_sdl           = 1 << 10;
@@ -27,3 +30,7 @@
   dbg_status = dbg_flags;
 }
 
+void SetDataPrefix(char* pref)
+{
+  data_prefix = pref;
+}

Added: branches/commonification/tux4kids-common/trunk/src/t4k-menu.c
===================================================================
--- branches/commonification/tux4kids-common/trunk/src/t4k-menu.c	                        (rev 0)
+++ branches/commonification/tux4kids-common/trunk/src/t4k-menu.c	2009-07-31 14:17:50 UTC (rev 1306)
@@ -0,0 +1,1053 @@
+/*
+  t4k-menu.c
+
+  Functions responsible for loading, parsing and displaying game menu.
+
+  Part of "Tux4Kids" Project
+  http://www.tux4kids.com/
+
+  Author: Boleslaw Kulbabinski <bkulbabinski at gmail.com>, (C) 2009
+
+  Copyright: See COPYING file that comes with this distribution.
+*/
+
+#include "tux4kids-common.h"
+#include "t4k-globals.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+struct mNode {
+  struct mNode* parent;
+
+  char* title;
+  int font_size;
+
+  char* icon_name;
+  sprite* icon;
+
+  SDL_Rect button_rect;
+  SDL_Rect icon_rect;
+  SDL_Rect text_rect;
+
+  /* submenu_size = 0 if no submenu */
+  int submenu_size;
+  struct mNode** submenu;
+
+  /* these fields are used only if submenu_size = 0 */
+  int activity;
+  int param;
+
+  /* these fields are used only if submenu_size > 0 */
+  bool show_title;
+  int entries_per_screen;
+  int first_entry;
+};
+
+typedef struct mNode MenuNode;
+
+#define QUIT -2
+#define STOP -1
+#define RUN_MAIN_MENU -3
+
+int n_of_activities;
+char** activities;
+
+SDL_Color red, yellow, white, black;
+
+Mix_Chunk* snd_click;
+Mix_Chunk* snd_hover;
+Mix_Music* menu_music;
+
+#define N_OF_MENUS 10
+MenuNode* menus[N_OF_MENUS];
+
+/* actions available while viewing the menu */
+enum { NONE, CLICK, PAGEUP, PAGEDOWN, STOP_ESC, RESIZED };
+
+/* stop button, left and right arrow positions do not
+   depend on currently displayed menu */
+SDL_Rect menu_rect, stop_rect, prev_rect, next_rect;
+SDL_Surface *stop_button, *prev_arrow, *next_arrow, *prev_gray, *next_gray;
+
+/*TODO: move these constants into a config file (maybe together with
+  titleGetScreen() paths and rects ? ) */
+const float menu_pos[4] = {0.38, 0.23, 0.55, 0.72};
+const float stop_pos[4] = {0.94, 0.0, 0.06, 0.06};
+const float prev_pos[4] = {0.87, 0.93, 0.06, 0.06};
+const float next_pos[4] = {0.94, 0.93, 0.06, 0.06};
+const char* stop_path = "status/stop.svg";
+const char* prev_path = "status/left.svg";
+const char* next_path = "status/right.svg";
+const char* prev_gray_path = "status/left_gray.svg";
+const char* next_gray_path = "status/right_gray.svg";
+const float button_gap = 0.2, text_h_gap = 0.4, text_w_gap = 0.5, button_radius = 0.27;
+const int min_font_size = 8, default_font_size = 20, max_font_size = 40;
+
+/* font size used in current resolution */
+int curr_font_size;
+
+/* menu title rect */
+SDL_Rect menu_title_rect;
+
+/* buffer size used when reading attributes or names */
+const int buf_size = 128;
+
+
+
+/* local functions */
+MenuNode*       create_empty_node();
+char*           get_attribute_name(const char* token);
+char*           get_attribute_value(const char* token);
+void            read_attributes(FILE* xml_file, MenuNode* node);
+MenuNode*       load_menu_from_file(FILE* xml_file, MenuNode* parent);
+void            free_menu(MenuNode* menu);
+MenuNode*       CreateOneLevelMenu(int items, char** item_names, char* title, char* trailer);
+
+int             RunMenu(MenuNode* root, bool return_choice, void (*draw_background)(), int (*handle_event)(SDL_Event*), void (*handle_animations)(), int (*handle_activity)(int, int));
+SDL_Surface**   render_buttons(MenuNode* menu, bool selected);
+void            prerender_menu(MenuNode* menu);
+char*           find_longest_text(MenuNode* menu, int* length);
+void            set_font_size();
+void            prerender_all();
+
+
+
+/*
+  functions responsible for parsing menu files
+  and creating menu trees
+*/
+
+/* creates new MenuNode struct with all fields set to NULL (or 0) */
+MenuNode* create_empty_node()
+{
+  MenuNode* new_node = malloc(sizeof(MenuNode));
+  new_node->parent = NULL;
+  new_node->title = NULL;
+  new_node->icon_name = NULL;
+  new_node->icon = NULL;
+  new_node->submenu_size = 0;
+  new_node->submenu = NULL;
+  new_node->activity = 0;
+  new_node->param = 0;
+  new_node->first_entry = 0;
+  new_node->show_title = false;
+
+  return new_node;
+}
+
+/* read attributes and fill appropriate node fields */
+void read_attributes(FILE* xml_file, MenuNode* node)
+{
+  char attr_name[buf_size];
+  char attr_val[buf_size];
+  int i;
+
+  /* read tokens until closing '>' is found */
+  do
+  {
+    fscanf(xml_file, " %[^=\n]", attr_name);
+
+    DEBUGMSG(dbg_menu_parser, "read_attributes(): read attribute name: %s\n", attr_name);
+    if(strchr(attr_name, '>'))
+      break;
+
+    fscanf(xml_file, "=\"%[^\"]\"", attr_val);
+    DEBUGMSG(dbg_menu_parser, "read_attributes(): read attribute value: %s\n", attr_val);
+
+    if(strcmp(attr_name, "title") == 0)
+      node->title = strdup(attr_val);
+    else if(strcmp(attr_name, "entries") == 0)
+      node->submenu_size = atoi(attr_val);
+    else if(strcmp(attr_name, "param") == 0)
+      node->param = atoi(attr_val);
+    else if(strcmp(attr_name, "sprite") == 0)
+      node->icon_name = strdup(attr_val);
+    else if(strcmp(attr_name, "run") == 0)
+    {
+      if(strcmp(attr_val, "RUN_MAIN_MENU") == 0)
+        node->activity = RUN_MAIN_MENU;
+      for(i = 0; i < n_of_activities; i++)
+        if(strcmp(attr_val, activities[i]) == 0)
+          node->activity = i;
+    }
+    else
+      DEBUGMSG(dbg_menu_parser, "read_attributes(): unknown attribute %s , omitting\n", attr_name);
+
+  } while(strchr(attr_val, '>') == NULL);
+}
+
+/* recursively read and parse given xml menu file and create menu tree
+   return NULL in case of problems */
+MenuNode* load_menu_from_file(FILE* xml_file, MenuNode* parent)
+{
+  MenuNode* new_node = create_empty_node();
+  char buffer[buf_size];
+  int i;
+
+  new_node->parent = parent;
+
+  DEBUGMSG(dbg_menu_parser, "entering load_menu_from_file()\n");
+  fscanf(xml_file, " < %s", buffer);
+
+  if(strcmp(buffer, "menu") == 0)
+  {
+    read_attributes(xml_file, new_node);
+    if(new_node->title == NULL)
+    {
+      DEBUGMSG(dbg_menu_parser, "load_menu_from_file(): no title attribute, exiting\n");
+      return NULL;
+    }
+
+    if(new_node->submenu_size > 0)
+    {
+      new_node->submenu = malloc(new_node->submenu_size * sizeof(MenuNode));
+      for(i = 0; i < new_node->submenu_size; i++)
+        new_node->submenu[i] = load_menu_from_file(xml_file, new_node);
+    }
+
+    fscanf(xml_file, " </%[^>\n]> ", buffer);
+    if(strcmp(buffer, "menu") != 0)
+      DEBUGMSG(dbg_menu_parser, "load_menu_from_file(): warning - no closing menu tag, found %s instead\n", buffer);
+  }
+  else if(strcmp(buffer, "item") == 0)
+  {
+    read_attributes(xml_file, new_node);
+    if(new_node->title == NULL)
+    {
+      DEBUGMSG(dbg_menu_parser, "load_menu_from_file(): no title attribute, exiting\n");
+      return NULL;
+    }
+  }
+  else
+  {
+    DEBUGMSG(dbg_menu_parser, "load_menu_from_file(): unknown tag: %s\n, exiting\n", buffer);
+    return NULL;
+  }
+
+  DEBUGMSG(dbg_menu_parser, "load_menu_from_file(): node loaded successfully\n");
+  return new_node;
+}
+
+/* recursively free all non-NULL pointers in a menu tree */
+void free_menu(MenuNode* menu)
+{
+  int i;
+
+  DEBUGMSG(dbg_menu, "entering free_menu()\n");
+  if(menu != NULL)
+  {
+    if(menu->title != NULL)
+      free(menu->title);
+    if(menu->icon_name != NULL)
+      free(menu->icon_name);
+    if(menu->icon != NULL)
+      FreeSprite(menu->icon);
+
+    if(menu->submenu != NULL)
+    {
+      for(i = 0; i < menu->submenu_size; i++)
+        if(menu->submenu[i] != NULL)
+        {
+          free_menu(menu->submenu[i]);
+          menu->submenu[i] = NULL;
+        }
+      free(menu->submenu);
+    }
+
+    free(menu);
+  }
+}
+
+/* create a simple one-level menu without sprites.
+   all given strings are copied */
+MenuNode* CreateOneLevelMenu(int items, char** item_names, char* title, char* trailer)
+{
+  MenuNode* menu = create_empty_node();
+  int i;
+
+  if(title)
+  {
+    menu->title = strdup(title);
+    menu->show_title = true;
+  }
+  menu->submenu_size = items + (trailer ? 1 : 0);
+  menu->submenu = (MenuNode**) malloc(menu->submenu_size * sizeof(MenuNode*));
+  for(i = 0; i < items; i++)
+  {
+    menu->submenu[i] = create_empty_node();
+    menu->submenu[i]->title = strdup(item_names[i]);
+    menu->submenu[i]->activity = i;
+  }
+
+  if(trailer)
+  {
+    menu->submenu[items] = create_empty_node();
+    menu->submenu[items]->title = strdup(trailer);
+    menu->submenu[items]->activity = items;
+  }
+
+  return menu;
+}
+
+void SetActivitiesList(int num, char** acts)
+{
+  n_of_activities = num;
+  activities = acts;
+}
+
+void SetMenuSounds(Mix_Music* music, Mix_Chunk* click, Mix_Chunk* hover)
+{
+  snd_click = click;
+  snd_hover = hover;
+  menu_music = music;
+}
+
+void InitMenu()
+{
+    black.r       = 0x00; black.g       = 0x00; black.b       = 0x00;
+    red.r         = 0xff; red.g         = 0x00; red.b         = 0x00;
+    white.r       = 0xff; white.g       = 0xff; white.b       = 0xff;
+    yellow.r      = 0xff; yellow.g      = 0xff; yellow.b      = 0x00;
+}
+
+/* Display the menu and run the event loop.
+   if return_choice = true then return chosen value instead of
+   running handle_activity()
+   this function is a modified copy of choose_menu_item() */
+int RunMenu(MenuNode* root, bool return_choice, void (*draw_background)(), int (*handle_event)(SDL_Event*), void (*handle_animations)(), int (*handle_activity)(int, int))
+{
+  SDL_Surface** menu_item_unselected = NULL;
+  SDL_Surface** menu_item_selected = NULL;
+  SDL_Surface* title_surf;
+  SDL_Event event;
+  MenuNode* menu = root;
+  MenuNode* tmp_node;
+
+  SDL_Rect tmp_rect;
+  sprite* tmp_sprite;
+  int i;
+  int stop = 0;
+  int items;
+
+  int action = NONE;
+
+  Uint32 frame_start = 0;       //For keeping frame rate constant
+  Uint32 frame_now = 0;
+  Uint32 frame_counter = 0;
+  int loc = -1;                  //The currently selected menu item
+  int old_loc = -1;
+  int click_flag = 1;
+
+  for(;;) /* one loop body execution for one menu page */
+  {
+    DEBUGMSG(dbg_menu, "run_menu(): drawing whole new menu page\n");
+
+    draw_background();
+    /* render buttons for current menu page */
+    menu_item_unselected = render_buttons(menu, false);
+    menu_item_selected = render_buttons(menu, true);
+    items = min(menu->entries_per_screen, menu->submenu_size - menu->first_entry);
+
+    DEBUGMSG(dbg_menu, "run_menu(): drawing %d buttons\n", items);
+    for(i = 0; i < items; i++)
+    {
+      if(loc == i)
+        SDL_BlitSurface(menu_item_selected[i], NULL, GetScreen(), &menu->submenu[menu->first_entry + i]->button_rect);
+      else
+        SDL_BlitSurface(menu_item_unselected[i], NULL, GetScreen(), &menu->submenu[menu->first_entry + i]->button_rect);
+      if(menu->submenu[menu->first_entry + i]->icon)
+        SDL_BlitSurface(menu->submenu[menu->first_entry + i]->icon->default_img, NULL, GetScreen(), &menu->submenu[menu->first_entry + i]->icon_rect);
+    }
+
+    SDL_BlitSurface(stop_button, NULL, GetScreen(), &stop_rect);
+
+    if(menu->entries_per_screen < menu->submenu_size)
+    {
+      /* display arrows */
+      if(menu->first_entry > 0)
+        SDL_BlitSurface(prev_arrow, NULL, GetScreen(), &prev_rect);
+      else
+        SDL_BlitSurface(prev_gray, NULL, GetScreen(), &prev_rect);
+      if(menu->first_entry + items < menu->submenu_size)
+        SDL_BlitSurface(next_arrow, NULL, GetScreen(), &next_rect);
+      else
+        SDL_BlitSurface(next_gray, NULL, GetScreen(), &next_rect);
+    }
+
+    if(menu->show_title)
+    {
+      menu_title_rect = menu->submenu[0]->button_rect;
+      menu_title_rect.y = menu_rect.y - menu_title_rect.h;
+      title_surf = BlackOutline(_(menu->title), curr_font_size, &red);
+      SDL_BlitSurface(title_surf, NULL, GetScreen(), &menu_title_rect);
+      SDL_FreeSurface(title_surf);
+    }
+    SDL_UpdateRect(GetScreen(), 0, 0, 0, 0);
+
+    SDL_WM_GrabInput(SDL_GRAB_OFF);
+
+    while (SDL_PollEvent(&event));  // clear pending events
+
+    /******** Main loop: *********/
+    stop = false;
+    DEBUGMSG(dbg_menu, "run_menu(): entering menu loop\n");
+    while (!stop)
+    {
+      frame_start = SDL_GetTicks();         /* For keeping frame rate constant.*/
+
+      action = NONE;
+      while (!stop && SDL_PollEvent(&event))
+      {
+        switch (event.type)
+        {
+          case SDL_QUIT:
+          {
+            FreeSurfaceArray(menu_item_unselected, items);
+            FreeSurfaceArray(menu_item_selected, items);
+            return QUIT;
+          }
+
+          case SDL_MOUSEMOTION:
+          {
+            loc = -1;
+            for (i = 0; i < items; i++)
+            {
+              if (inRect(menu->submenu[menu->first_entry + i]->button_rect, event.motion.x, event.motion.y))
+              {
+                if(snd_hover)
+                  playsound(snd_hover);
+                loc = i;
+                break;   /* from for loop */
+              }
+            }
+
+            /* "Left" button - make click if button active: */
+            if(inRect(prev_rect, event.motion.x, event.motion.y)
+               && menu->first_entry > 0)
+            {
+              if(click_flag)
+              {
+                if(snd_hover)
+                  playsound(snd_hover);
+                click_flag = 0;
+              }
+            }
+
+            /* "Right" button - make click if button active: */
+            else if(inRect(next_rect, event.motion.x, event.motion.y)
+               && menu->first_entry + items < menu->submenu_size)
+            {
+              if(click_flag)
+              {
+                if(snd_hover)
+                  playsound(snd_hover);
+                click_flag = 0;
+              }
+            }
+
+            /* "stop" button */
+            else if (inRect(stop_rect, event.motion.x, event.motion.y ))
+            {
+              if(click_flag)
+              {
+                if(snd_hover)
+                  playsound(snd_hover);
+                click_flag = 0;
+              }
+            }
+
+            else  // Mouse outside of arrow rects - re-enable click sound:
+              click_flag = 1;
+
+            break;
+          }
+
+          case SDL_MOUSEBUTTONDOWN:
+          {
+            loc = -1;  // By default, don't be in any entry
+            for (i = 0; i < items; i++)
+            {
+              if (inRect(menu->submenu[menu->first_entry + i]->button_rect, event.motion.x, event.motion.y))
+              {
+                // Play sound if loc is being changed:
+                if(snd_click)
+                  playsound(snd_click);
+                loc = i;
+                action = CLICK;
+                break;   /* from for loop */
+              }
+            }
+
+            /* "Left" button */
+            if (inRect(prev_rect, event.motion.x, event.motion.y)
+               && menu->first_entry > 0)
+            {
+              if(snd_click)
+                playsound(snd_click);
+              action = PAGEUP;
+            }
+
+            /* "Right" button - go to next page: */
+            else if (inRect(next_rect, event.motion.x, event.motion.y )
+               && menu->first_entry + items < menu->submenu_size)
+            {
+              if(snd_click)
+                playsound(snd_click);
+              action = PAGEDOWN;
+            }
+
+            /* "Stop" button - go to main menu: */
+            else if (inRect(stop_rect, event.button.x, event.button.y ))
+            {
+              if(snd_click)
+                playsound(snd_click);
+              action = STOP_ESC;
+            }
+
+            break;
+          } /* End of case SDL_MOUSEDOWN */
+
+          case SDL_KEYDOWN:
+          {
+            /* Proceed according to particular key pressed: */
+            switch (event.key.keysym.sym)
+            {
+              case SDLK_ESCAPE:
+              {
+                action = STOP_ESC;
+                break;
+              }
+
+              case SDLK_RETURN:
+              case SDLK_SPACE:
+              case SDLK_KP_ENTER:
+              {
+                if(snd_click)
+                  playsound(snd_click);
+                action = CLICK;
+                break;
+              }
+
+              /* Go to previous page, if present: */
+              case SDLK_LEFT:
+              case SDLK_PAGEUP:
+              {
+                if(snd_click)
+                  playsound(snd_click);
+                if (menu->first_entry > 0)
+                  action = PAGEUP;
+                break;
+              }
+
+              /* Go to next page, if present: */
+              case SDLK_RIGHT:
+              case SDLK_PAGEDOWN:
+              {
+                if(snd_click)
+                  playsound(snd_click);
+                if (menu->first_entry + items < menu->submenu_size)
+                  action = PAGEDOWN;
+                break;
+              }
+
+              /* Go up one entry, if present: */
+              case SDLK_UP:
+              {
+                if(snd_hover)
+                  playsound(snd_hover);
+                if (loc > 0)
+                  loc--;
+                else if (menu->submenu_size <= menu->entries_per_screen) 
+                  loc = menu->submenu_size - 1;  // wrap around if only 1 GetScreen()
+                else if (menu->first_entry > 0)
+                {
+                  loc = menu->entries_per_screen - 1;
+                  action = PAGEUP;
+                }
+                break;
+              }
+
+              case SDLK_DOWN:
+              {
+                if(snd_hover)
+                  playsound(snd_hover);
+                if (loc + 1 < min(menu->submenu_size, menu->entries_per_screen))
+                  loc++;
+                else if (menu->submenu_size <= menu->entries_per_screen) 
+                  loc = 0;  // wrap around if only 1 GetScreen()
+                else if (menu->first_entry + menu->entries_per_screen < menu->submenu_size)
+                {
+                  loc = 0;
+                  action = PAGEDOWN;
+                }
+                break;
+              }
+
+              /* Toggle GetScreen() mode: */
+              case SDLK_F10:
+              {
+                SwitchScreenMode();
+                action = RESIZED;
+                break;
+              }
+
+              /* Toggle menu music: */
+/*              case SDLK_F11:
+              {
+                if (Opts_GetGlobalOpt(MENU_MUSIC))
+                {
+                  audioMusicUnload( );
+                  Opts_SetGlobalOpt(MENU_MUSIC, 0);
+                }
+                else
+                {
+                  Opts_SetGlobalOpt(MENU_MUSIC, 1);
+                  audioMusicLoad("tuxi.ogg", -1);
+                }
+                break;
+              }*/
+
+              default:
+              {
+                /* Some other key - do nothing. */
+              }
+
+              break;  /* To get out of _outer_ switch/case statement */
+            }  /* End of key switch statement */
+          }  // End of case SDL_KEYDOWN in outer switch statement
+        }  // End event switch statement
+
+        if (old_loc != loc) {
+          DEBUGMSG(dbg_menu, "run_menu(): changed button focus, old=%d, new=%d\n", old_loc, loc);
+          if(old_loc >= 0 && old_loc < items)
+          {
+            tmp_rect = menu->submenu[old_loc + menu->first_entry]->button_rect;
+            SDL_BlitSurface(menu_item_unselected[old_loc], NULL, GetScreen(), &tmp_rect);
+            if(menu->submenu[menu->first_entry + old_loc]->icon)
+              SDL_BlitSurface(menu->submenu[menu->first_entry + old_loc]->icon->default_img, NULL, GetScreen(), &menu->submenu[menu->first_entry + old_loc]->icon_rect);
+            SDL_UpdateRect(GetScreen(), tmp_rect.x, tmp_rect.y, tmp_rect.w, tmp_rect.h);
+          }
+          if(loc >= 0 && loc < items)
+          {
+            tmp_rect = menu->submenu[loc + menu->first_entry]->button_rect;
+            SDL_BlitSurface(menu_item_selected[loc], NULL, GetScreen(), &tmp_rect);
+            if(menu->submenu[menu->first_entry + loc]->icon)
+            {
+              SDL_BlitSurface(menu->submenu[menu->first_entry + loc]->icon->default_img, NULL, GetScreen(), &menu->submenu[menu->first_entry + loc]->icon_rect);
+              menu->submenu[menu->first_entry + loc]->icon->cur = 0;
+            }
+            SDL_UpdateRect(GetScreen(), tmp_rect.x, tmp_rect.y, tmp_rect.w, tmp_rect.h);
+          }
+          old_loc = loc;
+        }
+
+        if(handle_event(&event))
+          stop = true;
+
+        switch(action)
+        {
+          case RESIZED:
+            RenderTitleScreen();
+            menu->first_entry = 0;
+            prerender_all();
+            stop = true;
+            break;
+
+          case CLICK:
+            if(loc < 0 || loc >= items)
+            {
+              DEBUGMSG(dbg_menu, "run_menu(): incorrect location for CLICK action (%d) !\n", loc);
+            }
+            else
+            {
+              tmp_node = menu->submenu[menu->first_entry + loc];
+              if(tmp_node->submenu_size == 0)
+              {
+                if(return_choice)
+                {
+                  FreeSurfaceArray(menu_item_unselected, items);
+                  FreeSurfaceArray(menu_item_selected, items);
+                  return tmp_node->activity;
+                }
+                else
+                {
+                  if(tmp_node->activity == RUN_MAIN_MENU)
+                  {
+                    /* go back to the root of this menu */
+                    menu = root;
+                  }
+                  else
+                  {
+                    if(handle_activity(tmp_node->activity, tmp_node->param) == QUIT)
+                    {
+                      DEBUGMSG(dbg_menu, "run_menu(): handle_activity() returned QUIT message, exiting.\n");
+                      FreeSurfaceArray(menu_item_unselected, items);
+                      FreeSurfaceArray(menu_item_selected, items);
+                      return QUIT;
+                    }
+                  }
+                }
+              }
+              else
+              {
+                menu->first_entry = 0;
+                menu = tmp_node;
+                menu->first_entry = 0;
+              }
+              stop = true;
+            }
+            break;
+
+          case STOP_ESC:
+            if(menu->parent == NULL)
+            {
+              FreeSurfaceArray(menu_item_unselected, items);
+              FreeSurfaceArray(menu_item_selected, items);
+              return STOP;
+            }
+            else
+              menu = menu->parent;
+            stop = true;
+            break;
+
+          case PAGEUP:
+            menu->first_entry -= menu->entries_per_screen;
+            stop = true;
+            break;
+
+          case PAGEDOWN:
+            menu->first_entry += menu->entries_per_screen;
+            stop = true;
+            break;
+        }
+
+      }  // End of SDL_PollEvent while loop
+
+      if(stop)
+        break;
+
+      if(!stop && frame_counter % 5 == 0 && loc >= 0 && loc < items)
+      {
+        tmp_sprite = menu->submenu[menu->first_entry + loc]->icon;
+        if(tmp_sprite)
+        {
+          SDL_BlitSurface(menu_item_selected[loc], NULL, GetScreen(), &menu->submenu[menu->first_entry + loc]->icon_rect);
+          SDL_BlitSurface(tmp_sprite->frame[tmp_sprite->cur], NULL, GetScreen(), &menu->submenu[menu->first_entry + loc]->icon_rect);
+          UpdateRect(GetScreen(), &menu->submenu[menu->first_entry + loc]->icon_rect);
+          NextFrame(tmp_sprite);
+        }
+      }
+
+      handle_animations();
+
+      /* Wait so we keep frame rate constant: */
+      frame_now = SDL_GetTicks();
+      if (frame_now < frame_start)
+        frame_start = frame_now;  // in case the timer wraps around
+      if((frame_now - frame_start) < 1000 / MAX_FPS)
+        SDL_Delay(1000 / MAX_FPS - (frame_now - frame_start));
+
+      frame_counter++;
+    } // End of while(!stop) loop
+
+    /* free button surfaces */
+    DEBUGMSG(dbg_menu, "run_menu(): freeing %d button surfaces\n", items);
+    FreeSurfaceArray(menu_item_unselected, items);
+    FreeSurfaceArray(menu_item_selected, items);
+  }
+
+  return QUIT;
+}
+
+/* return button surfaces that are currently displayed (without sprites) */
+SDL_Surface** render_buttons(MenuNode* menu, bool selected)
+{
+  SDL_Surface** menu_items = NULL;
+  SDL_Rect curr_rect;
+  SDL_Surface* tmp_surf = NULL;
+  int i;
+  int items = min(menu->entries_per_screen, menu->submenu_size - menu->first_entry);
+
+  menu_items = (SDL_Surface**) malloc(items * sizeof(SDL_Surface*));
+  if(NULL == menu_items)
+  {
+    DEBUGMSG(dbg_menu, "render_buttons(): failed to allocate memory for buttons!\n");
+    return NULL;  // error
+  }
+
+  for (i = 0; i < items; i++)
+  {
+    curr_rect = menu->submenu[menu->first_entry + i]->button_rect;
+    menu_items[i] = SDL_CreateRGBSurface(SDL_SWSURFACE|SDL_SRCALPHA,
+                                          curr_rect.w,
+                                          curr_rect.h,
+                                          32,
+                                          rmask, gmask, bmask, amask);
+
+    SDL_BlitSurface(GetScreen(), &curr_rect, menu_items[i], NULL);
+    /* button */
+    if(selected)
+      tmp_surf = CreateButton(curr_rect.w, curr_rect.h, button_radius * curr_rect.h, SEL_RGBA);
+    else
+      tmp_surf = CreateButton(curr_rect.w, curr_rect.h, button_radius * curr_rect.h, REG_RGBA);
+
+    SDL_BlitSurface(tmp_surf, NULL, menu_items[i], NULL);
+    SDL_FreeSurface(tmp_surf);
+
+    /* text */
+    tmp_surf = BlackOutline(_(menu->submenu[menu->first_entry + i]->title),
+                            curr_font_size, selected ? &yellow : &white);
+    SDL_BlitSurface(tmp_surf, NULL, menu_items[i], &menu->submenu[menu->first_entry + i]->text_rect);
+    SDL_FreeSurface(tmp_surf);
+  }
+
+  return menu_items;
+}
+
+/* recursively load sprites and calculate button rects
+   to fit into current screen */
+void prerender_menu(MenuNode* menu)
+{
+  SDL_Surface* temp_surf;
+  MenuNode* curr_node;
+  int i, imod, max_text_h = 0, max_text_w = 0;
+  int button_h, button_w;
+  bool found_icons = false;
+  char filename[buf_size];
+
+  if(NULL == menu)
+  {
+    DEBUGMSG(dbg_menu, "prerender_menu(): NULL pointer, exiting !\n");
+    return;
+  }
+
+  if(0 == menu->submenu_size)
+  {
+    DEBUGMSG(dbg_menu, "prerender_menu(): no submenu, exiting.\n");
+    return;
+  }
+
+  for(i = 0; i < menu->submenu_size; i++)
+  {
+    if(menu->submenu[i]->icon_name)
+      found_icons = true;
+    temp_surf = NULL;
+    temp_surf = SimpleText(_(menu->submenu[i]->title), curr_font_size, &black);
+    if(temp_surf)
+    {
+      max_text_h = max(max_text_h, temp_surf->h);
+      max_text_w = max(max_text_w, temp_surf->w);
+      SDL_FreeSurface(temp_surf);
+    }
+  }
+
+  button_h = (1.0 + 2.0 * text_h_gap) * max_text_h;
+  button_w = max_text_w + ( (found_icons ? 1.0 : 0.0) + 2.0 * text_w_gap) * button_h;
+
+  menu->entries_per_screen = (int) ( (menu_rect.h - button_gap * button_h) /
+                                   ( (1.0 + button_gap) * button_h ) );
+
+  for(i = 0; i < menu->submenu_size; i++)
+  {
+    curr_node = menu->submenu[i];
+    curr_node->button_rect.x = menu_rect.x;
+    imod = i % menu->entries_per_screen;
+    curr_node->button_rect.y = menu_rect.y + imod * button_h + (imod + 1) * button_gap * button_h;
+    curr_node->button_rect.w = button_w;
+    curr_node->button_rect.h = button_h;
+
+    curr_node->icon_rect = curr_node->button_rect;
+    curr_node->icon_rect.w = curr_node->icon_rect.h;
+
+    curr_node->text_rect.x = ( (found_icons ? 1.0 : 0.0) + text_w_gap) * curr_node->icon_rect.w;
+    curr_node->text_rect.y = text_h_gap * max_text_h;
+    curr_node->text_rect.h = max_text_h;
+    curr_node->text_rect.w = max_text_w;
+
+    if(curr_node->icon)
+      FreeSprite(curr_node->icon);
+
+    if(curr_node->icon_name)
+    {
+      sprintf(filename, "sprites/%s", curr_node->icon_name);
+      DEBUGMSG(dbg_menu, "prerender_menu(): loading sprite %s for item #%d.\n", filename, i);
+      curr_node->icon = LoadSpriteOfBoundingBox(filename, IMG_ALPHA, button_h, button_h);
+    }
+    else
+      DEBUGMSG(dbg_menu, "prerender_menu(): no sprite for item #%d.\n", i);
+
+    prerender_menu(menu->submenu[i]);
+  }
+}
+
+char* find_longest_text(MenuNode* menu, int* length)
+{
+  SDL_Surface* text = NULL;
+  char *ret = NULL, *temp = NULL;
+  int i;
+
+  if(menu->submenu_size == 0)
+  {
+    text = SimpleText(_(menu->title), curr_font_size, &black);
+    if(text->w > *length)
+    {
+      *length = text->w;
+      ret = menu->title;
+    }
+    SDL_FreeSurface(text);
+  }
+  else
+  {
+    for(i = 0; i < menu->submenu_size; i++)
+    {
+      temp = find_longest_text(menu->submenu[i], length);
+      if(temp)
+        ret = temp;
+    }
+  }
+  return ret;
+}
+
+/* find the longest text in all existing menus and binary search
+   for the best font size */
+void set_font_size()
+{
+  char* longest = NULL;
+  char* temp;
+  SDL_Surface* surf;
+  int length = 0, i, min_f, max_f, mid_f;
+
+  curr_font_size = default_font_size;
+
+  for(i = 0; i < N_OF_MENUS; i++)
+  {
+    if(menus[i])
+    {
+      temp = find_longest_text(menus[i], &length);
+      if(temp)
+        longest = temp;
+    }
+  }
+
+  if(!longest)
+    return;
+
+  min_f = min_font_size;
+  max_f = max_font_size;
+
+  while(min_f < max_f)
+  {
+    mid_f = (min_f + max_f) / 2;
+    surf = SimpleText(_(longest), mid_f, &black);
+    if(surf->w + (1.0 + 2.0 * text_w_gap) * (1.0 + 2.0 * text_h_gap) * surf->h < menu_rect.w)
+      min_f = mid_f + 1;
+    else
+      max_f = mid_f;
+    SDL_FreeSurface(surf);
+  }
+
+  curr_font_size = min_f;
+}
+
+/* prerender arrows, stop button and all non-NULL menus from menus[] array
+   this function should be invoked after every resolution change */
+void PrerenderAll()
+{
+  int i;
+
+  SetRect(&menu_rect, menu_pos);
+
+  SetRect(&stop_rect, stop_pos);
+  if(stop_button)
+    SDL_FreeSurface(stop_button);
+  stop_button = LoadImageOfBoundingBox(stop_path, IMG_ALPHA, stop_rect.w, stop_rect.h);
+  /* move button to the right */
+  stop_rect.x = GetScreen()->w - stop_button->w;
+
+  SetRect(&prev_rect, prev_pos);
+  if(prev_arrow)
+    SDL_FreeSurface(prev_arrow);
+  prev_arrow = LoadImageOfBoundingBox(prev_path, IMG_ALPHA, prev_rect.w, prev_rect.h);
+  if(prev_gray)
+    SDL_FreeSurface(prev_gray);
+  prev_gray = LoadImageOfBoundingBox(prev_gray_path, IMG_ALPHA, prev_rect.w, prev_rect.h);
+  /* move button to the right */
+  prev_rect.x += prev_rect.w - prev_arrow->w;
+
+  SetRect(&next_rect, next_pos);
+  if(next_arrow)
+    SDL_FreeSurface(next_arrow);
+  next_arrow = LoadImageOfBoundingBox(next_path, IMG_ALPHA, next_rect.w, next_rect.h);
+  if(next_gray)
+    SDL_FreeSurface(next_gray);
+  next_gray = LoadImageOfBoundingBox(next_gray_path, IMG_ALPHA, next_rect.w, next_rect.h);
+
+  set_font_size();
+
+  for(i = 0; i < N_OF_MENUS; i++)
+    if(menus[i])
+      prerender_menu(menus[i]);
+}
+
+void LoadMenu(int index, const char* file_name)
+{
+  FILE* menu_file = NULL;
+  int i;
+
+  if(menus[index])
+  {
+    free_menu(menus[index]);
+    menus[index] = NULL;
+  }
+
+  menu_file = fopen(file_name, "r");
+  if(menu_file == NULL)
+  {
+    DEBUGMSG(dbg_menu, "LoadMenu(): Could not load %s !\n", file_name);
+  }
+  else
+  {
+    menus[index] = load_menu_from_file(menu_file, NULL);
+    fclose(menu_file);
+  }
+}
+
+
+
+/* free all loaded menu trees */
+void UnloadMenus(void)
+{
+  int i;
+
+  DEBUGMSG(dbg_menu, "entering UnloadMenus()\n");
+
+  if(stop_button)
+  {
+    SDL_FreeSurface(stop_button);
+    stop_button = NULL;
+  }
+
+  if(prev_arrow)
+  {
+    SDL_FreeSurface(prev_arrow);
+    prev_arrow = NULL;
+  }
+
+  if(next_arrow)
+  {
+    SDL_FreeSurface(next_arrow);
+    next_arrow = NULL;
+  }
+
+  for(i = 0; i < N_OF_MENUS; i++)
+    if(menus[i] != NULL)
+    {
+      DEBUGMSG(dbg_menu, "UnloadMenus(): freeing menu #%d\n", i);
+      free_menu(menus[i]);
+    }
+
+  DEBUGMSG(dbg_menu, "leaving UnloadMenus()\n");
+}
+

Modified: branches/commonification/tux4kids-common/trunk/src/tux4kids-common.h
===================================================================
--- branches/commonification/tux4kids-common/trunk/src/tux4kids-common.h	2009-07-31 13:57:14 UTC (rev 1305)
+++ branches/commonification/tux4kids-common/trunk/src/tux4kids-common.h	2009-07-31 14:17:50 UTC (rev 1306)
@@ -13,6 +13,8 @@
 #define TUX4KIDS_COMMON_H
 
 #include "SDL.h"
+#include "SDL_image.h"
+#include "SDL_mixer.h"
 
 #if SDL_BYTEORDER == SDL_BIG_ENDIAN
 #define rmask 0xff000000
@@ -26,10 +28,27 @@
 #define amask 0xff000000
 #endif
 
+#define MAX_SPRITE_FRAMES 10
 
+typedef struct {
+  SDL_Surface *frame[MAX_SPRITE_FRAMES];
+  SDL_Surface *default_img;
+  int num_frames;
+  int cur;
+} sprite;
+
 /* functions from t4k-main.c */
 void            SetDebugMode(int dbg_flags);
 
+/* functions from tk4-menu.c */
+void            LoadMenus(void);
+int             RunLoginMenu(void);
+void            RunMainMenu(void);
+void            UnloadMenus(void);
+
+extern SDL_Rect menu_rect, stop_rect, prev_rect, next_rect;
+extern SDL_Surface *stop_button, *prev_arrow, *next_arrow, *prev_gray, *next_gray;
+
 /* functions from tk4-sdl.c */
 SDL_Surface*    GetScreen();
 void            DrawButton(SDL_Rect* target_rect, int radius, Uint8 r, Uint8 g, Uint8 b, Uint8 a);
@@ -37,4 +56,31 @@
 SDL_Surface*    CreateButton(int w, int h, int radius, Uint8 r, Uint8 g, Uint8 b, Uint8 a);
 void            RoundCorners(SDL_Surface* s, Uint16 radius);
 
+/* functions from tk4-loaders.c */
+#define IMG_REGULAR         0x01
+#define IMG_COLORKEY        0x02
+#define IMG_ALPHA           0x04
+#define IMG_MODES           0x07
+
+#define IMG_NOT_REQUIRED    0x10
+#define IMG_NO_PNG_FALLBACK 0x20
+
+
+SDL_Surface* LoadImage(const char* file_name, int mode);
+SDL_Surface* LoadScaledImage(const char* file_name, int mode, int width, int height);
+SDL_Surface* LoadImageOfBoundingBox(const char* file_name, int mode, int max_width, int max_height);
+
+SDL_Surface* LoadBkgd(const char* file_name, int width, int height);
+
+sprite*      LoadSprite(const char* name, int mode);
+sprite*      LoadScaledSprite(const char* name, int mode, int width, int height);
+sprite*      LoadSpriteOfBoundingBox(const char* name, int mode, int max_width, int max_height);
+sprite*      FlipSprite(sprite* in, int X, int Y);
+void         FreeSprite(sprite* gfx);
+void         NextFrame(sprite* s);
+
+Mix_Chunk*   LoadSound(char* datafile);
+Mix_Music*   LoadMusic(char *datafile);
+
+
 #endif




More information about the Tux4kids-commits mailing list