/*
 * File: rxwin.ri - X11 system-specific graphics interface code.
 */

#define RootState IconicState+1

/*
 * Global variables specific to X
 */
static XSizeHints size_hints;

/*
 * function prototypes
 */
static int	handle_misc	(wdp display, wbp w);
static int	handle_config	(wbp w, XConfigureEvent *event);
static int	handle_exposures (wbp w, XExposeEvent *event);
static void	handle_mouse	(wbp w, XButtonEvent *event);
static void	handle_keypress (wbp w, XKeyEvent *event);
static void	postcursor	(wbp w);
static void	scrubcursor	(wbp w);
static XImage *	getximage	(wbp w, int x, int y,
				 int width, int height, int init);
static void	moveWindow	(wbp w, int x, int y);
static void	makeIcon	(wbp w, int x, int y);
static int	wmap		(wbp w);
static Pixmap	loadimage	(wbp w, char *filename, unsigned int *height,
				unsigned int *width, int atorigin, int *status);


/*
 * write some text to both the window and the pixmap
 */
void xdis(wbp w, char *s, int n)
   {
   int x, y, delta_x;
   STDLOCALS(w);

   pollctr>>=1; pollctr++;
   x = ws->x;
   y = ws->y;
   delta_x = XTextWidth(wc->font->fsp,s,n);
   RENDER4(XDrawImageString,x,y,s,n);
   ws->x += delta_x;
   }



/*
 * put a character out to a window using the current attributes
 */
int wputc(int ci, wbp w)
   {
   int lh, width, height, over;
   char c = (char)ci;
   STDLOCALS(w);

   lh = wc->leading;
   width = ws->width;
   height = ws->height;

   switch(c) {
   case '\r': {
      ws->x = wc->dx;
      break;
      }
   case '\n': {
      if (ISCEOLON(w)) {
         /*
	  * Clear the rest of the line, like a terminal would.
	  * Its arguable whether this should clear to the window
	  * background or the current context background.  If you
	  * change it to use the context background you have to
	  * change the XClearArea call to another XFillRectangle
	  * (cf. eraseArea()).
	  */
	 if (wc->drawop != GXcopy) XSetFunction(stddpy, stdgc, GXcopy);
	 XSetForeground(stddpy, stdgc, wc->bg->c);
	 XClearArea(stddpy, stdwin,
		    ws->x, ws->y-wc->font->fsp->max_bounds.ascent,
		    width-ws->x, lh, False);
	 XFillRectangle(stddpy, stdpix, stdgc,
			ws->x, ws->y - wc->font->fsp->max_bounds.ascent,
			width - ws->x, lh);
	 XSetForeground(stddpy, stdgc,wc->fg->c^(ISXORREVERSE(w)?wc->bg->c:0));
	 if (wc->drawop != GXcopy) XSetFunction(stddpy, stdgc, wc->drawop);
         }
      ws->y += lh;
      ws->x = wc->dx;
      /*
       * Now for the exciting part: do we scroll the window?
       * Copy the pixmap upward, then repaint the window.
       */
      over = ws->y + wc->font->fsp->max_bounds.descent - height;
      if (over > 0) {
	 ws->y -= over;

	 if (wc->drawop != GXcopy) XSetFunction(stddpy, stdgc, GXcopy);
	 XCopyArea(stddpy, stdpix, stdpix, stdgc,
		   0, over,			/* x, y */
		   width, height - over,	/* w, h */
		   0, 0);			/* dstx,dsty */
	 XSetForeground(stddpy, stdgc, wc->bg->c);
	 XFillRectangle(stddpy, stdpix, stdgc,
		   0, height - over, width, over);
	 XSetForeground(stddpy, stdgc,wc->fg->c^(ISXORREVERSE(w)?wc->bg->c:0));
	 if (stdwin)
	    XCopyArea(stddpy, stdpix, stdwin, stdgc, 0, 0, width, height, 0,0);
	 if (wc->drawop != GXcopy) XSetFunction(stddpy, stdgc, wc->drawop);
         }
      break;
      }
   case '\t': {
      xdis(w, "        ", 8 - ((XTOCOL(w,ws->x))&7));
      break;
      }
   /*
    * Handle backspaces.  This implements cooked mode echo handling.
    */
   case '\177':
   case '\010': {
      int i = 0, pre_x;
      /*
       * Start with the last character queued up.
       */
      i--;
      /*
       * Trot back to the control-H itself.
       */
      while ((i>-EQUEUELEN) && (EVQUESUB(w,i) != c)) i--;
      if (i == -EQUEUELEN) break;
      /*
       * Go past the control-H.
       */
      i--;
      /*
       * Go back through any number of control-H's from prior lifetimes.
       */
      while((i > -EQUEUELEN) && !isprint(EVQUESUB(w,i))) i--;
      if (i == -EQUEUELEN) break;

      /*
       * OK, here's the character we're actually rubbing out.  Back up.
       */
      c = EVQUESUB(w,i);
      pre_x = ws->x;
      ws->x -= XTextWidth(wc->font->fsp, &c, 1);
      /*
       * Physically erase the character from the queue.  This results in
       * two control-H's present in the queue.
       */
      *evquesub(w,i) = '\010';
      /*
       * Save the backed-up position, and draw spaces through the erased.
       */
      i = ws->x;
      while(ws->x < pre_x) xdis(w," ",1);
      ws->x = i;
      break;
      }
   default: {
      xdis(w,&c,1);
      }
   }
   return 1;
   }


/*
 * handle_misc processes pending events on display.
 * if w is non-null, block until a returnable event arrives.
 * returns 1 on success, 0 on failure, and -1 on error.
 */
int handle_misc(wdp wd, wbp w)
   {
   XEvent event;
   Window evwin;
   static int presscount = 0;
   wbp wb;
   wsp ws;

   while ((w != NULL) || XPending(wd->display)) {

      XNextEvent(wd->display, &event);
      evwin = event.xexpose.window;  /* go ahead, criticize all you like */

/* could avoid doing this search every event by handling 1 window at a time */
      for (wb = wbndngs; wb; wb=wb->next) {
	 ws = wb->window;

	 if ((ws->display == wd) &&
	     ((ws->win == evwin) || (ws->iconwin == evwin) ||
	      (ws->pix == evwin) || (ws->initialPix == evwin))) break;
         }
      if (!wb) continue;
      if (evwin == ws->iconwin) {
         switch (event.type) {
	    case Expose:
               if (ws->iconpix)
	          XCopyArea(wd->display, ws->iconpix, ws->iconwin,
			    wd->icongc, 0, 0, ws->iconw, ws->iconh, 3, 3);
	       else
	          XDrawString(wd->display, evwin, wd->icongc, 4,
	                      ws->display->fonts->fsp->max_bounds.ascent + 2,
		              ws->iconlabel, strlen(ws->iconlabel));
	       if (ws->iconic == IconicState)
		  SETEXPOSED(wb);
	       break;
	    case KeyPress:
	       handle_keypress(wb, (XKeyEvent *)&event);
	       break;
	    case ButtonPress:
	       if (ws->iconic == IconicState)
	          XMapWindow(ws->display->display, ws->win);
	       ws->iconic = NormalState;        /* set the current state */
	       break;
	    case ConfigureNotify:
	       ws->iconx = ((XConfigureEvent *)&event)->x;
	       ws->icony = ((XConfigureEvent *)&event)->y;
	       break;
	    }
	 }
      else {
      switch (event.type) {
	 case KeyPress:
	    handle_keypress(wb, (XKeyEvent *)&event);
	    break;
	 case ButtonPress:
	    presscount++;
	    handle_mouse(wb, (XButtonEvent *)&event);
	    break;
	 case ButtonRelease:
	    if (--presscount < 0) presscount = 0;
	    handle_mouse(wb, (XButtonEvent *)&event);
	    break;
	 case MotionNotify:
	    if (presscount)
	       handle_mouse(wb, (XButtonEvent *)&event);
	    break;
         case NoExpose:
	    break;
	 case Expose:
	    if (!handle_exposures(wb, (XExposeEvent *)&event))
	       return 1;
	    continue;
         case UnmapNotify:
	    wb->window->iconic = IconicState;
            continue;
	 case MapNotify:
	    if ((ws->width != DisplayWidth(wd->display, wd->screen)) ||
		(ws->height != DisplayHeight(wd->display, wd->screen)))
	       ws->iconic = NormalState;
	    else
	       ws->iconic = MaximizedState;
	    continue;
	 case ConfigureNotify:
	    if (!handle_config(wb, (XConfigureEvent *)&event)) {
	       return 0;
               }
	    break;
	 case DestroyNotify:
	    if (!ISZOMBIE(wb)) return -1; /* error #141 */

	    /*
	     * first of all, we are done with this window
	     */
	    ws->win = (Window) NULL;

	    /*
	     * if there are no more references, we are done with the pixmap
	     *  too.  Free it and the colors allocated for this canvas.
	     */
	    if (ws->refcount == 0) {
	       if (wb->window->pix) {
		  Display *d = ws->display->display;
		  XSync(d, False);
		  if (ws->pix)
		     XFreePixmap(d, ws->pix);
		  ws->pix = (Pixmap) NULL;
	          }
	       if (ws->initialPix != (Pixmap) NULL) {
		  Display *d = ws->display->display;
		  XSync(d, False);
		  XFreePixmap(d, ws->initialPix);
		  ws->initialPix = (Pixmap) NULL;
	          }
               free_xcolors(wb, 2); /* free regular colors */
               free_xcolors(wb, 1); /* free icon colors */
	       }
	    break;
	 default:
	    continue;
	 }
      if ((w != NULL) &&
	  ((evwin == w->window->win) || (evwin == w->window->iconwin))) {
	 return 1;
         }
      }
   }
   return 1;
   }

/*
 * poll for available events on all opened displays.
 * this is where the interpreter calls into the X interface.
 */
int pollevent()
   {
   wdp wd;
   int hm;
   for (wd = wdsplys; wd; wd = wd->next) {
      if ((hm = handle_misc(wd, NULL)) < 1) {
	 if (hm == -1) return -1;
	 else if (hm == 0) {
	    /* how to handle failure? */
	    }
         }
      }
   return 400;
   }

/*
 * get a single item from w's pending queue
 */
int wgetq(wbp w, dptr res)
   {
   int posted = 0;

   while (1) {
      STDLOCALS(w);		/* leave inside loop; ws->pix can change! */
      if (!EVQUEEMPTY(w)) {
	 EVQUEGET(w,*res);
	 if (posted)
            scrubcursor(w);
	 return 1;
         }
      postcursor(w);		/* post every time in case resize erased it */
      posted = 1;
      if (handle_misc(wd, w) == -1) {
	 if (posted)
            scrubcursor(w);
	 return -1;
	 }
      }
   }

/*
 * postcursor/scrubcursor calls must be paired without any intervening output.
 */
static void postcursor(wbp w)
   {
   STDLOCALS(w);

   if (!ISCURSORON(w) || !stdwin) return;
   if (wc->drawop != GXcopy) XSetFunction(stddpy, stdgc, GXcopy);
   if (ISXORREVERSE(w)) XSetForeground(stddpy, stdgc, wc->fg->c);

   /* Draw only on window, not on backing pixmap */
   XFillRectangle(stddpy, stdwin, stdgc, ws->x, ws->y, FWIDTH(w), DESCENT(w));
   XSync(stddpy, False);
   }

static void scrubcursor(wbp w)
   {
   STDLOCALS(w);

   if (!ISCURSORON(w) || !stdwin) return;

   XCopyArea(stddpy, stdpix, stdwin, stdgc,	/* restore window from pixmap */
      ws->x, ws->y, FWIDTH(w), DESCENT(w), ws->x, ws->y);

   if (wc->drawop != GXcopy) XSetFunction(stddpy, stdgc, wc->drawop);
   if (ISXORREVERSE(w)) XSetForeground(stddpy, stdgc, wc->fg->c ^ wc->bg->c);
   }

/*
 * wclose - close a window.  If is a real on-screen window,
 * wait for a DestroyNotify event from the server before returning.
 */
int wclose(wbp w)
   {
   STDLOCALS(w);

   XSync(stddpy, False);
   if (pollevent() == -1) return -1;

   /*
    * Force window to close (turn into a pixmap)
    */
   if (ws->win && ws->refcount > 1) {
      SETZOMBIE(w);
      XDestroyWindow(stddpy,stdwin);
      XFlush(stddpy);
      ws->refcount--;
      while (ws->win)
	 if (pollevent() == -1) return -1;
      }
   /*
    * Entire canvas terminates
    */
   else {
      free_xcolors(w, 2);
      free_xcolors(w, 1);
      free_window(ws);
      }

   return 0;
   }
/*
 * flush a window
 */
void wflush(wbp w)
   {
   STDLOCALS(w);
   XFlush(stddpy);
   }
/*
 * flush all windows
 */
void wflushall()
   {
   wdp wd;
   for (wd = wdsplys; wd != NULL; wd = wd->next) {
      XFlush(wd->display);
      }
   }
/*
 * sync all the servers
 */
