 /*
  * Khoros: $Id: jp_playback.c,v 1.3 1992/03/20 22:47:59 dkhoros Exp $
  */

#if !defined(lint) && !defined(SABER)
static char rcsid[] = "Khoros: $Id: jp_playback.c,v 1.3 1992/03/20 22:47:59 dkhoros Exp $";
#endif

 /*
  * $Log: jp_playback.c,v $
 * Revision 1.3  1992/03/20  22:47:59  dkhoros
 * VirtualPatch5
 *
  */ 

/*
 *----------------------------------------------------------------------
 *
 * Copyright 1990, University of New Mexico.  All rights reserved.
 * 
 * Permission to copy and modify this software and its documen-
 * tation only for internal use in your organization is hereby
 * granted, provided that this notice is retained thereon and
 * on all copies.  UNM makes no representations as too the sui-
 * tability and operability of this software for any purpose.
 * It is provided "as is" without express or implied warranty.
 * 
 * UNM DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
 * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FIT-
 * NESS.  IN NO EVENT SHALL UNM BE LIABLE FOR ANY SPECIAL,
 * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY OTHER DAMAGES WHAT-
 * SOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
 * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
 * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PER-
 * FORMANCE OF THIS SOFTWARE.
 * 
 * No other rights, including for example, the right to redis-
 * tribute this software and its documentation or the right to
 * prepare derivative works, are granted unless specifically
 * provided in a separate license agreement.
 *----------------------------------------------------------------------
 */

#include "unmcopyright.h"	 /* Copyright 1990 by UNM */
#include "forms.h"
#include "jp.h"

/* >>>>>>>>>>>>>>>>>>>>>>>>>>>>> <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
   >>>>                                                       <<<<
   >>>>	       Journal Playback Routines                      <<<<
   >>>>                                                       <<<<
   >>>>                jp_elapsed_time()                      <<<<
   >>>>                jp_playback()                          <<<<
   >>>>                                                       <<<<
   >>>>>>>>>>>>>>>>>>>>>>>>>>>>> <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< */


static  Window lookup_window();


/************************************************************
*
*  Routine Name: jp_elapsed_time
*
*      Purpose:  This routine is used to get the playback time.
*		 The elapsed time in which the event was recorded is
*		 passed back.  This can be used by the routine to
*		 wait the elapsed time before reading and playing
*		 back the event.
*
*		 The window's name is also recorded in the journal file,
*		 which is used to lookup the current window and display.
*		 The event is assigned to current window and display before
*		 being dispatched by xvf_process_event().
*
*        Input:  None
*
*       Output:  elapsed  - the elapsed time to wait before reading
*			    we need to read and playback the event.
*
*   Written By:  Danielle Argiro, Mark Young
*
*************************************************************/

int jp_elapsed_time(elapsed)

int *elapsed;
{
	char	temp[MaxLength];


	/*
	 *  at end of journal playback file
	 */
	if (feof(play_file))
	{
	   fprintf(stderr, "End of playback for journal file '%s'.\n",
			   play_filename);
	   fclose(play_file); jp_playing = False;
	   return(False);
	}

	/*
	 * read a line from journal playback file
	 */
	if (!fgets(temp, MaxLength, play_file))
	{
	   /* unsuccessful read */
	   if (!(feof(play_file)))
	   {
 	       fprintf(stderr, "jp_elapsed_time:\n");
	       fprintf(stderr, "couldn't read elapsed time from ");
	       fprintf(stderr, "journal playback file\n");
	   }
	   /* end of file */
	   else
	   {
	       fprintf(stderr, "End of playback for journal file '%s'.\n",
		       play_filename);
	       fclose(play_file); jp_playing = False;
	   }
	   return(False);
	}

	/*
	 *  parse the elapsed time
	 */
	if (sscanf(temp, "%d\n", elapsed) != 1)
	{
	   fprintf(stderr, "jp_elapsed_time:\n");
	   fprintf(stderr, "couldn't parse elapsed time from journal playback \
file\n");
	   return(False);
	}
	return(True);
}



/************************************************************
*
*  Routine Name: jp_playback
*
*      Purpose:  This routine is used to playback an event.  When
*		 we are in playback mode, xvf_process_event() calls
*		 jp_playback() to read the next event out of the
*		 playback file.  This event is passed back to the
*		 xvf_process_event() where it will be dispatched.
*
*		 The time in which the event was recorded is also
*		 passed back.  This can be used by the routine to
*		 compare elapsed time between recorded and current
*		 time.  This would then indicate if the event should
*		 be played back right away or whether to wait for some
*		 elapsed amount of time.
*
*		 The window's name is also recorded in the journal file,
*		 which is used to lookup the current window and display.
*		 The event is assigned to current window and display before
*		 being dispatched by xvf_process_event().
*
*        Input:  None
*
*       Output:  event  - the next event to be played back.
*
*   Written By:  Danielle Argiro, Mark Young
*
*************************************************************/


