
/*
 * xanim.c
 *
 * Copyright (C) 1990,1991,1992 by Mark Podlipec. 
 * All rights reserved.
 *
 * This software may be freely copied, modified and redistributed
 * without fee provided that this copyright notice is preserved 
 * intact on all copies and modified copies.
 * 
 * There is no warranty or other guarantee of fitness of this software.
 * It is provided solely "as is". The author(s) disclaim(s) all
 * responsibility and liability with respect to this software's usage
 * or its effect upon hardware or computer systems.
 *
 */

#define DA_REV 2.29

/*
 *
 */

#include <X11/Xlib.h>
#include <X11/Intrinsic.h>
#include <X11/StringDefs.h>
#include <X11/Shell.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/signal.h>
#include <sys/param.h>
#include <sys/times.h>
#include <memory.h>
#include <ctype.h>
#include "mytypes.h"
#include "xanim.h"
#include "xanim_x11.h"

void TheEnd();
void TheEnd1();
void Usage();
ULONG GetWord();
ULONG GetHalfWord();
void ShowAnimation();
void ShowAction();
int Determine_Anim_Type();
void Cycle_It();

/*
 * Global X11 display configuation variables
 *
 * These are set by X11_Pre_Setup();
 *
 */

int x11_depth;
int x11_class;
int x11_bytes_pixel;
int x11_cmap_flag;
int x11_cmap_size;
int x11_display_type;

/*
 * Each animation is broken up into a series of individual actions.
 * For example, a gif image would become two actions, 1 to setup the
 * colormap and 1 to display the image.
 *
 * ACTION is defined in xanim.h. 
 *
 * action_cnt is a global variable that points to the next unused action.
 * Currently, individual routines access this. This will change.
 *
 * action_start is a variable passed to the Read_Animation routines, It
 * keeps track of the 1st action available to routine.
 *
 */
ACTION action[2000];
int action_cnt;
int action_start;

/*
 * This structure is set up for each animation file. It keeps track of
 * special flags and which animation type.
 *
 * anim_cnt indexes the next available anim structure.
 *
 * anim_type is one of IFF_,FLI_,GIF_,TXT_,FADE_ANIM. 
 *
 * merged_anim_flags is the or of all anims read in. FLI's anims need
 * only 1 buffer. IFF anims need two. This allows software to allocate
 * for the worst case.
 *
 */
ANIM_HDR anim[50];
int anim_cnt;
int anim_type;
int merged_anim_flags;


/*
 * cmap keeps track of the current colors to the screen.
 *
 * hmap is IFF HAM specific and is needed to decode HAM images into 332
 * and into 24bits when supported.
 *
 */
ColorReg *cmap = 0;
ColorReg ham_map[HMAP_SIZE];

/*
 * Global variable to keep track of Anim type
 */
int filetype;

/*
 * Global variables to keep track of current width, height, num of colors and
 * number of bit planes respectively. 
 *
 * the max_ variable are used for worst case allocation. Are useful for Anims
 * that have multiple image sizes.
 *
 * image_size and max_image_size are imagex * imagey, etc.
 */
int imagex, imagey, max_imagex, max_imagey;
int disp_y, max_disp_y;
int imagec, imaged;
int image_size;
int max_image_size;

/* 
 * These variable keep track of where we are in the animation.
 * cur_file  keeps track of which file we're displaying. (ie anim[ cur_file] )
 * cur_floop keeps track of how many times we've looped a file.
 * cur_frame keeps track of which frame(action) we're on.
 *
 * cycle_wait and cycle_cnt are used for color cycling.
 *
 * start_file indicates whether this is the 1st time we've looped a file
 * or not. Is used to initialize variables and resize window if necessary.
 * 
 */
int cur_file,cur_floop,cur_frame;
int cycle_wait,cycle_cnt;
int start_file;
int anim_forward_flag;
/*
 * Image buffers.
 * im_buff1 is used for double buffered anims(IFF).
 * hbuff is need as temporary space for storing the 332 images with HAM(IFF).
 *
 * pic is a pointer to im_buff0 or im_buff1 during double buffering.
 *
 */
char *im_buff0,*im_buff1,*pic,*hbuff;

/* 
 * Global flags that are set on command line.
 */
int buff_flag;
int loopeach_flag;
int anim_flags;
int cycle_on_flag;
int debug_flag;
int verbose;
int jiffy_flag;
int fade_flag,fade_time;
int noresize_flag;
int anim_running;
int update_flag;
int optimize_flag;

/*
 * old_dlta is used for optimizing screen updates for IFF and FLI animations.
 *
 * act is a global pointer to the current action.
 *
 */
ACT_IFF_DLTA_HDR old_dlta[2];
ACTION *act;

/*
 * This function (hopefully) provides a clean exit from our code.
 */
void TheEnd()
{
 if (action_cnt > 0 )
 {
  int i;
  for(i=0;i<action_cnt;i++)
   if (action[i].data != 0) free(action[i].data);
 }
 if (im_buff0) free(im_buff0);
 if (im_buff1) free(im_buff1);
 if (hbuff) free(hbuff);
 if (cmap) free(cmap);
 /*if (theDisp) XtCloseDisplay(theDisp); */
 exit(0);
}

/*
 * just prints a message before calling TheEnd()
 */
void TheEnd1(err_mess)
char *err_mess;
{
 fprintf(stderr,"%s\n",err_mess);
 TheEnd();
}

/*
 * This function attempts to expain XAnim's usage if the command line
 * wasn't correct.
 */
