/*
 * File: rxrsc.ri - X Window specific resource allocation/deallocation
 *
 * Resources are allocated through a layer of internal management
 * routines in order to handle aliasing and resource sharing.
 */

static int rgbhash[5000];	/* rgb hash table */

wdp wdsplys;

wfp findfont(wbp w, char *fam, int size, int flags);
int okfont(char *spec, int size, int flags);
int fontcmp(char *font1, char *font2, int size, int flags);

/* check for color match */
#define CMATCH(cp, rr, gg, bb) \
   ((cp)->r == (rr) && (cp)->g == (gg) && (cp->b) == (bb) && \
   (cp)->type == SHARED && (cp)->refcount > 0)

/*
 * Allocate a color given linear r, g, b. Colors are shared on a
 * per-display basis, but they are often freed on a per-window basis,
 * so they are remembered in two structures.
 */
wclrp alc_rgb(w,s,r,g,b,is_iconcolor)
wbp w;
char *s;
unsigned int r,g,b;
int is_iconcolor;
   {
   wclrp cp;
   LinearColor lc;
   XColor color;
   int h, i;
   int *numColors;
   short *theColors;
   STDLOCALS(w);

   /*
    * handle black and white specially (no allocation)
    */
   if ((r == 0) && (g == 0) && (b == 0))
      return wd->colrptrs[0];
   if ((r == 65535) && (g == 65535) && (b == 65535))
      return wd->colrptrs[1];

   if (is_iconcolor) {
      if (ws->iconColors == NULL) {
	 ws->iconColors = (short *)calloc(WMAXCOLORS, sizeof(short));
	 if (ws->iconColors == NULL) return NULL;
	 }
      numColors = &(ws->numiColors);
      theColors = ws->iconColors;
      }
   else {
      if (ws->theColors == NULL) {
	 ws->theColors = (short *)calloc(WMAXCOLORS, sizeof(short));
	 if (ws->theColors == NULL) return NULL;
	 }
      numColors = &(ws->numColors);
      theColors = ws->theColors;
      }

   /*
    * Change into server-dependent R G B
    */
   lc.red = r;
   lc.green = g;
   lc.blue = b;
   color = xcolor(w, lc);
   r = color.red;
   g = color.green;
   b = color.blue;
   h = (503 * r + 509 * g + 499 * b) % ElemCount(rgbhash);

   /*
    * Search for the color in w's display
    */
   if (wd->visual->class == TrueColor) {
      /*
       * TrueColor entries are linked on hash chains.
       */
      i = rgbhash[h];
      while (i != 0 && !CMATCH(wd->colrptrs[i],r,g,b))
         i = wd->colrptrs[i]->next;
      if (i == 0)
         i = wd->numColors;		/* indicate not found */
      }
   else {
      /*
       * Search linearly through the list of colors.
       */
      for (i = 2; i < wd->numColors; i++)
         if (CMATCH(wd->colrptrs[i],r,g,b))
            break;
      }

   if (i >= wd->numColors) {
      int j;
      /*
       * color not found, must allocate
       */
      if (!XAllocColor(stddpy, wd->cmap, &color)) {
	 /* try again with a virtual colormap (but not for an icon) */
	 if (is_iconcolor || !go_virtual(w) ||
	     !XAllocColor(stddpy, wd->cmap, &color))
            return NULL;
         }

      j = alc_centry(wd);
      if (j == 0)
         return NULL;
      cp = wd->colrptrs[j];
      cp->next = rgbhash[h];
      rgbhash[h] = j;
      strcpy(cp->name, s);
      /*
       * Store server color as requested in color table.
       */
      cp->r = r;
      cp->g = g;
      cp->b = b;
      cp->c = color.pixel;
      cp->type = SHARED;
      /*
       * Remember in window color list, too, if not TrueColor visual.
       */
      if (wd->visual->class != TrueColor && *numColors < WMAXCOLORS)
         theColors[(*numColors)++] = j;
      return cp;
      }
   else {
      /* color is found, alias it and put it in the window color table */
      int k;
      for(k=0; k < *numColors; k++){
         if (theColors[k] == i) {
	    /* already there, no further action needed */
	    return wd->colrptrs[i];
            }
         }
      wd->colrptrs[i]->refcount++;
      /*
       * Remember in window color list, too, if not TrueColor visual.
       */
      if (wd->visual->class != TrueColor && *numColors < WMAXCOLORS)
         theColors[(*numColors)++] = i;
      return wd->colrptrs[i];
      }
   }

