#include <stdio.h>
#include <ctype.h>
#include <sys/time.h>
#include <X11/Xlib.h>
#include <X11/Xutil.h>

#define STRINGLENGTH	256
#define SLEEPTIME	0    /* Sleep for this many seconds before redrawing */

char	**Argv;
int	Argc;
Display	*dpy;


main(argc,argv)
     int	argc;
     char	**argv;
{
  Window	watchWin,GetWindowByName();
  int		i,strPos,optIndex;
  char		displayName[64],xWatchName[STRINGLENGTH];
  char		*optstring;
  extern char	*optarg;
  extern int	optind,opterr;
  int		windowID,windowIDSet,updateTime,updateTimeSet;
  

  if (argc < 3)			    /* Did user enter enough arguments? */
    {				    /* Nope, print an error message and exit. */
      printf("Usage:\t%s HostName [-u UpdateTime] [-w windowID] [WindowName]\n",
	  argv[0]);
      exit(1);
    }

  /* Initialize var to update every SLEEPTIME seconds. */
  updateTime = SLEEPTIME;
  updateTimeSet = 0;
  windowIDSet = 0;
  optind = 2;	    /* Skip over first, obligatory argument (hostname) */
  while ((optIndex = getopt(argc,argv,"u:w:")) != -1) /* Get user's arguments */
    switch (optIndex)
      {
      case 'u':		/* User wants to update every 'optarg' seconds. */
	updateTime = atoi(optarg);
	updateTimeSet = 1;    /* Set flag saying user specified this option. */
	break;
      case 'w':   /* User wants to specify window by id instead of by name */
	sscanf(&optarg[2],"%lx",&windowID);
	windowIDSet = 1;    /* Set flag saying user specified this option. */
	break;
      }			    /* end switch (optIndex) */

  Argv = argv;
  Argc = argc;
  sprintf(displayName,"%s:0.0",argv[1]);    /* Set the X display name. */
  if (!windowIDSet)	   /* Did user specify window to watch by number? */
    for (i=2, bzero(xWatchName, STRINGLENGTH); i < argc; i++)
    /* No, parse rest of arguments as window name to watch */
      {
	if (!strcmp(argv[i],"-u"))	   /* Don't parse optional arguments */
	  {
	    i++;		   /* Skip over argument to '-u' switch. */
	    continue;
	  }
	/* Get current length of xWatchName string */
	strPos = strlen(xWatchName);
	/* Copy another argument to the end of the string */
	strcpy(&xWatchName[strPos],argv[i]);
      }

  /* Attempt to open a connection with the remote X server */
  if ((dpy = XOpenDisplay(displayName)) == NULL)
    {
      /* Couldn't open the display for some reason, so... */
      fprintf(stderr,"%s: Could not open Display %s\n", argv[0],displayName);
      exit(1);	    /* ...report the error and exit with an error code */
    }

  if (!windowIDSet)	    /* Did user specify a window to watch by id? */
    /* No, get window id from window name. */
    watchWin = GetWindowByName(XDefaultRootWindow(dpy),xWatchName);
  else watchWin = windowID; /* Yes, use user-specified window id. */
  if (watchWin)	   /* Did the user find the window s/he was looking for? */
    /* Yes, periodically show the contents of that window  */
    WatchWindow(watchWin,updateTime);
  else	/* No, report that the window was not found, and exit. */
    {
      printf("Could not find the window you specified.\n");
      exit(1);
    }
  
}	/* end function main */




/* Takes two strings, removes spaces from the second,... */
/* ...and compares them..  Returns 1 if equal, 0 if not. */
WinNamesEqual(str1,str2)
     char	*str1,*str2;
{
  char	tempStr[STRINGLENGTH],*tempStrPtr;
  int	index;
  
  bzero(tempStr,STRINGLENGTH);	/* Clear the contents of the string, if any */
  /* Go through each character in the second string. */
  for (tempStrPtr=tempStr; *str2; str2++)
    {
      if (!isspace(*str2))    /* Is this character a space?  */
	*tempStrPtr++ = *str2;	/* No, copy this character to a temp string. */
    }
  if (!strcmp(str1,tempStr))	/* Are the two resulting string equal? */
    return(1);			/* Yes, return 1 */
  else
    return(0);			/* No, return 0 */
}				/* end function WinNamesEqual */