void Usage()
{
 fprintf(stderr,"Usage:\n\n");
 fprintf(stderr,"xanim [ [+|-]opts ...] animfile [ [ [-opts] animfile] ... ]\n");
 fprintf(stderr,"\n");
 fprintf(stderr,"A + turns the following options on and a - turns them off.\n");
 fprintf(stderr,"Option f,j and l ignore leading + and -'s.\n");
 fprintf(stderr,"\n");
 fprintf(stderr,"Options:\n");
 fprintf(stderr,"\n");
 fprintf(stderr,"       b    buffers images ahead of time. default is off.\n"); 
 fprintf(stderr,"       c    disable looping for nonlooping iff anims.\n"); 
 fprintf(stderr,"               default is off.\n"); 
 fprintf(stderr,"       f#   fade to black in 16 frames. frame delay=#ms\n");
 fprintf(stderr,"               default is off.\n"); 
 fprintf(stderr,"       i    Interlace flag. Reduce image height by two\n"); 
 fprintf(stderr,"               default is off.\n"); 
 fprintf(stderr,"       j#   # is number of milliseconds between frames\n");
 fprintf(stderr,"               (default is 17 unless specified inside the\n");
 fprintf(stderr,"                the animation itself).\n");
 fprintf(stderr,"       l#   # is number of loops(default = 1) through each\n"); fprintf(stderr,"               file before moving next file.\n");
 fprintf(stderr,"       r    turn color cycling off. default is on\n"); 
 fprintf(stderr,"       o    turns on certain optimizations. Turning this\n"); 
 fprintf(stderr,"               off would only be useful if you are\n"); 
 fprintf(stderr,"               buffering the anim and wish to run it\n");
 fprintf(stderr,"               backwards. default is on.\n"); 
 fprintf(stderr,"       s    prevents window from resizing to match image\n"); 
 fprintf(stderr,"               size. Window is the size of largest image.\n"); 
 fprintf(stderr,"               default is on.\n"); 
 fprintf(stderr,"       u    when single stepping, anim is played until\n");
 fprintf(stderr,"               image is updated/changed.(a new cmap is\n");
 fprintf(stderr,"               not considered an update) default is on.\n");
 fprintf(stderr,"       v    verbose mode. default is off.\n"); 
 fprintf(stderr,"\n");
 fprintf(stderr,"Window commands when anim is NOT running\n");
 fprintf(stderr,"\n");
 fprintf(stderr,"        q    quit\n");
 fprintf(stderr,"        g    For cycling images, move to next file\n");
 fprintf(stderr,"        s    Stop cycling\n");
 fprintf(stderr,"        r    Restore Colors after stopping cycling\n");
 fprintf(stderr,"      space  toggle. starts/stops animation\n");
 fprintf(stderr,"        ,    go back one frame within file\n");
 fprintf(stderr,"        .    go forward one frame within file\n");
 fprintf(stderr,"        <    go back to start of previous file\n");
 fprintf(stderr,"        >    go forward to start of next file\n");
 fprintf(stderr,"\n");

 exit(0);
}

