1 /* Copyright (C) 1993, 1992 Nathan Sidwell */
2 /* RCS $Id: makecom.c,v 4.27 1995/12/21 15:55:04 nathan Exp $ */
3 /* common make stuff for xmris and xmred,
4  * included from makemris and makemred
5  */
6 
7 /*{{{  interesting color stuff*/
8 /*
9  * color is spelt as it is, 'cos its just too complicated spelling it as
10  * it should be, when X doesn't.
11  * The colour allocation is done by first parsing all the colour names
12  * to RGB values (using a type convertor from the resources). Each RGB
13  * value is converted to a 3space coordinate in a colour 'cone' (the
14  * colour circle, (well, hexagon in this case), and a height,
15  * corresponding to brightness). With that we can work out the
16  * perceived distances between different colours.
17  * These are then allocated in turn, by picking the unallocated colour
18  * farthest from the allocated colours. If the allocation fails, we pick
19  * the allocated colour which is nearest the desired colour. This way
20  * we try to get the most diverse set of colours possible with the
21  * colormap that's being used. Remember, black and white have already
22  * been allocated so we don't need to allocate those, and it gives us
23  * a starting ix in the allocation loop.
24  * The board background colours only need allocating, if it is a read
25  * only visual (Direct Colour or something). For writable visuals,
26  * we allocate two colorcells and set these as required, allowing
27  * dynamic colour changes, and saving entries in the colormap.
28  * You can see the order in which this is done by giving the -colours arg.
29  * There is a bug (IMHO) in the way that X parses hex color names to XColor.
30  * #FA5 is parsed as #F000A0005000, not #FFFFAAAA5555 as I would have
31  * expected it to be. That's why I've specified colours accurately.
32  * When the color #F000A0005000 is allocated, the rgb values are set,
33  * as I expected, to #F0F0A0A05050, on an 8 bit visual. There doesn't
34  * seem to be a way to get X to tell you what colour it would
35  * allocate on a colormap, assuming that there's a space.
36  */
37 /*}}}*/
38 /*{{{  static tables*/
39 /*{{{  static char CONST *names[] =*/
40 static char CONST *names[] =
41 {
42   "xmris",
43   "xmsit"
44 };
45 /*}}}*/
46 /*}}}*/
47 /*{{{  prototypes*/
48 static Boolean convert_string2color PROTOARG((Display *, XrmValue *,
49     Cardinal *, XrmValue *, XrmValue *, XtPointer *));
50 static Boolean convert_string2gender PROTOARG((Display *, XrmValue *,
51     Cardinal *, XrmValue *, XrmValue *, XtPointer *));
52 static VOIDFUNC create_resources PROTOARG((Widget));
53 static VOIDFUNC extract_options
54     PROTOARG((int *, String *, CommandOptionDesc CONST *));
55 static VOIDFUNC gettoplevelresources PROTOARG((VOIDARG));
56 static VOIDFUNC list_color_resource
57     PROTOARG((unsigned, char CONST *, char CONST *));
58 static VOIDFUNC make_color_image
59     PROTOARG((SPRITE *, SPRITE_DEF *, Window, unsigned, Pixmap));
60 static VOIDFUNC make_mono_image
61     PROTOARG((SPRITE *, SPRITE_DEF *, Window, unsigned, Pixmap));
62 static VOIDFUNC make_mask_image
63     PROTOARG((SPRITE *, SPRITE_DEF *, Window, unsigned, Pixmap));
64 /*}}}*/
65 /*{{{  Boolean convert_string2color(display, args, num_args, from, to, data)*/
66 static Boolean convert_string2color
67 /* ARGSUSED */
68 FUNCARG((display, args, num_args, from, to, data),
69 	Display   *display
70 ARGSEP  XrmValue  *args
71 ARGSEP  Cardinal  *num_args
72 ARGSEP  XrmValue  *from
73 ARGSEP  XrmValue  *to
74 ARGSEP  XtPointer *data
75 )
76 /*
77  * converts a string to a colordef
78  */
79 {
80   static XColor result;
81   Status    status;
82   Colormap  colormap;
83 
84   if(to->size < sizeof(XColor))
85     {
86       to->size = sizeof(XColor);
87       return False;
88     }
89   if(args && *num_args > 1)
90     colormap = *(Colormap *)args[1].addr;
91   else
92     {
93       Screen    *screen;
94 
95       if(args && *num_args)
96 	screen = *(Screen **)args[0].addr;
97       else
98 	screen = DefaultScreenOfDisplay(display);
99       colormap = DefaultColormapOfScreen(screen);
100     }
101   status = XParseColor(display, colormap,
102       (char CONST *)from->addr, to->addr ? (XColor *)to->addr : &result);
103   if(status)
104     {
105       to->size = sizeof(XColor);
106       if(to->addr)
107 	((XColor *)to->addr)->flags = DoRed | DoGreen | DoBlue;
108       else
109 	{
110 	  result.flags = DoRed | DoGreen | DoBlue;
111 	  to->addr = (XtPointer)&result;
112 	}
113       return True;
114     }
115   else
116     {
117       XtDisplayStringConversionWarning(display, from->addr, XtRColor);
118       return False;
119     }
120 }
121 /*}}}*/
122 /*{{{  Boolean convert_string2gender(display, args, num_args, from, to, data)*/
123 static Boolean convert_string2gender
124 /* ARGSUSED */
125 FUNCARG((display, args, num_args, from, to, data),
126 	Display   *display
127 ARGSEP  XrmValue  *args
128 ARGSEP  Cardinal  *num_args
129 ARGSEP  XrmValue  *from
130 ARGSEP  XrmValue  *to
131 ARGSEP  XtPointer *data
132 )
133 /*
134  * converts a string to a gender boolean (my new type XtRGender)
135  */
136 {
137   static char CONST *genders[] =
138     {"he", "she", "male", "female", "mris", "msit", "boy", "girl", NULL};
139   char CONST **ptr;
140   static Boolean  result;
141 
142   if(to->size < sizeof(Boolean))
143     {
144       to->size = sizeof(Boolean);
145       return False;
146     }
147   for(ptr = genders; *ptr; ptr++)
148     if(!strcmp(*ptr, (char CONST *)from->addr))
149       {
150 	to->size = sizeof(Boolean);
151 	result = (ptr - genders) & 1 ? True : False;
152 	if(to->addr)
153 	   *(Boolean *)to->addr = result;
154 	else
155 	  to->addr = (XtPointer)&result;
156 	return True;
157       }
158   XtDisplayStringConversionWarning(display, from->addr, XtRGender);
159   return False;
160 }
161 /*}}}*/
162 /*{{{  void create_resources(widget)*/
163 static VOIDFUNC create_resources
164 FUNCARG((widget),
165 	Widget  widget
166 )
167 /*
168  * Create the graphics contexts, sprites and colours and stuff.
169  * sets visual and stuff for the toplevel widget
170  */
171 {
172   Window    root;
173   unsigned  depth;
174   XVisualInfo visualinfo;
175 
176   /*{{{  get visual info*/
177   {
178     XVisualInfo *list;
179     int       found;
180 
181     visualinfo.visualid = XVisualIDFromVisual(display.visual);
182     list = XGetVisualInfo(display.display, VisualIDMask, &visualinfo, &found);
183     while(found--)
184       if(list[found].visual == display.visual)
185 	{
186 	  memcpy(&visualinfo, &list[found], sizeof(XVisualInfo));
187 	  break;
188 	}
189     XFree((VOID *)list);
190   }
191   /*}}}*/
192   depth = display.depth;
193   if(visualinfo.colormap_size < 16)
194     data.mono = True;
195   root = RootWindow(display.display, display.screen);
196   if(XDefaultVisual(display.display, display.screen) != display.visual &&
197       data.private == False)
198     fprintf(stderr, "Warning:Non-default visual with shared colormap\n");
199   /*{{{  show visual class?*/
200   if(data.colors != False)
201     {
202       VISUAL_CLASS CONST *vptr;
203 
204       for(vptr = visual_class; vptr->name; vptr++)
205 	if(vptr->class == visualinfo.class)
206 	  break;
207       fprintf(stdout, "Using %s visual with %s colormap of %d entries\n",
208 	  vptr->name ? vptr->name : vptr->class & 1 ?
209 	  "UnknownDynamic" : "UnknownStatic",
210 	  data.private != False ? "private" : "shared",
211 	  visualinfo.colormap_size);
212     }
213   /*}}}*/
214   /*{{{  make black and white*/
215   {
216     Status  status;
217     XColor  color;
218 
219     color.red = color.green = color.blue = 0xFFFF;
220     color.flags = DoRed | DoGreen | DoBlue;
221     status = XAllocColor(display.display, display.colormap, &color);
222     if(status)
223       {
224 	display.white = color.pixel;
225 	color.red = color.green = color.blue = 0x0000;
226 	color.flags = DoRed | DoGreen | DoBlue;
227 	status = XAllocColor(display.display, display.colormap, &color);
228 	display.black = color.pixel;
229       }
230     if(!status)
231       fatal_error("Cannot get hold of black and white pixels");
232     if(data.colors != False)
233       {
234 	fprintf(stdout, "Color white pixel %lu\n", display.white);
235 	fprintf(stdout, "Color black pixel %lu\n", display.black);
236       }
237   }
238   /*}}}*/
239   XtAppSetTypeConverter(display.context, XtRString, XtRColor,
240       convert_string2color, (XtConvertArgList)colorConvertArgs, 2,
241       XtCacheNone, (void (*)PROTOARG((XtAppContext, XrmValue *, XtPointer,
242 	  XrmValue *, Cardinal *)))NULL);
243   color_one = ((unsigned long)1 << depth) - (unsigned long)1;
244   if(data.mono == False)
245     {
246       unsigned  ix;
247       COLOR_DEF *nptr;
248       XColor    *cptr;
249       unsigned  count;
250       unsigned  new;
251       unsigned  share;
252 
253       /*{{{  get resources*/
254       {
255 	XtResource *resources;
256 	size_t    length;
257 	char      *text;
258 	char      *tptr;
259 	XtResource *rptr;
260 	unsigned  source;
261 	unsigned  mask;
262 
263 	resources = (XtResource *)XtMalloc(COLORS * sizeof(XtResource));
264 	length = COLORS;
265 	for(nptr = color_names, count = COLORS; count--; nptr++)
266 	  length += strlen(nptr->name);
267 	text = XtMalloc(length);
268 	colors[COLOR_WHITE].flags = DoRed | DoGreen | DoBlue;
269 	colors[COLOR_WHITE].red = 0xFF00;
270 	colors[COLOR_WHITE].green = 0xFF00;
271 	colors[COLOR_WHITE].blue = 0xFF00;
272 	colors[COLOR_BLACK].flags = DoRed | DoGreen | DoBlue;
273 	colors[COLOR_BLACK].red = 0;
274 	colors[COLOR_BLACK].green = 0;
275 	colors[COLOR_BLACK].blue = 0;
276 	source = (data.gender != False ? 2 : 0) +
277 	    (data.swap != False ? 1 : 0);
278 	mask = (2 << source) - 1;
279 	if(source == 2)
280 	  mask ^= 2;
281 	/*{{{  build the resource table*/
282 	{
283 	  for(nptr = color_names, rptr = resources, tptr = text, ix = 0;
284 	      ix != COLORS; ix++, nptr++)
285 	    {
286 	      char CONST **sptr;
287 	      unsigned  bit;
288 
289 	      rptr->resource_name = tptr;
290 	      strcpy(tptr, nptr->name);
291 	      if(isupper(*tptr))
292 		*tptr = lowercase(*tptr);
293 	      tptr += strlen(tptr) + 1;
294 	      rptr->resource_class = (String)nptr->name;
295 	      rptr->resource_type = XtRColor;
296 	      rptr->resource_size = sizeof(XColor);
297 	      rptr->resource_offset = sizeof(XColor) * ix;
298 	      rptr->default_type = XtRString;
299 	      for(sptr = &nptr->source[source], bit = 1 << source;
300 		  (!*sptr || !(bit & mask)) && bit; bit >>= 1, sptr--)
301 		/* EMPTY */;
302 	      if(bit)
303 		rptr++->default_addr = (XtPointer)*sptr;
304 	    }
305 	  assert(tptr - text == length);
306 	}
307 	/*}}}*/
308 	if(data.swap != False)
309 	  XtGetSubresources(widget, (XtPointer)colors, "swap", "Swap",
310 	      resources, rptr - resources, NULL, 0);
311 	else
312 	  XtGetApplicationResources(widget, colors,
313 	      resources, rptr - resources, NULL, 0);
314 	XtFree(text);
315 	XtFree((char *)resources);
316       }
317       /*}}}*/
318       display.dynamic = 0;
319 #ifndef XMRED
320       /*{{{  dynamic colors*/
321       if(data.nodynamic == False && visualinfo.class & 1)
322 	{
323 	  unsigned long pixels[2];
324 	  unsigned long planes;
325 
326 	  if(XAllocColorCells (display.display, display.colormap, False,
327 	      &planes, 0, pixels, 2))
328 	    {
329 	      display.dynamic = 1;
330 	      colors[COLOR_DYNAMIC].pixel = pixels[0];
331 	      colors[COLOR_DYNAMIC + 1].pixel = pixels[1];
332 	      if(data.colors != False)
333 		fprintf(stdout, "Dynamic: background %lu, foreground %lu\n",
334 		    pixels[0], pixels[1]);
335 	    }
336 	}
337       /*}}}*/
338 #endif /* XMRED */
339       count = new = 0;
340       /*{{{  parse all the colors*/
341       {
342 	unsigned  mask;
343 
344 	mask = (data.gender != False ? 4 : 1) << (data.swap != False);
345 	for(cptr = colors, nptr = color_names, ix = COLORS;
346 	    ix--; nptr++, cptr++)
347 	  if(cptr->flags)
348 	    {
349 	      nptr->coord[0] = RGB2X(cptr->red, cptr->green, cptr->blue);
350 	      nptr->coord[1] = RGB2Y(cptr->red, cptr->green, cptr->blue);
351 	      nptr->coord[2] = RGB2H(cptr->red, cptr->green, cptr->blue);
352 	      nptr->distance = 0;
353 	      nptr->nearest = NULL;
354 	      if(nptr->type & mask)
355 		{
356 		  if(nptr->type & 16 && display.dynamic)
357 		    nptr->alloc = 5;
358 		  else if(nptr->type & 32 && !display.dynamic)
359 		    nptr->alloc = 4;
360 		  else
361 		    {
362 		      nptr->alloc = 0;
363 		      count++;
364 		    }
365 		}
366 	      else
367 		nptr->alloc = 4;
368 	    }
369 	  else if(nptr->type & mask)
370 	    fatal_error("Have no color for %s", nptr->name);
371       }
372       /*}}}*/
373       share = 2;
374       /*{{{  allocate them in optimum order*/
375       for(colors[COLOR_WHITE].pixel = display.white,
376 	  nptr = &color_names[COLOR_WHITE], nptr->alloc = 1;
377 	  --count;)
378 	{
379 	  if(nptr->alloc == 1)
380 	    /*{{{  work out distance of just allocated color*/
381 	    {
382 	      unsigned  ix;
383 	      COLOR_DEF *optr;
384 
385 	      for(optr = color_names, ix = COLORS; ix--; optr++)
386 		if(!optr->alloc)
387 		  {
388 		    unsigned long distance;
389 		    unsigned  ix;
390 
391 		    distance = 0;
392 		    for(ix = 3; ix--;)
393 		      /*
394 		       * must be very careful about type promotion and
395 		       * overflow here
396 		       */
397 		      {
398 			unsigned  delta;
399 
400 			delta = nptr->coord[ix] < optr->coord[ix] ?
401 			    optr->coord[ix] - nptr->coord[ix] :
402 			    nptr->coord[ix] - optr->coord[ix];
403 			distance += ((unsigned long)delta *
404 			    (unsigned long)delta) / 4;
405 		      }
406 		    if(distance < optr->distance || !optr->nearest)
407 		      {
408 			optr->distance = distance;
409 			optr->nearest = nptr;
410 		      }
411 		  }
412 	    }
413 	    /*}}}*/
414 	  if(nptr == &color_names[COLOR_WHITE])
415 	    {
416 	      nptr = &color_names[COLOR_BLACK];
417 	      nptr->alloc = 1;
418 	      colors[COLOR_BLACK].pixel = display.black;
419 	    }
420 	  else
421 	    /*{{{  allocate the farthest one*/
422 	    {
423 	      unsigned long distance;
424 	      COLOR_DEF *optr;
425 	      Status    status;
426 	      unsigned  ix;
427 	      XColor    *fptr;
428 
429 	      nptr = NULL;
430 	      distance = 0;
431 	      for(optr = color_names, ix = COLORS; ix--; optr++)
432 		if(!optr->alloc && distance <= optr->distance)
433 		  {
434 		    nptr = optr;
435 		    distance = nptr->distance;
436 		  }
437 	      assert(nptr && nptr->nearest);
438 	      cptr = &colors[nptr - color_names];
439 	      fptr = &colors[nptr->nearest - color_names];
440 	      if(distance)
441 		/*{{{  different*/
442 		{
443 		  if(data.colors != False)
444 		    fprintf(stdout,
445 			"Color %s:#%04X%04X%04X near %s:#%04X%04X%04X (%lu)",
446 			nptr->name, cptr->red, cptr->green, cptr->blue,
447 			nptr->nearest->name,
448 			fptr->red, fptr->green, fptr->blue, distance);
449 		  if(data.distinct)
450 		    status = XAllocColor(display.display, display.colormap,
451 		      cptr);
452 		  else
453 		    status = 0;
454 		}
455 		/*}}}*/
456 	      else
457 		/*{{{  same*/
458 		{
459 		  if(data.colors != False)
460 		    fprintf(stdout,
461 			"Color %s:#%04X%04X%04X at %s:#%04X%04X%04X",
462 			nptr->name, cptr->red, cptr->green, cptr->blue,
463 			nptr->nearest->name,
464 			fptr->red, fptr->green, fptr->blue);
465 		  status = 0;
466 		}
467 		/*}}}*/
468 	      /*{{{  fixup*/
469 	      if(status)
470 		{
471 		  new++;
472 		  nptr->alloc = 1;
473 		  distance = 0;
474 		  data.distinct--;
475 		}
476 	      else
477 		{
478 		  nptr->alloc = 2 + !!distance;
479 		  cptr->pixel = fptr->pixel;
480 		  share++;
481 		}
482 	      /*}}}*/
483 	      if(data.colors != False)
484 		fprintf(stdout, nptr->alloc == 1 ?
485 		    " pixel %lu\n" : " sharing %lu\n", cptr->pixel);
486 	      /*{{{  error message?*/
487 	      if(distance)
488 		{
489 		  fprintf(stderr,
490 		      "No color %s:#%04X%04X%04X used %s:#%04X%04X%04X\n",
491 		      nptr->name, cptr->red, cptr->green, cptr->blue,
492 		      nptr->nearest->name,
493 		      fptr->red, fptr->green, fptr->blue);
494 		}
495 	      /*}}}*/
496 	    }
497 	    /*}}}*/
498 	}
499       /*}}}*/
500       if(data.colors != False)
501 	fprintf(stdout, "%u new colours and %u shared\n", new, share);
502       for(nptr = color_names, count = COLORS; count--; nptr++)
503 	assert(nptr->alloc);
504     }
505   /*{{{  swap b & w?*/
506   if(data.swap != False)
507     {
508       unsigned long temp;
509 
510       temp = display.black;
511       display.black = display.white;
512       display.white = temp;
513     }
514   /*}}}*/
515   if(data.mono == False)
516     {
517       display.white = colors[COLOR_BACKGROUND].pixel;
518       display.black = colors[COLOR_FOREGROUND].pixel;
519       display.border = colors[COLOR_BORDER].pixel;
520     }
521   else
522     display.border = display.black;
523   /*{{{  nadger created widgets*/
524   {
525     Widget    root;
526     Widget    parent;
527 
528     for(root = widget; (parent = XtParent(root)); root = parent)
529       /* EMPTY */;
530     nadger_widget_colors(root, (WidgetClass)NULL);
531   }
532   /*}}}*/
533   display.xor = display.black ^ display.white;
534   /*{{{  set foreground & background types*/
535   {
536     unsigned long set;
537     unsigned long clear;
538     unsigned  ix;
539 
540     set = color_zero;
541     clear = color_one;
542     /*{{{  set on and off*/
543     if(data.mono != False)
544       {
545 	set |= display.white;
546 	clear &= display.white;
547 	set |= display.black;
548 	clear &= display.black;
549       }
550     else
551       {
552 	for(ix = COLORS; ix--;)
553 	  {
554 	    set |= colors[ix].pixel;
555 	    clear &= colors[ix].pixel;
556 	  }
557       }
558     /*}}}*/
559     display.background = display.white == clear ? COLOUR_ZERO :
560 	display.white == set ? COLOUR_ONE : COLOUR_WEIRD;
561     display.foreground = display.black == clear ? COLOUR_ZERO :
562 	display.black == set ? COLOUR_ONE : COLOUR_WEIRD;
563   }
564   /*}}}*/
565   display.copy = XCreatePixmap(display.display, root,
566       WINDOW_WIDTH, WINDOW_HEIGHT, depth);
567 #ifndef XMRED
568   display.back = XCreatePixmap(display.display, root,
569       WINDOW_WIDTH, WINDOW_HEIGHT, depth);
570   {
571     Cursor  cursor;
572     String  cursor_name;
573 
574     cursor_name = NULL;
575     XtVaGetValues(widget, XtNcursor, (XtArgVal)&cursor,
576 #ifdef XtNcursorName
577 	XtNcursorName, (XtArgVal)&cursor_name,
578 #endif
579 	(String)NULL);
580     if(cursor == None && !cursor_name)
581       {
582 	Pixmap  blank;
583 	XColor  color;
584 	char    data;
585 
586 	data = 0;
587 	blank = XCreatePixmapFromBitmapData(display.display, root,
588 	    &data, 1, 1, (long)0, (long)0, 1);
589 	color.pixel = display.black;
590 	XQueryColor(display.display, display.colormap, &color);
591 	cursor = XCreatePixmapCursor(display.display, blank, blank,
592 	    &color, &color, 0, 0);
593 	XFreePixmap(display.display, blank);
594 	XtVaSetValues(widget, XtNcursor, (XtArgVal)cursor, (String)NULL);
595       }
596   }
597 #endif /* XMRED */
598   /*{{{  create graphics contexts*/
599   {
600     XGCValues gcv;
601     CONTEXT   *cptr;
602     unsigned  count;
603 
604     gcv.font = data.font;
605     gcv.graphics_exposures = False;
606     gcv.line_width = 1;
607     gcv.cap_style = CapNotLast;
608     for(cptr = gcsdefine, count = 0; count != GCS; count++, cptr++)
609       {
610 	gcv.function = cptr->function;
611 	gcv.foreground = *cptr->fgp;
612 	gcv.background = *cptr->bgp;
613 	GCN(count) = XCreateGC(display.display, display.copy,
614 	    GCForeground | GCBackground | GCFunction | GCFont |
615 	    GCGraphicsExposures | GCCapStyle | GCLineWidth, &gcv);
616       }
617   }
618   /*}}}*/
619   XFillRectangle(display.display, display.copy, GCN(GC_CLEAR),
620       0, 0, WINDOW_WIDTH, WINDOW_HEIGHT);
621   {
622     SPRITE_DEF CONST *sptr;
623     int       nsizes;
624     XIconSize *sizes;
625     Status    status;
626     SIZE      size;
627 
628 #ifndef XMRED
629     sptr = &icons[data.gender != False];
630 #else
631     sptr = &icons[0];
632 #endif /* XMRED */
633     status = XGetIconSizes(display.display, root, &sizes, &nsizes);
634     if(status)
635       /*{{{  find best size*/
636       {
637 	int       ix;
638 	SIZE      undersize;
639 	int       found;
640 
641 	size.x = size.y = 128;
642 	undersize.x = undersize.y = 1;
643 	found = 0;
644 	for(ix = nsizes; ix--; sizes++)
645 	  {
646 	    SIZE      this;
647 
648 	    if(sptr->size.x < sizes->min_width)
649 	      this.x = sizes->min_width;
650 	    else if(sptr->size.x > sizes->max_width)
651 	      this.x = sizes->max_width;
652 	    else
653 	      this.x = (sptr->size.x - sizes->min_width +
654 		  sizes->width_inc - 1) / sizes->width_inc *
655 		  sizes->width_inc + sizes->min_width;
656 	    if(sptr->size.y < sizes->min_height)
657 	      this.y = sizes->min_height;
658 	    else if(sptr->size.y > sizes->max_height)
659 	      this.y = sizes->max_height;
660 	    else
661 	      this.y = (sptr->size.y - sizes->min_height +
662 		  sizes->height_inc - 1) / sizes->height_inc *
663 		  sizes->height_inc + sizes->min_height;
664 	    if(this.x < sptr->size.x || this.y < sptr->size.y)
665 	      {
666 		if(this.x * this.y > undersize.x * undersize.y)
667 		  {
668 		    undersize.x = this.x;
669 		    undersize.y = this.y;
670 		  }
671 	      }
672 	    else if(this.x * this.y < size.x * size.y)
673 	      {
674 		found = 1;
675 		size.x = this.x;
676 		size.y = this.y;
677 	      }
678 	  }
679 	if(!found)
680 	  {
681 	    size.x = undersize.x;
682 	    size.y = undersize.y;
683 	  }
684       }
685       /*}}}*/
686     else
687       {
688 	size.x = sptr->size.x;
689 	size.y = sptr->size.y;
690       }
691     display.icon = XCreateBitmapFromData(display.display, root,
692 	(char CONST *)sptr->bitmap, sptr->size.x, sptr->size.y);
693     /*{{{  create correct sized icon*/
694     if(size.x != sptr->size.x || size.y != sptr->size.y)
695       {
696 	Pixmap    temp;
697 	GC        gc;
698 	XGCValues gcv;
699 
700 	temp = XCreatePixmap(display.display, root, size.x, size.y, 1);
701 	gc = XCreateGC(display.display, temp, 0, &gcv);
702 	XSetFunction(display.display, gc, GXclear);
703 	XFillRectangle(display.display, temp, gc, 0, 0, size.x, size.y);
704 	XSetFunction(display.display, gc, GXcopy);
705 	XCopyArea(display.display, display.icon, temp, gc, 0, 0,
706 	    sptr->size.x, sptr->size.y,
707 	    (size.x - sptr->size.x) / 2, (size.y - sptr->size.y) / 2);
708 	XFreeGC(display.display, gc);
709 	XFreePixmap(display.display, display.icon);
710 	display.icon = temp;
711       }
712     /*}}}*/
713     XtVaSetValues(display.toplevel, XtNiconPixmap, (XtArgVal)display.icon,
714 	NULL);
715   }
716   /*{{{  get a font*/
717   {
718     char CONST *string = "09\'\"";
719     int       direction;
720     XCharStruct chars;
721 
722     XQueryTextExtents(display.display, data.font, string, 4,
723 	    &direction, &font.ascent, &font.descent, &chars);
724     font.width = chars.width / 4;
725     font.center = (font.ascent - font.descent) / 2;
726     font.digitup = chars.ascent;
727     font.digitdown = chars.descent;
728   }
729   /*}}}*/
730   if(data.gender != False)
731     {
732       SPRITE_DEF *dptr;
733       SPRITE_DEF CONST *sptr;
734 
735       for(sptr = she_nadger; sptr->copy; sptr++)
736 	{
737 	  dptr = &sprites_def[sptr->copy];
738 	  dptr->bitmap = sptr->bitmap;
739 	  dptr->planes = sptr->bitmap ? sptr->planes : 0;
740 	  dptr->flags = sptr->flags;
741 	  dptr->copy = sptr->bitmap ? 0 : sptr->planes;
742 	  memcpy(&dptr->size, &sptr->size, sizeof(sptr->size));
743 	  memcpy(dptr->colors, sptr->colors, sizeof(sptr->colors));
744 	}
745 #ifndef XMRED
746       memcpy(ball_hold, she_hold, sizeof(ball_hold));
747       lettering[0] = letter_msit;
748 #endif /* XMRED */
749     }
750   /*{{{  create sprites*/
751   {
752     unsigned  i;
753     SPRITE    *dptr;
754     SPRITE_DEF *sptr;
755     unsigned  unknown;
756     unsigned  changed;
757 
758     /*{{{  generate all the ones from bitmaps*/
759     for(i = 0, sptr = sprites_def, dptr = sprites;
760 	i != SPRITES; i++, sptr++, dptr++)
761       {
762 	/* check that its the size we expected */
763 	if(sptr->bitmap)
764 	  {
765 	    Pixmap    bitmap;
766 
767 	    assert(!sptr->expected.x || sptr->expected.x == sptr->size.x);
768 	    assert(!sptr->expected.y || sptr->expected.y * sptr->planes == sptr->size.y);
769 	    assert(sptr->size.x && sptr->size.y && !sptr->copy);
770 	    dptr->size.x = sptr->size.x;
771 	    dptr->size.y = sptr->size.y / sptr->planes;
772 	    bitmap = XCreatePixmapFromBitmapData(display.display,
773 		root, (char *)sptr->bitmap,
774 		sptr->size.x, sptr->size.y, color_one, color_zero, depth);
775 	    dptr->image = XCreatePixmap(display.display, root,
776 		dptr->size.x, dptr->size.y, depth);
777 	    dptr->mask = XCreatePixmap(display.display, root,
778 		dptr->size.x, dptr->size.y, depth);
779 	    if(!sptr->colors[0])
780 	      XFillRectangle(display.display, dptr->image, GCN(GC_CLEAR),
781 		  0, 0, sptr->size.x, sptr->size.y);
782 	    else if(data.mono != False)
783 	      make_mono_image(dptr, sptr, root, depth, bitmap);
784 	    else
785 	      make_color_image(dptr, sptr, root, depth, bitmap);
786 	    make_mask_image(dptr, sptr, root, depth, bitmap);
787 	    XFreePixmap(display.display, bitmap);
788 	  }
789       }
790     /*}}}*/
791     do
792       {
793 	unknown = SPRITES;
794 	changed = 0;
795 	/*{{{  do what copies we can*/
796 	for(sptr = sprites_def, dptr = sprites, i = SPRITES;
797 	    i--; sptr++, dptr++)
798 	  if(!sptr->copy)
799 	    {
800 	      /*
801 	       * Sun's assert macro is broken, so I have to
802 	       * put it in a scope
803 	       */
804 	      assert(sptr->bitmap);
805 	    }
806 	  else if(!dptr->mask && !dptr->image)
807 	    {
808 	      SPRITE    *optr;
809 
810 	      optr = &sprites[sptr->copy];
811 	      if(!optr->size.x || !optr->size.y)
812 		unknown = i;
813 	      else
814 		{
815 		  changed = 1;
816 		  dptr->size.x = optr->size.x;
817 		  dptr->size.y = optr->size.y;
818 		  switch(sptr->flags)
819 		  {
820 		    /*{{{  case REFLECT_ALIAS:*/
821 		    case REFLECT_ALIAS:
822 		      dptr->mask = optr->mask;
823 		      dptr->image = optr->image;
824 		      break;
825 		    /*}}}*/
826 		    /*{{{  case REFLECT_VERTICAL:*/
827 		    case REFLECT_VERTICAL:
828 		    {
829 		      int       i;
830 
831 		      dptr->mask = XCreatePixmap(display.display, root,
832 			  dptr->size.x, dptr->size.y, depth);
833 		      dptr->image = XCreatePixmap(display.display, root,
834 			  dptr->size.x, dptr->size.y, depth);
835 		      for(i = dptr->size.x; i--;)
836 			{
837 			  XCopyArea(display.display, optr->mask, dptr->mask,
838 			      GCN(GC_COPY), i, 0, 1, dptr->size.y,
839 			      (int)dptr->size.x - i - 1, 0);
840 			  XCopyArea(display.display, optr->image, dptr->image,
841 			      GCN(GC_COPY), i, 0, 1, dptr->size.y,
842 			      (int)dptr->size.x - i - 1, 0);
843 			}
844 		      break;
845 		    }
846 		    /*}}}*/
847 		    /*{{{  case REFLECT_HORIZONTAL:*/
848 		    case REFLECT_HORIZONTAL:
849 		    {
850 		      int       i;
851 
852 		      dptr->mask = XCreatePixmap(display.display, root,
853 			  dptr->size.x, dptr->size.y, depth);
854 		      dptr->image = XCreatePixmap(display.display, root,
855 			  dptr->size.x, dptr->size.y, depth);
856 		      for(i = dptr->size.y; i--;)
857 			{
858 			  XCopyArea(display.display, optr->mask, dptr->mask,
859 			      GCN(GC_COPY), 0, i, dptr->size.x, 1,
860 			      0, (int)dptr->size.y - i - 1);
861 			  XCopyArea(display.display, optr->image, dptr->image,
862 			      GCN(GC_COPY), 0, i, dptr->size.x, 1,
863 			      0, (int)dptr->size.y - i - 1);
864 			}
865 		      break;
866 		    }
867 		    /*}}}*/
868 		    /*{{{  case REFLECT_DIAGONAL:*/
869 		    case REFLECT_DIAGONAL:
870 		    {
871 		      int       x, y;
872 
873 		      dptr->size.x = optr->size.y;
874 		      dptr->size.y = optr->size.x;
875 		      dptr->mask = XCreatePixmap(display.display, root,
876 			  dptr->size.x, dptr->size.y, depth);
877 		      dptr->image = XCreatePixmap(display.display, root,
878 			  dptr->size.x, dptr->size.y, depth);
879 		      for(x = optr->size.x; x--;)
880 			for(y = optr->size.y; y--;)
881 			  {
882 			    XCopyArea(display.display, optr->mask, dptr->mask,
883 				GCN(GC_COPY), x, y, 1, 1, y, x);
884 			    XCopyArea(display.display,
885 				optr->image, dptr->image,
886 				GCN(GC_COPY), x, y, 1, 1, y, x);
887 			  }
888 		      break;
889 		    }
890 		    /*}}}*/
891 		    /*{{{  default*/
892 		    default:
893 		      assert(0);
894 		    /*}}}*/
895 		  }
896 		}
897 	    }
898 	/*}}}*/
899       }
900     while(unknown != SPRITES && changed);
901     assert(unknown == SPRITES); /* check no circular definitions */
902   }
903   /*}}}*/
904 #ifndef XMRED
905   /*{{{  digits special*/
906   {
907     Pixmap    bitmap;
908     SPRITE    *dptr;
909 
910     dptr = &sprites[SPRITE_DIGITS];
911     bitmap = XCreatePixmap(display.display, root,
912 	dptr->size.x, dptr->size.y, depth);
913     XFillRectangle(display.display, bitmap, GCN(GC_CLEAR),
914 	0, 0, dptr->size.x, dptr->size.y);
915     XCopyArea(display.display, dptr->mask, bitmap, GCN(GC_MASK),
916 	0, 0, dptr->size.x, dptr->size.y, 0, 0);
917     XCopyArea(display.display, bitmap, dptr->image, GCN(GC_OR),
918 	0, 0, dptr->size.x, dptr->size.y, 0, 0);
919     XFreePixmap(display.display, bitmap);
920   }
921   /*}}}*/
922 #else
923   /*{{{  visual special*/
924   if(data.mono == False && visualinfo.class != StaticGray &&
925       visualinfo.class != GrayScale)
926   {
927     int       count;
928     COLOR_DEF *cptr;
929     SPRITE    *dptr;
930 
931     for(cptr = &color_names[COLOR_BACKGROUNDS], count = BACKGROUNDS * 2;
932 	count--; cptr++)
933       if(cptr->alloc == 3)
934 	break;
935     if(count < 0)
936       for(dptr = &sprites[SPRITE_COLORS], count = BACKGROUNDS; count--; dptr++)
937 	{
938 	  XFreePixmap(display.display, dptr->mask);
939 	  dptr->mask = 0;
940 	  XFreePixmap(display.display, dptr->image);
941 	  dptr->image = 0;
942 	}
943   }
944   /*}}}*/
945   /*{{{  sprite build*/
946   {
947     unsigned  ix;
948     SPRITE    *sptr;
949     SPRITE    *aptr;
950 
951     for(sptr = &sprites[SPRITE_APPLES], ix = 0; ix != 5; sptr++, ix++)
952       {
953 	unsigned  count;
954 
955 	for(count = 0; count != 4; count++)
956 	  {
957 	    aptr = &sprites[SPRITE_SMALL_APPLE + (count != ix)];
958 	    XCopyArea(display.display, aptr->mask, sptr->mask, GCN(GC_COPY),
959 		0, 0, aptr->size.x, aptr->size.y,
960 		(count % 2) * aptr->size.x, (count / 2) * aptr->size.y);
961 	    XCopyArea(display.display, aptr->image, sptr->image, GCN(GC_COPY),
962 		0, 0, aptr->size.x, aptr->size.y,
963 		(count % 2) * aptr->size.x, (count / 2) * aptr->size.y);
964 	  }
965       }
966     sptr = &sprites[SPRITE_APPLE_DROP];
967     aptr = &sprites[SPRITE_BIG_APPLE];
968     XCopyArea(display.display, aptr[1].mask, sptr->mask, GCN(GC_COPY),
969 	0, 0, sptr->size.x, sptr->size.y, 0, 0);
970     XCopyArea(display.display, aptr[1].image, sptr->image, GCN(GC_COPY),
971 	0, 0, sptr->size.x, sptr->size.y, 0, 0);
972     XCopyArea(display.display, aptr[3].mask, sptr->mask, GCN(GC_OR),
973 	0, 0, sptr->size.x, sptr->size.y, 0, 0);
974     XCopyArea(display.display, aptr[3].image, sptr->image, GCN(GC_OR),
975 	0, 0, sptr->size.x, sptr->size.y, 0, 0);
976   }
977   /*}}}*/
978 #endif /* XMRED */
979 #ifndef NDEBUG
980   /* check we've used all the colors we allocated */
981   if(data.mono == False)
982     {
983       unsigned  ix;
984       COLOR_DEF *cptr;
985 
986       color_names[COLOR_FOREGROUND].name = NULL;
987       color_names[COLOR_BACKGROUND].name = NULL;
988       color_names[COLOR_BORDER].name = NULL;
989 #ifndef XMRED
990       color_names[COLOR_DYNAMIC].name = NULL;
991       color_names[COLOR_DYNAMIC + 1].name = NULL;
992 #endif /* XMRED */
993       for(cptr = color_names, ix = COLORS; ix--; cptr++)
994 	{
995 	  /* again some broken compilers can't cope with asserts
996 	   * over several lines */
997 	  int     check;
998 
999 	  check = cptr->alloc > 3 || !cptr->name ||
1000 	      (cptr->type & 16 && !display.dynamic);
1001 	  assert(check);
1002 	}
1003     }
1004 #endif /* NDEBUG */
1005   if(display.background == COLOUR_ZERO)
1006     {
1007       SPRITE  *sptr;
1008       unsigned ix;
1009 
1010       for(sptr = &sprites[SPRITE_CENTER_BASE], ix = 4; ix--; sptr++)
1011 	XCopyArea(display.display, sptr->image, sptr->mask, GCN(GC_MASK),
1012 	    0, 0, sptr->size.x, sptr->size.y, 0, 0);
1013     }
1014   /*{{{  create fills*/
1015   {
1016     unsigned  i;
1017     SPRITE    *dptr;
1018     SPRITE_DEF *sptr;
1019 
1020     for(i = 0, sptr = fills_def, dptr = fills;
1021 	i != FILLS; i++, sptr++, dptr++)
1022       {
1023 	int       x, y;
1024 	char      c;
1025 	unsigned char *ptr;
1026 
1027 	assert(sptr->size.x && sptr->size.y);
1028 	dptr->size.x = sptr->size.x;
1029 	dptr->size.y = sptr->size.y;
1030 	if(data.mono)
1031 	  {
1032 	    if(data.swap != False)
1033 	      for(ptr = sptr->bitmap,
1034 		  x = (sptr->size.x + 7) / 8 * sptr->size.y; x--; ptr++)
1035 		*ptr ^= 0xFF;
1036 	    for(ptr = sptr->bitmap, y = sptr->size.y; y--;)
1037 	      for(c = 0x55 << (y & 1), x = (sptr->size.x + 7) / 8; x--; ptr++)
1038 		*ptr |= c;
1039 	  }
1040 	dptr->mask = XCreateBitmapFromData(display.display, root,
1041 	    (char CONST *)sptr->bitmap, sptr->size.x, sptr->size.y);
1042       }
1043   }
1044   /*}}}*/
1045 #ifndef XMRED
1046   /*{{{  create score pixmaps*/
1047   {
1048     unsigned  i;
1049 
1050     for(i = BOARD_SCORES; i--;)
1051       {
1052 	update.score.list[i].image = XCreatePixmap(display.display,
1053 	    root, DIGIT_WIDTH * 4, DIGIT_HEIGHT, depth);
1054 	update.score.list[i].mask = XCreatePixmap(display.display,
1055 	    root, DIGIT_WIDTH * 4, DIGIT_HEIGHT, depth);
1056       }
1057   }
1058   /*}}}*/
1059 #endif /* XMRED */
1060   return;
1061 }
1062 /*}}}*/
1063 #ifndef NDEBUG
1064 /*{{{  int error_handler(display, event)*/
1065 extern int error_handler
1066 FUNCARG((dpy, event),
1067 	Display *dpy
1068 ARGSEP  XErrorEvent *event
1069 )
1070 {
1071   char  msg[80];
1072 
1073   XGetErrorText(dpy, event->error_code, msg, 80);
1074   fprintf(stderr, "X Error: %s\n", msg);
1075   fprintf(stderr, "  Major opcode: %d\n", event->request_code);
1076   fprintf(stderr, "  Minor opcode: %d\n", event->minor_code);
1077   fprintf(stderr, "  Resource id: 0x%lx\n", (long)event->resourceid);
1078   fprintf(stderr, "  Serial number: %ld\n", (long)event->serial);
1079 #ifdef DEBUGEVENTLOOP
1080   while(QLength(display.display))
1081     {
1082       XEvent  buffered;
1083 
1084       XNextEvent(display.display, &buffered);
1085       fprintf(stderr, "0x%lx type %d\n", buffered.xany.window, buffered.type);
1086     }
1087 #endif /* DEBUGEVENTLOOP */
1088   fflush(stderr);
1089   abort();
1090   return 0;
1091 }
1092 /*}}}*/
1093 #endif /* NDEBUG */
1094 /*{{{  void extract_options(argcp, argv, options)*/
1095 static VOIDFUNC extract_options
1096 FUNCARG((argcp, argv, options),
1097 	int       *argcp
1098 ARGSEP  String    *argv
1099 ARGSEP  CommandOptionDesc CONST *options
1100 )
1101 {
1102   CommandOptionDesc CONST *ptr;
1103   unsigned  ix;
1104   unsigned  copy;
1105 
1106   for(ptr = options; ptr->option; ptr++)
1107     if(ptr->optional)
1108       *(char **)((char *)&data + ptr->offset) = NULL;
1109     else
1110       *(Boolean *)((char *)&data + ptr->offset) = False;
1111   for(ix = copy = 1; ix != *argcp; ix++)
1112     {
1113       size_t    length;
1114 
1115       length = strlen(argv[ix]);
1116       for(ptr = options; ptr->option; ptr++)
1117 	{
1118 	  if(!strncmp(argv[ix], ptr->option, length))
1119 	    {
1120 	      if(ptr->optional)
1121 		{
1122 		  if(ix < *argcp - 1)
1123 		    {
1124 		      *(char CONST **)((char *)&data + ptr->offset) =
1125 			  argv[ix + 1];
1126 		      ix++;
1127 		    }
1128 		  else
1129 		    {
1130 		      data.help = True;
1131 		      data.colors = False;
1132 		      fprintf(stderr, "Argument expected for %s\n",
1133 			  ptr->option);
1134 		    }
1135 		}
1136 	      else
1137 		*(Boolean *)((char *)&data + ptr->offset) = True;
1138 	      break;
1139 	    }
1140 	}
1141       if(!ptr->option)
1142 	argv[copy++] = argv[ix];
1143     }
1144   *argcp = copy;
1145   return;
1146 }
1147 /*}}}*/
1148 /*{{{  void gettoplevelresources()*/
1149 static VOIDFUNC gettoplevelresources FUNCARGVOID
1150 /*
1151  * get toplevel resources and
1152  * create a private colormap, if required
1153  * must be done before creating other widgets, in order
1154  * to get the visual inheritance correct
1155  */
1156 {
1157   Screen    *screenptr;
1158 
1159   XtVaGetValues(display.toplevel, XtNvisual, (XtArgVal)&display.visual,
1160       XtNdepth, (XtArgVal)&display.depth, XtNscreen, (XtArgVal)&screenptr,
1161       XtNcolormap, (XtArgVal)&display.colormap, NULL);
1162   display.screen = XScreenNumberOfScreen(screenptr);
1163   if(!display.visual)
1164     display.visual = DefaultVisualOfScreen(screenptr);
1165   if(data.private != False)
1166     {
1167       display.colormap = XCreateColormap(display.display,
1168 	  RootWindowOfScreen(screenptr), display.visual, AllocNone);
1169       XtVaSetValues(display.toplevel,
1170 	  XtNcolormap, (XtArgVal)display.colormap, NULL);
1171     }
1172   return;
1173 }
1174 /*}}}*/
1175 /*{{{  void fatal_error(text, ...)*/
1176 extern VOIDFUNC fatal_error
1177 FUNCVARARG((text, VARARGDEF),
1178 	char CONST *text
1179 )
1180 {
1181   va_list   args;
1182 
1183   fputs(myname, stderr);
1184   fputc(':', stderr);
1185   VARARGSET(args, text);
1186   vfprintf(stderr, text, args);
1187   fputc('\n', stderr);
1188   exit(1);
1189   return;
1190 }
1191 /*}}}*/
1192 /*{{{  void list_color_resource(mask, resource, value)*/
1193 static VOIDFUNC list_color_resource
1194 FUNCARG((mask, resource, value),
1195 	unsigned  mask
1196 ARGSEP  char CONST *resource
1197 ARGSEP  char CONST *value
1198 )
1199 {
1200   unsigned  len;
1201 
1202   assert(mask && mask != 6 && mask != 9);
1203   if(((mask + 1) | mask) == 0xF)
1204     mask = 15;
1205   fputs("Xmris", stderr);
1206   len = 5;
1207   if(mask == 15 || mask == 5)
1208     {
1209       fputc('*', stderr);
1210       len++;
1211     }
1212   else
1213     {
1214       if(!(mask & 3))
1215 	{
1216 	  fputs("*msit", stderr);
1217 	  len += 5;
1218 	}
1219       else if(!(mask & 12))
1220 	{
1221 	  fputs("*mris", stderr);
1222 	  len += 5;
1223 	}
1224       if(!(mask & 10))
1225 	{
1226 	  fputc('.', stderr);
1227 	  len++;
1228 	}
1229       else if(mask == 12 || mask == 3)
1230 	{
1231 	  fputc('*', stderr);
1232 	  len++;
1233 	}
1234       else if(mask == 10)
1235 	{
1236 	  fputs("*swap.", stderr);
1237 	  len += 6;
1238 	}
1239       else
1240 	{
1241 	  fputs(".swap.", stderr);
1242 	  len += 6;
1243 	}
1244     }
1245   len += strlen(resource);
1246   fprintf(stderr, "%s:%*s%s\n", resource,
1247       len < 32 ? (int)(32 - len) : 1, "", value);
1248   return;
1249 }
1250 /*}}}*/
1251 /*{{{  void list_help(name)*/
1252 extern VOIDFUNC list_help
1253 FUNCARG((name),
1254 	char CONST *name
1255 )
1256 {
1257   if(data.colors != False)
1258     /*{{{  color resources*/
1259     {
1260       COLOR_DEF CONST *cptr;
1261       unsigned  count;
1262 
1263       fprintf(stderr, "!Colour resources\n");
1264       fprintf(stderr, "!Xmris*{mris,msit}.{swap}.<name>:<color>\n");
1265       for(cptr = &color_names[4], count = COLORS - 4; count--; cptr++)
1266 	{
1267 	  unsigned  mask;
1268 	  char CONST *values[4];
1269 	  unsigned  point;
1270 
1271 	  mask = cptr->type & 15;
1272 	  memcpy(values, cptr->source, sizeof(values));
1273 	  if(!values[1])
1274 	    values[1] = values[0];
1275 	  if(!values[3])
1276 	    values[3] = values[2];
1277 	  if(!values[3])
1278 	    values[3] = values[1];
1279 	  if(!values[2])
1280 	    values[2] = values[0];
1281 	  for(point = 0; mask && point < 4; point++)
1282 	    {
1283 	      unsigned  ix;
1284 	      unsigned  select;
1285 
1286 	      for(select = 0, ix = 4; ix--;)
1287 		if(!strcmp(values[ix], values[point]))
1288 		  select |= 1 << ix;
1289 	      select &= mask;
1290 	      if(select)
1291 		list_color_resource(select, cptr->name, values[point]);
1292 	      mask ^= select;
1293 	    }
1294 	}
1295     }
1296     /*}}}*/
1297   else
1298     {
1299       HELP CONST *hptr;
1300 
1301       fprintf(stderr, "%s [-option ...] [-toolkitoption ...]\n", name);
1302       fprintf(stderr, "%s %s %s %s\n",
1303 #ifndef XMRED
1304 	  names[0], XMRISVERSION,
1305 #else
1306 	  "xmred", XMREDVERSION,
1307 #endif /* XMRED */
1308 	  DATE, COPYRIGHT);
1309       fprintf(stderr, " Option    Resource         Argument\n");
1310       for(hptr = help; hptr->help; hptr++)
1311 	fprintf(stderr, "%-10s %-16s %-16s %s\n",
1312 	    hptr->arg, hptr->resource, hptr->option, hptr->help);
1313     }
1314   return;
1315 }
1316 /*}}}*/
1317 /*{{{  void make_color_image(dptr, sptr, root, depth, bitmap)*/
1318 static VOIDFUNC make_color_image
1319 FUNCARG((dptr, sptr, root, depth, bitmap),
1320 	SPRITE    *dptr
1321 ARGSEP  SPRITE_DEF *sptr
1322 ARGSEP  Window    root
1323 ARGSEP  unsigned  depth
1324 ARGSEP  Pixmap    bitmap
1325 )
1326 /*
1327  * The colour sprite image is generated from the bitmap, which
1328  * is treated as a set of n planes vertically alligned.
1329  * The allows 2^n different colours per sprite (including the mask). The
1330  * mapping from the sprite planes to the colour map is
1331  * controlled by the sprite's colour table, which pointes the game's
1332  * colour table, which contains the pixel value to use.
1333  * We go through the bit planes, once for each colour, generating colour
1334  * stencils which we or together for the image. Rather like colour
1335  * printing really.
1336  */
1337 {
1338   Pixmap    temp;
1339   unsigned  ix;
1340 
1341   temp = XCreatePixmap(display.display, root,
1342       dptr->size.x, dptr->size.y, depth);
1343   XSetForeground(display.display, GCN(GC_BOARD), color_zero);
1344   XFillRectangle(display.display, dptr->image, GCN(GC_BOARD),
1345       0, 0, dptr->size.x, dptr->size.y);
1346   for(ix = 1 << sptr->planes; --ix;)
1347     {
1348       unsigned long color;
1349 
1350       color = sptr->colors[data.swap != False][ix];
1351       if(color != ~(unsigned long)0)
1352 	{
1353 	  unsigned  count;
1354 
1355 	  {
1356 	    /* stupid compilers with broken stringizizing
1357 	     * and stupid compilers which don't understand \
1358 	     * outside of #define */
1359 	    unsigned  alloc;
1360 
1361 	    alloc = color_names[(unsigned)color].alloc;
1362 	    assert(alloc == 1 || alloc == 2 || alloc == 3);
1363 	  }
1364 #ifndef NDEBUG
1365 	  color_names[(unsigned)color].name = NULL;
1366 #endif /* NDEBUG */
1367 	  XSetForeground(display.display, GCN(GC_BOARD),
1368 	      colors[(unsigned)color].pixel);
1369 	  XFillRectangle(display.display, temp, GCN(GC_BOARD),
1370 	      0, 0, dptr->size.x, dptr->size.y);
1371 	  for(count = sptr->planes; count--;)
1372 	    XCopyArea(display.display, bitmap, temp,
1373 		GCN(ix & (1 << count) ? GC_AND : GC_MASK),
1374 		0, (int)(count * dptr->size.y),
1375 		dptr->size.x, dptr->size.y, 0, 0);
1376 	  XCopyArea(display.display, temp, dptr->image, GCN(GC_OR),
1377 	      0, 0, dptr->size.x, dptr->size.y, 0, 0);
1378 	}
1379     }
1380   XFreePixmap(display.display, temp);
1381   return;
1382 }
1383 /*}}}*/
1384 /*{{{  void make_mask_image(dptr, sptr, root, depth, bitmap)*/
1385 static VOIDFUNC make_mask_image
1386 FUNCARG((dptr, sptr, root, depth, bitmap),
1387 	SPRITE  *dptr
1388 ARGSEP  SPRITE_DEF *sptr
1389 ARGSEP  Window    root
1390 ARGSEP  unsigned  depth
1391 ARGSEP  Pixmap    bitmap
1392 )
1393 /*
1394  * the mask is made from logical color ~0, ie or all the bitmap
1395  * planes together, and that's the mask.
1396  * the mask halo is generated from the mask by oring the mask ontop
1397  * of itself, offset by the 4 adjacent pixels. This grows the mask too.
1398  * the halo is the xor of the grown mask and the original mask.
1399  */
1400 {
1401   unsigned  ix;
1402 
1403   XSetForeground(display.display, GCN(GC_BOARD), color_zero);
1404   XFillRectangle(display.display, dptr->mask, GCN(GC_BOARD),
1405       0, 0, dptr->size.x, dptr->size.y);
1406   for(ix = sptr->planes; ix--;)
1407     XCopyArea(display.display, bitmap, dptr->mask, GCN(GC_OR),
1408 	0, (int)(dptr->size.y * ix), dptr->size.x, dptr->size.y, 0, 0);
1409   XCopyArea(display.display,
1410       dptr->mask, dptr->image, GCN(GC_AND),
1411       0, 0, dptr->size.x, dptr->size.y, 0, 0);
1412   if(sptr->flags & BORDER_HALO_MASK)
1413     /*{{{  make the halo*/
1414     {
1415       Pixmap    halo;
1416 
1417       halo = XCreatePixmap(display.display, root,
1418 	  dptr->size.x, dptr->size.y, depth);
1419       XFillRectangle(display.display, halo, GCN(GC_BOARD),
1420 	  0, 0, dptr->size.x, dptr->size.y);
1421       XCopyArea(display.display, dptr->mask, halo, GCN(GC_OR),
1422 	  0, 0, dptr->size.x, dptr->size.y, 0, 1);
1423       XCopyArea(display.display, dptr->mask, halo, GCN(GC_OR),
1424 	  0, 0, dptr->size.x, dptr->size.y, 0, -1);
1425       XCopyArea(display.display, dptr->mask, halo, GCN(GC_OR),
1426 	  0, 0, dptr->size.x, dptr->size.y, 1, 0);
1427       XCopyArea(display.display, dptr->mask, halo, GCN(GC_OR),
1428 	  0, 0, dptr->size.x, dptr->size.y, -1, 0);
1429       XCopyArea(display.display, dptr->mask, halo, GCN(GC_MASK),
1430 	  0, 0, dptr->size.x, dptr->size.y, 0, 0);
1431       XCopyArea(display.display, halo, dptr->mask, GCN(GC_OR),
1432 	  0, 0, dptr->size.x, dptr->size.y, 0, 0);
1433       XSetForeground(display.display, GCN(GC_BOARD),
1434 	  data.swap != False && sptr->flags & (data.mono != False ?
1435 	  BORDER_MONOSWAP_EDGE_MASK : BORDER_WHITE_EDGE_MASK) ?
1436 	  display.black : display.white);
1437       XSetFunction(display.display, GCN(GC_BOARD), GXand);
1438       XFillRectangle(display.display, halo, GCN(GC_BOARD),
1439 	  0, 0, dptr->size.x, dptr->size.y);
1440       XSetFunction(display.display, GCN(GC_BOARD), GXcopy);
1441       XCopyArea(display.display, halo, dptr->image, GCN(GC_OR),
1442 	  0, 0, dptr->size.x, dptr->size.y, 0, 0);
1443       XFreePixmap(display.display, halo);
1444     }
1445     /*}}}*/
1446   return;
1447 }
1448 /*}}}*/
1449 /*{{{  void make_mono_image(dptr, sptr, root, depth, bitmap)*/
1450 static VOIDFUNC make_mono_image
1451 FUNCARG((dptr, sptr, root, depth, bitmap),
1452 	SPRITE  *dptr
1453 ARGSEP  SPRITE_DEF *sptr
1454 ARGSEP  Window    root
1455 ARGSEP  unsigned  depth
1456 ARGSEP  Pixmap    bitmap
1457 )
1458 /*
1459  * As with the colour sprite image, the monochrome is generated from the
1460  * same bitmap and logical colors, but these either map to black or white
1461  * only.
1462  */
1463 {
1464   Pixmap    temp;
1465   unsigned long colors;
1466   unsigned  ix;
1467 
1468   temp = XCreatePixmap(display.display, root,
1469       dptr->size.x, dptr->size.y, depth);
1470   XSetForeground(display.display, GCN(GC_BOARD), color_zero);
1471   XFillRectangle(display.display, dptr->image, GCN(GC_BOARD),
1472       0, 0, dptr->size.x, dptr->size.y);
1473   colors = sptr->colors[data.swap != False][0];
1474   if(data.swap != False)
1475     colors = ~colors;
1476   for(ix = 1 << sptr->planes; ix; ix--)
1477     {
1478       unsigned  count;
1479 
1480       if(sptr->colors[data.swap != False][ix] != ~(unsigned long)0)
1481 	{
1482 	  XSetForeground(display.display, GCN(GC_BOARD),
1483 	      colors & (unsigned long)1 << ix ?
1484 	      display.black : display.white);
1485 	  XFillRectangle(display.display, temp, GCN(GC_BOARD),
1486 	      0, 0, dptr->size.x, dptr->size.y);
1487 	  for(count = sptr->planes; count--;)
1488 	    XCopyArea(display.display, bitmap, temp,
1489 		GCN(ix & (1 << count) ? GC_AND : GC_MASK),
1490 		0, (int)(count * dptr->size.y),
1491 		dptr->size.x, dptr->size.y, 0, 0);
1492 	  XCopyArea(display.display, temp, dptr->image, GCN(GC_OR),
1493 	     0, 0, dptr->size.x, dptr->size.y, 0, 0);
1494 	}
1495     }
1496   XFreePixmap(display.display, temp);
1497   return;
1498 }
1499 /*}}}*/
1500 /*{{{  void nadger_widget_colors(root, class)*/
1501 extern VOIDFUNC nadger_widget_colors
1502 FUNCARG((root, class),
1503 	Widget    root
1504 ARGSEP  WidgetClass class
1505 )
1506 /* traverse the widget tree and set the foreground, background and border
1507  * colors.
1508  * This has to be done, so that the swap attribute works as expected.
1509  * Without it, swap doesn't affect some widget's colors, which is wrong.
1510  */
1511 {
1512   /*{{{  typedef struct Stack*/
1513   typedef struct Stack
1514   {
1515     Widget  *children;
1516     Cardinal count;
1517   } STACK;
1518   /*}}}*/
1519   /*{{{  static Arg nadger[] =*/
1520   static Arg nadger[] =
1521   {
1522     {XtNforeground},
1523     {XtNbackground},
1524     {XtNborderColor},
1525     {XtNinternalBorderColor},
1526   };
1527   /*}}}*/
1528   /*{{{  static Arg values[] =*/
1529   static Arg values[] =
1530   {
1531     {XtNchildren},
1532     {XtNnumChildren},
1533   };
1534   /*}}}*/
1535   STACK     stack[8];
1536   STACK     *sptr;
1537 
1538   nadger[0].value = (XtArgVal)display.black;
1539   nadger[1].value = (XtArgVal)display.white;
1540   nadger[2].value = (XtArgVal)display.border;
1541   nadger[3].value = (XtArgVal)display.border;
1542   /* Ha, work this one out then! */
1543   for(sptr = stack; 1; root = *(sptr->children++), sptr++)
1544     {
1545       if(!class || XtIsSubclass(root, class) != False)
1546 	XtSetValues(root, nadger, XtNumber(nadger));
1547       if(XtIsComposite(root))
1548 	{
1549 	  values[0].value = (XtArgVal)&sptr->children;
1550 	  values[1].value = (XtArgVal)&sptr->count;
1551 	  sptr->count = 0;
1552 	  XtGetValues(root, values, XtNumber(values));
1553 	}
1554       else if(sptr != stack)
1555 	sptr--;
1556       while(sptr != stack && !sptr->count)
1557 	sptr--;
1558       if(!sptr->count--)
1559 	break;
1560     }
1561   return;
1562 }
1563 /*}}}*/
1564