WatchWindow(win,updateTime)
     Window	win;
     int	updateTime;
{
  Display		*dpy2;
  Window		copyWin;
  GC			gc;
  XWindowAttributes	copyWinInfo,newWinInfo;
  XSetWindowAttributes	copyWinAttrs;
  XWMHints		wmHints;
  XSizeHints		sizeHints;
  XImage		*image;
  struct timeval	currentTime;
  struct timezone	zone;
  long			timeInSecs;
  Bool			srcWinUnmapped;
  
  /* Get the window attributes of the window we're watching */
  XGetWindowAttributes(dpy,win,&copyWinInfo);
  /* Is the original window in a state to be watched?  */
  if (copyWinInfo.map_state != IsViewable)
    {  /* Nope, tell the user of the problem and exit. */
      printf("The window you wish to look at is not in a state to be viewed\n");
      printf("(perhaps it is iconified or not mapped)\n");
      exit(1);
    }
  /* Attempt to open a connection with the local X server */
  if ((dpy2 = XOpenDisplay(NULL)) == NULL)
    {
	  /* Couldn't open the display for some reason, so... */
      fprintf(stderr,"%s: Could not open Display.\n", Argv[0]);
      exit(1); /* ...report the error and exit with an error code */
    }
  /* Set a couple more attributes */
  copyWinAttrs.colormap = XDefaultColormap(dpy2,XDefaultScreen(dpy2));
  copyWinAttrs.bit_gravity = copyWinInfo.bit_gravity;
  /* Check for different depths b/w source & dest displays */
  if (copyWinInfo.depth != XDefaultDepth(dpy2,XDefaultScreen(dpy2)))
    {
      puts("Sorry, the original and copied windows contain incompatible");
      puts("information (i.e., they don't use the same number of bits per pixel.");
      exit(1);
    }
  /* Create a copy of the window we're watching */
  copyWin = XCreateWindow(dpy2,XDefaultRootWindow(dpy2),
			  copyWinInfo.x,copyWinInfo.y,
			  copyWinInfo.width,copyWinInfo.height,
			  copyWinInfo.border_width,
			  copyWinInfo.depth,
			  CopyFromParent,
			  XDefaultVisual(dpy2,XDefaultScreen(dpy2)),
			  (CWColormap|CWBitGravity),
			  &copyWinAttrs);
  /* Get size hints for window being watched */
  XGetNormalHints(dpy,win,&sizeHints);
  /* Set standard window properties for my window */
  XSetStandardProperties(dpy2,copyWin,"XWatchWin","XWatchWin",
			 None,Argv,Argc,&sizeHints);
  /* Get window mgr hints for the window being watched */
  XGetWMHints(dpy,win,&wmHints);
  /* Tell the X server about my window manager hints */
  XSetWMHints(dpy2,copyWin,&wmHints);
  gc = XDefaultGC(dpy2,0); /* Get a default graphics context */
  /* Only interested in exposures and button presses */
  XSelectInput(dpy2,copyWin,(ExposureMask|ButtonPressMask));
  /* Put the window up on the display */
  XMapWindow(dpy2,copyWin);
  XSelectInput(dpy,win, /* Only interested if the source window is... */
	       /* ...iconified or if it's mapped/unmapped */
	       (VisibilityChangeMask|StructureNotifyMask));
  /* Store an image of the original window in an XImage */
  image = XGetImage(dpy,(Drawable)win,
		    0,0,
		    copyWinInfo.width,copyWinInfo.height,
		    AllPlanes,XYPixmap);
  gettimeofday(&currentTime,&zone); /* Get the current time. */
  timeInSecs = currentTime.tv_sec; /* Save the current time in seconds */
  /* Set variable saying it's okay to watch the src window. */
  srcWinUnmapped = 0;
  while (1) /* Enter an infinite event loop */
    {
      XEvent	event;

      /* Check if the source window was turned into an... */
      if (XCheckWindowEvent(dpy,win,
			    (VisibilityChangeMask|StructureNotifyMask),
			    &event))
	{ /* ...icon or back into a window */
	  /* Get the window attributes of the window we're watching */
	  XGetWindowAttributes(dpy,win,&newWinInfo);
	  /* Is the original window in a state to be watched?  */
	  if (newWinInfo.map_state != IsViewable)
	    {
	      /* Let program know that the src window is unwatchable. */
	      srcWinUnmapped = 1;
	      printf("The window you are watching just became 'invisible'..\n");
	      printf("I will wait until it is 'visible' again...\n");
	      continue;
	    }
	  else
	    /* Let program know that src window is watchable again. */
	    srcWinUnmapped = 0;
	} /* end if(XCheckWindowEvent... */

      /* Look for window events, but don't sit around... */
      if (XCheckWindowEvent(dpy2,copyWin,
			    (ExposureMask|ButtonPressMask),
			    /* ...waiting for one (i.e., don't block) */
			    &event))
	switch (event.type)
	  {
	  case ButtonPress:
	    XDestroyImage(image);/* Free memory resources used by the image */
	    exit(0);		/* Get outtahere */
	    break;
	  case Expose:
	    /* Put the original window's image into the... */
	    XPutImage(dpy2,(Drawable)copyWin,
		      gc,image,0,0,0,0,	/* ...copy of the window. */
		      copyWinInfo.width,copyWinInfo.height);
	    break;
	  }				/* end switch (event.type) */
      gettimeofday(&currentTime,&zone);	/* Get the current time. */
      /* Have 'updateTime' seconds passed? */
      if (currentTime.tv_sec > (timeInSecs + updateTime))
	{ /* Yes, update the local copy of the window. */
	  if (srcWinUnmapped)/* Is the source window watchable? */
	    continue;	/* No, go through the event loop again. */
	  else	/* Yes, it's watchable, so get a new copy of the window. */
	    {
	      XDestroyImage(image);/* Free memory resources used by the image */
	      /* Store an image of the original window in an XImage */
	      image = XGetImage(dpy,(Drawable)win,
				0,0,
				copyWinInfo.width,copyWinInfo.height,
				AllPlanes,XYPixmap);
	    } /* end else XDestroyImage... */
	  /* Put the original window's image into the... */
	  XPutImage(dpy2,(Drawable)copyWin,
		    gc,image,0,0,0,0, /* ...copy of the window. */
		    copyWinInfo.width,copyWinInfo.height);
	  timeInSecs = currentTime.tv_sec;/* Update the current time */
	}				/* end if currentTime... */
    }					/* end while(1) */
}					/* end function WatchWindow */