void wsync(wbp w)
   {
   wdp wd;
   if (w == NULL) {
      for (wd = wdsplys; wd != NULL; wd = wd->next) {
	 XSync(wd->display, False);
	 }
      }
   else
      XSync(w->window->display->display, False);
   }

/*
 * open a window
 * This routine really just allocates a window data structure.
 * The interesting part is done in wmap, after the user preferences
 * passed to Icon have been parsed.  Returns NULL on error/failure;
 * err_index is set to one of:
 *  >= 0: the index of an offending attribute value
 *  -1  : ordinary failure
 *  -2  : out of memory
 */
FILE *wopen(char *name, struct b_list *lp, dptr attr, int n, int *err_index)
   {
   wbp w;
   wsp ws;
   char dispchrs[256];
   char answer[128];
   char *display = NULL;
   int i;
   tended struct b_list *tlp;
   tended struct descrip attrrslt;

   tlp = lp;

   for(i=0;i<n;i++) {
      if (is:string(attr[i]) &&
	  (StrLen(attr[i])>8) &&
	  !strncmp("display=",StrLoc(attr[i]),8)) {
         strncpy(dispchrs,StrLoc(attr[i])+8,StrLen(attr[i])-8);
	 dispchrs[StrLen(attr[i]) - 8] = '\0';
	 display = dispchrs;
         }
      }

   if ((w = alc_wbinding()) == NULL) {
      *err_index = -2;
      return NULL;
      }
   if ((w->window = alc_winstate()) == NULL) {
      *err_index = -2;
      free_binding(w);
      return NULL;
      }
   if ((w->window->display = alc_display(display)) == NULL) {
      *err_index = -1; /* might be out of memory, probably bad DISPLAY var. */
      free_binding(w);
      return NULL;
      }
   ws = w->window;
   ws->listp.dword = D_List;
   BlkLoc(ws->listp) = (union block *)tlp;

   /*
    * some attributes of the display and window are used in the context
    */
   if ((w->context = alc_context(w)) == NULL) {
      *err_index = -2;
      free_binding(w);
      return NULL;
      }

   /*
    * some attributes of the context determine window defaults
    */
   ws->height = w->context->font->height * 12;
   ws->width  = w->context->font->fsp->max_bounds.width * 80;
   ws->y = w->context->font->fsp->max_bounds.ascent;
   ws->x = 0;
   ws->y += w->context->dy;
   ws->x += w->context->dx;

   /*
    * Loop through any remaining arguments.
    */
   for (i = 0; i < n; i++){
      /*
       * write the attribute,
       *  except "display=" attribute, which is done earlier
       */
      if((StrLen(attr[i])<9)||strncmp(StrLoc(attr[i]),"display=",8)) {
	 switch (wattrib((wbp) w, StrLoc(attr[i]), StrLen(attr[i]), &attrrslt,
			 answer)) {
	 case Error:
	    *err_index = i;
	    return NULL;
	 case Failed:
	    free_binding((wbp)w);
	    *err_index = -1;
	    return NULL;
	    }
	 }
      }
   if (ws->windowlabel == NULL) {
      ws->windowlabel = salloc(name);
      if (ws->windowlabel == NULL) { /* out of memory */
	 *err_index = -2;
	 return NULL;
	 }
      }

   if ((i = wmap(w)) != Succeeded) {
      if (i == Failed) *err_index = -1;
      else *err_index = 0;
      return NULL;
      }
   return (FILE *)w;
   }

/*
 * make an icon for a window
 * x,y are current mouse position
 */
void makeIcon(wbp w, int x, int y)
{
   int status;
   STDLOCALS(w);

   /* if a pixmap image has been specified, load it */
   if (ws->initicon.width) {
      ws->iconpix = XCreatePixmap(stddpy, DefaultRootWindow(stddpy),
				  ws->iconw, ws->iconh,
				  DefaultDepth(stddpy,wd->screen));
      }
   else if (ws->iconimage && strcmp(ws->iconimage, "")) {
      ws->iconpix = loadimage(w, ws->iconimage, &(ws->iconh), &(ws->iconw),
			      0, &status);
      ws->iconh += 6;
      ws->iconw += 6;
      }
   else {    /* determine the size of the icon window */
      ws->iconh = wd->fonts->fsp->max_bounds.ascent +
		  wd->fonts->fsp->max_bounds.descent + 5;
      if (ws->iconlabel == NULL) ws->iconlabel = "";
      ws->iconw = XTextWidth(wd->fonts->fsp, ws->iconlabel,
		  strlen(ws->iconlabel)) + 6;
   }

   /* if icon position hint exists, get it */
   if (ws->wmhintflags & IconPositionHint) {
      x = ws->iconx;
      y = ws->icony;
   }

   /* create the icon window */
   ws->iconwin = XCreateSimpleWindow(stddpy, DefaultRootWindow(stddpy), x, y,
				     ws->iconw, ws->iconh, 2, wc->fg->c,
				     wc->bg->c);

   /* select events for the icon window */
   XSelectInput(stddpy, ws->iconwin,
		ExposureMask | KeyPressMask | ButtonPressMask |
		StructureNotifyMask);

}

/*
 * Create a canvas.
 *  If a window, cause the window to actually become visible on the screen.
 *  returns Succeeded, Failed, or Error
 */
int wmap(wbp w)
   {
   XWindowAttributes attrs;
   XGCValues gcv;
   unsigned long gcmask =
      GCFont | GCForeground | GCBackground | GCFillStyle | GCCapStyle;
   struct imgdata *imd;
   int i, r;
   int new_pixmap = 0;
   char *p, *s;
   XWMHints wmhints;
   XClassHint clhints;
   STDLOCALS(w);

   /*
    *  Create a pixmap for this canvas if there isn't one already.
    */
   if (ws->pix == (Pixmap) NULL) {
      if (ws->initialPix) {
	 ws->pix = ws->initialPix;
	 ws->initialPix = (Pixmap) NULL;
	 }
      else {
	 ws->pix = XCreatePixmap(stddpy, DefaultRootWindow(stddpy),
				 ws->width, ws->height,
				 DefaultDepth(stddpy,wd->screen));
	 ws->pixwidth = ws->width;
	 ws->pixheight = ws->height;
	 new_pixmap = 1;
	 }
      stdpix = ws->pix;
      }

   /*
    * create the X window (or use the DefaultRootWindow if requested)
    */
   if (ws->iconic != HiddenState) {
      ws->win = ((ws->iconic == RootState) ? DefaultRootWindow(stddpy) :
		 XCreateSimpleWindow(stddpy, DefaultRootWindow(stddpy),
				     ws->posx < 0 ? 0 : ws->posx,
				     ws->posy < 0 ? 0 : ws->posy, ws->width,
				     ws->height, 1, wc->fg->c, wc->bg->c));
      if (ws->win == (Window) NULL)
	 return Failed;
      stdwin = ws->win;
      XClearWindow(stddpy, stdwin);
      }

   /*
    * before creating the graphics context, construct a description
    * of any non-default initial graphics context values.
    */
   gcv.foreground = wc->fg->c ^ (ISXORREVERSE(w) ? wc->bg->c : 0);
   gcv.background = wc->bg->c;
   gcv.font       = wc->font->fsp->fid;
   if (wc->fillstyle)
      gcv.fill_style = wc->fillstyle;
   else
      gcv.fill_style = wc->fillstyle = FillSolid;
   if (wc->linestyle || wc->linewidth) {
      gcmask |= (GCLineWidth | GCLineStyle);
      gcv.line_width = wc->linewidth;
      gcv.line_style = wc->linestyle;
      if (wc->linewidth > 1) {
         gcv.dashes = 3 * wc->linewidth;
         gcmask |= GCDashList;
         }
      }
   else
      wc->linestyle = LineSolid;
   gcv.cap_style = CapProjecting;

   /*
    * Create a graphics context (or change an existing one to conform
    * with initial values).
    */
   if (stdgc == NULL) {
      wc->gc = XCreateGC(stddpy, stdpix, gcmask, &gcv);
      stdgc = wc->gc;
      if (stdgc == NULL) return Failed;
      }
   else
      XChangeGC(stddpy, stdgc, gcmask, &gcv);

   if (wc->clipw >= 0)
      setclip(w);

   if (new_pixmap) {
      XSetForeground(stddpy, stdgc, wc->bg->c);
      XFillRectangle(stddpy, ws->pix, stdgc, 0, 0, ws->width, ws->height);
      XSetForeground(stddpy, stdgc, wc->fg->c ^(ISXORREVERSE(w)?wc->bg->c:0));
      }

   imd = &ws->initimage;
   if (imd->width) {
      r = strimage(w, 0, 0, imd->width, imd->height, imd->paltbl,
         imd->data, (word)imd->width * (word)imd->height, 0);
      free((pointer)imd->paltbl);
      free((pointer)imd->data);
      imd->width = 0;
      if (r < 0)
         return Failed;
      }

   imd = &ws->initicon;
   if (imd->width) {
      r = strimage(w, 0, 0, imd->width, imd->height, imd->paltbl,
         imd->data, (word)imd->width * (word)imd->height, 1);
      free((pointer)imd->paltbl);
      free((pointer)imd->data);
      imd->width = 0;
      if (r < 0)
         return Failed;
      wmhints.icon_window = ws->iconwin;
      ws->wmhintflags |= IconWindowHint;
      }

   if (wc->patternname != NULL) {
      if (SetPattern(w, wc->patternname, strlen(wc->patternname)) != Succeeded)
	 return Failed;
      }

   /*
    *  if we are opening a pixmap, we are done at this point.
    */
   if (stdwin == (Window) NULL) return Succeeded;

   if (ws->iconic != RootState) {
      size_hints.flags = PSize | PMinSize | PMaxSize;
      size_hints.width = ws->width;
      size_hints.height= ws->height;
      if (ws->posx == -(MaxInt)) ws->posx = 0;
      else size_hints.flags |= USPosition;
      if (ws->posy == -(MaxInt)) ws->posy = 0;
      else size_hints.flags |= USPosition;
      size_hints.x = ws->posx;
      size_hints.y = ws->posy;
      if (ISRESIZABLE(w)) {
	 size_hints.min_width = 0;
	 size_hints.min_height = 0;
	 size_hints.max_width = DisplayWidth(stddpy, wd->screen);
	 size_hints.max_height = DisplayHeight(stddpy, wd->screen);
	 }
      else {
	 size_hints.min_width = size_hints.max_width = ws->width;
	 size_hints.min_height = size_hints.max_height = ws->height;
	 }
      if (ws->iconlabel == NULL) {
	 if ((ws->iconlabel = salloc(ws->windowlabel)) == NULL)
	    ReturnErrNum(305, Error);
	 }
      XSetStandardProperties(stddpy, stdwin, ws->windowlabel, ws->iconlabel,
			     0,0,0, &size_hints);
      XSelectInput(stddpy, stdwin, ExposureMask | KeyPressMask |
		   ButtonPressMask | ButtonReleaseMask | ButtonMotionMask |
		   StructureNotifyMask);
      }

   wmhints.input = True;
   wmhints.flags = InputHint;
   if (ws->iconic != RootState) {
      if (ws->iconimage != NULL) {
	 makeIcon(w, ws->posx < 0 ? 0 : ws->posx, ws->posy < 0 ? 0 : ws->posy);
	 wmhints.icon_window = ws->iconwin;
	 ws->wmhintflags |= IconWindowHint;
         }
      wmhints.flags |= (ws->wmhintflags | StateHint);
      wmhints.initial_state = ws->iconic;
      wmhints.icon_x = ws->iconx;
      wmhints.icon_y = ws->icony;
      }
   XSetWMHints(stddpy, stdwin, &wmhints);

   /*
    * Set the class hints that name the program (for reference by the
    * window manager) following conventions given in O'Reilly.
    */
   if (! (s = getenv("RESOURCE_NAME"))) {
      p = StrLoc(kywd_prog);
      s = p + StrLen(kywd_prog);
      while (s > p && s[-1] != '/')
	 s--;				/* find tail of prog_name */
      }
   clhints.res_name = s;
   clhints.res_class = "IconProg";
   XSetClassHint(stddpy, stdwin, &clhints);

   if (wd->cmap != DefaultColormap(stddpy,wd->screen))
      XSetWindowColormap(stddpy, stdwin, wd->cmap);

   if (ws->iconic != RootState) {
      CLREXPOSED(w);
      XMapWindow(stddpy, stdwin);
      }

   XGetWindowAttributes(stddpy, stdwin, &attrs);
   ws->width = attrs.width;
   ws->height = attrs.height;
   if (!resizePixmap(w, ws->width, ws->height)) return Failed;

   if (stdwin) {
      i = ws->theCursor;
      if (!(wd->cursors[i]))
         wd->cursors[i] = XCreateFontCursor(stddpy, 2 * i);
      XDefineCursor(stddpy, stdwin, wd->cursors[i]);
      }

   /*
    * busy loop for an expose event, unless of course we are starting out
    *  in an iconic state
    */
   CLRZOMBIE(w);
   if (ws->win != (Window) NULL) {
      int hm;
      while (!ISEXPOSED(w) && (ws->iconic != IconicState || ws->iconwin)) {
	 if ((hm = handle_misc(wd, w)) < 1) {
	    if (hm == -1) return Error;
	    else if (hm == 0) {
	       /* how to handle failure? */
	       }
	    }
         }
      }

   XSetFunction(stddpy, stdgc, wc->drawop);
   XSync(stddpy, False);
   return Succeeded;
}