main(argc, argv)
int argc;
char *argv[];
{
 char *filename,*in;
 int i,j;

/* 
 * Initialize global variables.
 */
 theDisp = NULL;

 im_buff0 = 0;
 im_buff1 = 0;
 hbuff = 0;
 max_imagex = 0;
 max_imagey = 0;
 max_disp_y = 0;

 anim_cnt = 0;    
 action_cnt = 0;

 anim_forward_flag = TRUE;
 anim_running  = TRUE;
 buff_flag     = FALSE;
 cycle_on_flag = TRUE;
 noresize_flag = FALSE;
 verbose       = FALSE;
 debug_flag    = FALSE;
 update_flag   = TRUE;
 optimize_flag = TRUE;

 anim_flags        = ANIM_CYCON;
 merged_anim_flags = 0;
 loopeach_flag     = 1;
 jiffy_flag        = 0;
 

/* What rev are we running
 */
 fprintf(stderr,"XAnim Rev %2.2f Beta by Mark Podlipec (c) 1990,1991,1992\n",DA_REV);

/* quick command line check.
 */
 if (argc<2) Usage();

/* setup for dying time.
 */
 signal(SIGINT,TheEnd);

 /* PreSet of X11 Display to find out what we're dealing with
  */
 X11_Pre_Setup();

 cmap = (ColorReg *) malloc( x11_cmap_size * sizeof(ColorReg) );

/* Parse command line.
 */
 for(i=1;i<argc;i++)
 {
   in = argv[i];
   if ( (in[0]=='-') || (in[0]=='+') )
   {
     int len,opt_on;

     if (in[0]=='-') opt_on = FALSE;
     else opt_on = TRUE;

     len = strlen(argv[i]);
     j = 1;
     while( (j<len) && (in[j]!=0) )
     {
       switch(in[j])
       {
        case 'b':
		buff_flag = opt_on;
		j++;
  		break;
        case 'c':
                if (opt_on==TRUE)   anim_flags |= ANIM_NOLOP;
                else            anim_flags &= (~ANIM_NOLOP);
		j++;
  		break;
        case 'd':
		debug_flag = opt_on;
		if (opt_on==TRUE)   anim_flags |= ANIM_DEBUG;
                else            anim_flags &= (~ANIM_DEBUG);
                j++;
  		break;
        case 'f':
		fade_time = atoi(&in[2]);
		if (fade_time <= 0) fade_flag = 0;
		else fade_flag = 1;
		j+=len;
		break;
        case 'l':
		loopeach_flag = atoi(&in[2]);
		if (loopeach_flag<=0) loopeach_flag = 1;
		j+=len;
  		break;
        case 'i':
		if (opt_on==TRUE) anim_flags |= ANIM_LACE;
                else anim_flags &= (~ANIM_LACE);
                j++;
  		break;
        case 'j':
		jiffy_flag = atoi(&in[2]);
		if (jiffy_flag<=0) jiffy_flag = 1;
		j+=len;
  		break;
        case 'o':
		optimize_flag = opt_on;
                j++;
  		break;
        case 'r':
		cycle_on_flag = opt_on;
                if (opt_on==TRUE)   anim_flags |= ANIM_CYCON;
                else            anim_flags &= (~ANIM_CYCON);
		j++;
  		break;
        case 's':
		noresize_flag = opt_on;
                j++;
  		break;
        case 'u':
		update_flag = opt_on;
                j++;
  		break;
        case 'v':
		verbose = opt_on;
		j++;
  		break;
        case '-':
		opt_on = FALSE;
		j++;
  		break;
        case '+':
		opt_on = TRUE;
		j++;
  		break;
        default:
		Usage();
       } /* end of option switch */
     } /* end of loop through options */
   } /* end of if - */
   else 
   /* If no hyphen in front of argument, assume it's a file.
    */
   {
     action_start = action_cnt;
     filename = argv[i];

	/* default is FLI  */
     anim_type = Determine_Anim_Type(filename);
     anim[anim_cnt].anim_type = anim_type;
     switch(anim_type)
     {
        int action_number,ii;
        case IFF_ANIM:
		if (verbose) fprintf(stderr,"Reading IFF File %s\n",filename);
		merged_anim_flags |= ANIM_IFF;
	 	IFF_Read_File(filename);
	 	if (buff_flag) IFF_Buffer_Action(action_start);
		break;
        case GIF_ANIM:
		if (verbose) fprintf(stderr,"Reading GIF File %s\n",filename);
		GIF_Read_File(filename,1);

		action_number = action_cnt - action_start;
		anim[anim_cnt].frame_lst = 
                      (int *)malloc(sizeof(int) * (action_number+1));
		if (anim[anim_cnt].frame_lst == NULL)
                      TheEnd1("GIF_ANIM: couldn't malloc for frame_lst\0");
		for(ii=0; ii < action_number; ii++)
                            anim[anim_cnt].frame_lst[ii]=action_start+ii;
                anim[anim_cnt].frame_lst[action_number] = -1;
                anim[anim_cnt].loop_frame = 0;
                anim[anim_cnt].last_frame = action_number-1;
		break;
        case TXT_ANIM:
		if (verbose) fprintf(stderr,"Reading TXT File %s\n",filename);
		TXT_Read_File(filename);
		break;
        case FLI_ANIM:
		if (verbose) fprintf(stderr,"Reading FLI File %s\n",filename);
		Read_Fli_File(filename);
		if (buff_flag) Fli_Buffer_Action(action_start);

		action_number = action_cnt - action_start;
		anim[anim_cnt].frame_lst = 
                      (int *)malloc(sizeof(int) * (action_number+1));
		if (anim[anim_cnt].frame_lst == NULL)
                      TheEnd1("GIF_ANIM: couldn't malloc for frame_lst\0");
		for(ii=0; ii < action_number; ii++)
                            anim[anim_cnt].frame_lst[ii]=action_start+ii;
                anim[anim_cnt].frame_lst[action_number] = -1;
                anim[anim_cnt].loop_frame = 0;
                anim[anim_cnt].last_frame = action_number-1;
		break;
        default:
		fprintf(stderr,"Unknown or unsupported animation type\n");
		break;
      } 
      /*
       * Setup up anim header.
       */
      anim[anim_cnt].imagex = imagex;
      anim[anim_cnt].imagey = imagey;
      anim[anim_cnt].imagec = imagec;
      anim[anim_cnt].imaged = imaged;
      anim[anim_cnt].anim_flags = anim_flags;
      anim[anim_cnt].loop_num = loopeach_flag;
      merged_anim_flags |= anim_flags;

      anim_cnt++;

      if (imagex > max_imagex) max_imagex = imagex;
      if (imagey > max_imagey) max_imagey = imagey;
      if (anim_flags & ANIM_LACE) 
      {
        if ((imagey/2) > max_disp_y) max_disp_y = imagey/2;
      }
      else
      {
        if ( imagey     > max_disp_y) max_disp_y = imagey;
      }

      /*
       * If fade flag is on, basically insert a fade anim.
       */
      if (fade_flag && (x11_cmap_flag == TRUE) )
      {
        int ii;
        anim[anim_cnt].anim_type = FADE_ANIM;
   
        /* Fade to Black action */
        action_start = action_cnt;
        action[action_cnt].type = ACT_FADE;
        action[action_cnt].time = fade_time;
        action[action_cnt].data = 0;
        action_cnt++;
        /* Blank screen so next CMAP doesn't show */
        action[action_cnt].type = ACT_FLI_BLACK;
        action[action_cnt].time = fade_time;
        action[action_cnt].data = 0;
        action_cnt++;


        /* 
         * Setup frame list to call ACT_FADE sixteen times and FLI_BLACK once.
         */
        anim[anim_cnt].frame_lst = (int *)malloc(sizeof(int) * (18));
        if (anim[anim_cnt].frame_lst == NULL)
                  TheEnd1("FADE_ANIM: couldn't malloc for frame_lst\0");
        for(ii=0;ii<16;ii++) anim[anim_cnt].frame_lst[ii] = action_start;
        anim[anim_cnt].frame_lst[16] = action_start+1;
        /* Last frame is always -1
         */
        anim[anim_cnt].frame_lst[17] = -1;

        anim[anim_cnt].imagex = imagex;
        anim[anim_cnt].imagey = imagey;
        anim[anim_cnt].imagec = imagec;
        anim[anim_cnt].imaged = imaged;
        anim[anim_cnt].anim_flags = anim_flags;
        anim[anim_cnt].loop_num = 1;
        anim[anim_cnt].loop_frame = 0;
        anim[anim_cnt].last_frame = 16;
        anim_cnt++;
      } /* end of fade anim insertion */

    } /* end of read in file */
 } /* end of loopin through arguments */

 /* No anims listed
  */
 if (anim_cnt == 0) Usage();

 /* Set up X11 Display
  */

 if (noresize_flag==TRUE)
  X11_Setup(max_imagex,max_disp_y,max_imagex,max_disp_y);
 else
  X11_Setup(max_imagex,max_disp_y,anim[0].imagex,
     (anim[0].anim_flags & ANIM_LACE)?(anim[0].imagey/2):(anim[0].imagey) );

 /* Start off Animation.
  */
 ShowAnimation();
 /* Wait for user input.
  */
 xanim_events();

 /* Self-Explanatory
  */
 TheEnd();
}

/* Routine to read a long word.
 * yeah, I know macro's are faster.
 */