/*
 * allocate a color entry, return index
 */
int alc_centry(wd)
wdp wd;
{
   int j;

   if (wd->visual->class == TrueColor) {
      /*
       * TrueColor entries are never freed, so skip the search.
       */
      j = wd->numColors;
      }
   else {
      /*
       * Look for allocated but unused entry (beyond reserved entries 0 and 1)
       */
      for (j = 2; j < wd->numColors; j++) {
         if (wd->colrptrs[j]->refcount == 0) {
            wd->colrptrs[j]->refcount = 1;
            return j;
            }
         }
      } 

   /*
    * No unused entry found.  Make sure there's room for another pointer.
    */
   if (wd->numColors == wd->cpSize) {
      j = 2 * wd->cpSize;			/* double the array size */
      wd->colrptrs = realloc(wd->colrptrs, j * sizeof(struct wcolor *));
      if (wd->colrptrs == NULL)
         ReturnErrNum(305, 0);
      wd->cpSize = j;
      }
   /*
    * Now allocate a new entry.
    */
   j = wd->numColors;
   wd->colrptrs[j] = calloc(1, sizeof(struct wcolor));
   if (wd->colrptrs[j] == NULL)
      ReturnErrNum(305, 0);
   wd->colrptrs[j]->refcount = 1;
   wd->numColors++;
   return j;
}

/*
 * allocate by named color and return Icon color pointer.
 *  This is used by setfg and setbg.
 */
wclrp alc_color(w,s)
wbp w;
char *s;
   {
   wclrp rv;
   long r, g, b;

   /*
    * convert color to an r,g,b triple
    */
   if (parsecolor(w, s, &r, &g, &b) != Succeeded)
      return 0;

   /*
    * return Icon color structure, allocated & reference counted in display
    */
   Protect(rv = alc_rgb(w, s, r, g, b, 0), return 0);
   return rv;
   }

/*
 * copy color entries to reflect pixel transmission via CopyArea()
 * (assumes w1 and w2 are on the same display)
 */
void copy_colors(w1, w2)
wbp w1, w2;
   {
   wsp ws1 = w1->window, ws2 = w2 -> window;
   wdp wd = ws1->display;
   int i1, i2, j;

   for (i1 = 0; i1 < ws1->numColors; i1++) {
      j = ws1->theColors[i1];
      if (wd->colrptrs[j]->refcount > 0 && wd->colrptrs[j]->type != MUTABLE) {
	 for (i2 = 0; i2 < ws2->numColors; i2++) {
	    if (j == ws2->theColors[i2])
	       break;
	    }
	 if (i2 >= ws2->numColors) {
	    /* need to add this color */
	    wd->colrptrs[j]->refcount++;
	    if (ws2->display->visual->class != TrueColor
	    && ws2->numColors < WMAXCOLORS) {
	       if (ws2->theColors == NULL)
		  ws2->theColors = (short *)calloc(WMAXCOLORS, sizeof(short));
	       if (ws2->theColors == NULL)
		  break; /* unlikely bug; should fail or something */
	       ws2->theColors[ws2->numColors++] = j;
	       }
	    /* else cannot record it -- table full, or unneeded for TrueColor */
	    }
	 }
      }
   }

/*
 * free a single color allocated by a given window
 */
void free_xcolor(w,c)
wbp w;
unsigned long c;
   {
   int i;
   STDLOCALS(w);

   for (i = 0; i < ws->numColors; i++) {
      if (wd->colrptrs[ws->theColors[i]]->c == c) break;
      }
   if (i >= ws->numColors) {
      /* "free_xcolor couldn't find the color in the window\n" */
      /* (for TrueColor visuals, this is normal) */
      }
   else {
      if (--(wd->colrptrs[ws->theColors[i]]->refcount) == 0) {
	 XFreeColors(stddpy, wd->cmap, &c, 1, 0);
	 ws->numColors--;
	 if (ws->numColors != i)
	    ws->theColors[i] = ws->theColors[ws->numColors];
         }
      }
   }

/*
 * free the colors allocated by a given window.  extent indicates how much
 * to free.  extent == 0 implies window colors except black, white,
 * fg, bg, wbg, and mutable colors.  extent == 1 implies free icon colors.
 * extent == 2 implies free window AND fg/bg/wbg (window is closed)
 */