int jp_playback(event)

XEvent *event;
{
	int	type, toplevel, x, y;
	char	name[MaxLength], temp[MaxLength];

	static  char   *oldname = NULL;
	static  Window window; 


	/*
	 *  at end of journal playback file
	 */
	if (feof(play_file))
	{
	   fprintf(stderr, "jp_playback:\n");
	   fprintf(stderr, "End of playback for journal file '%s'.  Will \
continue normally.\n", play_filename);
	   fclose(play_file);
	   jp_playing = False;
	   return(False);
	}

	/*
	 * read a line from journal playback file
	 */
	if (!fgets(temp, MaxLength, play_file))
	{
	   fprintf(stderr, "jp_playback:\n");
	   fprintf(stderr, "couldn't read window name from journal playback \
file\n");
	   return(False);
	}

	/*
	 *  parse name and type (XVF_WIDGET or XVF_WINDOW) from line
	 */
	if (sscanf(temp, "%*[']%[^']%*['] %d\n", name, &type) != 2)
	{
	   fprintf(stderr, "jp_playback:\n");
	   fprintf(stderr, "couldn't parse window name and type from journal \
playback file\n");
	   return(False);
	}

#ifdef DEBUG
fprintf(stderr, "%s\n", temp);
#endif

	/*
	 *  read event from journal playback file
	 */
	if (!read_event(event, play_file))
	   return(False);

	/*
	 *  Quick check to see if the previous event orginated from the same
	 *  window.  If so there is no need to search for the window since we
	 *  saved it from the last time.
	 */
	if (oldname != NULL)
	{
	   if (strcmp(oldname, name) != 0)
	   {
	      free(oldname);
	      oldname = xvf_strcpy(name);
	      window = lookup_window(type, name, &toplevel);
	   }
	}
	else
	{
	   oldname = xvf_strcpy(name);
	   window = lookup_window(type, name, &toplevel);
	}


	/*
	 *  over-write the currrent display and window for the event
	 */
	if (window == NULL)
	{
	   free(oldname);
	   oldname = NULL;
	   return(False);
	}

	event->xany.display = xvf_display;
	event->xany.window  = window;

	/*
	 *  Want to warp the pointer to mimic original pointer motion if
	 *  or if the event is either a KeyPress or KeyRelease event then
	 *  we need to also retrieve the string associated with that event.
	 */
/*
	if ((event->type == MotionNotify || event->type == ButtonPress ||
	     event->type == ButtonRelease) && jp_grab == True)
 */
	if (event->type == MotionNotify && jp_grab == True)
	{
	    if (event->type == MotionNotify)
	    {
	       x = event->xmotion.x;
	       y = event->xmotion.y;
	    }
	    else
	    {
	       x = event->xbutton.x;
	       y = event->xbutton.y;
	    }
	    XWarpPointer(xvf_display, None, event->xany.window,
			 0, 0, 0, 0, x, y);
	}
/*
	if (event->type == MotionNotify)
	{
	    event->xany.send_event = True;
	    XSendEvent(xvf_display, window, True, MotionNotify, event);
	}
*/
	else if (event->type == KeyPress || event->type == KeyRelease)
	{
	   KeySym keysym;
	   char   *end, string[MaxLength];

	   /*
	    * read a line from journal playback file
	    */
	   if (!fgets(string, MaxLength, play_file))
	   {
	      fprintf(stderr, "jp_playback:\n");
	      fprintf(stderr, "couldn't read key string from journal playback \
file\n");
	      return(False);
	   }
	   if ((end = strrchr(string,'\n')) != NULL)
	      *end = '\0';

	   keysym = XStringToKeysym(string);
	   event->xkey.keycode = XKeysymToKeycode(xvf_display, keysym);
	}

	/*
	 *  return True so that the calling routine (xvf_process_event())
	 *  will dispatch the event - no need to do it here
	 *
	 *  we also need to save the current name so that if we get the same
	 *  name next time we will not need to repeat the search
	 */
	return(True);
}

static Window lookup_window(type, name, toplevel)