ULONG GetWord(fp)
FILE *fp;
{
 ULONG ret;

 ret =  fgetc(fp);
 ret |= fgetc(fp) << 8;
 ret |= fgetc(fp) << 16;
 ret |= fgetc(fp) << 24;
 return ret;
}

/* Routine to read a half word.
 * yeah, I know macro's are faster.
 */
ULONG GetHalfWord(fp)
FILE *fp;
{
 ULONG ret;

 ret =  fgetc(fp);
 ret |= fgetc(fp) << 8;
 return ret;
}


/*
 * ShowAnimation allocates and sets up required image buffers and
 * initializes animation variables.
 * It then kicks off the animation.
 */
void ShowAnimation()
{

 max_image_size = max_imagex * max_imagey;
 im_buff0 = (char *) malloc( max_image_size * x11_bytes_pixel);
 if (im_buff0 == 0) TheEnd1("ShowAnimation: im_buff0 malloc failed");
 if (merged_anim_flags & ANIM_IFF)
 {
  im_buff1 = (char *) malloc( max_image_size * x11_bytes_pixel);
  if (im_buff1 == 0) TheEnd1("ShowAnimation: im_buff1 malloc failed");

  if ( (merged_anim_flags & ANIM_HAM) || (merged_anim_flags & ANIM_LACE) )
  {
   hbuff = (char *) malloc( max_image_size * x11_bytes_pixel);
   if (hbuff == 0) TheEnd1("ShowAnimation: hbuff malloc failed");
  }
  old_dlta[0].minx = max_imagex; old_dlta[0].miny = max_imagey;
  old_dlta[0].maxx = 0;          old_dlta[0].maxy = 0;
  old_dlta[1].minx = max_imagex; old_dlta[1].miny = max_imagey;
  old_dlta[1].maxx = 0;          old_dlta[1].maxy = 0;
 }
 pic = im_buff0;

 cur_file = 0;
 cur_floop = 0;
 cur_frame = 0;
 start_file = 0;
 cycle_wait = 0;
 cycle_cnt = 0;

 XtAppAddTimeOut(theContext, 1, ShowAction, NULL);
}

/*
 * This is the heart of this program. It does each action and then calls
 * itself with a timeout via the XtAppAddTimeOut call.
 */