void free_xcolors(w, extent)
wbp w;
int extent;
   {
   int i;
   unsigned long toFree[WMAXCOLORS];
   int freed = 0;
   int *numColors;
   int numSaved;
   short *theColors;
   STDLOCALS(w);

   numColors = (extent==1 ? &(ws->numiColors) : &(ws->numColors));
   theColors = (extent==1 ? ws->iconColors : ws->theColors);

   numSaved = 0;
   for (i = *numColors-1; i >= 0; i--) {
      int j = theColors[i];

      if (j < 2)		/* black & white are permanent residents */
	 continue;
      /*
       * don't free fg, bg, or mutable color
       */
      if (((extent==0) && (wd->colrptrs[j] == w->context->fg)) ||
	  ((extent==0) && (wd->colrptrs[j] == w->context->bg)) ||
	  (wd->colrptrs[j]->type == MUTABLE)) {
	     theColors[numSaved++] = j;
	     continue;
	     }

#ifdef FreeColorFix
      /*
       * don't free ANY context's fg or bg
       */
      {
      wcp wc; int numhits = 0;
      for(wc=wcntxts; wc; wc=wc->next) {
	 if ((wc->fg == wd->colrptrs[j]) ||
	     (wc->bg == wd->colrptrs[j])) {
	    if (numhits == 0)
	       theColors[numSaved++] = j;
	    numhits++;
	    }
	 }
      if (numhits) {
	 if (numhits > wd->colrptrs[j]->refcount)
	    wd->colrptrs[j]->refcount = numhits;
	 continue;
	 }
      }
#endif					/* FreeColorFix */

      if (--(wd->colrptrs[j]->refcount) == 0) {
	 toFree[freed++] = wd->colrptrs[j]->c;
         }
      }
   if (freed>0)
      XFreeColors(stddpy, wd->cmap, toFree, freed,0);
   *numColors = numSaved;
   }

/*
 * Allocate a virtual colormap with all colors used by the client copied from
 * the default colormap to new colormap, and set all windows to use this new
 * colormap.  Returns 0 on failure.
 */
int go_virtual(w)
wbp w;
{
   wsp win;
   STDLOCALS(w);

   if (wd->cmap != DefaultColormap(stddpy,wd->screen))
      return 0;    /* already using a virtual colormap */

   wd->cmap = XCopyColormapAndFree(stddpy,wd->cmap);

   /* set the colormap for all the windows to the new colormap */

   for (win = wstates; win; win = win->next)
      if ((win->display->display == stddpy) & (win->win != (Window)NULL))
         XSetWindowColormap(stddpy, win->win, wd->cmap);

   return 1;
}

/*
 * allocate a display on machine s
 */