int do_config(wbp w, int status)
   {
   wsp ws = w->window;
   wdp wd = ws->display;
   int wid = ws->width, ht = ws->height;
   int posx = ws->posx, posy = ws->posy;
   XTextProperty textprop;

   if (! resizePixmap(w, ws->width, ws->height))
      return Failed;
   if (ws->win) {
      XSync(wd->display, False);
      pollevent();
      if (status == 1)
	 moveWindow(w, posx, posy);
      else {
	 if (status == 2)
	    posx = posy = -MaxInt;
	 if (moveResizeWindow(w, posx, posy, wid, ht) == Failed)
	    return Failed;
	 }

      /* XSync is not enough because the window manager gets involved here. */
      XFlush(wd->display);			      /* force out request */
      XGetWMName(wd->display, ws->win, &textprop);    /* force WM round trip */
      XSync(wd->display, False);		      /* NOW sync */
      }
   return Succeeded;
   }

int setheight(wbp w, SHORT new_height)
   {
   STDLOCALS(w);
   if (new_height < 0) return Failed;
   ws->height = size_hints.height = new_height;
   return Succeeded;
   }

int setwidth(wbp w, SHORT new_width)
{
   STDLOCALS(w);
   if (new_width < 0) return Failed;
   ws->width = size_hints.width = new_width;
   return Succeeded;
}

int setgeometry(wbp w, char *geo)
   {
   int width = 0, height = 0;
   int x = 0, y = 0, status;
   STDLOCALS(w);

   if ((status = parsegeometry(geo, &x, &y, &width, &height)) == 0)
      return Error;
   if (status & 1) {
      ws->width = size_hints.width = width;
      ws->height = size_hints.height = height;
      }
   /*
    * can't set position on hidden windows
    */
   if ((stdwin || !stdpix) && (status & 2)) {
      ws->posx = x;
      ws->posy = y;
      }
   /* insert assigns here:
    *  ws->posx = ((sign > 0) ? tmp :
    *              DisplayWidth(stddpy,wd->screen) - ws->width - tmp);
    *  ws->posy = ((sign > 0) ? tmp :
    *              DisplayHeight(stddpy,wd->screen) - ws->height - tmp);
    */
   return Succeeded;
   }

int allowresize(wbp w, int on)
   {
   if (on)
      SETRESIZABLE(w);
   else
      CLRRESIZABLE(w);
   return Succeeded;
   }

void warpPointer(wbp w, int x, int y)
   {
   wsp ws = w->window;
   XWarpPointer(ws->display->display, None, ws->win, 0,0,0,0, x, y);
   }

/*
 * #@#@ This is a bug
 */
int seticonlabel(wbp w, char *val)
   {
   STDLOCALS(w);
   if (ws->iconlabel != NULL) free(ws->iconlabel);
   if ((ws->iconlabel = salloc(val)) == NULL)
      ReturnErrNum(305, Error);

   if (stddpy && stdwin) {
      XSetIconName(stddpy, stdwin, w->window->iconlabel);
      if (ws->iconic == IconicState && !ws->iconpix && ws->iconwin) {
	 XClearWindow(stddpy, ws->iconwin);
	 XDrawString(stddpy, ws->iconwin, wd->icongc, 4,
		     wd->fonts->fsp->max_bounds.ascent + 2,
		     ws->iconlabel, strlen(ws->iconlabel));
	 }
      }
   return Succeeded;
   }

/*
 * setwindowlabel
 */
int setwindowlabel(wbp w, char *s)
{
   wsp ws = w->window;
   if (ws->windowlabel != NULL) free(ws->windowlabel);
   if ((ws->windowlabel = salloc(s)) == NULL)
      ReturnErrNum(305, Error);
   if (ws->display && ws->display->display && ws->win)
      XStoreName(ws->display->display, ws->win,
	 *ws->windowlabel ? ws->windowlabel : " ");    /* empty string fails */
   return Succeeded;
}

/*
 * setcursor() - a no-op under X at present
 */
int setcursor(wbp w, int on)
{
   if (on)
      SETCURSORON(w);
   else
      CLRCURSORON(w);
   return Succeeded;
}


/*
 * setpointer() - define a mouse pointer shape
 */
int setpointer(wbp w, char *val)
   {
   int i = si_s2i(cursorsyms,val) >> 1;
   STDLOCALS(w);
   if (i < 0 || i >= NUMCURSORSYMS) return Failed;

   ws->theCursor = i;
   if (!(wd->cursors[i]))
      wd->cursors[i] = XCreateFontCursor(stddpy, 2 * i);
   if (stdwin)
      XDefineCursor(stddpy, stdwin, wd->cursors[i]);
   return Succeeded;
   }

/*
 * setdrawop() - set the drawing operation
 */
int setdrawop(wbp w, char *val)
   {
   STDLOCALS(w);
   XSync(stddpy, False);
   if (!strcmp(val,"reverse")) {
      if (!ISXORREVERSE(w)) {
	 SETXORREVERSE(w);
	 wc->drawop = GXxor;
	 if (stdgc)
	    XSetForeground(stddpy, stdgc, wc->fg->c ^ wc->bg->c);
	 }
      }
   else {
      if (ISXORREVERSE(w)) {
	 CLRXORREVERSE(w);
	 if (stdgc)
	    XSetForeground(stddpy, stdgc, wc->fg->c);
	 }
      wc->drawop = si_s2i(drawops,val);
      if (wc->drawop == -1) { wc->drawop = GXcopy; return Error; }
      }
   if (stdgc) XSetFunction(stddpy, stdgc, wc->drawop);
   return Succeeded;
   }

/*
 * rebind() - bind w's context to that of w2.
 */
int rebind(wbp w, wbp w2)
   {
   if (w->window->display != w2->context->display) return Failed;
   w->context = w2->context;
   return Succeeded;
   }


void setclip(wbp w)
   {
   wcp wc = w->context;
   XRectangle rec;
   if (wc->gc) {
      rec.x = wc->clipx;
      rec.y = wc->clipy;
      rec.width = wc->clipw;
      rec.height = wc->cliph;
      XSetClipRectangles(wc->display->display, wc->gc, 0, 0, &rec, 1,Unsorted);
      }
   }

void unsetclip(wbp w)
   {
   wcp wc = w->context;
   if (wc->gc) {
      XSetClipMask(wc->display->display, wc->gc, None);
      }
   }

void getcanvas(wbp w, char *s)
   {
   if (w->window->win == (Window) NULL) sprintf(s, "hidden");
   else
      switch (w->window->iconic) {
      case RootState:
	 sprintf(s, "root");
	 break;
      case NormalState:
	 sprintf(s, "normal");
	 break;
      case IconicState:
	 sprintf(s, "iconic");
	 break;
      case MaximizedState:
	 sprintf(s, "maximal");
	 break;
      case HiddenState:
	 sprintf(s, "hidden");
	 break;
      default:
	 sprintf(s, "???");
      }
   }

/*
 *  Set the canvas type, either during open (pixmap is null, set a flag)
 *   or change an existing canvas to a different type.
 */
int setcanvas(wbp w, char *s)
   {
   XTextProperty textprop;
   STDLOCALS(w);

   if (!strcmp(s, "iconic")) {
      if (ws->pix == (Pixmap) NULL) {
	 ws->wmhintflags |= StateHint;
	 ws->iconic = IconicState;
	 }
      else {
	 if (ws->iconic != IconicState) {
	    return Failed;
	    }
	 }
      }

   else if (!strcmp(s, "normal")) {
      if (ws->pix == (Pixmap) NULL) {
	 ws->iconic = NormalState;
	 }
      else {
	 if (ws->win == (Window) NULL) {
	    ws->iconic = NormalState;
	    ws->initialPix = ws->pix;
	    ws->pix = (Window) NULL;
	    wmap(w);
	    }
	 else if (ws->iconic == IconicState) {
	    XMapWindow(stddpy, stdwin);
	    XSync(stddpy, False);
	    while (ws->iconic == IconicState)
	       pollevent();
	    }
	 else if (ws->iconic == MaximizedState) {
	    moveResizeWindow(w, ws->normalx, ws->normaly,
			     ws->normalw, ws->normalh);
	    ws->iconic = NormalState;
	    }
	 }
      }
   else if (!strcmp(s, "maximal")) {
      if (ws->iconic != MaximizedState) {
	 int expect_config= (ws->width != DisplayWidth(stddpy, wd->screen)) ||
	    (ws->height != DisplayHeight(stddpy, wd->screen));
	 ws->normalx = ws->posx;
	 ws->normaly = ws->posy;
	 ws->normalw = ws->width;
	 ws->normalh = ws->height;
	 ws->width = DisplayWidth(stddpy, wd->screen);
	 ws->height= DisplayHeight(stddpy, wd->screen);
	 if (ws->pix != (Pixmap) NULL) {
	    if (ws->win == (Window) NULL) {
	       ws->iconic = MaximizedState;
	       ws->initialPix = ws->pix;
	       ws->pix = (Window) NULL;
	       wmap(w);
	       }
	    else if (ws->iconic == IconicState) {
	       XMapWindow(stddpy, stdwin);
	       XSync(stddpy, False);
	       while (ws->iconic == IconicState)
		  pollevent();
	       }
	    else if (expect_config) {
	       moveResizeWindow(w, 0, 0, ws->width, ws->height);
	       /* XSync is not enough because window manager gets involved. */
	       XFlush(wd->display);			      /* flush req */
	       XGetWMName(wd->display, ws->win, &textprop);   /* force WM RT */
	       XSync(wd->display, False);		      /* NOW sync */
	       if (pollevent() == -1) return Error;
	       moveWindow(w, -ws->posx, -ws->posy);
	       XFlush(wd->display);			      /* flush req */
	       XGetWMName(wd->display, ws->win, &textprop);   /* force WM RT */
	       XSync(wd->display, False);		      /* NOW sync */
	       }
	    }
	 ws->iconic = MaximizedState;
	 }
      }
   else if (!strcmp(s, "hidden")) {
      if (ws->pix == (Pixmap)NULL) {
	 ws->iconic = HiddenState;
	 }
      else {
	 if (ws->win != (Window) NULL) {
	    if (ws->iconic == MaximizedState) {
	       ws->posx = ws->normalx;
	       ws->posy = ws->normaly;
	       ws->width = ws->normalw;
	       ws->height = ws->normalh;
	       ws->iconic = NormalState;
	       }
	    if (ws->iconic != IconicState) {
	       SETZOMBIE(w);
	       XDestroyWindow(stddpy, stdwin);
	       XFlush(stddpy);
	       while (ws->win)
		  if (pollevent() == -1)
		     return Error;
	       }
	    }
	 }
      }
   else return Error;
   XSync(ws->display->display, False);
   return Succeeded;
   }

int seticonicstate(wbp w, char *s)
   {
   STDLOCALS(w);

   if (!strcmp(s, "icon")) {
      if (ws->pix == (Pixmap) NULL) {
	 ws->wmhintflags |= StateHint;
	 ws->iconic = IconicState;
	 }
      else {
	 if (ws->iconic != IconicState) {
	    return Failed;
	    }
	 }
      }
   else if (!strcmp(s, "window")) {
      if (ws->win != (Window) NULL) {
	 if (ws->iconic == IconicState) {
	    XMapWindow(stddpy, stdwin);
	    }
	 }
      }
   else if (!strcmp(s, "root")) {
      if (ws->win == (Window) NULL)
	 ws->iconic = RootState;
      else return Failed;
      }
   else return Error;
   XSync(ws->display->display, False);
   return Succeeded;
   }

int seticonpos(wbp w, char *s)
   {
   char *s2;
   wsp ws = w->window;

   ws->wmhintflags |= IconPositionHint;
   s2 = s;
   ws->iconx = atol(s2);
   while (isspace(*s2)) s2++;
   while (isdigit(*s2)) s2++;
   if (*s2++ != ',') return Error;
   ws->icony = atol(s2);

   if (ws->win) {
      if (ws->iconwin == (Window) NULL)
	 makeIcon(w, ws->iconx, ws->icony);
      if (remap(w, ws->iconx, ws->icony) == -1) return Error;
      }
   return Succeeded;
   }

int geticonpos(wbp w, char *s)
   {
   wsp ws = w->window;
   sprintf(s,"%d,%d", ws->iconx, ws->icony);
   return Succeeded;
   }


/*
 * if the window exists and is visible, set its position to (x,y)
 */
void moveWindow(wbp w, int x, int y)
{
   STDLOCALS(w);
   ws->posx = x;
   ws->posy = y;
   if (stdwin) {
      XMoveWindow(stddpy, stdwin, ws->posx, ws->posy);
      XSync(stddpy, False);
      }
}