void ShowAction(nothing,id)
char *nothing;
XtIntervalId *id;
{
 int screen_updated;
 int t_frame_start,t_frame_end,t_frame_int;
 struct tms tms3;

 screen_updated = FALSE;
/* If color cycling, wait for user go ahead.
 */
 if (cycle_wait && cycle_cnt)
 {
  XtAppAddTimeOut(theContext, 10, ShowAction, NULL);
  return;
 }

 /* sanity check.
  */
 if (cur_file >= anim_cnt) return;

 /* 1st throught this particular file.
  * Resize if necessary and init required variables.
  */
 if (start_file == 0)
 {
  start_file=1;
  cycle_wait=0;
  anim_flags = anim[cur_file].anim_flags;

  if (anim_flags & ANIM_DEBUG) debug_flag = 1;
  else debug_flag = 0;
  if (anim_flags & ANIM_CYCON) cycle_on_flag = 1;
  else cycle_on_flag = 0;

  /* if interlaced flag then compare half-height images to current settings.
   */
  if (noresize_flag == FALSE)
  {
   if (  anim[cur_file].anim_flags & ANIM_LACE )
   {
    if (   (imagex != anim[cur_file].imagex)
        || (disp_y != (anim[cur_file].imagey/2)) ) 
     XResizeWindow(theDisp,mainW,anim[cur_file].imagex,
		                 anim[cur_file].imagey/2);
   }
   else
   {
    if (   (imagex != anim[cur_file].imagex)
       || (disp_y != anim[cur_file].imagey) ) 
     XResizeWindow(theDisp,mainW,anim[cur_file].imagex,
                                 anim[cur_file].imagey);
   }
  }
  else
  {
   if (imagex > anim[cur_file].imagex)
     XClearArea(theDisp,mainW,anim[cur_file].imagex,0, 0,0,FALSE);
   if (  anim[cur_file].anim_flags & ANIM_LACE )
   {
    if (disp_y > (anim[cur_file].imagey/2) )
      XClearArea(theDisp,mainW,0,(anim[cur_file].imagey/2), 0,0,FALSE);
   }
   else
   {
    if (disp_y > anim[cur_file].imagey )
      XClearArea(theDisp,mainW,0,anim[cur_file].imagey, 0,0,FALSE);
   }
  }
  /* Initialize variables
   */
  imagex = anim[cur_file].imagex;
  imagey = anim[cur_file].imagey;
  imaged = anim[cur_file].imaged;
  imagec = anim[cur_file].imagec;
  image_size = imagex * imagey;

  /*
   * What's this!? Direct access to X11 structures. tsch tsch.
   *
   * With half-height images, I fool X11 by telling it the image is
   * twice as wide and half as high, then only display left half of image.
   *
   * NOTE: when adding TrueColor bytes_per_line needs modification.
   */
  if (anim_flags & ANIM_LACE) 
  {
   theImage->width = 2*imagex;
   theImage->height = imagey/2;
   theImage->bytes_per_line = 2*imagex * x11_bytes_pixel;
   disp_y = imagey/2;
  }
  else
  {
   theImage->width = imagex;
   theImage->height = imagey;
   theImage->bytes_per_line = imagex * x11_bytes_pixel;
   disp_y = imagey;
  }
 }
 
 /* OK. A quick sanity check and then we act.
  */
 if (   (anim[cur_file].frame_lst[cur_frame] >= 0)
     && (anim[cur_file].frame_lst[cur_frame] <  action_cnt)  )
 {
    /* initialize act and get the current time.
     */
    act = &action[ anim[cur_file].frame_lst[cur_frame] ];
    t_frame_start = times(&tms3);
    t_frame_int = act->time;


    /* lesdoit */
    switch(act->type)
    {
       /* 
	* NOP and DELAY don't change anything but can have timing info
	* that might prove useful. ie dramatic pauses :^)
	*/
     case ACT_NOP:	
			break;
     case ACT_DELAY:	
			break;
       /* 
	* Change Color Map.
	*/
     case ACT_CMAP:	
			if (x11_cmap_flag == TRUE)
			{
			 int off,j;
			 ColorReg *cptr;
			 CMAP_HDR *cmap_hdr;

			 cmap_hdr = (CMAP_HDR *)act->data;
			 imagec = cmap_hdr->cmap_size;
			 cptr = (ColorReg *)cmap_hdr->data;

			 off = x11_cmap_size - imagec;
 			 for(j=0;j<imagec;j++)
			 {
			  defs[j].pixel = off+j;
			  defs[j].red   = cptr[j].red   << 8;
			  defs[j].green = cptr[j].green << 8;
			  defs[j].blue  = cptr[j].blue  << 8;
			  defs[j].flags = DoRed | DoGreen | DoBlue;
			  cmap[off+j].red   = cptr[j].red;
			  cmap[off+j].green = cptr[j].green;
			  cmap[off+j].blue  = cptr[j].blue;
			 }
			 XStoreColors(theDisp,theCmap,defs,imagec);
 			 XFlush(theDisp);
			}
			break;
       /* 
	* Display an Image. Assumed to by imagex by imagey in size. 
	*/
     case ACT_IMAGE:	

			theImage->data = act->data;

			XPutImage(theDisp,mainW,theGC,theImage,0,0,0,0,
							       imagex,disp_y);
			XFlush(theDisp);
			cycle_wait = 1;
			screen_updated = TRUE;
			break;
       /* 
	* Resize Window
	*/
     case ACT_SIZE:
                        {
                         SIZE_HDR *size_hdr;

                         size_hdr = (SIZE_HDR *)act->data;
                         imagex = size_hdr->imagex;
                         imagey = size_hdr->imagey;
			 if (anim_flags & ANIM_LACE)
                            disp_y = size_hdr->imagey/2;
			 else
                            disp_y = size_hdr->imagey;
			 theImage->height = disp_y;
			 theImage->width = imagex;
			 theImage->bytes_per_line = imagex * x11_bytes_pixel;
                        }
                        break;
       /* 
	* Lower Color Intensity by 1/16.
	*/
     case ACT_FADE:	
			{
			 int off,j;

			 off = x11_cmap_size - imagec;
 			 for(j=0;j<imagec;j++)
			 {
			  if (cmap[off+j].red   <= 16) cmap[off+j].red   = 0;
			  else cmap[off+j].red   -= 16;
			  if (cmap[off+j].green <= 16) cmap[off+j].green = 0;
			  else cmap[off+j].green -= 16;
			  if (cmap[off+j].blue  <= 16) cmap[off+j].blue  = 0;
			  else cmap[off+j].blue  -= 16;

			  defs[j].pixel = off+j;
			  defs[j].red   = cmap[off+j].red   << 8;
			  defs[j].green = cmap[off+j].green << 8;
			  defs[j].blue  = cmap[off+j].blue  << 8;
			  defs[j].flags = DoRed | DoGreen | DoBlue;
			 }
			 XStoreColors(theDisp,theCmap,defs,imagec);
 			 XFlush(theDisp);
			}
			screen_updated = TRUE;
			break;
       /* 
	* A REGION display only part of an IMAGE.
	*/
     case ACT_REGION:	
			{
			 ACT_REGION_HDR *act_reg_hdr;

			 act_reg_hdr = (ACT_REGION_HDR *)(act->data);

			 theImage->data = (char *)(act_reg_hdr->data);

			 if (anim_flags & ANIM_LACE) 
			 {
   			  XPutImage(theDisp,mainW,theGC,theImage,
			         act_reg_hdr->xpos,  act_reg_hdr->ypos/2,
			         act_reg_hdr->xpos,  act_reg_hdr->ypos/2,
			         act_reg_hdr->xsize, act_reg_hdr->ysize/2  );
                         }
   			 else 
			 {
			  XPutImage(theDisp,mainW,theGC,theImage,
			         act_reg_hdr->xpos,  act_reg_hdr->ypos,
			         act_reg_hdr->xpos,  act_reg_hdr->ypos,
			         act_reg_hdr->xsize, act_reg_hdr->ysize  );
			 }
			 XFlush(theDisp);
			 cycle_wait = 1;
			}
			screen_updated = TRUE;
			break;
       /* 
	* Act upon IFF Color Cycling chunk.
	*   currently we don't color cycling during animations. will be fixed.
	*/
     case ACT_IFF_CRNG:
                        {
                         CRNG_HDR *crng;

                         crng = (CRNG_HDR *)act->data;
                         if (   (crng->active & CRNG_ACTIVE)
                             && (crng->low < crng->high)
                             && (crng->rate != 0)
                             && (anim_flags & ANIM_CYCLE) 
                             && (cycle_on_flag)                 
                            )
                          {
			   cycle_cnt++;
                           XtAppAddTimeOut(theContext, 
			      (int)(CRNG_INTERVAL/(crng->rate)),Cycle_It, crng);
                          }

                        }
                        break;
       /* 
	*  FLI BRUN chunk.
	*/
     case ACT_FLI_BRUN:	
			Decode_Fli_BRUN(act->data,pic);
			theImage->data = pic;
   			XPutImage(theDisp,mainW,theGC,theImage,0,0,0,0,
							imagex,disp_y);
			XFlush(theDisp);
			screen_updated = TRUE;
			break;
       /* 
	*  FLI BRUN chunk
	*/
     case ACT_FLI_LC:	
			{
			 ACT_FLI_LC_HDR *fli_lc_hdr;

			 fli_lc_hdr = (ACT_FLI_LC_HDR *)(act->data);
			 Decode_Fli_LC( fli_lc_hdr->data, pic, fli_lc_hdr);
			 theImage->data = pic;
   			 XPutImage(theDisp,mainW,theGC,theImage,
			              fli_lc_hdr->xpos,  fli_lc_hdr->ypos,
			              fli_lc_hdr->xpos,  fli_lc_hdr->ypos,
			              fli_lc_hdr->xsize, fli_lc_hdr->ysize  );
			 XFlush(theDisp);
			}
			screen_updated = TRUE;
			break;
       /* 
	*  FLI_COLOR not supported. Actually it become a CMAP when read in.
	*/
     case ACT_FLI_COLOR:	
			fprintf(stderr,"fli_color not supported\n");
			break;
       /* 
	*  FLI COPY chunk
	*/
     case ACT_FLI_COPY:	
			theImage->data = act->data;
   			XPutImage(theDisp,mainW,theGC,theImage,0,0,0,0,
							imagex,disp_y);
			XFlush(theDisp);
			screen_updated = TRUE;
			break;
       /* 
	*  Display Image of color 0.
	*/
     case ACT_FLI_BLACK:	
                        memset(im_buff0,0x00,image_size);
			theImage->data = im_buff0;
   			XPutImage(theDisp,mainW,theGC,theImage,0,0,0,0,
							imagex,disp_y);
			XFlush(theDisp);
			screen_updated = TRUE;
			break;
       /* 
	*  Display IFF body. Note that both buffers are set to this body.
	*  this is necessary since next delta works on 2nd buffer.
	*/
     case ACT_IFF_BODY:	

			memcpy(im_buff0,act->data,image_size);
			memcpy(im_buff1,act->data,image_size);
			if (anim_flags & ANIM_HAM) 
			{
   			 IFF_HAM_To_332(act->data,hbuff);
			 theImage->data = hbuff;
			}
			else 
			{
			 theImage->data = act->data;
			}

   			XPutImage(theDisp,mainW,theGC,theImage,0,0,
			 	           0,0,imagex,disp_y);
			XFlush(theDisp);
			cycle_wait = 1;
			old_dlta[0].minx = old_dlta[1].minx = 0;
			old_dlta[0].miny = old_dlta[1].miny = 0;
			old_dlta[0].maxx = old_dlta[1].maxx = imagex;
			old_dlta[0].maxy = old_dlta[1].maxy = imagey;
			screen_updated = TRUE;
			break;
       /* 
	*  Handle IFF anim op 5.
	*/
     case ACT_IFF_DLTA5:
			{
			 ACT_IFF_DLTA_HDR *iff_dlta_hdr;

			 iff_dlta_hdr = (ACT_IFF_DLTA_HDR *)(act->data);
			 IFF_Delta5(im_buff0,iff_dlta_hdr->data,iff_dlta_hdr);

		/* This mess keeps track of the largest rectangle needed to
		 * display all changes. Since things are double buffered, the
		 * min/maxes of the corners of the current and previous two
 		 * images are taken. If the animation is in single step mode
		 * it's best to display the entire image.
		 */
			if ((anim_running==FALSE) || (anim_forward_flag==FALSE))
			{
			 old_dlta[0].minx = old_dlta[1].minx = 0;
			 old_dlta[0].miny = old_dlta[1].miny = 0;
			 old_dlta[0].maxx = old_dlta[1].maxx = imagex;
			 old_dlta[0].maxy = old_dlta[1].maxy = imagey;
			}
			else
			{
			 old_dlta[0].minx = 
				MIN(old_dlta[0].minx,iff_dlta_hdr->minx);
			 old_dlta[0].miny = 
				MIN(old_dlta[0].miny,iff_dlta_hdr->miny);
			 old_dlta[0].maxx = 
				MAX(old_dlta[0].maxx,iff_dlta_hdr->maxx);
			 old_dlta[0].maxy = 
				MAX(old_dlta[0].maxy,iff_dlta_hdr->maxy);
			 old_dlta[1].minx = 
				MIN(old_dlta[1].minx,old_dlta[0].minx);
			 old_dlta[1].miny = 
				MIN(old_dlta[1].miny,old_dlta[0].miny);
			 old_dlta[1].maxx = 
				MAX(old_dlta[1].maxx,old_dlta[0].maxx);
			 old_dlta[1].maxy = 
				MAX(old_dlta[1].maxy,old_dlta[0].maxy);
			}

			 if (anim_flags & ANIM_HAM) 
			 {
   			  IFF_HAM_To_332(pic,hbuff);
			  theImage->data = hbuff;
			 }
			 else 
			 {
			  theImage->data = im_buff0;
			 }

			 if (anim_flags & ANIM_LACE) 
			 {
   			  XPutImage(theDisp,mainW,theGC,theImage,
			     old_dlta[1].minx,  old_dlta[1].miny/2,
			     old_dlta[1].minx,  old_dlta[1].miny/2,
			     old_dlta[1].maxx - old_dlta[1].minx, 
			    (old_dlta[1].maxy - old_dlta[1].miny)/2  );
                         }
   			 else XPutImage(theDisp,mainW,theGC,theImage,
			     old_dlta[1].minx,  old_dlta[1].miny,
			     old_dlta[1].minx,  old_dlta[1].miny,
			     old_dlta[1].maxx - old_dlta[1].minx, 
			     old_dlta[1].maxy - old_dlta[1].miny  );
			 XFlush(theDisp);
			 pic = im_buff0; im_buff0 = im_buff1; im_buff1 = pic;

		 /* Throw out oldest rectangle and store current
		  * you get the idea.
		  */
			 old_dlta[1].minx = old_dlta[0].minx;
			 old_dlta[1].miny = old_dlta[0].miny;
			 old_dlta[1].maxx = old_dlta[0].maxx;
			 old_dlta[1].maxy = old_dlta[0].maxy;
			 old_dlta[0].minx = iff_dlta_hdr->minx;
			 old_dlta[0].miny = iff_dlta_hdr->miny;
			 old_dlta[0].maxx = iff_dlta_hdr->maxx;
			 old_dlta[0].maxy = iff_dlta_hdr->maxy;
			}
			screen_updated = TRUE;
			break;
       /* 
	*  Handle IFF anim op 3 compressed actions.
 	*  NOTE: eventually put in largest rectangle optimization.
	*/
     case ACT_IFF_DLTA3:
			{
                         ACT_IFF_DLTA_HDR *iff_dlta_hdr;

                         iff_dlta_hdr = (ACT_IFF_DLTA_HDR *)(act->data);
			 IFF_Delta3(im_buff0, iff_dlta_hdr->data);
			 if (anim_flags & ANIM_HAM) 
			 {
   			  IFF_HAM_To_332(pic,hbuff);
			  theImage->data = hbuff;
			 }
			 else 
			 {
			  theImage->data = im_buff0;
			 }

   			 XPutImage(theDisp,mainW,theGC,theImage,0,0,0,0,
			 				imagex,disp_y);
			 XFlush(theDisp);
			 pic = im_buff0; im_buff0 = im_buff1; im_buff1 = pic;
			}
			screen_updated = TRUE;
			break;
       /* 
	* Handle IFF anim opt J compressed actions. 
	*/
     case ACT_IFF_DLTAJ:
			{
			 ACT_IFF_DLTA_HDR *iff_dlta_hdr;

                         iff_dlta_hdr = (ACT_IFF_DLTA_HDR *)(act->data);
			 IFF_DeltaJ(im_buff0,iff_dlta_hdr->data,iff_dlta_hdr);

		/* This mess keeps track of the largest rectangle needed to
		 * display all changes. Since things are double buffered, the
		 * min/maxes of the corners of the current and previous two
 		 * images are taken.
		 */
			if ((anim_running==FALSE) || (anim_forward_flag==FALSE))
			{
			 old_dlta[0].minx = old_dlta[1].minx = 0;
			 old_dlta[0].miny = old_dlta[1].miny = 0;
			 old_dlta[0].maxx = old_dlta[1].maxx = imagex;
			 old_dlta[0].maxy = old_dlta[1].maxy = imagey;
			}
			else
			{
			 old_dlta[0].minx = 
				MIN(old_dlta[0].minx,iff_dlta_hdr->minx);
			 old_dlta[0].miny = 
				MIN(old_dlta[0].miny,iff_dlta_hdr->miny);
			 old_dlta[0].maxx = 
				MAX(old_dlta[0].maxx,iff_dlta_hdr->maxx);
			 old_dlta[0].maxy = 
				MAX(old_dlta[0].maxy,iff_dlta_hdr->maxy);
			 old_dlta[1].minx = 
				MIN(old_dlta[1].minx,old_dlta[0].minx);
			 old_dlta[1].miny = 
				MIN(old_dlta[1].miny,old_dlta[0].miny);
			 old_dlta[1].maxx = 
				MAX(old_dlta[1].maxx,old_dlta[0].maxx);
			 old_dlta[1].maxy = 
				MAX(old_dlta[1].maxy,old_dlta[0].maxy);
			}

			 if (anim_flags & ANIM_HAM) 
			 {
   			  IFF_HAM_To_332(pic,hbuff);
			  theImage->data = hbuff;
			 }
			 else 
			 {
			  theImage->data = im_buff0;
			 }
 
 			 if (anim_flags & ANIM_LACE) 
 			 {
   			  XPutImage(theDisp,mainW,theGC,theImage,
			     old_dlta[1].minx,  old_dlta[1].miny/2,
			     old_dlta[1].minx,  old_dlta[1].miny/2,
			     old_dlta[1].maxx - old_dlta[1].minx, 
			    (old_dlta[1].maxy - old_dlta[1].miny)/2  );
                         }
   			 else XPutImage(theDisp,mainW,theGC,theImage,
			     old_dlta[1].minx,  old_dlta[1].miny,
			     old_dlta[1].minx,  old_dlta[1].miny,
			     old_dlta[1].maxx - old_dlta[1].minx, 
			     old_dlta[1].maxy - old_dlta[1].miny  );
			 XFlush(theDisp);
			 pic = im_buff0; im_buff0 = im_buff1; im_buff1 = pic;

		 /* Throw out oldest rectangle and store current
		  * you get the idea.
		  */
			 old_dlta[1].minx = old_dlta[0].minx;
			 old_dlta[1].miny = old_dlta[0].miny;
			 old_dlta[1].maxx = old_dlta[0].maxx;
			 old_dlta[1].maxy = old_dlta[0].maxy;
			 old_dlta[0].minx = iff_dlta_hdr->minx;
			 old_dlta[0].miny = iff_dlta_hdr->miny;
			 old_dlta[0].maxx = iff_dlta_hdr->maxx;
			 old_dlta[0].maxy = iff_dlta_hdr->maxy;
			}
			screen_updated = TRUE;
                        break;
       /*
        *  Handle IFF anim op l(small L not one 1) compressed actions.
        *  NOTE: eventually put in largest rectangle optimization.
        */
     case ACT_IFF_DLTAl:
                        {
                         ACT_IFF_DLTA_HDR *iff_dlta_hdr;

                         iff_dlta_hdr = (ACT_IFF_DLTA_HDR *)(act->data);
                         IFF_Deltal(im_buff0, (short *)iff_dlta_hdr->data,
                                                1,iff_dlta_hdr);
                         if (anim_flags & ANIM_HAM)
                         {
                          IFF_HAM_To_332(pic,hbuff);
                          theImage->data = hbuff;
                         }
                         else
                         {
                          theImage->data = im_buff0;
                         }

                         XPutImage(theDisp,mainW,theGC,theImage,0,0,0,0,
                                                        imagex,disp_y);
                         XFlush(theDisp);
                         pic = im_buff0; im_buff0 = im_buff1; im_buff1 = pic;
                        }
			screen_updated = TRUE;
                        break;

       /* 
	*  Update ham_map if it changes during an animation. ham_map is
 	*  needed in order to convert HAM images into 332.
	*/
     case ACT_IFF_HMAP:	
			{
			 ColorReg *hptr;
			 int i;

			 hptr = (ColorReg *)act->data;
			 for(i=0;i<HMAP_SIZE;i++)
			 {
			  ham_map[i].red   = hptr[i].red;
			  ham_map[i].green = hptr[i].green;
			  ham_map[i].blue  = hptr[i].blue;
			 }
			}
			break;
     default:		
			fprintf(stderr,"Unknown not supported %lx\n",act->type);
    } /* end of switch of action type */
 } /* end of action valid */

/* Move on if we're running. If we're in single step mode and the update_flag
 * is set, we move on until the screen has been updated. 
 */
 if (anim_running==TRUE) 
 {
  if (anim_forward_flag == TRUE) Step_Action_Next();
  else Step_Frame_Prev();
 }
 else if ((update_flag == TRUE) && (screen_updated==FALSE))
      {
       if (anim_forward_flag == TRUE) Step_Frame_Next();
       else Step_Frame_Prev();
      }
      else return;

  /* Harry, what time is it?
   */
 t_frame_end = times(&tms3) - t_frame_start;
 t_frame_end *= 1000/HZ;  /* convert to milliseconds */

  /* how much time left 
   */
 t_frame_end = t_frame_int - t_frame_end;  
  /* default to 1 ms, other wise 100 clk_tcks gives 10ms per tick.
   * NOTE: change all timing to milliseconds, not ticks.
   */
 if (t_frame_end <=0 ) t_frame_end = 1; 
 
 XtAppAddTimeOut(theContext, t_frame_end, ShowAction, NULL);
}