wdp alc_display(s)
char *s;
   {
   int i;
   double g;
   wdp wd;
   XColor color;
   wclrp cp;

   if (s == NULL) s = getenv("DISPLAY");
   if (s == NULL) s = "";
   for(wd = wdsplys; wd; wd = wd->next)
      if (!strcmp(wd->name,s)) {
         wd->refcount++;
	 return wd;
         }

   GRFX_ALLOC(wd, _wdisplay);

   strcpy(wd->name,s);
   wd->display = XOpenDisplay((*s=='\0') ? NULL : s);

   if (wd->display == NULL) {
      wd->refcount = 0;
      free(wd);
      return NULL;
      }
   wd->screen = DefaultScreen(wd->display);
   wd->visual = DefaultVisual(wd->display, wd->screen);
   wd->cmap = DefaultColormap(wd->display, wd->screen);

   /*
    * Allocate initial set of color slots.
    */
   wd->cpSize = 8;		/* start with room for 8 colors */
   wd->colrptrs = alloc(wd->cpSize * sizeof(struct wcolor *));
   if (wd->colrptrs == NULL)
      ReturnErrNum(305, NULL);

   /*
    * Color slots 0 and 1 are permanently reserved for black and white
    * respectively.
    */
   alc_centry(wd);		/* allocate slot 0 (ambiguous return value) */
   if (!alc_centry(wd))		/* allocate slot 1 */
      ReturnErrNum(305, NULL);

   cp = wd->colrptrs[0];
   strcpy(cp->name,"black");
   cp->type = SHARED;
   cp->r = cp->g = cp->b = 0;
   color.red = color.green = color.blue = 0;
   if (XAllocColor(wd->display, wd->cmap, &color))
      cp->c = color.pixel;
   else
      cp->c = BlackPixel(wd->display,wd->screen);

   cp = wd->colrptrs[1];
   strcpy(cp->name,"white");
   cp->type = SHARED;
   cp->r = cp->g = cp->b = 65535;
   color.red = color.green = color.blue = 65535;
   if (XAllocColor(wd->display, wd->cmap, &color))
      cp->c = color.pixel;
   else
      cp->c = WhitePixel(wd->display,wd->screen);

   /*
    * Set the default gamma correction value for windows that are
    * opened on this display.  Start with configuration default,
    * but if we can get an interpretation of "RGBi:.5/.5/.5",
    * calculate a gamma value from that instead.
    */
   wd->gamma = GammaCorrection;
   if (XParseColor(wd->display, wd->cmap, "RGBi:.5/.5/.5", &color)) {
      g = .299 * color.red + .587 * color.green + .114 * color.blue;
      g /= 65535;
      if (g >= 0.1 && g <= 0.9)		/* sanity check */
         wd->gamma = log(0.5) / log(g);
      }

   /*
    * Initialize fonts and other things.
    */
   wd->numFonts = 1;
   wd->fonts = (wfp)malloc(sizeof(struct _wfont));
   if (wd->fonts == NULL) {
      free(wd);
      return NULL;
      }
   wd->fonts->refcount = 1;
   wd->fonts->next = wd->fonts->previous = NULL;
   wd->fonts->name = malloc(6);
   if (wd->fonts->name == NULL) {
      free(wd);
      return NULL;
      }
   strcpy(wd->fonts->name,"fixed");
   wd->fonts->fsp = XLoadQueryFont(wd->display, "fixed");
   if (wd->fonts->fsp == NULL) { /* couldn't load "fixed"! */
      free(wd);
      return NULL;
      }

   {
   XGCValues gcv;
   Display *stddpy = wd->display;
   gcv.font = wd->fonts->fsp->fid;
   gcv.foreground = wd->colrptrs[0]->c;
   gcv.background = wd->colrptrs[1]->c;
   gcv.fill_style = FillSolid;
   gcv.cap_style = CapProjecting;
   wd->icongc = XCreateGC(stddpy, DefaultRootWindow(stddpy),
      GCFont | GCForeground | GCBackground | GCFillStyle | GCCapStyle, &gcv);
   if (wd->icongc == NULL) {
      free(wd);
      return NULL;
      }
   }

   wd->fonts->height = wd->fonts->fsp->ascent + wd->fonts->fsp->descent;

   GRFX_LINK(wd, wdsplys);
   return wd;
   }

/*
 * allocate font s in the display attached to w
 */
wfp alc_font(w,s)
wbp w;
char **s;
   {
   int flags, size;
   wfp rv;
   char family[MAXFONTWORD+1];
   char *stdfam;

   if (strcmp(*s, "fixed") != 0 && parsefont(*s, family, &flags, &size)) {
      /*
       * This is a legal Icon font spec (and it's not an unadorned "fixed").
       * Check first for special "standard" family names.
       */
      if (!strcmp(family, "mono")) {
         stdfam = "lucidatypewriter";
         flags |= FONTFLAG_MONO + FONTFLAG_SANS;
         }
      else if (!strcmp(family, "typewriter")) {
         stdfam = "courier";
         flags |= FONTFLAG_MONO + FONTFLAG_SERIF;
         }
      else if (!strcmp(family, "sans")) {
         stdfam = "helvetica";
         flags |= FONTFLAG_PROPORTIONAL + FONTFLAG_SANS;
         }
      else if (!strcmp(family, "serif")) {
         stdfam = "times";
         flags |= FONTFLAG_PROPORTIONAL + FONTFLAG_SERIF;
         }
      else stdfam = NULL;

      if (stdfam) {
         /*
          * Standard name: first try preferred family, then generalize.
          */
         rv = findfont(w, stdfam, size, flags);
         if (!rv)
            rv = findfont(w, "*", size, flags);
         }
      else {
         /*
          * Any other name: must match as specified.
          */
         rv = findfont(w, family, size, flags);
         }

      if (rv != NULL)
         return rv;
      }

   /*
    * Not found as an Icon name; may be an X font name.
    */
   return tryfont(w, *s);
   }

/*
 * return pointer to field i inside XLFD (X Logical Font Description) s.
 */
