/* root.c:
 *
 * this loads an image onto the root window.  changes to allow proper
 * freeing of previously allocated resources made by Deron Dann Johnson
 * (dj@eng.sun.com).
 *
 * jim frost 10.03.89
 *
 * Copyright 1989, 1990, 1991 Jim Frost.
 * See included file "copyright.h" for complete copyright information.
 */

#include "copyright.h"
#include "xloadimage.h"
#include <X11/Xatom.h>

#define RETAIN_PROP_NAME	"_XSETROOT_ID"

/* Sets the close-down mode of the client to 'RetainPermanent'
 * so all client resources will be preserved after the client
 * exits.  Puts a property on the default root window containing
 * an XID of the client so that the resources can later be killed.
 */

static void
preserveResource(dpy, w)
     Display	*dpy;
     Window	w;
{
  /* create dummy resource */
  Pixmap pm= XCreatePixmap(dpy, w, 1, 1, 1);
  unsigned char *data = (unsigned char *) &pm;
	
  /* intern the property name */
  char *name = RETAIN_PROP_NAME;
  Atom atom = XInternAtom(dpy, name, 0);

  /* create or replace the property */
  XChangeProperty(dpy, w, atom, XA_PIXMAP, 32, PropModeReplace,
                 data, sizeof(Pixmap)/4);
	
  /* retain all client resources until explicitly killed */
  XSetCloseDownMode(dpy, RetainPermanent);
}


/* Flushes any resources previously retained by the client,
 * if any exist.
 */

static void
freePrevious(dpy, w, verbose)
     Display	*dpy;
     Window	w;
     unsigned int  verbose;
{
  Pixmap *pm;
  unsigned char *charpm;
  Atom actual_type;
  int	format;
  unsigned long        nitems;
  unsigned long        bytes_after;
  int   returncode;

  /* intern the property name */
  Atom atom = XInternAtom(dpy, RETAIN_PROP_NAME, 0);

  /* look for existing resource allocation */
  nitems = sizeof(Pixmap)/4;
  returncode = XGetWindowProperty(dpy, w, atom,
                 0, nitems, 1/*delete*/,
                XA_PIXMAP, &actual_type,
                &format, &nitems,
                &bytes_after, &charpm);
  if (returncode != Success) {
    if (verbose)
      fprintf(stderr, "failed to look for %s with return code %i.\n",
              RETAIN_PROP_NAME, returncode);
    return;
  }

  /* Check if the property was found */
  if (actual_type == None) {
    if (verbose)
      fprintf(stderr, "didn't find evidence of prior run.\n");
    return;
  }

  /* Make sure the dummy value is still present */
  if (actual_type != XA_PIXMAP) {
    if (verbose)
      fprintf(stderr, "found wrong data type - skipped.\n");
    return;
  }

  /* Check size, in case we're a different architecture */
  if ((nitems != sizeof(Pixmap)/4) ||
      (format != 32) ||
      (bytes_after != 0)) {
    if (verbose)
      fprintf(stderr, "saw wrong %li / word size %i / architecture %li.\n",
              bytes_after, format, nitems);
    return;
  }

  /* blast it away */
  pm = (Pixmap*) charpm;
  XKillClient(dpy, (XID) *pm);
  XFree(charpm);
}

#if FIND_DEC_ROOTWINDOW
/* this returns the root window for DECWindows servers.  this is right
 * out of xsetroot with minor formatting changes.
 */

static Window getWmRootWindow(dpy, root)
     Display *dpy;
     Window root; 
{
  Window parent;
  Window *child;
  unsigned int nchildren;
  XWindowAttributes rootatt, childatt;
  if (!XGetWindowAttributes (dpy, root, &rootatt))
    return(0);

  if (XQueryTree (dpy, root, &root, &parent, &child, &nchildren)) {
    int i;
    for (i = 0; i < nchildren; i++) {
      if (!XGetWindowAttributes (dpy, child[i], &childatt))
	continue;
      if ((rootatt.width == childatt.width) &&
	  (rootatt.height == childatt.height))
	return child[i];
    }
    return root;
  }
  else
    return((Window)0);
}