Step_Action_Next()
{
 /* Move to next frame
  */
 cur_frame++;
 if (debug_flag) fprintf(stderr,"frame = %ld\n",cur_frame);

 /* Are we at the end of an anim?
  */
 if (   (anim[cur_file].frame_lst[cur_frame] < 0)
     || (anim[cur_file].frame_lst[cur_frame] >= action_cnt) )
 {
   cur_frame = anim[cur_file].loop_frame;

   cur_floop++;
   if (debug_flag) fprintf(stderr,"  loop = %ld\n",cur_floop);

   /* Done looping animation. Move on to next file if present
    */
   if (cur_floop >= anim[cur_file].loop_num)
   {
     cur_floop = 0;             /* Reset Loop Count */

     /* This is a special case check.
      * If more that one file, reset start_file, otherwise
      * if we're only displaying 1 animation jump to the loop_frame
      * which has already been set up above.
      */
     if (anim_cnt > 1)
     {
       start_file = 0;
       cur_frame = 0;
     } /* end of move on to next file/anim */

     cur_file++;

     if (cur_file >= anim_cnt)
     {
       cur_file=0;
     } /* end done with last file */
     if (debug_flag) fprintf(stderr,"  file = %ld\n",cur_file);
   } /* end done looping file */
 } /* end done with frames in file */
}