char *xlfd_field(s, i)
char *s;
int i;
   {
   int j = 0;
   while (j < i) {
      if (*s == '\0') return "";	/* if no such field */
      if (*s++ == '-') j++;
      }
   return s;
   }

/*
 * return size of font, treating a scalable font as having size n
 */
int xlfd_size(s, n)
char *s;
int n;
   {
   char *f;
   int z;

   f = xlfd_field(s, XLFD_Size);
   if (!*f)
      return 0;
   z = atoi(f);
   if (z != 0)
      return z;
   else
      return n;
   }

/*
 * Find the best font matching a set of specifications.
 */
wfp findfont(w, family, size, flags)
wbp w;
char *family;
int size, flags;
   {
   char fontspec[MAXFONTWORD+100];
   char *p, *weight, *slant, *width, *spacing, **fontlist;
   int n, champ, challenger, bestsize;

   /*
    * Construct a font specification that enforces any stated requirements
    * of size, weight, slant, set width, or proportionality.
    */
   if (size > 0)
      bestsize = size;
   else
      bestsize = DEFAULTFONTSIZE;

   if (flags & FONTFLAG_MEDIUM)
      weight = "medium";
   else if ((flags & FONTFLAG_DEMI) && (flags & FONTFLAG_BOLD))
      weight = "demibold";
   else if (flags & FONTFLAG_BOLD)
      weight = "bold";
   else if (flags & FONTFLAG_DEMI)
      weight = "demi";
   else if (flags & FONTFLAG_LIGHT)
      weight = "light";
   else
      weight = "*";

   if (flags & FONTFLAG_ITALIC)
      slant = "i";
   else if (flags & FONTFLAG_OBLIQUE)
      slant = "o";
   else if (flags & FONTFLAG_ROMAN)
      slant = "r";
   else
      slant = "*";

   if (flags & FONTFLAG_NARROW)
      width = "narrow";
   else if (flags & FONTFLAG_CONDENSED)
      width = "condensed";
   else if (flags & FONTFLAG_NORMAL)
      width = "normal";
   else if (flags & FONTFLAG_WIDE)
      width = "wide";
   else if (flags & FONTFLAG_EXTENDED)
      width = "extended";
   else
      width = "*";

   if (flags & FONTFLAG_PROPORTIONAL)
      spacing = "p";
   else
      spacing = "*";			/* can't specify {m or c} to X */

   sprintf(fontspec, "-*-%s-%s-%s-%s-*-*-*-*-*-%s-*-*-*",
      family, weight, slant, width, spacing);

   /*
    * Get a list of matching fonts from the X server and find the best one.
    */
   fontlist = XListFonts(w->window->display->display, fontspec, 2500, &n);
   champ = 0;
   while (champ < n && !okfont(fontlist[champ], size, flags))
      champ++;
   if (champ >= n) {
      XFreeFontNames(fontlist);
      return NULL;			/* nothing acceptable */
      }
   for (challenger = champ + 1; challenger < n; challenger++)
      if (okfont(fontlist[challenger], size, flags)
      && fontcmp(fontlist[challenger], fontlist[champ], bestsize, flags) < 0)
         champ = challenger;

   /*
    * Set the scaling field, if needed, and load the font.
    */
   p = xlfd_field(fontlist[champ], XLFD_Size);
   if (p[0] == '0' && p[1] == '-')
      sprintf(fontspec, "%.*s%d%s", p - fontlist[champ],
         fontlist[champ], bestsize, p + 1);
   else
      strcpy(fontspec, fontlist[champ]);
   XFreeFontNames(fontlist);
   return tryfont(w, fontspec);
   }

/*
 * check for minimum acceptability of a font
 * (things that couldn't be filtered by the XLFD pattern):
 * -- size wrong (there's a bug in OpenWindows 3.3 else X could do it)
 * -- not monospaced (can't set pattern to match m or c but not p)
 */
int okfont(spec, size, flags)
char *spec;
int size, flags;
   {
   if (size > 0 && xlfd_size(spec, size) != size)
      return 0;			/* can't match explicit size request */
   if ((flags & FONTFLAG_MONO) && xlfd_field(spec, XLFD_Spacing)[0] == 'p')
      return 0;			/* requested mono, but this isn't */
   return 1;
   }

/*
 * rank two fonts based on whether XLFD field n matches a preferred value.
 * returns <0 if font1 is better, >0 if font2 is better, else 0.
 */