int  type, *toplevel;
char *name;
{
	Widget  widget;
	Window  window;
	Window	rootwindow = XRootWindow(xvf_display, xvf_screen_num);


	/*
	 *  event originated from a widget - find the widget that it 
	 *  is associated with, and the window that it corresponds to
	 */
	if (type == XVF_WIDGET)
	{
	   if ((widget = xvf_find_xvform_widget(name)) != NULL)
	      window = XtWindow(widget);
	   else if ((widget = xvf_find_widget(rootwindow, name)) != NULL)
	      window = XtWindow(widget);
	   else
	      return(NULL);

	   /*
	    *  Need to know if this widget is a toplevel widget.  Since
	    *  toplevel widgets are treated different than it's children.
	    */
	   if (XtParent(widget) == NULL)
	      *toplevel = True;
	   else
	      *toplevel = False;
	}

	/*
	 *  event originated from a window - 
	 *  find the window that it is associated with
	 */
	else if (type == XVF_WINDOW)
	{
	   *toplevel = True;
	   if (!(window = xvf_find_window(rootwindow, name, toplevel)))
	      return(NULL);
	}

	/*
	 *  type unknown - internal error
	 */
	else
	{
	   fprintf(stderr, "NULL:\n");
	   fprintf(stderr, "Internal Error!  Unknown type for name '%s', valid \
types are either widget or a window.\n", name);
	   return(NULL);
	   
	}
	return(window);
}

int cast_jp_int(value)

jp_int value;
{
	int	num;
	int	status;
	static	char *array = NULL;
	static  int  dimension = -1;
	static  int  elem_size, new_elem_size, dest_type;


	if (dimension == -1)
	{
	   dimension = 1;
	   elem_size = sizeof(jp_int);
	   new_elem_size = sizeof(int);
	   dest_type = machtype(NULL);
	}

	if (array == NULL)
	{
	   if ((array = (char *) malloc(sizeof(elem_size))) == NULL)
	   {
	      fprintf(stderr,"cast_jp_int: Out of memory!\n");
	      return(-1);
	   }
	}
	bcopy((char *) value, (char *) array, elem_size);
	status = cast_data((unsigned char **)&(array),
			       (unsigned int)dimension,
			       (unsigned int)VFF_TYP_4_BYTE,
			       (unsigned int)VFF_DEP_NSORDER,
			       (unsigned int)dest_type);
	bcopy((char *) array, (char *) &num, new_elem_size);

	if (elem_size > new_elem_size)
	{
	   free(array);
	   array = NULL;
	}
	return(num);
}

long cast_jp_long(value)

jp_long value;
{
	long	num;
	int	status;
	static	char *array = NULL;
	static  int  dimension = -1;
	static  int  elem_size, new_elem_size, dest_type;


	if (dimension == -1)
	{
	   dimension = 1;
	   elem_size = sizeof(jp_long);
	   new_elem_size = sizeof(long);
	   dest_type = machtype(NULL);
	}

	if (array == NULL)
	{
	   if ((array = (char *) malloc(sizeof(elem_size))) == NULL)
	   {
	      fprintf(stderr,"cast_jp_long: Out of memory!\n");
	      return(-1);
	   }
	}
	bcopy((char *) value, (char *) array, elem_size);
	status = cast_data((unsigned char **)&(array),
			       (unsigned int)dimension,
			       (unsigned int)VFF_TYP_4_BYTE,
			       (unsigned int)VFF_DEP_NSORDER,
			       (unsigned int)dest_type);
	bcopy((char *) array, (char *) &num, new_elem_size);

	if (elem_size > new_elem_size)
	{
	   free(array);
	   array = NULL;
	}
	return(num);
}

short cast_jp_short(value)

jp_short value;
{
	short	num;
	int	status;
	static	char *array = NULL;
	static  int  dimension = -1;
	static  int  elem_size, new_elem_size, dest_type;


	if (dimension == -1)
	{
	   dimension = 1;
	   elem_size = sizeof(jp_short);
	   new_elem_size = sizeof(short);
	   dest_type = machtype(NULL);
	}

	if (array == NULL)
	{
	   if ((array = (char *) malloc(sizeof(elem_size))) == NULL)
	   {
	      fprintf(stderr,"cast_jp_short: Out of memory!\n");
	      return(-1);
	   }
	}
	bcopy((char *) value, (char *) array, elem_size);
	status = cast_data((unsigned char **)&(array),
			       (unsigned int)dimension,
			       (unsigned int)VFF_TYP_2_BYTE,
			       (unsigned int)VFF_DEP_NSORDER,
			       (unsigned int)dest_type);
	bcopy((char *) array, (char *) &num, new_elem_size);

	if (elem_size > new_elem_size)
	{
	   free(array);
	   array = NULL;
	}
	return(num);
}

