[comp.sources.x] v03i069: xwatchwin2 -- improved version of xwatchwin, Part01/01

argv@island.uu.net (Dan Heller) (04/08/89)

Submitted-by: John Bradley <bradley@cis.upenn.edu>
Posting-number: Volume 3, Issue 69
Archive-name: xwatchwin2/part01

[ Besure to read the README to find out why this is a whole new posting.
  I included the Imakefile, but not the Makefile -- you can use the other
  one or make a new one.  On my sun3/60, I just issued the command:
	cc -O -s xwatchwin.c -lX11 -o xwatchwin
  Try "xwatchwin <host> root"  Someone should modify to typing can go
  both ways.  --argv ]

#!/bin/sh
# This is a shell archive file. Remove everything above this line
# to unbundle. chmod +x "thisfile", then run it: e.g. % thisfile
# SHAR archive format.  Archive created Fri Apr 7 11:29:46 PDT 1989
# file contains: 
#	README
#	xwatchwin.man
#	xwatchwin.c
#	Imakefile
echo x - README
sed 's/^X//' > README <<'+END+OF+README'
X
XI've taken the 'xwatchwin' code that was posted on comp.sources.x a few days
Xago, and added two new features to it.  The major feature is you can now 
Xrun the program and have it work out even when the two displays (yours, and
Xthe one you're monitoring) are of different depths.  It should work for any
Xcombinations of depths, and it has special case code for displaying a 1-bit
Xwindow on an 8-bit display, and displaying an 8-bit window on a 1-bit display.
XIt does this reasonably quickly, however, I can't guarantee that the special
Xcase code'll work on every server.  I've tried it out on all combinations of
X8-bit RTs, 8-bit Suns, and 1-bit Suns.
X
XThe second win is that you can now specify the root window by name.  (Either 
X'root' or 'X Root Window').  
X
XJohn Bradley  -  University of Pennsylvania  -  bradley@cis.upenn.edu
X
XP.S.  I was going to submit a patch, but the patch wound up being larger than
Xthe actual source code, so I'm just sending the whole thing again.
X
+END+OF+README
echo '-rw-rw-r--  1 argv          979 Apr  6 00:15 README    (as sent)'
chmod u=rw,g=rw,o=r README
ls -l README
echo x - xwatchwin.man
sed 's/^X//' > xwatchwin.man <<'+END+OF+xwatchwin.man'
X.TH xwatchwin 1 "17 March 1989" "X Version 11"
X.SH NAME
Xxwatchwin - watch a window on another X server
X.SH SYNOPSIS
X.B "xwatchwin"
Xhostname
X[\-u \fIupdatetime\fP] [\-w \fIwindowID\fP] [window name]
X.SH DESCRIPTION
X.PP
X\fIxwatchwin\fP allows you to peek at a window on another X server.
XTo use it, you must specify the name of the machine you want to watch, then
Xthe name of the window on that machine.  \fIXwatchwin\fP will attempt to
Xconnect with the X server \fIhostname\fP:0.0, and if successful, will try
Xto retrieve a copy of the window in which you specified interest.
X
XYou may specify the window you want to watch either by name or by its
Xwindow id, usually a hexidecimal number.  Usually specifying the
Xwindow by name is simpler, although not all windows have names
Xassociated with them; in that case you must use the window id option.
X
XIf the window you want to watch is not in a viewable state,
X\fIxwatchwin\fP will tell you so and exit.  If while you are watching a
Xwindow it becomes 'unviewable', \fIxwatchwin\fP will print a message to
Xstdout and wait until the window becomes 'viewable' again.
X
X\fIxwatchwin\fP was written as an aid to a class for people learning to
Xuse X.  The idea is that the instructor would type into an xterm
Xwindow on his/her display and the students would use \fIxwatchwin\fP to
Xsee what the instructor typed.  The students could then type the same
Xthing in their own terminal windows.  I hope that others will find
Xequally (if not more) constructive uses.
X
X.SH OPTIONS
X.TP 8
X.B \-u \fIupdatetime\fP
XThis option specifies how often (in seconds) you want to get a new copy
Xof the window you're watching.  It is in effect a 'sample rate'.  By default,
X\fIxwatchwin\fP updates your copy of the window as often as it can.  The time
Xit takes to actually do the update is dependent on the speed of the X server
Xon both machines, the speed of the intervening network, and other factors.
X.TP 8
X.B \-w \fIwindowID\fP
XThis option specifies the window you want to watch by number, for example,
X"0x50000b".  Use the xlswins(1) command to get a list of window id's and
Xpossibly their names on the remote server.  
X
XYou must specify a window to watch either by name or by id.  Specifying
Xa window to watch by name is usually easier if you know what you're looking for.
X.SH EXAMPLES
XIf there is an X server on the remote machine "crow" and if on that server
Xthere is a window called "X Terminal Emulator", you can watch that window
Xby typing
X
Xxwatchwin crow X Terminal Emulator
X
XIf there is a window on "crow" that has no name but has a window id of
X"0x50000b", you can watch it by typing
X
Xxwatchwin crow -w 0x50000b
X
XIf you want to get new copies of a window only every 30 seconds, you can
Xdo so by typing
X
Xxwatchwin crow -u 30 -w 0x50000b
X.SH "SEE ALSO"
Xxlswins(1), xwininfo(1), xdpyinfo(1),
X.SH BUGS
X\fIxwatchwin\fP doesn't support the \-display option.  You must set the
Xdisplay on which the \fIxwatchwin\fP window is created by changing your
XDISPLAY environment variable.
X
XIf the window you're watching is unmapped (made 'invisible')
Xwhile \fIxwatchwin\fP
Xis getting a new copy of that window, the program will crash.  The smaller
Xyour update interval, the more likely you are to experience this bug (although
Xit hasn't happened all that often to me).
X
XParsing arguments is messy and not as forgiving as it should be.
X
XThe event loop is a mess.
X
X\fIxwatchwin\fP can now deal with two displays of different depths.  There
Xis special-case code for the conversions between 1-bit displays and 8-bit
Xdisplays (either direction) which may garble the image on some machines.
XThe general case code should work on anything, albeit somewhat more slowly.
XOne note:  ABSOLUTELY no attempt is made to make the colors match up.  If
Xyou're on a 5-bit display, and you're monitoring someone elses 8-bit display,
Xthe conversion just takes his 8 bits and chops the top 3 bits off, and puts
Xit on the screen.  Maybe in the next version...
X.SH COPYRIGHT
XCopyright 1989, George D. Drapeau
X.SH AUTHOR
XGeorge D. Drapeau
X.PP
XStanford University
X.br
XAcademic Information Resources / Systems Development
X.br
XInternet: drapeau@jessica.stanford.edu
X.br
XUUCP: labrea!drapeau@jessica
X.PP
XDisplay depth conversion code added by John Bradley  (bradley@cis.upenn.edu)
+END+OF+xwatchwin.man
echo '-rw-rw-r--  1 argv         4264 Apr  7 10:51 xwatchwin.man    (as sent)'
chmod u=rw,g=rw,o=r xwatchwin.man
ls -l xwatchwin.man
echo x - xwatchwin.c
sed 's/^X//' > xwatchwin.c <<'+END+OF+xwatchwin.c'
X#include <stdio.h>
X#include <ctype.h>
X#include <sys/time.h>
X#include <X11/Xlib.h>
X#include <X11/Xutil.h>
X
X#define STRINGLENGTH	256
X#define SLEEPTIME	0    /* Sleep for this many seconds before redrawing */
X
Xint     dpy2depth;
X
Xtypedef unsigned char byte;
X
Xextern byte *malloc();
X
Xchar	**Argv;
Xint	Argc;
XDisplay	*dpy;
X
X
Xmain(argc,argv)
X     int	argc;
X     char	**argv;
X{
X  Window	watchWin,GetWindowByName();
X  int		i,strPos,optIndex;
X  char		displayName[64],xWatchName[STRINGLENGTH];
X  char		*optstring;
X  extern char	*optarg;
X  extern int	optind,opterr;
X  int		windowID,windowIDSet,updateTime,updateTimeSet;
X  
X
X  if (argc < 3)			    /* Did user enter enough arguments? */
X    {				    /* Nope, print an error message and exit */
X    printf("Usage:\t%s HostName [-u UpdateTime] [-w windowID] [WindowName]\n",
X	   argv[0]);
X    exit(1);
X    }
X
X  /* Initialize var to update every SLEEPTIME seconds. */
X  updateTime = SLEEPTIME;
X  updateTimeSet = 0;
X  windowIDSet = 0;
X  optind = 2;	    /* Skip over first, obligatory argument (hostname) */
X
X  while ((optIndex = getopt(argc,argv,"u:w:")) != -1)   /* get arguments */
X    switch (optIndex)
X      {
X      case 'u':		/* User wants to update every 'optarg' seconds. */
X	updateTime = atoi(optarg);
X	updateTimeSet = 1;    /* Set flag saying user specified this option. */
X	break;
X      case 'w':   /* User wants to specify window by id instead of by name */
X	sscanf(&optarg[2],"%lx",&windowID);
X	windowIDSet = 1;    /* Set flag saying user specified this option. */
X	break;
X      }			    /* end switch (optIndex) */
X
X  Argv = argv;
X  Argc = argc;
X
X  /* if no ':' in display name, tack default ':0.0' onto end */
X  if (index(argv[1],':')==NULL) sprintf(displayName,"%s:0.0",argv[1]);
X                           else strcpy(displayName,argv[1]);
X
X  if (!windowIDSet)	   /* Did user specify window to watch by number? */
X    for (i=2, bzero(xWatchName, STRINGLENGTH); i < argc; i++)
X    /* No, parse rest of arguments as window name to watch */
X      {
X	if (!strcmp(argv[i],"-u"))	   /* Don't parse optional arguments */
X	  {
X	    i++;		   /* Skip over argument to '-u' switch. */
X	    continue;
X	  }
X	/* Get current length of xWatchName string */
X	strPos = strlen(xWatchName);
X	/* Copy another argument to the end of the string */
X	strcpy(&xWatchName[strPos],argv[i]);
X      }
X
X  /* Attempt to open a connection with the remote X server */
X  if ((dpy = XOpenDisplay(displayName)) == NULL)
X    {
X      /* Couldn't open the display for some reason, so... */
X      fprintf(stderr,"%s: Could not open remote display %s\n", 
X	      argv[0],displayName);
X      exit(1);	    /* ...report the error and exit with an error code */
X    }
X
X  if (!windowIDSet)	    /* Did user specify a window to watch by id? */
X    /* No, get window id from window name. */
X    watchWin = GetWindowByName(XDefaultRootWindow(dpy),xWatchName);
X  else watchWin = windowID; /* Yes, use user-specified window id. */
X
X  if (watchWin)	   /* Did the user find the window s/he was looking for? */
X    /* Yes, periodically show the contents of that window  */
X    WatchWindow(watchWin,updateTime);
X  else	/* No, report that the window was not found, and exit. */
X    {
X      printf("Could not find the window you specified.\n");
X      exit(1);
X    }
X  
X}	/* end function main */
X
X
X
X
X/* Takes two strings, removes spaces from the second,... */
X/* ...and compares them..  Returns 1 if equal, 0 if not. */
XWinNamesEqual(str1,str2)
X     char	*str1,*str2;
X{
X  char	tempStr[STRINGLENGTH],*tempStrPtr;
X  int	index;
X  
X  bzero(tempStr,STRINGLENGTH);	/* Clear the contents of the string, if any */
X  /* Go through each character in the second string. */
X  for (tempStrPtr=tempStr; *str2; str2++)
X    {
X      if (!isspace(*str2))    /* Is this character a space?  */
X	*tempStrPtr++ = *str2;	/* No, copy this character to a temp string. */
X    }
X  if (!strcmp(str1,tempStr))	/* Are the two resulting string equal? */
X    return(1);			/* Yes, return 1 */
X  else
X    return(0);			/* No, return 0 */
X}				/* end function WinNamesEqual */
X
X
XWatchWindow(win,updateTime)
X     Window	win;
X     int	updateTime;
X{
X  Display		*dpy2;
X  Window		copyWin;
X  GC			gc;
X  XWindowAttributes	copyWinInfo,newWinInfo;
X  XSetWindowAttributes	copyWinAttrs;
X  XWMHints		wmHints;
X  XSizeHints		sizeHints;
X  XImage		*image;
X  struct timeval	currentTime;
X  struct timezone	zone;
X  long			timeInSecs;
X  Bool			srcWinUnmapped;
X  int                   imageform;
X  
X  /* Get the window attributes of the window we're watching */
X  XGetWindowAttributes(dpy,win,&copyWinInfo);
X
X  /* Is the original window in a state to be watched?  */
X  if (copyWinInfo.map_state != IsViewable)
X    {  /* Nope, tell the user of the problem and exit. */
X    printf("The window you wish to look at is not in a state to be viewed\n");
X    printf("(perhaps it is iconified or not mapped)\n");
X    exit(1);
X    }
X
X  /* Attempt to open a connection with the local X server */
X  if ((dpy2 = XOpenDisplay(NULL)) == NULL)
X    {
X    /* Couldn't open the display for some reason, so... */
X    fprintf(stderr,"%s: Could not open local display.\n", Argv[0]);
X    exit(1); /* ...report the error and exit with an error code */
X    }
X
X  /* Set a couple more attributes */
X  copyWinAttrs.colormap = XDefaultColormap(dpy2,XDefaultScreen(dpy2));
X  copyWinAttrs.bit_gravity = copyWinInfo.bit_gravity;
X
X  /* Check for different depths b/w source & dest displays */
X  dpy2depth = XDefaultDepth(dpy2,XDefaultScreen(dpy2));
X
X  if ((copyWinInfo.depth == dpy2depth) || 
X      (copyWinInfo.depth == 1 && dpy2depth == 8) ||
X      (copyWinInfo.depth == 8 && dpy2depth == 1) ) imageform = ZPixmap;
X  else imageform = XYPixmap;
X
X  /* Create a copy of the window we're watching */
X  copyWin = XCreateWindow(dpy2,XDefaultRootWindow(dpy2),
X			  copyWinInfo.x,copyWinInfo.y,
X			  copyWinInfo.width,copyWinInfo.height,
X			  copyWinInfo.border_width,
X			  dpy2depth,
X			  CopyFromParent,
X			  XDefaultVisual(dpy2,XDefaultScreen(dpy2)),
X			  (CWColormap|CWBitGravity),
X			  &copyWinAttrs);
X
X  /* Get size hints for window being watched */
X  XGetNormalHints(dpy,win,&sizeHints);
X
X  /* Set standard window properties for my window */
X  XSetStandardProperties(dpy2,copyWin,"XWatchWin","XWatchWin",
X			 None,Argv,Argc,&sizeHints);
X
X  /* Get window mgr hints for the window being watched */
X  XGetWMHints(dpy,win,&wmHints);
X
X  /* Tell the X server about my window manager hints */
X  XSetWMHints(dpy2,copyWin,&wmHints);
X  gc = XDefaultGC(dpy2,0); /* Get a default graphics context */
X
X  /* Only interested in exposures and button presses */
X  XSelectInput(dpy2,copyWin,(ExposureMask|ButtonPressMask));
X
X  /* Put the window up on the display */
X  XMapWindow(dpy2,copyWin);
X  XSelectInput(dpy,win, /* Only interested if the source window is... */
X	       /* ...iconified or if it's mapped/unmapped */
X	       (VisibilityChangeMask|StructureNotifyMask));
X
X  /* Store an image of the original window in an XImage */
X  image = XGetImage(dpy, win, 0,0, copyWinInfo.width, copyWinInfo.height,
X		    AllPlanes, imageform);
X  ConvertImage(image);
X
X  gettimeofday(&currentTime,&zone); /* Get the current time. */
X  timeInSecs = currentTime.tv_sec; /* Save the current time in seconds */
X
X  /* Set variable saying it's okay to watch the src window. */
X  srcWinUnmapped = 0;
X  while (1) /* Enter an infinite event loop */
X    {
X      XEvent	event;
X
X      /* Check if the source window was turned into an... */
X      /* ...icon or back into a window */
X
X      if (XCheckWindowEvent(dpy,win,
X			    (VisibilityChangeMask|StructureNotifyMask),
X			    &event))
X	{
X	  /* Get the window attributes of the window we're watching */
X	  XGetWindowAttributes(dpy,win,&newWinInfo);
X
X	  /* Is the original window in a state to be watched?  */
X	  if (newWinInfo.map_state != IsViewable)
X	    {
X	      /* Let program know that the src window is unwatchable. */
X	      srcWinUnmapped = 1;
X	      printf("The window you are watching just became 'invisible'.\n");
X	      printf("I will wait until it is 'visible' again...\n");
X	      continue;
X	    }
X	  else
X	    /* Let program know that src window is watchable again. */
X	    srcWinUnmapped = 0;
X	} /* end if(XCheckWindowEvent... */
X
X      /* Look for window events, but don't sit around... */
X      if (XCheckWindowEvent(dpy2,copyWin,
X			    (ExposureMask|ButtonPressMask),
X			    /* ...waiting for one (i.e., don't block) */
X			    &event))
X	switch (event.type)
X	  {
X	  case ButtonPress:
X	    XDestroyImage(image);/* Free memory resources used by the image */
X	    exit(0);		/* Get outtahere */
X	    break;
X	  case Expose:
X	    /* Put the original window's image into the copy of the window */
X            XPutImage(dpy2, copyWin, gc, image, 0,0,0,0,
X		      copyWinInfo.width, copyWinInfo.height);
X
X	    break;
X	  }				/* end switch (event.type) */
X
X      gettimeofday(&currentTime,&zone);	/* Get the current time. */
X
X      /* Have 'updateTime' seconds passed? */
X      if (currentTime.tv_sec > (timeInSecs + updateTime))
X	{ /* Yes, update the local copy of the window. */
X
X	  if (srcWinUnmapped) /* Is the source window watchable? */
X	    continue;	/* No, go through the event loop again. */
X	  else	/* Yes, it's watchable, so get a new copy of the window. */
X	    {
X	      XDestroyImage(image); /* Free memory used by the image */
X	      /* Store an image of the original window in an XImage */
X	      image = XGetImage(dpy,(Drawable)win,
X				0,0,
X				copyWinInfo.width,copyWinInfo.height,
X				AllPlanes,imageform);
X	      ConvertImage(image);
X	    } /* end else XDestroyImage... */
X
X	  /* Put the original window's image into the copy of the window */
X	  XPutImage(dpy2, copyWin, gc, image, 0,0,0,0,
X		    copyWinInfo.width, copyWinInfo.height);
X
X	  timeInSecs = currentTime.tv_sec;  /* Update the current time */
X	}				/* end if currentTime... */
X    }					/* end while(1) */
X}					/* end function WatchWindow */
X
X
X/* Given the name of a window and the top of a ... */
X/* ...window tree, this function will try to find... */
X/* the Window ID corresponding to the window name... */
X/* ...given as argument. */
XWindow GetWindowByName(window,windowName)
X     Window	window;
X     char	*windowName;
X{
X  Window	rootWin,parentWin,wID;
X  Window	*childWinList;
X  int		numChildren,i;
X  char		*childWinName;
X
X  if (strcmp(windowName,"root")==0 || strcmp(windowName,"XRootWindow")==0) 
X    return XDefaultRootWindow(dpy);
X
X  /* Get information about windows that are children... */
X  XQueryTree(dpy,window,
X	     &rootWin,&parentWin,&childWinList,	/* ...of 'window'. */
X	     &numChildren);
X  for (i=0;i<numChildren;i++) /* Look at each child of 'window' */
X    {
X      /* Get the name of that window */
X      XFetchName(dpy,childWinList[i],&childWinName);
X      if (childWinName != NULL) /* Is there a name attached to this window? */
X	{
X	  /* Is this the window the user is looking for? */
X	  if (WinNamesEqual(windowName,childWinName))
X	    {
X	      XFree(childWinList);/* Free up space taken by list of windows */
X	      XFree(childWinName);/* Return space taken by this window's name */
X	      /* Yes, return the Window ID of this window */
X	      return(childWinList[i]);
X	    }
X	  XFree(childWinName);/* Return space taken by this window's name */
X	}		/* end if childWinName... */
X    }			/* end for i=0... */
X  /* If this section of code is reached, then no match was found at this
X   * level of the tree
X   */
X  for (i=0;i<numChildren;i++) /* Recurse on the children of this window */
X    {
X      wID = GetWindowByName(childWinList[i],windowName);
X      if (wID)		/* Was a match found in this window's children? */
X	{
X	  XFree(childWinList); /* Free up space taken by list of windows */
X	  return(wID);	/* Return the ID of the window that matched */
X	}
X    }			/* end for i=0... */
X  /* If this section of code is reached, then no match was found below
X   * this level of the tree
X   */
X  XFree(childWinList); /* Free up space taken by list of windows */
X  return((Window)0); /* No match was found, return 0. */
X}		/* end function GetWindowByName */
X
X
X
X
X
X
XConvertImage(image)
XXImage *image;
X{
X  int i,j;
X  static byte bit[8] = {0x80,0x40,0x20,0x10,0x08,0x04,0x02,0x01};
X
X  /* conversion between images of different depths.  if the two depths
X     are the same, nothing is done.  If the two depths are '1' and '8'
X     (either way), uses special case code and ZPixmaps.  Otherwise, uses
X     XYPixmap and general code below */
X
X
X  /* note:  on every color/greyscale server I've had the pleasure to deal
X     with (IBM RT Megapel and Sun cgfour), XGetImage(XYPixmap) returns an
X     image in which the planes are (in my opinion) reversed.  That is, plane 0
X     (the first plane in the image) corresponds to the highest order bit
X     plane returned by the server.
X
X     So, in order to correctly display the image when the two depths are
X     different, I have to move the planes around.  Examples:  when displaying
X     a 1-bit image on an 8-bit display, I convert the image by making up
X     an 8-bit image, and copying the 1-bit plane to the 8th plane in the 
X     8-bit image, rather than to the 1st plane.
X
X     Likewise, when displaying an 8-bit image on a 1-bit display, I make
X     up a 1-bit image, and copy the 8th plane to the (only) plane in the
X     1-bit image.
X
X     In theory, if I was displaying a 4 bit image on an 8-bit display, I'd
X     copy planes 0-3 of the 4-bit image to planes 4-7, respectively, in the
X     8-bit image.
X
X     The code does this, though I haven't checked to see if it does it
X     correctly, or even if this is the right thing to do.
X
X     --jhb */
X
X  if (dpy2depth == image->depth) return;
X
X  else if (dpy2depth == 8 || image->depth == 1) {   /* expand ZPixmap 1->8 */
X    byte *iptr, *optr, *ilptr, *olptr, *tmp;
X    int   obperlin,bit;
X
X    obperlin = ((image->width*8 + image->bitmap_pad - 1) 
X		/ image->bitmap_pad) * (image->bitmap_pad / 8);
X
X    ilptr = (byte *) image->data;
X    olptr = tmp = malloc(image->height * obperlin);
X    if (!olptr) { fprintf(stderr,"couldn't allocate image\n"); exit(1); }
X
X    for (i=0; i<image->height; i++) {
X      iptr = ilptr;  optr = olptr;
X      for (j=bit=0; j<image->width; j++) {
X	*optr++ =  (*iptr&0x80) ? 1 : 0;
X	*iptr <<= 1;
X	if (!(++bit&7)) iptr++;
X      }
X      ilptr += image->bytes_per_line;  olptr += obperlin;
X    }
X
X    free(image->data);
X    image->data = (char *) tmp;
X    image->bytes_per_line = obperlin;
X    image->depth = image->bits_per_pixel = dpy2depth;
X  }
X    
X
X  else if (dpy2depth == 1 || image->depth == 8) {   /* compress ZPixmap 8->1 */
X    byte *iptr, *optr, *ilptr, *olptr, *tmp;
X    int   obperlin,bit;
X
X    obperlin = ((image->width + image->bitmap_pad - 1) 
X		/ image->bitmap_pad) * (image->bitmap_pad / 8);
X
X    ilptr = (byte *) image->data;
X    olptr = tmp = malloc(image->height * obperlin);
X    if (!olptr) { fprintf(stderr,"couldn't allocate image\n"); exit(1); }
X
X    for (i=0; i<image->height; i++) {
X      iptr = ilptr;  optr = olptr;
X      for (j=bit=0; j<image->width; j++) {
X	*optr = (*optr<<1) | (*iptr++ & 0x01);
X	if (!(++bit&7)) optr++;
X      }
X      ilptr += image->bytes_per_line;  olptr += obperlin;
X    }
X
X    free(image->data);
X    image->data = (char *) tmp;
X    image->bytes_per_line = obperlin;
X    image->depth = image->bits_per_pixel = dpy2depth;
X  }
X    
X
X  else if (dpy2depth > image->depth) {    /* expand XYPixmap */
X    byte *tmp;
X    long planelen = image->height * image->bytes_per_line;
X    tmp = malloc(planelen * dpy2depth);
X    if (!tmp) { fprintf(stderr,"couldn't allocate image\n"); exit(1); }
X    bzero(tmp,planelen * dpy2depth);
X
X    bcopy(image->data, tmp + (dpy2depth - image->depth)*planelen,
X	  image->depth * planelen);
X
X    free(image->data);
X    image->data = (char *) tmp;
X    image->depth = dpy2depth;
X  }
X
X  else if (dpy2depth < image->depth) {   /* compress XYPixmap */
X    byte *tmp;
X    long planelen = image->height * image->bytes_per_line;
X    tmp = malloc(planelen * dpy2depth);
X    if (!tmp) { fprintf(stderr,"couldn't allocate image\n"); exit(1); }
X
X    bcopy(image->data + (image->depth - dpy2depth)*planelen, tmp,
X	  dpy2depth * planelen);
X
X    free(image->data);
X    image->data = (char *) tmp;
X    image->depth = dpy2depth;
X  }
X}
X
+END+OF+xwatchwin.c
echo '-rw-rw-r--  1 argv        16138 Apr  7 10:51 xwatchwin.c    (as sent)'
chmod u=rw,g=rw,o=r xwatchwin.c
ls -l xwatchwin.c
echo x - Imakefile
sed 's/^X//' > Imakefile <<'+END+OF+Imakefile'
X#ifdef SunSharedLibs
XSYS_LIBRARIES = -lX11
X#else
XLOCAL_LIBRARIES = $(XLIB)
X#endif
XSimpleProgramTarget(xwatchwin)
+END+OF+Imakefile
echo '-rw-rw-r--  1 argv          113 Apr  6 00:15 Imakefile    (as sent)'
chmod u=rw,g=rw,o=r Imakefile
ls -l Imakefile
exit 0