/* Given the name of a window and the top of a ... */
/* ...window tree, this function will try to find... */
/* the Window ID corresponding to the window name... */
/* ...given as argument. */
Window GetWindowByName(window,windowName)
     Window	window;
     char	*windowName;
{
  Window	rootWin,parentWin,wID;
  Window	*childWinList;
  int		numChildren,i;
  char		*childWinName;

  /* Get information about windows that are children... */
  XQueryTree(dpy,window,
	     &rootWin,&parentWin,&childWinList,	/* ...of 'window'. */
	     &numChildren);
  for (i=0;i<numChildren;i++) /* Look at each child of 'window' */
    {
      /* Get the name of that window */
      XFetchName(dpy,childWinList[i],&childWinName);
      if (childWinName != NULL) /* Is there a name attached to this window? */
	{
	  /* Is this the window the user is looking for? */
	  if (WinNamesEqual(windowName,childWinName))
	    {
	      XFree(childWinList);/* Free up space taken by list of windows */
	      XFree(childWinName);/* Return space taken by this window's name */
	      /* Yes, return the Window ID of this window */
	      return(childWinList[i]);
	    }
	  XFree(childWinName);/* Return space taken by this window's name */
	}		/* end if childWinName... */
    }			/* end for i=0... */
  /* If this section of code is reached, then no match was found at this
   * level of the tree
   */
  for (i=0;i<numChildren;i++) /* Recurse on the children of this window */
    {
      wID = GetWindowByName(childWinList[i],windowName);
      if (wID)		/* Was a match found in this window's children? */
	{
	  XFree(childWinList); /* Free up space taken by list of windows */
	  return(wID);	/* Return the ID of the window that matched */
	}
    }			/* end for i=0... */
  /* If this section of code is reached, then no match was found below
   * this level of the tree
   */
  XFree(childWinList); /* Free up space taken by list of windows */
  return((Window)0); /* No match was found, return 0. */
}		/* end function GetWindowByName */