int moveResizeWindow(wbp w, int x, int y, int width, int height)
   {
   wsp ws = w->window;
   wdp wd = ws->display;
   ws->width = width;
   ws->height = height;

   size_hints.flags = PMinSize | PMaxSize;
   if (ISRESIZABLE(w)) {
      size_hints.min_width = 0;
      size_hints.min_height = 0;
      size_hints.max_width = DisplayWidth(wd->display, wd->screen);
      size_hints.max_height = DisplayHeight(wd->display, wd->screen);
      }
   else {
      size_hints.min_width = size_hints.max_width = width;
      size_hints.min_height = size_hints.max_height = height;
      }
   XSetNormalHints(wd->display, ws->win, &size_hints);

   if (resizePixmap(w, width, height) == 0) return Failed;

   if (ws->win != (Window) NULL) {
      if (x == -MaxInt && y == -MaxInt)
	 XResizeWindow(wd->display, ws->win, width, height);
      else
         XMoveResizeWindow(wd->display, ws->win, x, y, width, height);
      XSync(wd->display, False);
      }
   return Succeeded;
   }

/*
 * Set the context's fill style by name.
 */
int setfillstyle(wbp w, char *s)
   {
   STDLOCALS(w);

   if (!strcmp(s, "solid")) {
      wc->fillstyle = FillSolid;
      }
   else if (!strcmp(s, "masked")
	    || !strcmp(s, "stippled") || !strcmp(s, "patterned")) {
      wc->fillstyle = FillStippled;
      }
   else if (!strcmp(s, "textured")
      || !strcmp(s, "opaquestippled") || !strcmp(s, "opaquepatterned")) {
      wc->fillstyle = FillOpaqueStippled;
      }
   else return Error;
   if (stdpix) {
      XSetFillStyle(stddpy, stdgc, wc->fillstyle);
      }
   return Succeeded;
   }

/*
 * Set the context's line style by name.
 */
int setlinestyle(wbp w, char *s)
   {
   STDLOCALS(w);

   if (!strcmp(s, "solid")) {
      wc->linestyle = LineSolid;
      }
   else if (!strcmp(s, "onoff") || !strcmp(s, "dashed")) {
      wc->linestyle = LineOnOffDash;
      }
   else if (!strcmp(s, "doubledash") || !strcmp(s, "striped")) {
      wc->linestyle = LineDoubleDash;
      }
   else return Error;
   if (stdpix) {
      XSetLineAttributes(stddpy, stdgc,
	 wc->linewidth, wc->linestyle, CapProjecting, JoinMiter);
      }
   return Succeeded;
   }

/*
 * Set the context's line width
 */
int setlinewidth(wbp w, LONG linewid)
   {
   unsigned long gcmask;
   XGCValues gcv;
   STDLOCALS(w);

   if (linewid < 0) return Error;
   wc->linewidth = linewid;
   if (stdpix) {
      gcv.line_width = linewid;
      gcv.line_style = wc->linestyle;
      if (linewid > 1)
         gcv.dashes = 3 * wc->linewidth;
      else
	 gcv.dashes = 4;
      gcmask = GCLineWidth | GCLineStyle | GCDashList;
      XChangeGC(stddpy, stdgc, gcmask, &gcv);
      }

   return Succeeded;
   }

/*
 * Reset the context's foreground color to whatever it is supposed to be.
 */
int resetfg(wbp w)
   {
   wcp wc = w->context;
   if (wc->gc != NULL)
      XSetForeground(wc->display->display, wc->gc,
		     wc->fg->c ^ (ISXORREVERSE(w) ? wc->bg->c : 0));
   return Succeeded;
   }

/*
 * Set the context's foreground color by name.
 */
int setfg(wbp w, char *s)
   {
   wclrp cp;
   STDLOCALS(w);

   Protect(cp = alc_color(w,s), return Failed);
   wc->fg = cp;
   return resetfg(w);
   }

int setfgrgb(wbp w, int r, int g, int b)
{
   char sbuf1[MaxCvtLen];
   sprintf(sbuf1, "%d,%d,%d", r, g, b);
   return setfg(w, sbuf1);
}

/*
 * Set the context's foreground color by color cell.
 */
int isetfg(wbp w, int fg)
   {
   int i, r, g, b;
   STDLOCALS(w);

   if (fg >= 0) {
      b = fg & 255;
      fg >>= 8;
      g = fg & 255;
      fg >>= 8;
      r = fg & 255;
      return setfgrgb(w, r * 257, g * 257, b * 257);
      }
   for (i = 2; i < wd->numColors; i++)
      if (wd->colrptrs[i]->type == CMUTABLE && wd->colrptrs[i]->c == -fg - 1)
	 break;
   if (i == wd->numColors) return Failed;
   wc->fg = wd->colrptrs[i];
   return resetfg(w);
   }

/*
 * Set the window context's background color by name.
 */
int setbg(wbp w, char *s)
   {
   wclrp cp;
   STDLOCALS(w);

   Protect(cp = alc_color(w,s), return Failed);
   wc->bg = cp;

   if (stdgc != NULL)
      XSetBackground(stddpy, stdgc, wc->bg->c);
   return ISXORREVERSE(w) ? resetfg(w) : Succeeded;
   }

int setbgrgb(wbp w, int r, int g, int b)
{
   char sbuf1[MaxCvtLen];
   sprintf(sbuf1, "%d,%d,%d", r, g, b);
   return setbg(w, sbuf1);
}

/*
 * Set the context's background color by color cell.
 */
int isetbg(wbp w, int bg)
   {
   int i, r, g, b;
   STDLOCALS(w);

   if (bg >= 0) {
      b = bg & 255;
      bg >>= 8;
      g = bg & 255;
      bg >>= 8;
      r = bg & 255;
      return setbgrgb(w, r * 257, g * 257, b * 257);
      }
   for (i = 2; i < wd->numColors; i++)
      if (wd->colrptrs[i]->type == CMUTABLE && wd->colrptrs[i]->c == -bg - 1)
	 break;
   if (i == wd->numColors) return Failed;
   wc->bg = wd->colrptrs[i];
   if (stdgc != NULL)
      XSetBackground(stddpy, stdgc, wc->bg->c);
   return ISXORREVERSE(w) ? resetfg(w) : Succeeded;
   }

/*
 * Set the gamma correction value.
 */
int setgamma(wbp w, double gamma)
   {
   w->context->gamma = gamma;
   setfg(w, w->context->fg->name);	/* reinterpret current Fg/Bg spec */
   setbg(w, w->context->bg->name);
   return Succeeded;
   }

/*
 * Set the display by name.  Really should cache answers as per fonts below;
 * for now just open a new display each time.  Note that this can only be
 * called before a window is instantiated...
 */
int setdisplay(wbp w, char *s)
   {
   wdp d;
   /* can't change display for mapped window! */
   if (w->window->pix != (Pixmap) NULL)
      return Failed;

   Protect(d = alc_display(s), return 0);
   w->window->display = d;
   w->context->fg = d->colrptrs[0];
   w->context->bg = d->colrptrs[1];
   w->context->font = d->fonts;
   return Succeeded;
   }

int setleading(wbp w, int i)
{
   w->context->leading = i;
   return Succeeded;
}

int setimage(wbp w, char *val)
   {
   wsp ws = w->window;
   int status;
   ws->initialPix = loadimage(w, val, &(ws->height), &(ws->width),
			  0, &status);
   if (ws->initialPix == (Pixmap) NULL) return Failed;
   return Succeeded;
   }

void toggle_fgbg(wbp w)
{
   wclrp tmp;
   STDLOCALS(w);
   tmp = wc->fg;
   wc->fg = wc->bg;
   wc->bg = tmp;
   if (stdpix) {
      XSetForeground(stddpy, stdgc,
		     wc->fg->c ^ (ISXORREVERSE(w)?wc->bg->c:0));
      XSetBackground(stddpy, stdgc, wc->bg->c);
      }
}

void getdisplay(wbp w, char *answer)
   {
   char *tmp;
   wdp wd = w->window->display;
   if (!strcmp(wd->name, "")) {
      if ((tmp = getenv("DISPLAY")) != NULL)
	 sprintf(answer, "%s", tmp);
      else
	 *answer = '\0';
      }
   else sprintf(answer, "%s", wd->name);
   }

int getvisual(wbp w, char *answer)
{
   wdp wd = w->window->display;
   Visual * v = DefaultVisual(wd->display,wd->screen);
   sprintf(answer, "%d,%d,%d", v->class, v->bits_per_rgb, v->map_entries );
   return Succeeded;
}
/*
 * getpos() - update the window state's notion of its current position
 */
int getpos(wbp w)
{
   Window garbage1, garbage2;
   int root_x, root_y, win_x, win_y;
   unsigned int key_buttons;
   STDLOCALS(w);
   if (!stdwin) return Failed;
   /*
    * This call is made because it is guaranteed to generate
    * a synchronous request of the server, not just ask Xlib
    * what the window position was last it knew.
    */
   if (XQueryPointer(stddpy, stdwin, &garbage1, &garbage2,
		     &root_x, &root_y, &win_x, &win_y, &key_buttons) ==
       False) {
      return Failed;
      }
   ws->posx = root_x - win_x;
   ws->posy = root_y - win_y;
   return Succeeded;
}

void getfg(wbp w, char *answer)
{
   sprintf(answer, "%s", w->context->fg->name);
}

void getbg(wbp w, char *answer)
{
   sprintf(answer, "%s", w->context->bg->name);
}

void getlinestyle(wbp w, char *answer)
{
   wcp wc = w->context;
   sprintf(answer,"%s",
	   (wc->linestyle==LineSolid)?"solid":
	   ((wc->linestyle==LineOnOffDash)?"dashed":"striped"));
}

void getfntnam(wbp w, char *answer)
{
   sprintf(answer,"%s", w->context->font->name);
}

void getpointername(wbp w, char *answer)
{
   strcpy(answer, si_i2s(cursorsyms, 2 * w->window->theCursor));
}

void getdrawop(wbp w, char *answer)
{
   char *s;
   if (ISXORREVERSE(w)) s = "reverse";
   else s = si_i2s(drawops, w->context->drawop);
   if (s) sprintf(answer, "%s", s);
   else strcpy(answer, "copy");
}

void geticonic(wbp w, char *answer)
{
   switch (w->window->iconic) {
   case RootState:
     sprintf(answer, "root");
     break;
   case NormalState:
     sprintf(answer, "window");
     break;
   case IconicState:
     sprintf(answer, "icon");
     break;
   default:
     sprintf(answer, "???");
   }
}

/*
 * Set the window's font by name.
 */
int setfont(wbp w, char **s)
   {
   wfp tmp;
   STDLOCALS(w);

   /* could free up previously allocated font here */

   Protect(tmp = alc_font(w,s), return Failed);
   wc->font = tmp;

   if (stdgc != NULL)
      XSetFont(stddpy, stdgc, wc->font->fsp->fid);

   if (stdpix == (Pixmap) NULL) {
      ws->y = wc->font->fsp->max_bounds.ascent;
      ws->x = 0;
      }
   return Succeeded;
   }

/*
 * callback procedures
 */

static int handle_exposures(wbp w, XExposeEvent *event)
   {
   int returnval;
   STDLOCALS(w);

   returnval = ISEXPOSED(w);
   SETEXPOSED(w);
   if (stdwin && !ISZOMBIE(w)) {
      if (wc->drawop != GXcopy)
	 XSetFunction(stddpy, stdgc, GXcopy);
      if (wc->clipw >= 0)
	 unsetclip(w);
      XCopyArea(stddpy, stdpix, stdwin, stdgc, event->x,event->y,
		event->width,event->height, event->x,event->y);
      if (wc->clipw >= 0)
	 setclip(w);
      if (wc->drawop != GXcopy)
	 XSetFunction(stddpy,stdgc,wc->drawop);
      }
   return returnval;
   }
#ifndef min
#define min(x,y) (((x)<(y))?(x):(y))
#define max(x,y) (((x)>(y))?(x):(y))
#endif

/*
 * resizePixmap(w,width,height) -- ensure w's backing pixmap is at least
 * width x height pixels.
 *
 * Resizes the backing pixmap, if needed.  Called when X resize events
 * arrive, as well as when programs make explicit resize requests.
 *
 * Returns 0 on failure.
 */
