/*
 * xthings.c
 *
 * most of this program stolen from xwud.c in the x11r1 distribution.
 * The code that actually installs the map is my own fault.
 * The structure of this program may show it's evolution; it certainly
 * doesn't show good coding style.
 *
 * usage:
 * xroot [-weld] [-i] [< windowdump]
 * 
 * 
 */
extern int errno;

#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <X11/XWDFile.h>
#include <stdio.h>
#include <sysexits.h>

char *calloc();
char *fore_color = "black";
char *back_color = "white";
unsigned long Resolve_Color();
int debug = 0;

Usage(basename)
     char *basename;
{
  printf("Usage: %s [-i] [-weld] [-norefresh] [-id windowid] [<xwindowdump]\n",
	 basename);
  exit(EX_USAGE);
}

#ifndef _swaplong
static void _swaplong (ptr, size)
    char *ptr;
    int size;
{
    char tmp;
    if (size % 4 != 0)
	abort ();
    while (size) {
	tmp = ptr[0];
	ptr[0] = ptr[3];
	ptr[3] = tmp;
	tmp = ptr[1];
	ptr[1] = ptr[2];
	ptr[2] = tmp;
	size -= 4, ptr += 4;
    }
}

static void _swapshort (ptr, size)
    char *ptr;
    int size;
{
    char tmp;
    if (size % 2 != 0)
	abort ();
    while (size) {
	tmp = ptr[0];
	ptr[0] = ptr[1];
	ptr[1] = tmp;
	size -= 2, ptr += 2;
    }
}
#endif

main(argc,argv)
     int argc;
     char **argv;
{
    /*
     * Snarf the pixmap with XGetImage.
     */
  XImage *image;
  unsigned int w, h;
  Pixmap pix;
  GC gc;
  XGCValues gc_init;
  long temp;
  Display *dpy;
  Window backwindow;
  int screen = 0;
  int reverse = 0;
  int weld=0;
  int norefresh=0;
  char *basename=argv[0];
   
  
  dpy = XOpenDisplay("");
  screen = DefaultScreen(dpy);
  backwindow = RootWindow(dpy, screen);
  
  while(++argv, --argc)
    {
      if (!strcmp(argv[0],"-i"))
	{
	  reverse++;
	}
      else if (!strcmp(argv[0],"-weld"))
	{
	  weld++;
	}
      else if (!strcmp(argv[0],"-norefresh"))
	{
	  norefresh++;
	}
      else if (!strcmp(argv[0],"-id"))
	{
	  argv++;
	  argc--;
	  if(!argc) Usage(basename);
	  backwindow = itoi(*argv);
	}
      else if (!strcmp(argv[0],"-d"))
	{
    	  debug++;
	}
      else
	{
	  Usage(basename);
	}
    }
  
  if(!weld)
    {
      read_the_pix(dpy,backwindow,reverse);
    }
  if(weld)
    {
      gc_init.foreground= Resolve_Color(dpy,backwindow,fore_color);
      gc_init.background= Resolve_Color(dpy,backwindow,back_color);
      if (reverse) {
	temp=gc_init.foreground;
	gc_init.foreground=gc_init.background;
	gc_init.background=temp;
      }

      gc = XCreateGC(dpy, backwindow, GCForeground|GCBackground,
		     &gc_init);

      pix = XCreatePixmap(dpy, backwindow,
			  w = DisplayWidth(dpy,screen),
			  h = DisplayHeight(dpy,screen),
			  DefaultDepth(dpy,screen));
  
      image = XGetImage ( dpy, backwindow, 0, 0,
			 w,h,
			 ~0, XYPixmap); 

      XPutImage(dpy, pix, gc, image, 0,0, 0,0, w,h);

      XSetWindowBackgroundPixmap(dpy, backwindow, pix);

      XFlush(dpy);
      
    }
  /* Some of the following stolen from the ``improved'' xrefresh... */
  if(!norefresh)
    {
      int screen=DefaultScreen(dpy);
      int mask = 0;
      XSetWindowAttributes xswa;
      Visual visual;
      Window win;
      
      xswa.background_pixmap = None;
      mask |= CWBackPixmap;
      xswa.override_redirect = True;
      mask |= CWOverrideRedirect;
      visual.visualid = CopyFromParent;
      win = XCreateWindow(dpy, backwindow,
			  0, 0,
			  DisplayWidth(dpy,screen), DisplayHeight(dpy,screen),
			  0, DefaultDepth(dpy, screen), InputOutput,
			  &visual, mask, &xswa);
      
      XLowerWindow(dpy, win);	/* so we only expose the root... */
      XMapWindow (dpy, win);
    }
  
  XCloseDisplay (dpy);
  exit(EX_OK);
}
unsigned long Resolve_Color(dpy, w, name)
	Display *dpy;
	Window w;
	char *name;
{
	XColor c;
	Colormap colormap;
	XWindowAttributes wind_info;

	/*
	 * The following is a hack to insure machines without a rgb table
	 * handle at least white & black right.
	 */
	if (!strcmp(name, "white"))
	  name="#ffffffffffff";
	if (!strcmp(name, "black"))
	  name="#000000000000";

	XGetWindowAttributes(dpy, w, &wind_info);
	colormap = wind_info.colormap;

	if (!XParseColor(dpy, colormap, name, &c))
	  printf("Bad color format '%s'.", name);

	if (!XAllocColor(dpy, colormap, &c))
	  printf("XAllocColor failed!");

	return(c.pixel);
}
#define STDIN 0