static Window getDECRootWindow(dpy, root)
     Display *dpy;
     Window root; 
{
  Window temporary_rootW;

  if (!(temporary_rootW = getWmRootWindow(dpy, root)) ||
      !(temporary_rootW = getWmRootWindow(dpy, temporary_rootW))) {
    fprintf(stderr, "Can't find false root window on DECWINDOWS server, using true root\n");
    return(root);
  }
  return(temporary_rootW);
}
#endif /* FIND_DEC_ROOTWINDOW */

void imageOnRoot(disp, scrn, image, options, verbose)
     Display      *disp;
     int           scrn;
     Image        *image;
     OptionSet    *options;
     unsigned int  verbose;
{ Option       *opt;
  Pixmap        pixmap;
  XImageInfo   *ximageinfo;
  Atom          __SWM_VROOT = None;
  Window        root, rootReturn, parentReturn, *children;
  unsigned int  numChildren;
  int           i;
#ifdef FIND_DEC_ROOTWINDOW
  char         *s;
#endif /* FIND_DEC_ROOTWINDOW */

  if ((opt= getOption(options, WINDOWID)))
    root= opt->info.windowid;
  else {
    root = RootWindow(disp, scrn);

#ifdef FIND_DEC_ROOTWINDOW
    /* look for DECWindows servers because they do strange stuff with the
     * root window.  they should be shot for that one.
     *
     * some X terminal vendors put DECWINDOWS in their vendor string to
     * get DEC X utilities to work.  to get around them, we look for
     * the string "DECWINDOWS Digital" because as of this writing DEC's
     * vendorstrings look like "DECWINDOWS DigitalEquipmentCorporation".
     */

    for (s= ServerVendor(disp); *s; s++)
      if (!strncmp(s, "DECWINDOWS Digital", 18))
	root= getDECRootWindow(disp, scrn);
#endif /* FIND_DEC_ROOTWINDOW */

    /* Added for window managers like swm and tvtwm that follow solbourne's 
     * virtual root window concept.
     */

    __SWM_VROOT = XInternAtom(disp, "__SWM_VROOT", False);
    XQueryTree(disp, root, &rootReturn, &parentReturn, &children, &numChildren);
    for(i = 0; i < numChildren; i++) {
      Atom actual_type;
      int actual_format;
      unsigned long nitems, bytesafter;
      unsigned char *newRoot = 0;

      if ((XGetWindowProperty (disp, children[i], __SWM_VROOT,0,1,
                             False, XA_WINDOW,
                             &actual_type, &actual_format,
                             &nitems, &bytesafter, &newRoot)
          == Success) &&
         newRoot) {
        root = *((Window*) newRoot);
	break;
      }
    }
  }
  freePrevious(disp, root, verbose);

  if (! (ximageinfo= imageToXImage(disp, scrn,
				   DefaultVisual(disp, scrn),
				   DefaultDepth(disp, scrn),
				   image, False, True, verbose))) {
    fprintf(stderr, "Cannot convert Image to XImage\n");
    exit(1);
  }
  if ((pixmap= ximageToPixmap(disp, root, ximageinfo)) == None) {
    printf("Cannot create background (not enough resources, sorry)\n");
    exit(1);
  }

  /* changing the root colormap is A Bad Thing, so deny it.
   */

  if (ximageinfo->cmap != DefaultColormap(disp, scrn)) {
    printf("Loading image onto root would change default colormap (sorry)\n");
    XFreePixmap(disp, pixmap);
    exit(1);
  }

  XSetWindowBackgroundPixmap(disp, root, pixmap);
  XClearWindow(disp, root);
  XFreePixmap(disp, pixmap);
  freeXImage(image, ximageinfo);
  preserveResource(disp, root);
}