int fieldcmp(font1, font2, value, field)
char *font1, *font2, *value;
int field;
   {
   int len, r1, r2;

   len = strlen(value);
   r1 = (strncmp(xlfd_field(font1, field), value, len) == 0);
   r2 = (strncmp(xlfd_field(font2, field), value, len) == 0);
   return r2 - r1;			/* -1, 0, or 1 */
   }

/*
 * rank two fonts.
 * returns <0 if font1 is better, >0 if font2 is better, else 0.
 *
 * Note that explicit requests for size, slant, weight, and width caused
 * earlier filtering in findfont(), so all those flags aren't checked
 * again here; normal values are just favored in case nothing was specified.
 */
int fontcmp(font1, font2, size, flags)
char *font1, *font2;
int size, flags;
   {
   int n;

/* return if exactly one of the fonts matches value s in field n */
#define PREFER(s,n) \
do { int r = fieldcmp(font1, font2, s, n); if (r != 0) return r; } while (0)

/* return if exactly one of the fonts does NOT match value s in field n */
#define SPURN(s,n) \
do { int r = fieldcmp(font1, font2, s, n); if (r != 0) return -r; } while (0)

   /*
    * Prefer the font that is closest to the desired size.
    */
   n = abs(size - xlfd_size(font1, size)) - abs(size - xlfd_size(font2, size));
   if (n != 0)
      return n;

   /*
    * try to check serifs (though not always indicated in X font description)
    */
   if (flags & FONTFLAG_SANS) {
      PREFER("sans", XLFD_AddStyle);
      SPURN("serif", XLFD_AddStyle);
      }
   else if (flags & FONTFLAG_SERIF) {
      PREFER("serif", XLFD_AddStyle);
      SPURN("sans", XLFD_AddStyle);
      }

   /*
    * prefer normal values for other fields.  These only have an effect
    * for fields that were wildcarded when requesting the font list.
    */
   PREFER("r", XLFD_Slant);		/* prefer roman slant */
   PREFER("medium", XLFD_Weight);	/* prefer medium weight */
   SPURN("demi", XLFD_Weight);		/* prefer non-demi if no medium */
   PREFER("normal", XLFD_SetWidth);	/* prefer normal width */
   PREFER("iso8859", XLFD_CharSet);	/* prefer font of ASCII chars */
   SPURN("0", XLFD_PointSize);		/* prefer tuned font to scaled */
   PREFER("adobe", XLFD_Foundry);	/* these look better than others */

   /* no significant difference */
   return 0;
   }

/*
 * load a font and return a font structure.
 */

wfp tryfont(w,s)
wbp w;
char *s;
   {
   wdp wd = w->window->display;
   wfp rv;
   /*
    * see if the font is already loaded on this display
    */
   for(rv = wd->fonts; rv != NULL; rv = rv->next) {
      if (!strcmp(s,rv->name)) break;
      }
   if (rv != NULL) {
      rv->refcount++;
      return rv;
      }

   /*
    * load a new font
    */
   GRFX_ALLOC(rv, _wfont);
   rv->name = malloc(strlen(s) + 1);
   if (rv->name == NULL) ReturnErrNum(305, NULL);
   strcpy(rv->name, s);
   rv->fsp = XLoadQueryFont(wd->display, rv->name);
   if (rv->fsp == NULL){
      free(rv->name);
      free(rv);
      return NULL;
      }
   rv->height = rv->fsp->ascent + rv->fsp->descent;
   w->context->leading = rv->height;

   /*
    * link the font into this displays fontlist (but not at the head!)
    */
   rv->next = wd->fonts->next;
   rv->previous = wd->fonts;
   if (wd->fonts->next) wd->fonts->next->previous = rv;
   wd->fonts->next = rv;
   return rv;
   }

/*
 * allocate a context.  Can't be called until w has a display and window.
 */
wcp alc_context(w)
wbp w;
   {
   wcp wc;
   wdp wd = w->window->display;

   GRFX_ALLOC(wc, _wcontext);
   wc->serial = ++context_serial;
   wc->display = wd;
   wd->refcount++;
   wc->fg = wd->colrptrs[0];
   wc->fg->refcount++;
   wc->bg = wd->colrptrs[1];
   wc->bg->refcount++;
   wc->font = wd->fonts;
   wc->leading = wd->fonts->height;
   wc->drawop = GXcopy;
   wc->gamma = wd->gamma;
   wc->clipx = wc->clipy = 0;
   wc->clipw = wc->cliph = -1;
   wc->linewidth = 1;

   GRFX_LINK(wc, wcntxts);
   return wc;
   }