int resizePixmap(wbp w, int width, int height)
   {
   Pixmap p;
   STDLOCALS(w);
   if (ws->pix == (Pixmap) NULL) return 1;
   if ((width > ws->pixwidth) || (height > ws->pixheight)) {
      int x = ws->pixwidth, y = ws->pixheight;

      ws->pixheight = max(ws->pixheight, height);
      ws->pixwidth  = max(ws->pixwidth, width);
      p = XCreatePixmap(stddpy, DefaultRootWindow(stddpy), ws->pixwidth,
			ws->pixheight, DefaultDepth(stddpy,wd->screen));
      if (p == (Pixmap) NULL)
	 return 0;

      /*
       * This staggering amount of redudancy manages to make sure the new
       * pixmap gets initialized including areas not in the old pixmap.
       * The window is redrawn.
       */
      XSetForeground(stddpy, stdgc, wc->bg->c);
      if (wc->drawop != GXcopy)
	 XSetFunction(stddpy, stdgc, GXcopy);
      if (wc->fillstyle != FillSolid)
	 XSetFillStyle(stddpy, stdgc, FillSolid);
      if (wc->clipw >= 0)
	 unsetclip(w);

      if (width > x) {
	 XFillRectangle(stddpy, p, stdgc, x, 0, width-x, ws->pixheight);
	 if (stdwin != (Window) NULL)
	    XFillRectangle(stddpy,stdwin,stdgc, x, 0, width-x, ws->pixheight);
         }
      if (height > y) {
	 XFillRectangle(stddpy, p, stdgc, 0, y, x, height - y);
	 if (stdwin != (Window) NULL)
	    XFillRectangle(stddpy, stdwin, stdgc, 0, y, x, height - y);
         }
      XSetForeground(stddpy, stdgc, wc->fg->c ^ (ISXORREVERSE(w)?wc->bg->c:0));
      XCopyArea(stddpy, stdpix, p, stdgc, 0, 0, x, y, 0, 0);
      if (wc->drawop != GXcopy)
	 XSetFunction(stddpy,stdgc,wc->drawop);
      if (wc->fillstyle != FillSolid)
	 XSetFillStyle(stddpy, stdgc, wc->fillstyle);
      if (wc->clipw >= 0)
	 setclip(w);

      XFreePixmap(stddpy, stdpix);	/* free old pixmap */
      ws->pix = p;
      }
   return 1;
   }

/*
 * Resize operations are made as painless as possible, but the
 * user program is informed anyhow.  The integer coordinates are
 * the new size of the window, in pixels.
 */
static int handle_config(wbp w, XConfigureEvent *event)
   {
   struct descrip d;
   STDLOCALS(w);

   /*
    * Update X-Icon's information about the window's configuration
    */
   ws->x = min(ws->x, event->width - FWIDTH(w));
   ws->y = min(ws->y, event->height);

   ws->posx = event->x;
   ws->posy = event->y;

   /*
    * If this was not a resize, drop it
    */
   if ((event->width == ws->width) && (event->height == ws->height))
      return 1;

   ws->width = event->width;
   ws->height = event->height;

   if (! resizePixmap(w, event->width, event->height)) return 0;

   /*
    * The initial configure event generates no Icon-level "events"
    */
   if (!ISEXPOSED(w))
      return 1;

   MakeInt(RESIZED, &d);
   qevent(w->window, &d, ws->width, ws->height, ~(uword)0, 0);
   return 1;
   }

/*
 * Queue up characters for keypress events.
 */
static void handle_keypress(wbp w, XKeyEvent *event)
   {
   int i,j;
   char s[10];
   struct descrip d;
   KeySym k;

   w->window->pointerx = event->x;
   w->window->pointery = event->y;

   switch (i=translate_key_event(event, s, &k)) {
   case -1:
      return;
   case 0:
      MakeInt(k, &d);
      qevent(w->window, &d, event->x, event->y,
	     (uword)event->time, event->state);
      break;
   default:
      StrLen(d) = 1;
      for (j = 0; j < i; j++) {
	 StrLoc(d) = (char *)&allchars[s[j] & 0xFF];
	 qevent(w->window, &d, event->x, event->y,
		(uword)event->time, event->state);
	 }
      }
   }

#define swap(a,b) { tmp = a; a = b; b = tmp; }
/*
 * Handle button presses and drag events.  In the case of drags, we should
 * really be looking at an XMotionEvent instead of an XButtonEvent, but
 * the structures are identical up to the button field (which we do not
 * examine for drag events).  Mouse coordinates are queued up after the event.
 */
static void handle_mouse(wbp w, XButtonEvent *event)
   {
   static unsigned int buttonorder[3] =
     { Button1Mask, Button2Mask, Button3Mask };
   unsigned int tmp;
   int eventcode = 0;
   struct descrip d;

   if (event->type == MotionNotify) {
      if (event->state | buttonorder[0]) {
         if (buttonorder[0] == Button1Mask)
	    eventcode = MOUSELEFTDRAG;
         else if (buttonorder[0] == Button2Mask)
            eventcode = MOUSEMIDDRAG;
         else
            eventcode = MOUSERIGHTDRAG;
         }
      else if (event->state | buttonorder[1]) {
         if (buttonorder[1] == Button1Mask)
	    eventcode = MOUSELEFTDRAG;
         else if (buttonorder[1] == Button2Mask)
            eventcode = MOUSEMIDDRAG;
         else
            eventcode = MOUSERIGHTDRAG;
         }
      else if (event->state | buttonorder[2]) {
         if (buttonorder[2] == Button1Mask)
	    eventcode = MOUSELEFTDRAG;
         else if (buttonorder[2] == Button2Mask)
            eventcode = MOUSEMIDDRAG;
         else
            eventcode = MOUSERIGHTDRAG;
         }
      }
   else switch (event->button) {
      case Button1: {
	 eventcode = MOUSELEFT;
	 if (buttonorder[2] == Button1Mask)
	    swap(buttonorder[1],buttonorder[2]);
	 if (buttonorder[1] == Button1Mask)
	    swap(buttonorder[0],buttonorder[1]);
	 break;
         }
      case Button2: {
	 eventcode = MOUSEMID;
	 if (buttonorder[2] == Button2Mask)
	    swap(buttonorder[1],buttonorder[2]);
	 if (buttonorder[1] == Button2Mask)
	    swap(buttonorder[0],buttonorder[1]);
	 break;
         }
      case Button3: {
	 eventcode = MOUSERIGHT;
	 if (buttonorder[2] == Button3Mask)
	    swap(buttonorder[1],buttonorder[2]);
	 if (buttonorder[1] == Button3Mask)
	    swap(buttonorder[0],buttonorder[1]);
	 break;
         }
      }
   if (event->type == ButtonRelease) {
      eventcode -= (MOUSELEFT - MOUSELEFTUP);
      swap(buttonorder[0],buttonorder[1]);
      swap(buttonorder[1],buttonorder[2]);
      }

   w->window->pointerx = event->x;
   w->window->pointery = event->y;
   MakeInt(eventcode,&d);
   qevent(w->window, &d, event->x, event->y, (uword)event->time, event->state);
   }


/*
 * fill a series of rectangles
 */
void fillrectangles(wbp w, XRectangle *recs, int nrecs)
   {
   STDLOCALS(w);

   /*
    * Free colors if drawop=copy, fillstyle~=masked, no clipping,
    * and a single rectangle that fills the whole window.
    */
   if (!RECX(*recs) && !RECY(*recs) && RECWIDTH(*recs) >= ws->width &&
      RECHEIGHT(*recs) >= ws->height && nrecs == 1 &&
      wc->drawop == GXcopy && wc->fillstyle != FillStippled && wc->clipw < 0) {
         RECWIDTH(*recs) = ws->pixwidth;	/* fill hidden part */
	 RECHEIGHT(*recs) = ws->pixheight;
	 free_xcolors(w, 0);			/* free old colors */
	 }
   RENDER2(XFillRectangles, recs, nrecs);
   }

/*
 * erase an area
 */
void eraseArea(wbp w, int x, int y, int width, int height)
   {
   STDLOCALS(w);

   /*
    * if width >= window width or height >= window height, clear any
    * offscreen portion as well in order to allow the freeing of colors.
    */
   if (x + width  >= ws->width)  width  = ws->pixwidth - x;
   if (y + height >= ws->height) height = ws->pixheight - y;

   /*
    * fill the rectangle with the background color
    */
   XSetForeground(stddpy, stdgc, wc->bg->c);
   if (wc->drawop != GXcopy) XSetFunction(stddpy, stdgc, GXcopy);
   if (wc->fillstyle != FillSolid) XSetFillStyle(stddpy, stdgc, FillSolid);
   RENDER4(XFillRectangle, x, y, width, height);
   XSetForeground(stddpy, stdgc, wc->fg->c ^ (ISXORREVERSE(w)?wc->bg->c:0));
   if (wc->drawop != GXcopy) XSetFunction(stddpy, stdgc, wc->drawop);
   if (wc->fillstyle != FillSolid) XSetFillStyle(stddpy, stdgc, wc->fillstyle);

   /*
    * if the entire window is cleared, free up colors
    */
   if (!x && !y && width >= ws->pixwidth && height >= ws->pixheight &&
      wc->clipw < 0)
      free_xcolors(w, 0);
   }

/*
 * copy an area
 */
int copyArea(wbp w, wbp w2, int x, int y, int width, int height, int x2, int y2)
   {
   int lpad, rpad, tpad, bpad;
   Pixmap src;
   wsp ws1 = w->window, ws2 = w2->window;
   wclrp cp1, cp2 = NULL, *cpp;
   STDLOCALS(w2);

   if (w->window->display->display != w2->window->display->display) {
      wdp wd1 = ws1->display;
      unsigned long c = 0;
      int i, j;
      Display *d1 = wd1->display;
      XColor clr;
      XImage *xim;

      /*
       * Copying is between windows on two different displays.
       */
      if (x<0 || y<0 || x+width > ws1->pixwidth || y+height > ws1->pixheight)
	 return Failed;		/*#%#%# BOGUS, NEEDS FIXING */
      xim = XGetImage(d1, ws1->pix, x, y, width, height,
		      (1<<DefaultDepth(d1,wd1->screen))-1,XYPixmap);
      XSetFunction(stddpy, stdgc, GXcopy);
      for (i=0; i < width; i++) {
	 for (j=0; j < height; j++) {
	    clr.pixel = XGetPixel(xim, i, j);
	    if (cp2 != NULL && c == clr.pixel) {
	       XSetForeground(stddpy, stdgc, cp2->c);
	       RENDER2(XDrawPoint, i + x2, j + y2);
	       continue;
	       }
	    c = clr.pixel;
	    cp2 = NULL;
	    for (cpp = wd1->colrptrs; cpp < wd1->colrptrs+wd->numColors; cpp++){
	       cp1 = *cpp;
	       if (cp1->c == c) {
		  if (cp1->name[0]=='\0') {
		     XQueryColor(d1, wd1->cmap, &clr);
		     cp1->r = clr.red;
		     cp1->g = clr.green;
		     cp1->b = clr.blue;
		     sprintf(cp1->name,"%d,%d,%d",cp1->r,cp1->g,cp1->b);
		     }
		  cp2 = alc_rgb(w2, cp1->name, cp1->r, cp1->g, cp1->b, 0);
		  if (cp2 == NULL) return Failed;
		  break;
		  }
	       }
	    if (cp2 == NULL) {
	       XQueryColor(d1, wd1->cmap, &clr);
	       cp2 = alc_rgb(w2, "unknown", clr.red, clr.green, clr.blue, 0);
	       }
	    if (cp2 == NULL) return Failed;
	    XSetForeground(stddpy, stdgc, cp2->c);
	    RENDER2(XDrawPoint, i + x2, j + y2);
	    }
	 }
      XSetForeground(stddpy, stdgc,
		     wc->fg->c ^ (ISXORREVERSE(w2) ? wc->bg->c : 0));
      XSetFunction(stddpy, stdgc, wc->drawop);
      XSync(stddpy,False);
      XDestroyImage(xim);
      }
   else {
      /*
       * Copying is between windows on one display, perhaps the same window.
       */
      src = ws1->pix;
      if (src != stdpix) {
	 /* copying between different windows; handle color bookkeeping */
         if (!x2 && !y2 &&
            ((width  >= ws2->pixwidth)  || !width) &&
            ((height >= ws2->pixheight) || !height) && w2->context->clipw < 0)
	       free_xcolors(w2, 0);
	 copy_colors(w, w2);
	 }

      XSetForeground(stddpy, stdgc, wc->bg->c);
      XSetFunction(stddpy, stdgc, GXcopy);

      if (x+width<0 || y+height<0 || x>=ws1->pixwidth || y>=ws1->pixheight) {
	 /* source is entirely offscreen */
         RENDER4(XFillRectangle, x2, y2, width, height);
         }
      else {
	 /*
	  * Check for source partially offscreen, but copy first and
	  * fill later in case the source and destination overlap.
	  */
	 lpad = rpad = tpad = bpad = 0;
         if (x < 0) {			/* source extends past left edge */
	    lpad = -x;
            width -= lpad;
            x2 += lpad;
            x = 0;
            }
         if (x + width > ws1->pixwidth) {  /* source extends past right edge */
            rpad = x + width - ws1->pixwidth;
            width -= rpad;
            }
         if (y < 0) {			/* source extends above top edge */
	    tpad = -y;
            height -= tpad;
            y2 += tpad;
            y = 0;
            }
         if (y + height > ws1->pixheight) {  /* source extends below bottom */
            bpad = y + height - ws1->pixheight;
            height -= bpad;
            }
	 /*
	  * Copy the area.
	  */
         if (stdwin)
            XCopyArea(stddpy, src, stdwin, stdgc, x, y, width, height, x2, y2);
         XCopyArea(stddpy, src, stdpix, stdgc, x, y, width, height, x2, y2);
	 /*
	  * Fill any edges not provided by source.
	  */
	 if (lpad > 0)
            RENDER4(XFillRectangle, x2-lpad, y2-tpad, lpad, tpad+height+bpad);
	 if (rpad > 0)
            RENDER4(XFillRectangle, x2+width, y2-tpad, rpad, tpad+height+bpad);
	 if (tpad > 0)
	    RENDER4(XFillRectangle, x2, y2-tpad, width, tpad);
	 if (bpad > 0)
	    RENDER4(XFillRectangle, x2, y2+height, width, bpad);
	 }

      XSetForeground(stddpy,stdgc,wc->fg->c^(ISXORREVERSE(w2) ? wc->bg->c :0));
      XSetFunction(stddpy, stdgc, wc->drawop);
      }
   return Succeeded;
   }