Step_Frame_Next()
{
 /* Move to next frame
  */
 cur_frame++;
 if (debug_flag) fprintf(stderr,"frame = %ld\n",cur_frame);

 /* Are we at the end of an anim?
  */
 if (   (anim[cur_file].frame_lst[cur_frame] < 0)
     || (anim[cur_file].frame_lst[cur_frame] >= action_cnt) )
 {
   cur_frame = anim[cur_file].loop_frame;
 }
}

Step_Frame_Prev()
{
 /* Move to next frame
  */
 cur_frame--;
 if (debug_flag) fprintf(stderr,"frame = %ld\n",cur_frame);

 /* Are we at the beginning of an anim?
  */
 if (   (anim[cur_file].frame_lst[cur_frame] < 0)
     || (anim[cur_file].frame_lst[cur_frame] >= action_cnt) 
     || (cur_frame < anim[cur_file].loop_frame)
     || (cur_frame < 0)
    )
 {
   cur_frame = anim[cur_file].last_frame;
 }
}

Step_File_Next()
{
  start_file = 0;
  cur_frame = 0;
  cur_floop = 0; /* used if things start up again */

  cur_file++;
  if (cur_file >= anim_cnt)
  {
   cur_file=0;
  } /* end done with last file */

  if (debug_flag) fprintf(stderr,"  file = %ld\n",cur_file);
}