int read_event(event, play_file)

XEvent *event;
FILE   *play_file;
{
	JPEvent jp;
	static  int xsize = sizeof(XEvent);
	static  int jpsize = sizeof(JPEvent);


	if (jpsize == xsize && machtype(NULL) == VFF_DEP_NSORDER)
	{
	   if (!fread((char *) event, xsize, 1, play_file))
	   {
	      fprintf(stderr, "jp_playback:\n");
	      fprintf(stderr, "couldn't read event from journal playback \
file\n");
	      return(False);
	   }
	}
	else
	{
	   if (!fread((char *) &jp, jpsize, 1, play_file))
	   {
	      fprintf(stderr, "jp_playback:\n");
	      fprintf(stderr, "couldn't read event from journal playback \
file\n");
	      return(False);
	   }
	   bzero((char *) event, xsize);
	   event->xany.type   = cast_jp_long(jp.xany.type);
	   event->xany.serial = cast_jp_long(jp.xany.serial);
	   event->xany.send_event = cast_jp_int(jp.xany.send_event);

	   switch(event->type)
	   {
	      case KeyPress:
	      case KeyRelease:
		   event->xkey.time = cast_jp_long(jp.xkey.time);
		   event->xkey.x = cast_jp_int(jp.xkey.x);
		   event->xkey.y = cast_jp_int(jp.xkey.y);
		   event->xkey.x_root = cast_jp_int(jp.xkey.x_root);
		   event->xkey.y_root = cast_jp_int(jp.xkey.y_root);
		   event->xkey.state = cast_jp_int(jp.xkey.state);
		   event->xkey.keycode = cast_jp_int(jp.xkey.keycode);
		   event->xkey.same_screen = cast_jp_int(jp.xkey.same_screen);
		   break;

	      case ButtonPress:
	      case ButtonRelease:
		   event->xbutton.time = cast_jp_long(jp.xbutton.time);
		   event->xbutton.x = cast_jp_int(jp.xbutton.x);
		   event->xbutton.y = cast_jp_int(jp.xbutton.y);
		   event->xbutton.x_root = cast_jp_int(jp.xbutton.x_root);
		   event->xbutton.y_root = cast_jp_int(jp.xbutton.y_root);
		   event->xbutton.state = cast_jp_int(jp.xbutton.state);
		   event->xbutton.button = cast_jp_int(jp.xbutton.button);
		   event->xbutton.same_screen =
					cast_jp_int(jp.xbutton.same_screen);
		   break;

	      case MotionNotify:
		   event->xmotion.time = cast_jp_long(jp.xmotion.time);
		   event->xmotion.x = cast_jp_int(jp.xmotion.x);
		   event->xmotion.y = cast_jp_int(jp.xmotion.y);
		   event->xmotion.x_root = cast_jp_int(jp.xmotion.x_root);
		   event->xmotion.y_root = cast_jp_int(jp.xmotion.y_root);
		   event->xmotion.state = cast_jp_int(jp.xmotion.state);
		   event->xmotion.is_hint = jp.xmotion.is_hint;
		   event->xmotion.same_screen =
					cast_jp_int(jp.xmotion.same_screen);
		   break;

	      case EnterNotify:
	      case LeaveNotify:
		   event->xcrossing.time = cast_jp_long(jp.xcrossing.time);
		   event->xcrossing.x = cast_jp_int(jp.xcrossing.x);
		   event->xcrossing.y = cast_jp_int(jp.xcrossing.y);
		   event->xcrossing.x_root = cast_jp_int(jp.xcrossing.x_root);
		   event->xcrossing.y_root = cast_jp_int(jp.xcrossing.y_root);
		   event->xcrossing.mode = cast_jp_int(jp.xcrossing.mode);
		   event->xcrossing.detail = cast_jp_int(jp.xcrossing.detail);
		   event->xcrossing.same_screen =
					cast_jp_int(jp.xcrossing.same_screen);
		   event->xcrossing.focus = cast_jp_int(jp.xcrossing.focus);
		   event->xcrossing.state = cast_jp_int(jp.xcrossing.state);
		   break;

	      case FocusIn:
	      case FocusOut:
		   event->xfocus.mode = cast_jp_int(jp.xfocus.mode);
		   event->xfocus.detail = cast_jp_int(jp.xfocus.detail);
		   break;

	      case KeymapNotify:
		   bcopy(jp.xkeymap.key_vector, event->xkeymap.key_vector, 32);
		   break;

	      case Expose:
		   event->xexpose.x = cast_jp_int(jp.xexpose.x);
		   event->xexpose.y = cast_jp_int(jp.xexpose.y);
		   event->xexpose.width = cast_jp_int(jp.xexpose.width);
		   event->xexpose.height = cast_jp_int(jp.xexpose.height);
		   event->xexpose.count = cast_jp_int(jp.xexpose.count);
		   break;

	      case GraphicsExpose:
		   event->xgraphicsexpose.x = cast_jp_int(jp.xgraphicsexpose.x);
		   event->xgraphicsexpose.y = cast_jp_int(jp.xgraphicsexpose.y);
		   event->xgraphicsexpose.width =
				cast_jp_int(jp.xgraphicsexpose.width);
		   event->xgraphicsexpose.height =
				cast_jp_int(jp.xgraphicsexpose.height);
		   event->xgraphicsexpose.count =
				cast_jp_int(jp.xgraphicsexpose.count);
		   event->xgraphicsexpose.major_code =
				cast_jp_int(jp.xgraphicsexpose.major_code);
		   event->xgraphicsexpose.minor_code =
				cast_jp_int(jp.xgraphicsexpose.minor_code);
		   break;

	      case NoExpose:
		   event->xnoexpose.major_code =
				cast_jp_int(jp.xnoexpose.major_code);
		   event->xnoexpose.minor_code =
				cast_jp_int(jp.xnoexpose.minor_code);
		   break;

	      case VisibilityNotify:
		   event->xvisibility.state = cast_jp_int(jp.xvisibility.state);
		   break;

	      case CreateNotify:
		   event->xcreatewindow.x = cast_jp_int(jp.xcreatewindow.x);
		   event->xcreatewindow.y = cast_jp_int(jp.xcreatewindow.y);
		   event->xcreatewindow.width =
				cast_jp_int(jp.xcreatewindow.width);
		   event->xcreatewindow.height =
				cast_jp_int(jp.xcreatewindow.height);
		   event->xcreatewindow.border_width =
				cast_jp_int(jp.xcreatewindow.border_width);
		   event->xcreatewindow.override_redirect =
				cast_jp_int(jp.xcreatewindow.override_redirect);
		   break;

	      case DestroyNotify:
		   /*  do nothing */
		   break;

	      case UnmapNotify:
		   event->xunmap.from_configure =
				cast_jp_int(jp.xunmap.from_configure);
		   break;

	      case MapNotify:
		   event->xmap.override_redirect =
				cast_jp_int(jp.xmap.override_redirect);
		   break;

	      case MapRequest:
		   /*  do nothing */
		   break;

	      case ReparentNotify:
		   event->xreparent.x = cast_jp_int(jp.xreparent.x);
		   event->xreparent.y = cast_jp_int(jp.xreparent.y);
		   event->xreparent.override_redirect =
				cast_jp_int(jp.xreparent.override_redirect);
		   break;

	      case ConfigureNotify:
		   event->xconfigure.x = cast_jp_int(jp.xconfigure.x);
		   event->xconfigure.y = cast_jp_int(jp.xconfigure.y);
		   event->xconfigure.width = cast_jp_int(jp.xconfigure.width);
		   event->xconfigure.height = cast_jp_int(jp.xconfigure.height);
		   event->xconfigure.border_width =
				cast_jp_int(jp.xconfigure.border_width);
		   event->xconfigure.override_redirect =
				cast_jp_int(jp.xconfigure.override_redirect);
		   break;

	      case GravityNotify:
		   event->xgravity.x = cast_jp_int(jp.xgravity.x);
		   event->xgravity.y = cast_jp_int(jp.xgravity.y);
		   break;

	      case ConfigureRequest:
		   fprintf(stderr,"play ConfigureRequest\n");
		   break;
	      case ResizeRequest:
		   fprintf(stderr,"play ResizeRequest\n");
		   break;
	      case CirculateNotify:
		   fprintf(stderr,"play CirculateNotify\n");
		   break;
	      case CirculateRequest:
		   fprintf(stderr,"play CirculateRequest\n");
		   break;
	      case PropertyNotify:
		   fprintf(stderr,"play PropertyNotify\n");
		   break;
	      case SelectionClear:
		   fprintf(stderr,"play SelectionClear\n");
		   break;
	      case SelectionRequest:
		   fprintf(stderr,"play SelectionRequest\n");
		   break;
	      case SelectionNotify:
		   fprintf(stderr,"play SelectionNotify\n");
		   break;
	      case ColormapNotify:
		   fprintf(stderr,"play ColormapNotify\n");
		   break;
	      case ClientMessage:
		   fprintf(stderr,"play ClientMessage\n");
		   break;
	   }
	}
	return(True);
}