read_the_pix(dpy, destwin, inverse)
     Display *dpy;
     Window destwin;
     int inverse;
{	
  XVisualInfo vinfo, *vinfos;
  Visual *visual=NULL;
  unsigned buffer_size;
  register char *buffer;
  GC gc;
  int stat;
  Pixmap pix;
  int swaptest=1;
  XImage image;
  XWDFileHeader header;
  int win_name_size;
  char *win_name;
  XColor *colors;
  int ncolors;
  int i, j;
  int status;
  int screen=DefaultScreen(dpy);
  int win_depth;
  XGCValues gc_val;
  Colormap colormap;
  
  
  

  
  /*
   * Read in header information.
   */
  if(read(STDIN, (char *)&header, sizeof(header))<1) /* 0==eof, -1==error */
      Error("Unable to read dump file header.");

  if (*(char *) &swaptest)
    _swaplong((char *) &header, sizeof(header));

  /*
   * check to see if the dump file is in the proper format.
   */
  if (header.file_version != XWD_FILE_VERSION) {
    fprintf(stderr,"xwud: XWD file format version missmatch.");
    Error("exiting.");
  }
  if (header.header_size < sizeof(header)) {
    fprintf(stderr,"xwud: XWD header size is too small.");
    Error("exiting.");
  }

  /*
   * Calloc window name.
   */
  win_name_size = (header.header_size - sizeof(header));
  if((win_name = calloc((unsigned) win_name_size, sizeof(char))) == NULL)
    Error("Can't calloc window name storage.");

  /*
   * Read in window name.
   */
  if(read(STDIN, win_name, sizeof(char)*win_name_size) !=
     win_name_size*sizeof(char))
    Error("Unable to read window name from dump file.");

  image.width = (int) header.pixmap_width;
  image.height = (int) header.pixmap_height;
  image.xoffset = (int) header.xoffset;
  image.format = (int) header.pixmap_format;
  image.byte_order = (int) header.byte_order;
  image.bitmap_unit = (int) header.bitmap_unit;
  image.bitmap_bit_order = (int) header.bitmap_bit_order;
  image.bitmap_pad = (int) header.bitmap_pad;
  image.depth = (int) header.pixmap_depth;
  image.bits_per_pixel = (int) header.bits_per_pixel;
  image.bytes_per_line = (int) header.bytes_per_line;
  image.red_mask = header.red_mask;
  image.green_mask = header.green_mask;
  image.blue_mask = header.blue_mask;
  image.obdata = NULL;
  _XInitImageFuncPtrs(&image);


  /* Calloc the color map buffer.
   * Read it in, copy it and use the copy to query for the
   * existing colors at those pixel values.
   */
  if(ncolors = header.ncolors) {
    colors = (XColor *)calloc(ncolors,sizeof(XColor));
    if(read(STDIN, (char *) colors, sizeof(XColor)*ncolors) !=
       sizeof(XColor)*ncolors)
      Error("Unable to read color map from dump file.");
    if(debug)
      fprintf(stderr,"Read %d colors\n", ncolors);
    if (*(char *) &swaptest) {
      for (i = 0; i < ncolors; i++) {
	_swaplong((char *) &colors[i].pixel, sizeof(long));
	_swapshort((char *) &colors[i].red, 3 * sizeof(short));
      }
    }
  }

  /*
   * Calloc the pixel buffer.
   */
  buffer_size = Image_Size(&image);
  if (debug)
      printf ("data buffer: calloc (%d, 1)\n", buffer_size);
  if((buffer = calloc(buffer_size, 1)) == NULL)
    Error("Can't calloc data buffer.");
  image.data = buffer;

  /*
   * Read in the pixmap buffer.
   */
  {
    int datasize= sizeof(char)*(int)buffer_size;
    char *bufptr = buffer;

    while((status = read(STDIN, bufptr, datasize)) != datasize)
      {
	if (status>0)
	  {
	    bufptr += status;
	    datasize -= status;
	  }
	else
	  {
	    break;
	  }
      }
  }
  vinfo.screen = screen;
  vinfo.depth = (int) header.pixmap_depth;
  vinfo.class = (int) header.visual_class;
  vinfo.red_mask = header.red_mask;
  vinfo.green_mask = header.green_mask;
  vinfo.blue_mask = header.blue_mask;
  vinfo.colormap_size = (int) header.colormap_entries;
  vinfo.bits_per_rgb = (int) header.bits_per_rgb;

  vinfos = (XVisualInfo *)
    XGetVisualInfo(dpy,
		   /* XXX ignoring rgb mask differences */
		   VisualScreenMask|VisualDepthMask|VisualClassMask|
		   VisualColormapSizeMask|VisualBitsPerRGBMask,
		   &vinfo,
		   &j);
  if (j > 0) {
    visual = vinfos[0].visual;
    win_depth = vinfo.depth;
  } else if (header.pixmap_depth == 1) {
    visual = DefaultVisual(dpy, screen);
    win_depth = DefaultDepth(dpy, screen);
    image.format = XYBitmap;
  } else {
    fprintf(stderr, "xwud: could not find matching visual.\n");
    Error("exiting.");
  }
    
  /* XXX */
  if (visual == DefaultVisual(dpy, screen))
    colormap = DefaultColormap(dpy, screen);
  /* XXX */
#ifdef notdef
  colormap = ModifyColors(image, visual, colormap, colors, ncolors);
#endif
  else {
    colormap = XCreateColormap(dpy, destwin, visual,
			       visual->class & 1);
    if (visual->class & 1)
      XStoreColors(dpy, colormap, colors, ncolors);
    /* XXX colors may not be accurate for static maps */
  }
  
  
  if (colormap != DefaultColormap(dpy, screen))
    XInstallColormap(dpy, colormap); /* XXX */

  /*
   * Create the image window.
   */
  
  pix = XCreatePixmap(dpy, destwin,
		      header.pixmap_width, header.pixmap_height,
		      DefaultDepth(dpy,screen));


  if (!pix) Error("Can't create image window.");

  /*
   * Select mouse ButtonPressed on the window, this is how we determine
   * when to stop displaying the window.
   */
  if (inverse) {
    gc_val.foreground = (unsigned long) WhitePixel (dpy, screen); 
    gc_val.background = (unsigned long) BlackPixel (dpy, screen); 
  } else {
    gc_val.foreground = (unsigned long) BlackPixel (dpy, screen);
    gc_val.background = (unsigned long) WhitePixel (dpy, screen); 
  }
  gc = XCreateGC (dpy, pix, GCForeground|GCBackground, &gc_val);

  XPutImage(dpy, pix, gc, &image,
	    header.window_x, header.window_y,
	    0,0,
	    header.pixmap_width, header.pixmap_height);
  

  stat = XSetWindowBackgroundPixmap(dpy, destwin, pix);

  XFlush(dpy);
}

int Image_Size(image)
     XImage *image;
{
    if (image->format != ZPixmap)
      return(image->bytes_per_line * image->height * image->depth);

    return(image->bytes_per_line * image->height);

}

/*
 * Error - Fatal xwud error.
 */
Error(string)
	char *string;	/* Error description string. */
{
	fprintf(stderr, "xwud: Error => %s\n", string);

	if (errno != 0) {
		perror("xwud");
		fprintf(stderr, "\n");
	}

	exit(1);
}