int getdefault(wbp w, char *prog, char *opt, char *answer)
   {
   char *p;
   STDLOCALS(w);


   if ((p = XGetDefault(stddpy,prog,opt)) == NULL)
      return Failed;
   strcpy(answer, p);
   return Succeeded;
   }


/*
 * Allocate a mutable color
 */
int mutable_color(wbp w, dptr argv, int ac, int *retval)
   {
   XColor colorcell;
   LinearColor clr;
   unsigned long plane_masks[1], pixels[1];
   char *colorname;
   tended char  *str;
   int i;
   {
   STDLOCALS(w);

   if (!XAllocColorCells(stddpy,wd->cmap,False,plane_masks,0,pixels,1)) {
      /*
       * try again with a virtual colormap
       */
      if (!go_virtual(w) ||
	  !XAllocColorCells(stddpy,wd->cmap,False,plane_masks,0,pixels,1))
	 return Failed;                         /* cannot allocate an entry */
      }

   /*
    * allocate a slot in wdisplay->colors and wstate->theColors arrays
    */
   i = alc_centry(wd);
   if (i == 0)
      return Failed;
   wd->colrptrs[i]->type = CMUTABLE;
   wd->colrptrs[i]->c = pixels[0];

   /* save color index as "name", followed by a null string for value */
   colorname = wd->colrptrs[i]->name;
   sprintf(colorname, "%ld", -pixels[0] - 1);	/* index is name */
   colorname = colorname + strlen(colorname) + 1;
   *colorname = '\0';				/* value unknown */

   if (ws->numColors < WMAXCOLORS) {
      if (ws->theColors == NULL) {
	 ws->theColors = (short *)calloc(WMAXCOLORS, sizeof(short));
	 if (ws->theColors == NULL)
	    return Error;
	 }
      ws->theColors[ws->numColors++] = i;
      }

   if (ac > 0) {                     /* set the color */
      if (ac != 1) return Error;
      /*
       * old-style check for C integer
       */
      else if (argv[0].dword == D_Integer) {/* check for color cell */
	 if (IntVal(argv[0]) >= 0)
	    return Failed;        /* must be negative */
	 colorcell.pixel = -IntVal(argv[0]) - 1;
	 XQueryColor(stddpy, wd->cmap, &colorcell);
         clr = lcolor(w, colorcell);
         sprintf(colorname, "%ld,%ld,%ld", clr.red, clr.green, clr.blue);
	 }
      else {
	 if (!cnv:C_string(argv[0],str)) {
	    ReturnErrVal(103,argv[0], Error);
	    }
         if (parsecolor(w, str, &clr.red, &clr.green, &clr.blue) != Succeeded) {
            free_xcolor(w, pixels[0]);
	    return Failed;                   /* invalid color specification */
	    }
	 strcpy(colorname, str);
         colorcell = xcolor(w, clr);
	 }
      colorcell.pixel = pixels[0];
      XStoreColor(stddpy, wd->cmap, &colorcell);
      }

   *retval = (-pixels[0] - 1);
   return Succeeded;
   }
   }

char *get_mutable_name(wbp w, int mute_index)
   {
   wdp dp;
   int i;
   char *colorname;

   dp = w->window->display;
   for (i = 2; i < dp->numColors; i++)
      if (dp->colrptrs[i]->type == CMUTABLE
      && dp->colrptrs[i]->c == - mute_index - 1)
         break;
   if (i == dp->numColors)
      return NULL;
   colorname = dp->colrptrs[i]->name;			/* color name field */
   colorname = colorname + strlen(colorname) + 1;	/* set value follows */
   return colorname;
   }

int set_mutable(wbp w, int i, char *s)
   {
   LinearColor clr;
   XColor colorcell;
   wdp dp = w->window->display;

   if (parsecolor(w, s, &clr.red, &clr.green, &clr.blue) != Succeeded)
      return Failed;                        /* invalid color specification */
   colorcell = xcolor(w, clr);
   colorcell.pixel = -i - 1;
   XStoreColor(dp->display, dp->cmap, &colorcell);
   return Succeeded;
   }

void free_mutable(wbp w, int mute_index)
   {
   wdp dp;
   int i;

   dp = w->window->display;
   for (i = 2; i < dp->numColors; i++)
      if (dp->colrptrs[i]->type == CMUTABLE
      && dp->colrptrs[i]->c == - mute_index - 1)
         break;
   if (i != dp->numColors)
      free_xcolor(w, dp->colrptrs[i]->c);
   }


void freecolor(wbp w, char *s)
   {
   wdp dp;
   int i;
   LinearColor clr;
   XColor color;

   if (parsecolor(w, s, &clr.red, &clr.green, &clr.blue) != Succeeded)
      return;
   dp = w->window->display;
   color = xcolor(w, clr);

   for (i = 2; i < dp->numColors; i++)
      if (dp->colrptrs[i]->r == color.red && dp->colrptrs[i]->g == color.green
      && dp->colrptrs[i]->b == color.blue && dp->colrptrs[i]->type != CMUTABLE)
         break;
   if (i != dp->numColors)
      free_xcolor(w, dp->colrptrs[i]->c);
   }

/*
 * Draw a bilevel image
 */
int blimage(wbp w, int x, int y, int width, int height,
   int ch, unsigned char *s, word len)
   {
   unsigned int m, msk1, c, ix, iy;
   long fg, bg;
   XImage *im;
   STDLOCALS(w);

   /*
    * Get an XImage structure and free the old color set if possible.
    */
   im = getximage(w, x, y, width, height, ch == TCH1);
   if (im == NULL)
      return Error;

   /*
    * Read the image string and set the pixel values.  Note that
    * the hex digits in sequence fill the rows *right to left*.
    */
   m = width % 4;
   if (m == 0)
      msk1 = 8;
   else
      msk1 = 1 << (m - 1);		/* mask for first byte of row */

   fg = wc->fg->c;
   bg = wc->bg->c;
   ix = width;
   iy = 0;
   m = msk1;
   while (len--) {
      if (isxdigit(c = *s++)) {		/* if hexadecimal character */
         if (!isdigit(c))		/* fix bottom 4 bits if necessary */
            c += 9;
         while (m > 0) {		/* set (usually) 4 pixel values */
	    --ix;
	    if (c & m)
	       XPutPixel(im, ix, iy, fg);
	    else if (ch != TCH1)	/* if zeroes aren't transparent */
	       XPutPixel(im, ix, iy, bg);
            m >>= 1;
            }
         if (ix == 0) {			/* if end of row */
            ix = width;
            iy++;
            m = msk1;
            }
         else
            m = 8;
         }
      }
   if (ix > 0)				/* pad final row if incomplete */
      while (ix < width)
         XPutPixel(im, ix++, iy, bg);

   /*
    * Put it on the screen.
    */
   if (wc->drawop != GXcopy) XSetFunction(stddpy, stdgc, GXcopy);
   RENDER7(XPutImage, im, 0, 0, x, y, width, height);
   XDestroyImage(im);
   if (wc->drawop != GXcopy) XSetFunction(stddpy, stdgc, wc->drawop);
   return Succeeded;
   }

/*
 * Draw a character-per-pixel image
 */
int strimage(wbp w, int x, int y, int width, int height,
   struct palentry *e, unsigned char *s, word len, int on_icon)
   {
   int c, v, ret, trans;
   unsigned int r, g, b, ix, iy;
   wclrp cp, cplist[256];
   char tmp[24];
   XImage *im;
   STDLOCALS(w);

   /*
    * Get an XImage structure and free the old color set if possible.
    */
   trans = 0;
   for (c = 0; c < 256; c++)
      trans |= e[c].used && e[c].transpt;
   im = getximage(w, x, y, width, height, trans);
   if (im == NULL)
      return -1;

   /*
    * Allocate the colors we need.  Use black or white if unsuccessful.
    */
   ret = 0;
   for (c = 0; c < 256; c++)
      if (e[c].used && e[c].valid) {
         r = e[c].clr.red;
         g = e[c].clr.green;
         b = e[c].clr.blue;
         sprintf(tmp, "%d,%d,%d", r, g, b);
         cp = alc_rgb(w, tmp, r, g, b, 0);
         if (cp == NULL) {
            ret++;
            if ((0.299 * r + 0.587 * g + 0.114 * b) > 32767)
               cp = alc_rgb(w, "white", 65535, 65535, 65535, 0);
            else
               cp = alc_rgb(w, "black", 0, 0, 0, 0);
            }
         cplist[c] = cp;
         }

   /*
    * Read the image string and set the pixel values.
    */
   ix = iy = 0;
   while (len--) {
      c = *s++;
      v = e[c].valid;
      if (v)				/* put char if valid */
         XPutPixel(im, ix, iy, cplist[c]->c);
      if (v || e[c].transpt) {		/* advance if valid or transparent */
         if (++ix >= width) {
            ix = 0;			/* reset for new row */
            iy++;
            }
         }
      }
   if (ix > 0)				/* pad final row if incomplete */
      while (ix < width)
         XPutPixel(im, ix++, iy, wc->bg->c);

   /*
    * Put it on the screen.
    */
   if (wc->drawop != GXcopy) XSetFunction(stddpy, stdgc, GXcopy);
   if (on_icon) {
      if (ws->iconwin == (Window) NULL) makeIcon(w, 0, 0);
      XPutImage(stddpy, ws->iconwin, stdgc, im, 0, 0, x, y, width, height);
      XPutImage(stddpy, ws->iconpix, stdgc, im, 0, 0, x, y, width, height);
      }
   else {
      XPutImage(stddpy, ws->pix, stdgc, im, 0, 0, x, y, width, height);
      if (ws->win)
	 XCopyArea(stddpy, ws->pix, ws->win, stdgc, x, y, width, height, x, y);
      }
   XDestroyImage(im);
   if (wc->drawop != GXcopy) XSetFunction(stddpy, stdgc, wc->drawop);
   return ret;
   }

/*
 * capture(w, x, y, width, height, data) -- get image region.
 *
 * Stores the specified subimage in data as 15-bit linear color.
 */
int capture(wbp w, int x, int y, int width, int height, short *data)
   {
   Visual *v;
   XImage *im;
   XColor colorcell;
   unsigned char cmap[256];
   short *cval;
   int i, r, g, b;
   unsigned long px, *ckey;
   double gamma = w->context->gamma;
   STDLOCALS(w);

   /*
    * Get an XImage structure containing window pixel values.
    */
   im = getximage(w, x, y, width, height, 1);
   if (!im)
      return 0;

   /*
    * Make a mapping table from X color to 5-bit linear color.
    */
   for (i = 0; i < 256; i++)
      cmap[i] = 31 * pow(i / 255., gamma) + 0.5;

   /*
    * Translate the colors and store in the data buffer.
    */
   v = wd->visual;
   if (v->class == TrueColor && v->red_mask == 0x00FF0000L
   && v->green_mask == 0x0000FF00L && v->blue_mask == 0x000000FFL) {
      /*
       * 24-bit RGB is decomposed and mapped directly
       */
      for (y = 0; y < height; y++) {
         for (x = 0; x < width; x++) {
            px = XGetPixel(im, x, y);
            r = cmap[(px >> 16) & 0xFF];
            g = cmap[(px >> 8) & 0xFF];
            b = cmap[px & 0xFF];
            *data++ = (r << 10) | (g << 5) | b;
            }
         }
      }
   else {
      /*
       * General case uses a cache to improve performance.
       */
      #define CCACHE 4987	/* cache size; should be odd */
      ckey = calloc(CCACHE, sizeof(ckey[0]));
      cval = calloc(CCACHE, sizeof(cval[0]));
      if (!ckey || !cval)
         return 0;
      for (y = 0; y < height; y++) {
         for (x = 0; x < width; x++) {
            px = XGetPixel(im, x, y);		/* get pixel value */
            i = px % CCACHE;			/* get cache index */
            if (ckey[i] != px) {		/* if color not cached */
               colorcell.pixel = px;
               colorcell.flags = DoRed | DoGreen | DoBlue;
               XQueryColor(stddpy, wd->cmap, &colorcell);	/* costly */
               ckey[i] = px;
               cval[i] = (cmap[colorcell.red >> 8] << 10) |
   	          (cmap[colorcell.green >> 8] << 5) | cmap[colorcell.blue >> 8];
               }
            *data++ = cval[i];			/* save rgb15 color value */
            }
         }
      free(cval);
      free(ckey);
      }
   XDestroyImage(im);
   return 1;
   }