Step_File_Prev()
{
  start_file = 0;
  cur_frame = 0;
  cur_floop = 0; /* used if things start up again */

  cur_file--;
  if (cur_file < 0)
  {
   cur_file = anim_cnt-1;
  } /* end done with last file */

  if (debug_flag) fprintf(stderr,"  file = %ld\n",cur_file);
}


/*
 * Simple routine to find out the file type. Defaults to FLI.
 */
int Determine_Anim_Type(filename)
char *filename;
{
 if ( Is_IFF_File(filename) ) return(IFF_ANIM); 
 if ( Is_GIF_File(filename) ) return(GIF_ANIM); 
 if ( Is_TXT_File(filename) ) return(TXT_ANIM); 
 return(FLI_ANIM);
}
 
void Cycle_It(crng, id)
CRNG_HDR   *crng;
XtIntervalId *id;
{
 int             i,c_off,clow,chi;
 XColor          tmp_def;

 if ((anim_flags & ANIM_CYCLE) && cycle_on_flag) 
  XtAppAddTimeOut(theContext,(int)(CRNG_INTERVAL/(crng->rate)),Cycle_It, crng);
 else cycle_cnt--;

 c_off = x11_cmap_size - imagec;
 clow = crng->low;
 chi = crng->high;

 if (crng->active & CRNG_REVERSE) 
 {
  tmp_def = defs[clow];
  for (i = clow; i < chi; i++) defs[i] = defs[i + 1];
  defs[chi] = tmp_def;
 } 
 else 
 {
  tmp_def = defs[chi];
  for (i = chi; i > clow; i--) defs[i] = defs[i - 1];
  defs[clow] = tmp_def;
 }

 for (i = clow; i <= chi; i++) defs[i].pixel = i+c_off;

 XStoreColors(theDisp,theCmap,&defs[clow],(chi - clow + 1) );
 XFlush(theDisp);
}