/*
 * allocate a context, cloning attributes from an existing context
 */
wcp clone_context(w)
wbp w;
   {
   wcp wc, rv;
   XGCValues gcv;
   XRectangle rec;
   unsigned long gcmask = GCFont | GCForeground | GCBackground |
		GCFillStyle | GCCapStyle | GCLineWidth | GCLineStyle;

   wc = w->context;
   Protect(rv = alc_context(w), return NULL);

   rv->dx = wc->dx;
   rv->dy = wc->dy;
   rv->clipx = wc->clipx;
   rv->clipy = wc->clipy;
   rv->clipw = wc->clipw;
   rv->cliph = wc->cliph;
   rv->fg = wc->fg;
   rv->fg->refcount++;
   rv->bg = wc->bg;
   rv->bg->refcount++;
   rv->font = wc->font;
   rv->font->refcount++;
   rv->fillstyle = wc->fillstyle;
   rv->linestyle = wc->linestyle;
   rv->linewidth = wc->linewidth;
   rv->drawop = wc->drawop;
   rv->gamma = wc->gamma;
   rv->bits = wc->bits;

   if (ISXORREVERSE(w))
      gcv.foreground = rv->fg->c ^ rv->bg->c;
   else
      gcv.foreground = rv->fg->c;
   gcv.background = rv->bg->c;
   gcv.font       = rv->font->fsp->fid;
   gcv.line_style = rv->linestyle;
   gcv.line_width = rv->linewidth;
   if (rv->linewidth > 1) {
      gcv.dashes = 3 * rv->linewidth;
      gcmask |= GCDashList;
      }
   gcv.fill_style = rv->fillstyle;
   gcv.cap_style  = CapProjecting;
   rv->gc = XCreateGC(w->window->display->display,w->window->pix,gcmask,&gcv);
   if (rv->gc == NULL) {
      free(rv);
      return NULL;
      }
   if (rv->clipw >= 0) {
      rec.x = rv->clipx;
      rec.y = rv->clipy;
      rec.width = rv->clipw;
      rec.height = rv->cliph;
      XSetClipRectangles(rv->display->display, rv->gc, 0, 0, &rec, 1,Unsorted);
      }
   return rv;
   }

/*
 * allocate a window state structure
 */
wsp alc_winstate()
   {
   wsp ws;

   GRFX_ALLOC(ws, _wstate);
   ws->serial = ++canvas_serial;
   ws->bits = 1024;				/* echo ON; others OFF */
   ws->filep = nulldesc;
   ws->listp = nulldesc;
   ws->theCursor = si_s2i(cursorsyms, "left ptr") >> 1;
   ws->iconic = NormalState;
   ws->posx = ws->posy = -(MaxInt);
   GRFX_LINK(ws, wstates);
   return ws;
   }

/*
 * free a window state
 */
int free_window(ws)
wsp ws;
   {
   ws->refcount--;
   if(ws->refcount == 0) {
      ws->bits |= 1;			/* SETZOMBIE */
      if (ws->win != (Window) NULL) {
	 XDestroyWindow(ws->display->display, ws->win);
	 XFlush(ws->display->display);
	 while (ws->win != (Window) NULL)
	    if (pollevent() == -1) return -1;
	 }
      GRFX_UNLINK(ws, wstates);
      }
   return 0;
   }

/*
 * free a window context
 */
void free_context(wc)
wcp wc;
   {
   wc->refcount--;
   if(wc->refcount == 0) {
      if (wc->gc != NULL)
	 XFreeGC(wc->display->display, wc->gc);
      free_display(wc->display);
      GRFX_UNLINK(wc, wcntxts);
      }
   }

/*
 * free a display
 */
void free_display(wd)
wdp wd;
   {
   wd->refcount--;
   if(wd->refcount == 0) {
      if (wd->cmap != DefaultColormap(wd->display, wd->screen))
	 XFreeColormap(wd->display, wd->cmap);
      XCloseDisplay(wd->display);
      if (wd->previous) wd->previous->next = wd->next;
      else wdsplys = wd->next;
      if (wd->next) wd->next->previous = wd->previous;
      free(wd);
      }
   }