/*
 * Create an XImage structure corresponding to subimage (x, y, w, h).
 * If init is nonzero, initialize it with current contents.
 * If init is zero and (x,y,w,h) fills the window, free existing color set.
 */
static XImage *getximage(wbp w, int x, int y, int width, int height, int init)
   {
   int tx, ty;
   XImage *im;
   STDLOCALS(w);

   im = XCreateImage(stddpy, DefaultVisual(stddpy, wd->screen),
      DefaultDepth(stddpy, wd->screen), ZPixmap, 0, NULL, width, height, 32, 0);
   if (im == NULL)
      return NULL;
   im->data = malloc(im->bytes_per_line * height);
   if (im->data == NULL) {
      XDestroyImage(im);
      return NULL;
      }

   if (!init) {
      if (x <= 0 && y <= 0 && x + width >= ws->pixwidth &&
	 y + height >= ws->pixheight && wc->clipw < 0)
            free_xcolors(w, 0);
      return im;
      }

   tx = ty = 0;
   if (x < 0)  { tx -= x; width += x; x = 0; }
   if (y < 0)  { ty -= y; height += y; y = 0; }
   if (x + width > ws->width)  { width = ws->width - x; }
   if (y + height > ws->height) { height = ws->height - y; }
   if (width > 0 && height > 0)
      XGetSubImage(stddpy, stdpix, x, y, width, height, AllPlanes, ZPixmap,
		   im, tx, ty);
   return im;
   }

int readimage(wbp w, char *filename, int x, int y, int *status)
   {
   Pixmap p;
   unsigned int width, height;
   STDLOCALS(w);
   if (!x && !y)
      p = loadimage(w, filename, &height, &width, 1, status);
   else
      p = loadimage(w, filename, &height, &width, 0, status);
   if (p == (Pixmap) NULL) return Failed;

   if (wc->drawop != GXcopy)
      XSetFunction(stddpy, stdgc, GXcopy);
   if (stdwin)
      XCopyArea(stddpy, p, stdwin, stdgc, 0, 0, width, height, x, y);
   XCopyArea(stddpy, p, stdpix, stdgc, 0, 0, width, height, x, y);
   if (wc->drawop != GXcopy)
      XSetFunction(stddpy,stdgc,wc->drawop);

   /*
    * Make sure previous ops on p are complete, then free it.
    */
   XSync(stddpy, False);
   XFreePixmap(stddpy, p);
   return Succeeded;
   }


/*
 * Initialize client for producing pixels from a window
 */
int getpixel_init(wbp w, struct imgmem *imem)
   {
   STDLOCALS(w);

   if (imem->width <= 0 || imem->height <= 0) {
      imem->im = NULL;
      return Succeeded;
      }

   imem->im = XGetImage(stddpy, stdpix,
			imem->x, imem->y, imem->width, imem->height,
		  (1 << DefaultDepth(stddpy, wd->screen))-1, XYPixmap);

   if (imem->im == NULL) return Failed;
   return Succeeded;
   }

int getpixel_term(wbp w, struct imgmem *imem)
   {
   if (imem->im != NULL)
      XDestroyImage(imem->im);
   return Succeeded;
   }

/*
 * Return pixel (x,y) from a window in long value (rv)
 */
int getpixel(wbp w, int x, int y, long *rv, char *s, struct imgmem *imem)
   {
   XColor colorcell;
   LinearColor clr;
   wclrp *cpp;
   unsigned long c;
   STDLOCALS(w);

   if (x < imem->x || x >= imem->x + imem->width ||
       y < imem->y || y >= imem->y + imem->height)
      c = colorcell.pixel = wc->bg->c;
   else
      c = colorcell.pixel = XGetPixel(imem->im, x - imem->x, y - imem->y);
   *rv = 0xff000000;

   for (cpp = wd->colrptrs ; cpp < wd->colrptrs + wd->numColors; cpp++) {
      if ((*cpp)->c == c) {
	 if ((*cpp)->type == CMUTABLE)
	    *rv = -c - 1;
	 else {
            *rv = 1;
            colorcell.red = (*cpp)->r;
            colorcell.green = (*cpp)->g;
            colorcell.blue = (*cpp)->b;
            clr = lcolor(w, colorcell);
            sprintf(s, "%ld,%ld,%ld", clr.red, clr.green, clr.blue);
	    }
	 break;
	 }
      }
   if (*rv == 0xff000000) {
      XQueryColor(stddpy, wd->cmap, &colorcell);
      *rv = 1;
      clr = lcolor(w, colorcell);
      sprintf(s, "%ld,%ld,%ld", clr.red, clr.green, clr.blue);
      }
   return Succeeded;
   }


int query_pointer(wbp w, XPoint *pp)
   {
   Display *theDisplay;
   Window theWindow;
   Window garbage1, garbage2;
   int root_x, root_y, win_x, win_y;
   unsigned int key_buttons;

   theDisplay = w->window->display->display;
   theWindow  = w->window->win;
   if (theWindow == (Window) NULL) return Failed;

   XQueryPointer(theDisplay, theWindow, &garbage1, &garbage2,
		 &root_x, &root_y, &win_x, &win_y, &key_buttons);
   pp->x = w->window->pointerx = win_x;
   pp->y = w->window->pointery = win_y;
   return Succeeded;
   }

int query_rootpointer(XPoint *pp)
   {
   Display *theDisplay;
   Window theWindow;
   Window garbage1, garbage2;
   int root_x, root_y, win_x, win_y;
   unsigned int key_buttons;
   wdp wd;
   if (wdsplys == NULL) {
      /*
       * Initialize the window system
       */
      Protect(wd = alc_display(NULL), return Failed);

      theDisplay = wd->display;
      theWindow  = DefaultRootWindow(wd->display);
      }
   else {
      wd = wdsplys;
      theDisplay = wd->display;
      theWindow  = DefaultRootWindow(wd->display);
      }
   XQueryPointer(theDisplay, theWindow, &garbage1, &garbage2,
		 &root_x, &root_y, &win_x, &win_y, &key_buttons);
   pp->x = root_x;
   pp->y = root_y;
   return Succeeded;
   }


int patbits[] = {
  0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
  0xFE,0xFF,0xEF,0xFF,0xFE,0xFF,0xEF,0xFF,
  0x77,0xDD,0x77,0xDD,0x77,0xDD,0x77,0xDD,
  0x55,0xAA,0x55,0xAA,0x55,0xAA,0x55,0xAA,
  0x11,0x44,0x11,0x44,0x11,0x44,0x11,0x44,
  0x01,0x00,0x10,0x00,0x01,0x00,0x10,0x00,
  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,

  0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,
  0x80,0x40,0x20,0x10,0x08,0x04,0x02,0x01,
  0x00,0x00,0x00,0xFF,0x00,0x00,0x00,0x00,
  0x10,0x10,0x10,0xFF,0x10,0x10,0x10,0x10,
  0x82,0x44,0x28,0x10,0x28,0x44,0x82,0x01,

  0x0F,0x0F,0x0F,0x0F,0xF0,0xF0,0xF0,0xF0,
  0x1B,0x18,0x81,0xB1,0x36,0x06,0x60,0x63,
  0x02,0x02,0x05,0xF8,0x20,0x20,0x50,0x8F,
  0x03,0x84,0x48,0x30,0x03,0x84,0x48,0x30,
};

/*
 * pattern symbols
 */
stringint siPatternSyms[] = {
  {0,		16},
  { "black",	 0},
  { "checkers",	 12},
  { "darkgray",	 2},
  { "diagonal",	 8},
  { "grains",	 13},
  { "gray",	 3},
  { "grid",	 10},
  { "horizontal",9},
  { "lightgray", 4},
  { "scales",	 14},
  { "trellis",	 11},
  { "vertical",	 7},
  { "verydark",	 1},
  { "verylight", 5},
  { "waves",	 15},
  { "white",	 6},
};

/*
 * SetPattern
 */
int SetPattern(wbp w, char *name, int len)
   {
   int width, nbits;
   int i;
   int symbol;
   C_integer v, bits[MAXXOBJS];
   Pixmap p;
   char data[MAXXOBJS];
   char *buf = data;
   STDLOCALS(w);

   if (wc->patternname != NULL)
      free(wc->patternname);
   wc->patternname = malloc(len+1);
   if (wc->patternname == NULL) ReturnErrNum(305, Error);
   strncpy(wc->patternname, name, len);
   wc->patternname[len] = '\0';

   /*
    * If the pattern starts with a number it is a width , bits encoding
    */
   if ((len > 0) && isdigit(name[0])) {
      nbits = MAXXOBJS;
      switch (parsepattern(name, len, &width, &nbits, bits)) {
      case Failed:
	 return Failed;
      case Error:
	 ReturnErrNum(145, Error);
	 }
      if (!stdgc) return Succeeded;
      return SetPatternBits(w, width, bits, nbits);
      }

   /*
    * Otherwise, it is a named pattern.  Find the symbol id.
    */
   if ((symbol = si_s2i(siPatternSyms, wc->patternname)) >= 0) {
      if (!stdgc) return Succeeded;
      for(i = 0; i < 8; i++) {
	 v = patbits[symbol * 8 + i];
	 *buf++ = v;
	 }
      p = XCreateBitmapFromData(stddpy, stdpix, data, 8, 8);
      XSetStipple(stddpy, stdgc, p);
      XSync(stddpy, False);
      XFreePixmap(stddpy, p);
      return Succeeded;
      }
   return Failed;
   }

int SetPatternBits(wbp w, int width, C_integer *bits, int nbits)
   {
   C_integer v;
   int i, j;
   Pixmap p;
   char data[MAXXOBJS];
   char *buf = data;
   STDLOCALS(w);

   for(i = 0; i < nbits; i++) {
      v = bits[i];
      for(j=0; j<width; j+=8) {
	 *buf++ = v;
	 v >>= 8;
	 }
      }

   p = XCreateBitmapFromData(stddpy, stdpix, data, width, nbits);
   XSetStipple(stddpy, stdgc, p);
   XSync(stddpy, False);
   XFreePixmap(stddpy, p);
   return Succeeded;
   }



/*
 * remap a window ... this time with an iconwin
 */
int remap(wbp w, int x, int y)
   {
   XSizeHints size_hints;
   XWMHints   *wmhints;
   STDLOCALS(w);

   XGetSizeHints(stddpy, stdwin, &size_hints, XA_WM_NORMAL_HINTS);
   wmhints = XGetWMHints(stddpy, stdwin);
   if (ws->iconwin)
      XDestroyWindow(stddpy, ws->iconwin);
   if (stdwin)
      XDestroyWindow(stddpy, stdwin);

   ws->win = XCreateSimpleWindow(stddpy, DefaultRootWindow(stddpy),
				 ws->posx, ws->posy, ws->width,
				 ws->height, 4, wc->fg->c, wc->bg->c);
   XSetStandardProperties(stddpy, ws->win, ws->windowlabel,
			  ws->iconlabel, 0, 0, 0, &size_hints);
   XSelectInput(stddpy, ws->win, ExposureMask | KeyPressMask |
		ButtonPressMask | ButtonReleaseMask | ButtonMotionMask |
		StructureNotifyMask);

   ws->iconwin = XCreateSimpleWindow(stddpy, DefaultRootWindow(stddpy),
				     ws->iconx, ws->icony, ws->iconw,
				     ws->iconh, 2, wc->fg->c, wc->bg->c);
   XSelectInput(stddpy, ws->iconwin,
		ExposureMask | KeyPressMask | ButtonPressMask);

   wmhints->flags |= IconPositionHint;
   wmhints->icon_x = x;
   wmhints->icon_y = y;
   wmhints->initial_state = ws->iconic;
   wmhints->icon_window = ws->iconwin;
   wmhints->flags |= IconWindowHint;
   XSetWMHints(stddpy, ws->win, wmhints);
   CLREXPOSED(w);
   XMapWindow(stddpy, ws->win);
   if (ws->iconic == NormalState) {
      while (!ISEXPOSED(w))
         if (pollevent() == -1) return -1;
      }
   ws->iconx = x;
   ws->icony = y;
   XSync(stddpy, False);
   XFree((char *)wmhints);
   return 1;
   }


int seticonimage(wbp w, dptr dp)
   {
   int status;
   Pixmap pix;
   tended char *tmp;
   {
   STDLOCALS(w);
   /*
    * get the preloaded (in another window value) pixmap image
    */
   if (is:file(*dp) && (BlkLoc(*dp)->file.status & Fs_Window)) {
      wbp x = (wbp)BlkLoc(*dp)->file.fd;
      if ((ws->iconimage = salloc(x->window->windowlabel)) == NULL)
	 ReturnErrNum(305, Error);
      pix = XCreatePixmap(stddpy, DefaultRootWindow(stddpy),
			  x->window->width, x->window->height,
			  DefaultDepth(stddpy,wd->screen));

      XCopyArea(stddpy, x->window->pix, pix, wd->icongc, 0, 0,
		x->window->width, x->window->height, 0, 0);
      if (ws->iconpix) {
	 XSync(stddpy, False);
	 XFreePixmap(stddpy, ws->iconpix);
	 }
      ws->iconpix = pix;
      ws->iconw = x->window->width;
      ws->iconh = x->window->height;
      if (!ws->iconx && !ws->icony) {
	 ws->iconx = ws->x;
	 ws->icony = ws->y;
	 }
      if (remap(w,ws->iconx,ws->icony) == -1)
	 ReturnErrNum(144, Error);

      }
   /* get the pixmap file named by x */
   else if (is:string(*dp)) {
      unsigned int height, width;
      if (!cnv:C_string(*dp,tmp))
	 ReturnErrVal(103, *dp, Error);

      if ((ws->iconimage != NULL) && strcmp(ws->iconimage, ""))
	free(ws->iconimage);
      if ((ws->iconimage = salloc(tmp)) == NULL)
	 ReturnErrNum(305, Error);
      if (ws->iconwin == (Window) NULL) makeIcon(w,0,0);
      else {
	 pix = loadimage(w, ws->iconimage, &height, &width, 0, &status);
	 if (pix == (Pixmap) NULL)
	    return Failed;
	 XCopyArea(stddpy, pix, ws->iconwin, wd->icongc,
		   0, 0, width, height, 0, 0);
	 if (ws->iconpix) {
	    XSync(stddpy, False);
	    XFreePixmap(stddpy, ws->iconpix);
	    }
	 ws->iconpix = pix;
	 ws->iconw = width;
	 ws->iconh = height;
	 if (remap(w,ws->iconx,ws->icony) == -1)
	    ReturnErrNum(144, Error);
	 }
      }
   else
      return Failed;
   return Succeeded;
   }
   }


/*
 * dumpimage -- write an image to a disk file in an X format.
 *
 * Accepts only .xpm and .xbm file names, returning NoCvt for anything else.
 */

int dumpimage(wbp w, char *filename,
   unsigned int x, unsigned int y, unsigned int width, unsigned int height)
   {
   int status;
   STDLOCALS(w);

   /*
    * Check for bilevel XBM (X BitMap) format.
    */
   if (!strcmp(".xbm", filename + strlen(filename) - 4) ||
       !strcmp(".XBM", filename + strlen(filename) - 4)) {
      /*
       * Write a bitmap from a "color" window (presumed to have only BW in it).
       * BlackPixel ^ WhitePixel will have a 1 in the first bit in which
       * they are different, so this bit is the plane we want to copy.
       */

      if (DefaultDepth(stddpy,wd->screen) != 1) {
         unsigned long bw =
            BlackPixel(stddpy,wd->screen) ^ WhitePixel(stddpy,wd->screen);
	 Pixmap p1 = XCreatePixmap(stddpy, DefaultRootWindow(stddpy),
				   width, height, 1);
	 XGCValues xgc;
	 GC thinGC;
	 int i;
	 /*
	  * pick out the bitplane on which Black and White differ
	  */
	 for(i=0;!((1<<i) & bw);i++);
	 bw &= (1<<i);
	 /*
	  * Construct a 1-bit-deep GC for use in copying the plane.
	  */
	 xgc.foreground = BlackPixel(stddpy,wd->screen);
	 xgc.background = WhitePixel(stddpy,wd->screen);
	 thinGC = XCreateGC(stddpy,p1,GCForeground|GCBackground,&xgc);

	 if (i>DefaultDepth(stddpy,wd->screen)) return Failed;
	 XCopyPlane(stddpy,stdpix,p1,thinGC,x,y,width,height,0,0,bw);
	 status= XWriteBitmapFile(stddpy, filename, p1, width, height, -1, -1);

	 XSync(stddpy, False);
	 XFreePixmap(stddpy, p1);
	 XFreeGC(stddpy,thinGC);
	 if (status != BitmapSuccess) return Failed;
         }
      else {
	 if(x || y) {
	    Pixmap p1 = XCreatePixmap(stddpy, DefaultRootWindow(stddpy), width,
				      height, DefaultDepth(stddpy,wd->screen));

	    XCopyArea(stddpy, stdpix, p1, stdgc, x, y, width, height, 0, 0);
	    XSync(stddpy, False);

	    status = XWriteBitmapFile(stddpy,filename,p1,width,height,-1,-1);

	    XSync(stddpy, False);
	    XFreePixmap(stddpy, p1);

	    if (status != BitmapSuccess) return Failed;

	  }
	 else if (XWriteBitmapFile(stddpy, filename, stdpix,
				   width, height, -1, -1) != BitmapSuccess)
	    return Failed;

       }
   return Succeeded;
   }
   /*
    * Check for XPM (color X PixMap) format.
    */
   else if (!strcmp(".xpm", filename + strlen(filename) - 4) ||
       !strcmp(".XPM", filename + strlen(filename) - 4) ||
       !strcmp(".xpm.Z", filename + strlen(filename) - 6)) {
#ifdef HaveXpmFormat
      /*
       * Could optimize by calling XpmWriteFileFromPixmap directly on the
       * stdpix...
       */
      Pixmap p1 = XCreatePixmap(stddpy, DefaultRootWindow(stddpy), width,
			       height, DefaultDepth(stddpy,wd->screen));

      XCopyArea(stddpy, stdpix, p1, stdgc, x, y, width, height, 0, 0);
      XSync(stddpy, False);

      status = XpmWriteFileFromPixmap(stddpy, filename, p1,
				      (Pixmap) NULL, NULL);
      XSync(stddpy, False);
      XFreePixmap(stddpy, p1);

      if (status == XpmSuccess)
         return Succeeded;
#endif					/* HaveXpmFormat */
      return Failed;
      }
   else
      return NoCvt;		/* not an X format -- write GIF instead */
   }

/*
 * Load an image, in any format we can figure out.
 */
Pixmap loadimage(wbp w, char *filename,
   unsigned int *height, unsigned int *width, int atorigin, int *status)
   {
   Pixmap p1, p2 = (Pixmap) NULL;
   int xhot, yhot, i, j;
   XGCValues gcv;
   unsigned long gcmask = GCFont | GCForeground | GCBackground;
   int isxbm;
   STDLOCALS(w);

   if (!strcmp(".xbm", filename + strlen(filename) - 4))
      isxbm = 1;
   else if (!strcmp(".xpm", filename + strlen(filename) - 4) ||
	    !strcmp(".xpm.Z", filename + strlen(filename) - 6))
      isxbm = 0;
   else {
      /*
       * Not sure what kind of file this is, make a guess
       * For example, the format might be on the first line of the file,
       * so open it up and read some.
       */
      FILE *ftemp = fopen(filename,"r");
      char s[6];
      int  i;

      if (!ftemp) {
	 return (Pixmap) NULL;
         }
      if ((long)fread(s,1,6,ftemp) < (long)6) {
	 fclose(ftemp);
	 return (Pixmap) NULL;
         }
      fclose(ftemp);
      /* check s for XPM string */
      isxbm = 1;         /* default to xbm */
      for (i = 0; i <= 3; i++)
	 if (!strncmp(&s[i], "XPM", 3))
	    isxbm = 0;
      }

   if (isxbm) {    /* isxbm = 1 => .xbm file */
      if (XReadBitmapFile(stddpy, DefaultRootWindow(stddpy), filename,
			  width, height, &p1, &xhot, &yhot) != BitmapSuccess)
	 return (Pixmap) NULL;
      else *status = 0;
      p2 = XCreatePixmap(stddpy, DefaultRootWindow(stddpy), *width, *height,
			 DefaultDepth(stddpy,DefaultScreen(stddpy)));
      }
   else {				/* isxbm == 0 => .xpm file */
#ifndef HaveXpmFormat
      return NULL;
#else					/* HaveXpmFormat */
      XpmAttributes a;
      XColor color;
      LinearColor clr;
      Pixmap dummy;
      a.npixels = 0;
      a.colormap = wd->cmap;
      a.valuemask = XpmReturnPixels | XpmColormap;

      *status = XpmReadFileToPixmap(stddpy, DefaultRootWindow(stddpy),
			    filename, &p2, &dummy, &a);

      if (*status == XpmColorFailed && go_virtual(w)) {
	 /* try again with a virtual colormap */
	 a.npixels = 0;
	 a.colormap = wd->cmap;
	 a.valuemask = XpmReturnPixels | XpmColormap;
         *status = XpmReadFileToPixmap(stddpy, DefaultRootWindow(stddpy),
			               filename, &p2, &dummy, &a);
	 }

      if (*status != XpmSuccess) {
         if (*status == XpmColorFailed)
	    *status = 1;
	 else
	    return (Pixmap) NULL;
         }
      else *status = 0;
      *height = a.height;
      *width = a.width;

      /*
       * if the loaded image is to cover an entire window, free up colors
       * currently in use by the window
       */
      if (atorigin && *width >= ws->pixwidth && *height >= ws->pixheight
	 && wc->clipw < 0)
            free_xcolors(w, 0);

      /*
       * OK, now register all the allocated colors with the display
       * and window in which we are residing.
       */
      for (i = 0; i < a.npixels; i++) {
	 j = alc_centry(wd);
	 if (j == 0)
	    return (Pixmap) NULL;
	 /*
	  * Store their allocated pixel (r,g,b) values.
	  */
	 color.pixel = wd->colrptrs[j]->c = a.pixels[i];
	 XQueryColor(stddpy, wd->cmap, &color);
	 wd->colrptrs[j]->r = color.red;
	 wd->colrptrs[j]->g = color.green;
	 wd->colrptrs[j]->b = color.blue;
         clr = lcolor(w, color);
         sprintf(wd->colrptrs[j]->name, "%ld,%ld,%ld",
	    clr.red, clr.green, clr.blue);
	 if (ws->numColors <= WMAXCOLORS) {
	    if (ws->theColors == NULL) {
	       ws->theColors = (short *)calloc(WMAXCOLORS, sizeof(short));
	       if (ws->theColors == NULL)
		  return (Pixmap) NULL;
	       }
	    ws->theColors[ws->numColors++] = j;
	    }
         }
#endif					/* HaveXpmFormat */
      }

   if (p2 == (Pixmap) NULL) {
      return (Pixmap) NULL;
      }

   if (stdgc == NULL) {
      gcv.foreground = wc->fg->c;
      gcv.background = wc->bg->c;
      gcv.font       = wc->font->fsp->fid;
      wc->gc = XCreateGC(stddpy, p2, gcmask, &gcv);
      stdgc = wc->gc;
      }

   if (isxbm) {
      XCopyPlane(stddpy, p1, p2, stdgc, 0, 0, *width, *height, 0, 0, 1);
      XSync(stddpy, False);
      XFreePixmap(stddpy, p1);
      }
   return p2;
   }

/*
 * Interpret a platform-specific color name s.
 * Under X, we can do this only if there is a window.
 */
int nativecolor(wbp w, char *s, long *r, long *g, long *b)
   {
   XColor colorcell;
   LinearColor clr;
   wsp ws;
   wdp wd;

   if (!w)				/* if no window, give up */
      return 0;
   ws = w->window;
   wd = ws->display;
   if (!XParseColor(wd->display, wd->cmap, s, &colorcell))
      return 0;				/* if unknown to X */
   clr = lcolor(w, colorcell);
   *r = clr.red;
   *g = clr.green;
   *b = clr.blue;
   return 1;
   }

/*
 * Convert an X color into an Icon linear color.
 */
LinearColor lcolor(wbp w, XColor colorcell)
   {
   LinearColor l;
   double gamma = w->context->gamma;

   l.red   = 65535 * pow((int)colorcell.red   / 65535.0, gamma);
   l.green = 65535 * pow((int)colorcell.green / 65535.0, gamma);
   l.blue  = 65535 * pow((int)colorcell.blue  / 65535.0, gamma);
   return l;
   }

/*
 * Convert an Icon linear color into an X colorcell.
 */
XColor xcolor(wbp w, LinearColor c)
   {
   XColor x;
   double invgamma = 1.0 / w->context->gamma;

   x.red   = 65535 * pow(c.red   / 65535.0, invgamma);
   x.green = 65535 * pow(c.green / 65535.0, invgamma);
   x.blue  = 65535 * pow(c.blue  / 65535.0, invgamma);
   x.flags = DoRed | DoGreen | DoBlue;
   return x;
   }


int raiseWindow(wbp w)
   {
   wsp ws = w->window;
   if (ws->win) {
      XRaiseWindow(ws->display->display, ws->win);
      XSetInputFocus(ws->display->display,ws->win,RevertToParent,CurrentTime);
      }
   return Succeeded;
   }

int lowerWindow(wbp w)
   {
   if (w->window->win)
      XLowerWindow(w->window->display->display, w->window->win);
   return Succeeded;
   }

int walert(wbp w, int volume)
{
  STDLOCALS(w);
  XBell(stddpy, volume);
  XFlush(stddpy);
  return Succeeded;
  }
