1 /* Copyright (C) 1993, 1992 Nathan Sidwell */
2 /* RCS $Id: garden.c,v 4.6 1994/01/06 10:20:24 nathan Stable $ */
3 /*{{{  includes*/
4 #include "xmred.h"
5 #include <X11/IntrinsicP.h>
6 #include <X11/StringDefs.h>
7 #include <X11/Xaw/SimpleP.h>
8 /*}}}*/
9 /*{{{  defines*/
10 #define WIDTH_EXPAND  (GAP_WIDTH / 2)
11 #define HEIGHT_EXPAND (GAP_HEIGHT / 2)
12 /*}}}*/
13 /*{{{  structs*/
14 /*{{{  typedef struct _IconClass*/
15 typedef struct _GardenClass
16 {
17   int     ansi_compliance; /* not used */
18 } GardenClassPart;
19 /*}}}*/
20 /*{{{  typedef struct _GardenClassRec*/
21 typedef struct _GardenClassRec
22 {
23   CoreClassPart   core_class;
24   SimpleClassPart simple_class;
25   GardenClassPart garden_class;
26 } GardenClassRec;
27 /*}}}*/
28 /*{{{  typedef struct _GardenPart*/
29 typedef struct
30 {
31   /* private state */
32   Position  x;              /* corner of pixmap */
33   Position  y;              /* corner of pixmap */
34   COORD     cell;           /* last known cell */
35   unsigned  place;          /* place within cell
36 			     * 0-3 edges */
37   COORD     dir;            /* tracking direction */
38   unsigned  onoff;          /* adding or removing? */
39   int       option;         /* button currently pressed */
40 } GardenPart;
41 /*}}}*/
42 /*{{{  typedef struct _GardenRec*/
43 typedef struct _GardenRec
44 {
45   CorePart    core;
46   SimplePart  simple;
47   GardenPart  garden;
48 } GardenRec;
49 /*}}}*/
50 /*}}}*/
51 /*{{{  statics*/
52 static Widget garden;
53 /*}}}*/
54 /*{{{  prototypes*/
55 static VOIDFUNC Move PROTOARG((Widget, XEvent *, String *, Cardinal *));
56 static VOIDFUNC Press PROTOARG((Widget, XEvent *, String *, Cardinal *));
57 static VOIDFUNC Release PROTOARG((Widget, XEvent *, String *, Cardinal *));
58 
59 static VOIDFUNC Destroy PROTOARG((Widget));
60 static VOIDFUNC Initialize PROTOARG((Widget, Widget, ArgList, Cardinal *));
61 static XtGeometryResult QueryGeometry
62     PROTOARG((Widget, XtWidgetGeometry *, XtWidgetGeometry *));
63 static VOIDFUNC Redisplay PROTOARG((Widget, XEvent *, Region));
64 static VOIDFUNC Resize PROTOARG((Widget));
65 static Boolean SetValues
66     PROTOARG((Widget, Widget, Widget, ArgList, Cardinal *));
67 
68 static VOIDFUNC RedrawRect
69     PROTOARG((Widget, Position, Position, Dimension, Dimension));
70 
71 static unsigned do_option
72     PROTOARG((GardenWidget, unsigned, unsigned, char *, unsigned));
73 static VOIDFUNC draw_board_icon
74     PROTOARG((DESCRIPTOR CONST *, int, int, unsigned, unsigned));
75 static unsigned get_cell PROTOARG((GardenWidget, XEvent *, COORD *));
76 static unsigned get_paths PROTOARG((unsigned, unsigned, char **));
77 static unsigned long change_path
78     PROTOARG((unsigned, unsigned, unsigned, unsigned, char *, unsigned));
79 static VOIDFUNC cut_back
80     PROTOARG((int, int, unsigned, unsigned, int, int, SPRITE CONST *));
81 static VOIDFUNC cut_sprite PROTOARG((unsigned, int, int));
82 static unsigned long force_blank PROTOARG((unsigned, unsigned, char *));
83 static VOIDFUNC paint_cell PROTOARG((unsigned, unsigned, unsigned, unsigned));
84 static unsigned long set_random PROTOARG((unsigned, unsigned, char *));
85 /*}}}*/
86 /*{{{  translations*/
87 static char translations[] = "\
88 <BtnDown>:press() move()\n\
89 <BtnUp>:release()\n\
90 <BtnMotion>:move()\n\
91 ";
92 /*}}}*/
93 /*{{{  actions*/
94 static XtActionsRec actions[] =
95 {
96   {"move", Move},
97   {"press", Press},
98   {"release", Release},
99 };
100 /*}}}*/
101 #define SuperClass (WidgetClass)&simpleClassRec
102 /*{{{  GardenClassRec gardenClassRec =*/
103 GardenClassRec gardenClassRec =
104 {
105   /*{{{  core class part*/
106   {
107     SuperClass,                       /* superclass */
108     "Garden",                         /* class_name */
109     sizeof(GardenRec),                /* size */
110     NULL,                             /* class_initialize */
111     NULL,                             /* class_part_initialize */
112     False,                            /* class_inited */
113     Initialize,                       /* initialize */
114     NULL,                             /* initialize_hook */
115     XtInheritRealize,                 /* realize */
116     actions,                          /* actions */
117     XtNumber(actions),                /* num_actions */
118     NULL,                             /* resources */
119     0,                                /* num_resources */
120     NULLQUARK,                        /* xrm_class */
121     True,                             /* compress_motion */
122     XtExposeCompressMultiple,         /* compress_exposure */
123     True,                             /* compress_enterleave */
124     False,                            /* visible_interest */
125     Destroy,                          /* destroy */
126     Resize,                           /* resize */
127     Redisplay,                        /* expose */
128     SetValues,                        /* set_values */
129     NULL,                             /* set_values_hook */
130     XtInheritSetValuesAlmost,         /* set_values_almost */
131     NULL,                             /* get_values_hook */
132     NULL,                             /* accept_focus */
133     XtVersion,                        /* version */
134     NULL,                             /* callback_private */
135     translations,                     /* default_translations */
136     QueryGeometry,                    /* query_geometry */
137     XtInheritDisplayAccelerator,      /* display_accelerator */
138     NULL,                             /* extension */
139   },
140   /*}}}*/
141   /*{{{  simple class part*/
142   {
143     XtInheritChangeSensitive      /* change_sensitive */
144   },
145   /*}}}*/
146   /*{{{  garden class part*/
147   {
148     0,        /* dummy */
149   },
150   /*}}}*/
151 };
152 /*}}}*/
153 WidgetClass gardenWidgetClass = (WidgetClass)&gardenClassRec;
154 /* actions */
155 /*{{{  void Move(widget, event, params, num_params)*/
156 static VOIDFUNC Move
157 FUNCARG((widget, event, params, num_params),
158 	Widget    widget
159 ARGSEP  XEvent    *event
160 ARGSEP  String    *params
161 ARGSEP  Cardinal  *num_params
162 )
163 /* deal with pointer pressed motion events
164  */
165 {
166   GardenWidget gw;
167 
168   gw = (GardenWidget)widget;
169   if(event->type == MotionNotify && gw->garden.option >= 0)
170     {
171       unsigned  place;
172       COORD     cell;
173       COORD     dir;
174       unsigned  cont;
175       unsigned  icon;
176 
177       place = get_cell(gw, event, &cell);
178       dir.x = cell.x < gw->garden.cell.x ? -1 :
179 	  cell.x > gw->garden.cell.x ? 1 : 0;
180       dir.y = cell.y < gw->garden.cell.y ? -1 :
181 	  cell.y > gw->garden.cell.y ? 1 : 0;
182       cont = ((dir.x != 0) << 1) | (dir.y != 0);
183       icon = 0;
184       if(cont)
185 	/*{{{  moved to new cell*/
186 	{
187 	  unsigned  first;
188 
189 	  first = 1;
190 	  if(dir.x * gw->garden.dir.x < 0)
191 	    {
192 	      gw->garden.dir.x = 0;
193 	      gw->garden.dir.y = dir.y;
194 	    }
195 	  else if(dir.y * gw->garden.dir.y < 0)
196 	    {
197 	      gw->garden.dir.x = dir.x;
198 	      gw->garden.dir.y = 0;
199 	    }
200 	  if(!gw->garden.dir.y && !gw->garden.dir.x)
201 	    {
202 	      gw->garden.dir.y = dir.x ? 0 : dir.y;
203 	      gw->garden.dir.x = dir.x;
204 	    }
205 	  while(cont)
206 	    {
207 	      unsigned  mask;
208 
209 	      /*{{{  arrived @ x?*/
210 	      if(cell.x == gw->garden.cell.x)
211 		{
212 		  cont &= 1;
213 		  if(dir.y)
214 		    {
215 		      gw->garden.dir.x = 0;
216 		      gw->garden.dir.y = dir.y;
217 		    }
218 		}
219 	      /*}}}*/
220 	      /*{{{  arrived @ y?*/
221 	      if(cell.y == gw->garden.cell.y)
222 		{
223 		  cont &= 2;
224 		  if(dir.x)
225 		    {
226 		      gw->garden.dir.x = dir.x;
227 		      gw->garden.dir.y = 0;
228 		    }
229 		}
230 	      /*}}}*/
231 	      mask = gw->garden.dir.x < 0 ? 0x8 : gw->garden.dir.x > 0 ? 0x4 :
232 		  gw->garden.dir.y < 0 ? 0x2 : gw->garden.dir.y > 0 ? 0x1 : 0;
233 	      assert(mask);
234 	      if(first)
235 		mask = (mask ^ 0xF) & gw->garden.place ? 0 : 0x10;
236 	      else if(cont)
237 		mask |= 0x10;
238 	      else if((mask & place) != place)
239 		mask |= 0x10 | place;
240 	      if(mask)
241 		icon |= do_option(gw, 0, mask, NULL, 0);
242 	      first = 0;
243 	      if(cont)
244 		{
245 		  gw->garden.cell.x += gw->garden.dir.x;
246 		  gw->garden.cell.y += gw->garden.dir.y;
247 		}
248 	    }
249 	}
250 	/*}}}*/
251       else if(place != gw->garden.place)
252 	icon = do_option(gw, 0, place | 0x10, NULL, 0);
253       gw->garden.place = place;
254       if(icon)
255 	repaint_garden_icon();
256     }
257   return;
258 }
259 /*}}}*/
260 /*{{{  void Press(widget, event, params, num_params)*/
261 static VOIDFUNC Press
262 FUNCARG((widget, event, params, num_params),
263 	Widget    widget
264 ARGSEP  XEvent    *event
265 ARGSEP  String    *params
266 ARGSEP  Cardinal  *num_params
267 )
268 /* deal with button press event,
269  * override current button press, fetch option
270  * and determine whether adding or removing
271  */
272 {
273   GardenWidget gw;
274 
275   gw = (GardenWidget)widget;
276   if(event->type != ButtonPress)
277     /* EMPTY */;
278   else if((gw->garden.option = state.button[event->xbutton.button - 1]) < 0)
279     /* EMPTY */;
280   else
281     {
282       unsigned  place;
283       COORD     cell;
284 
285       place = get_cell(gw, event, &cell);
286       if(place)
287 	{
288 	  char      *cptr;
289 	  unsigned  current;
290 
291 	  save_garden();
292 	  gw->garden.place = place;
293 	  gw->garden.cell.x = cell.x;
294 	  gw->garden.cell.y = cell.y;
295 	  current = get_paths(cell.x, cell.y, &cptr);
296 	  /*{{{  set or release?*/
297 	  switch(gw->garden.option)
298 	  {
299 	    /*{{{  case OPTION_APPLES:*/
300 	    case OPTION_APPLES:
301 	      gw->garden.onoff = !ISAPPLE(*cptr) ||
302 		  !(GARDENAPPLE(*cptr) & (1 << state.apple));
303 	      break;
304 	    /*}}}*/
305 	    /*{{{  case OPTION_RANDOM:*/
306 	    case OPTION_RANDOM:
307 	      gw->garden.onoff = ISPATH(*cptr) || *cptr == GARDEN_NOAPPLE ||
308 		  *cptr == GARDEN_CHERRY;
309 	      break;
310 	    /*}}}*/
311 	    /*{{{  case OPTION_CHERRY:*/
312 	    case OPTION_CHERRY:
313 	      gw->garden.onoff = *cptr != GARDEN_CHERRY &&
314 		  !ISPATHCHERRY(*cptr);
315 	      break;
316 	    /*}}}*/
317 	    /*{{{  case OPTION_PATH:*/
318 	    case OPTION_PATH:
319 	      gw->garden.onoff = current & place;
320 	      break;
321 	    /*}}}*/
322 	    /*{{{  case OPTION_DEN:*/
323 	    case OPTION_DEN:
324 	      gw->garden.onoff = !ISPATHDEN(*cptr);
325 	      break;
326 	    /*}}}*/
327 	    /*{{{  case OPTION_PLAYER:*/
328 	    case OPTION_PLAYER:
329 	      gw->garden.onoff = !ISPATHPLAYER(*cptr);
330 	      break;
331 	    /*}}}*/
332 	  }
333 	  /*}}}*/
334 	  if(do_option(gw, 1, place, cptr, current))
335 	    repaint_garden_icon();
336 	}
337     }
338   return;
339 }
340 /*}}}*/
341 /*{{{  void Release(widget, event, params, num_params)*/
342 static VOIDFUNC Release
343 FUNCARG((widget, event, params, num_params),
344 	Widget    widget
345 ARGSEP  XEvent    *event
346 ARGSEP  String    *params
347 ARGSEP  Cardinal  *num_params
348 )
349 /* deal with button release event
350  * if its the current button being released, then
351  * set current to none
352  */
353 {
354   GardenWidget gw;
355 
356   gw = (GardenWidget)widget;
357   if(event->type == ButtonRelease &&
358       state.button[event->xbutton.button - 1] == gw->garden.option)
359     gw->garden.option = -1;
360   return;
361 }
362 /*}}}*/
363 /* methods */
364 /*{{{  void Destroy(widget)*/
365 static VOIDFUNC Destroy
366 FUNCARG((widget),
367 	Widget    widget
368 )
369 /* free the GC and remove the flash timeout
370  */
371 {
372   return;
373 }
374 /*}}}*/
375 /*{{{  void Initialize(treq, tnew, args, num_args)*/
376 static VOIDFUNC Initialize
377 FUNCARG((treq, tnew, args, num_args),
378 	Widget    treq
379 ARGSEP  Widget    tnew
380 ARGSEP  ArgList   args
381 ARGSEP  Cardinal  *num_args
382 )
383 /* set out default size
384  */
385 {
386   GardenWidget ngw;
387 
388   ngw = (GardenWidget)tnew;
389   ngw->garden.x = ngw->garden.y = 0;
390   ngw->garden.place = 0;
391   ngw->garden.dir.x = ngw->garden.dir.y = 0;
392   ngw->garden.option = -1;
393   if(!ngw->core.width)
394     ngw->core.width = WINDOW_WIDTH;
395   if(!ngw->core.height)
396     ngw->core.height = WINDOW_HEIGHT;
397   return;
398 }
399 /*}}}*/
400 /*{{{  XtGeometryResult QueryGeometry(widget, proposed, answer)*/
401 static XtGeometryResult QueryGeometry
402 FUNCARG((widget, proposed, answer),
403 	Widget    widget
404 ARGSEP  XtWidgetGeometry *proposed
405 ARGSEP  XtWidgetGeometry *answer
406 )
407 /* tell our parent what size we'd like to be
408  */
409 {
410   GardenWidget gw;
411 
412   gw = (GardenWidget)widget;
413   answer->request_mode = CWWidth | CWHeight;
414   answer->width = WINDOW_WIDTH;
415   answer->height = WINDOW_HEIGHT;
416   if((proposed->request_mode & (CWWidth | CWHeight)) == (CWWidth | CWHeight) &&
417       proposed->width == answer->width && proposed->height == answer->height)
418     return XtGeometryYes;
419   else if(answer->width == gw->core.width && answer->height == gw->core.height)
420     return XtGeometryNo;
421   else
422     return XtGeometryAlmost;
423 }
424 /*}}}*/
425 /*{{{  void Redisplay(widget, event, region)*/
426 static VOIDFUNC Redisplay
427 FUNCARG((widget, event, region),
428 	Widget    widget
429 ARGSEP  XEvent    *event
430 ARGSEP  Region    region
431 )
432 /* repaint ourselves
433  */
434 {
435   GardenWidget  gw;
436   XRectangle    rect;
437 
438   if(!XtIsRealized(widget))
439     return;
440   gw = (GardenWidget)widget;
441   XClipBox(region, &rect);
442   XCopyArea(display.display, display.copy, XtWindow((Widget)gw), GCN(GC_COPY),
443       rect.x - gw->garden.x, rect.y - gw->garden.y, rect.width, rect.height,
444       rect.x, rect.y);
445   return;
446 }
447 /*}}}*/
448 /*{{{  void Resize(widget)*/
449 static VOIDFUNC Resize
450 FUNCARG((widget),
451 	Widget    widget
452 )
453 /* recenter the pixmap, when we get resized
454  */
455 {
456   GardenWidget gw;
457 
458   gw = (GardenWidget)widget;
459   gw->garden.x = (gw->core.width - WINDOW_WIDTH) / 2;
460   gw->garden.y = (gw->core.height - WINDOW_HEIGHT) / 2;
461   return;
462 }
463 /*}}}*/
464 /*{{{  Boolean SetValues(cw, rw, nw, args, num_args)*/
465 static Boolean SetValues
466 FUNCARG((cw, rw, nw, args, num_args),
467 	Widget    cw
468 ARGSEP  Widget    rw
469 ARGSEP  Widget    nw
470 ARGSEP  ArgList   args
471 ARGSEP  Cardinal  *num_args
472 )
473 {
474   return False;
475 }
476 /*}}}*/
477 /* public routines */
478 /*{{{  void RedrawRect(widget, x, y, w, h)*/
479 static VOIDFUNC RedrawRect
480 FUNCARG((widget, x, y, w, h),
481 	Widget    widget
482 ARGSEP  Position  x
483 ARGSEP  Position  y
484 ARGSEP  Dimension w
485 ARGSEP  Dimension h
486 )
487 {
488   GardenWidget  gw;
489 
490   if(!XtIsRealized(widget))
491     return;
492   gw = (GardenWidget)widget;
493   XCopyArea(display.display, display.copy, XtWindow((Widget)gw), GCN(GC_COPY),
494       x, y, w, h, gw->garden.x + x, gw->garden.y + y);
495   return;
496 }
497 /*}}}*/
498 /* non-widget routines */
499 /*{{{  unsigned long change_path(x, y, mask, set, cptr, current)*/
500 static unsigned long change_path
501 FUNCARG((x, y, mask, set, cptr, current),
502 	unsigned  x     /* cell to changed */
503 ARGSEP  unsigned  y
504 ARGSEP  unsigned  mask  /* mask to change
505 			 * 0..3 edges,
506 			 * 4 center,
507 			 * 5 place random
508 			 * 6 clear cherry */
509 ARGSEP  unsigned  set   /* set or clear those specified */
510 ARGSEP  char      *cptr /* map pointer */
511 ARGSEP  unsigned  current /* current paths */
512 )
513 /* sets or clears the specified parts of a cell.
514  * forces the center to blank
515  * clears the center of adjacent cells if wall is cleared (by recursive call)
516  * returns a mask specifying which bits should be redrawn.
517  * 0..3 edges
518  * 4..7 adjacent centers
519  * 8 center
520  * 9..11 above, left and right tops edges
521  * 13..15 above, left and right above centers
522  * 16..19 adjacent centers changed
523  */
524 {
525   unsigned long redraw;
526   unsigned  random;
527   unsigned  cherry;
528   unsigned  cleared;
529 
530   random = mask & 0x20;
531   redraw = cherry = 0;
532   cleared = mask & 0x40;
533   if(cleared)
534     redraw = force_blank(x, y, cptr);
535   /*{{{  clip*/
536   {
537     if(!x)
538       mask &= ~4;
539     if(!y)
540       mask &= ~1;
541     if(x == CELLS_ACROSS - 1)
542       mask &= ~8;
543     if(y == CELLS_DOWN - 1)
544       mask &= ~2;
545   }
546   /*}}}*/
547   if(set)
548     {
549       if(mask & 0x10)
550 	mask |= 0xF;
551       mask &= current;
552     }
553   else
554     {
555       if(mask & 0x0F)
556 	mask |= 0x10;
557       mask &= ~current;
558     }
559   if(mask & 0x10)
560     {
561       cherry = *cptr == GARDEN_CHERRY || ISPATHCHERRY(*cptr);
562       if(cherry)
563 	random = 0;
564       else if(!cleared)
565 	redraw = force_blank(x, y, cptr);
566     }
567   if(mask & 0x10 && !y && INRANGE(x, 4, 8))
568     redraw |= 0x1;
569   if(mask)
570     changed_flag |= state.change;
571   /*{{{  clear center?*/
572   if(!set && mask & 0x10)
573     {
574       redraw |= 0x100;
575       if(y && (cptr[-1 - CELLS_ACROSS] == GARDEN_RANDOM ||
576 	  ISAPPLE(cptr[-1 - CELLS_ACROSS])))
577 	{
578 	  adjust_count(COUNT_FALL, 1);
579 	  redraw |= 0x10;
580 	}
581       *cptr = cherry ? GARDEN_PATH + GARDEN_PATH_CHERRY : GARDEN_PATH;
582     }
583   /*}}}*/
584   /*{{{  change above?*/
585   if(mask & 0x01)
586     {
587       if(!set && !ISPATH(cptr[-1 - CELLS_ACROSS]))
588 	{
589 	  redraw |= (change_path(x, y - 1, 0x10, 0,
590 	      cptr - CELLS_ACROSS - 1, get_paths(x, y - 1, NULL)) & 0x11) << 9;
591 	  redraw |= 0x10010;
592 	}
593       if(ISPATHDEN(cptr[-1 - CELLS_ACROSS]))
594 	redraw |= 0x10;
595       assert(ISPATH(*cptr) && y);
596       redraw |= 0x01;
597       cptr[-1 - CELLS_ACROSS] =
598 	  (GARDENPATH(cptr[-1 - CELLS_ACROSS]) ^ 1) + GARDEN_PATH;
599       if(!(GARDENPATH(*cptr) & 2) || !x || !ISPATH(cptr[-1]) ||
600 	  !(GARDENPATH(cptr[-1]) & 2))
601 	redraw |= 0x100;
602       if(!(GARDENPATH(cptr[-1 - CELLS_ACROSS]) & 2) || !x ||
603 	  !ISPATH(cptr[-2 - CELLS_ACROSS]) ||
604 	  !(GARDENPATH(cptr[-2 - CELLS_ACROSS]) & 2))
605 	redraw |= 0x10;
606     }
607   /*}}}*/
608   /*{{{  change below?*/
609   if(mask & 0x02)
610     {
611       if(!set && !ISPATH(cptr[1 + CELLS_ACROSS]))
612 	{
613 	  change_path(x, y + 1, 0x10, 0,
614 	      cptr + CELLS_ACROSS + 1, get_paths(x, y + 1, NULL));
615 	  redraw |= 0x20020;
616 	}
617       if(ISPATHDEN(cptr[1 + CELLS_ACROSS]))
618 	redraw |= 0x20;
619       assert(ISPATH(*cptr) && y != CELLS_DOWN - 1);
620       redraw |= 0x02;
621       *cptr = (GARDENPATH(*cptr) ^ 1) + GARDEN_PATH;
622       if(!(GARDENPATH(*cptr) & 2) || !x || !ISPATH(cptr[-1]) ||
623 	  !(GARDENPATH(cptr[-1]) & 2))
624 	redraw |= 0x100;
625       if(!(GARDENPATH(cptr[1 + CELLS_ACROSS]) & 2) || !x ||
626 	  !ISPATH(cptr[CELLS_ACROSS]) ||
627 	    !(GARDENPATH(cptr[CELLS_ACROSS]) & 2))
628 	redraw |= 0x20;
629     }
630   /*}}}*/
631   /*{{{  change left?*/
632   if(mask & 0x04)
633     {
634       if(!set && !ISPATH(cptr[-1]))
635 	{
636 	  redraw |= (change_path(x - 1, y, 0x10, 0,
637 	      cptr - 1, get_paths(x - 1, y, NULL)) & 0x11) << 10;
638 	  redraw |= 0x40040;
639 	}
640       if(ISPATHDEN(cptr[-1]))
641 	redraw |= 0x40;
642       assert(x && ISPATH(*cptr));
643       redraw |= 0x04;
644       cptr[-1] = (GARDENPATH(cptr[-1]) ^ 2) + GARDEN_PATH;
645       if(!(GARDENPATH(*cptr) & 1) || !y || !ISPATH(cptr[-1 - CELLS_ACROSS]) ||
646 	  !(GARDENPATH(cptr[-1 - CELLS_ACROSS]) & 1))
647 	redraw |= 0x100;
648       if(!(GARDENPATH(cptr[-1]) & 1) || !y ||
649 	  !ISPATH(cptr[-2 - CELLS_ACROSS]) ||
650 	  !(GARDENPATH(cptr[-2 - CELLS_ACROSS]) & 1))
651 	redraw |= 0x40;
652     }
653   /*}}}*/
654   /*{{{  change right?*/
655   if(mask & 0x08)
656     {
657       if(!set && !ISPATH(cptr[1]))
658 	{
659 	  redraw |= (change_path(x + 1, y, 0x10, 0,
660 	      cptr + 1, get_paths(x + 1, y, NULL)) & 0x011) << 11;
661 	  redraw |= 0x80080;
662 	}
663       if(ISPATHDEN(cptr[1]))
664 	redraw |= 0x80;
665       assert(ISPATH(*cptr) && x != CELLS_ACROSS - 1);
666       redraw |= 0x08;
667       *cptr = (GARDENPATH(*cptr) ^ 2) + GARDEN_PATH;
668       if(!(GARDENPATH(*cptr) & 1) || !y || !ISPATH(cptr[-1 - CELLS_ACROSS]) ||
669 	  !(GARDENPATH(cptr[-1 - CELLS_ACROSS]) & 1))
670 	redraw |= 0x100;
671       if(!(GARDENPATH(cptr[1]) & 1) || !y || !ISPATH(cptr[-CELLS_ACROSS]) ||
672 	  !(GARDENPATH(cptr[-CELLS_ACROSS]) & 1))
673 	redraw |= 0x80;
674     }
675   /*}}}*/
676   /*{{{  set center?*/
677   if(set && mask & 0x10)
678     {
679       redraw |= 0x100;
680       assert((ISPATHBLANK(*cptr) || ISPATHCHERRY(*cptr)) &&
681 	  !(GARDENPATH(*cptr) & 3));
682       *cptr = cherry ? GARDEN_CHERRY : GARDEN_NOAPPLE;
683       if(random)
684 	redraw |= set_random(x, y, cptr);
685       if(y && (cptr[-1 - CELLS_ACROSS] == GARDEN_RANDOM ||
686 	  ISAPPLE(cptr[-1 - CELLS_ACROSS])))
687 	{
688 	  adjust_count(COUNT_FALL, -1);
689 	  redraw |= 0x10;
690 	}
691     }
692   /*}}}*/
693   return redraw;
694 }
695 /*}}}*/
696 /*{{{  void cut_back(sx, sy, width, height, dx, dy, sprite)*/
697 static VOIDFUNC cut_back
698 FUNCARG((sx, sy, width, height, dx, dy, sprite),
699 	int       sx            /* offset within source sprite */
700 ARGSEP  int       sy
701 ARGSEP  unsigned  width         /* size of blat */
702 ARGSEP  unsigned  height
703 ARGSEP  int       dx            /* destination address */
704 ARGSEP  int       dy
705 ARGSEP  SPRITE CONST *sprite    /* sprite to blat on */
706 )
707 /*
708  * munches the background image with the specified sprite
709  */
710 {
711   if(display.background != COLOUR_ONE)
712     XCopyArea(display.display, sprite->mask, display.copy, GCN(GC_MASK),
713 	sx, sy, width, height, dx, dy);
714   if(display.background != COLOUR_ZERO)
715     XCopyArea(display.display, sprite->image, display.copy, GCN(GC_OR),
716 	sx, sy, width, height, dx, dy);
717   return;
718 }
719 /*}}}*/
720 /*{{{  void cut_sprite(ix, flag, x, y)*/
721 static VOIDFUNC cut_sprite
722 FUNCARG((ix, x, y),
723 	unsigned  ix
724 ARGSEP  int       x
725 ARGSEP  int       y
726 )
727 /*
728  * copy a sprite onto the background
729  */
730 {
731   SPRITE CONST *sptr;
732 
733   sptr = &sprites[ix];
734   if(!INRANGE(ix, SPRITE_CENTER_BASE, 4) || display.background != COLOUR_ZERO)
735     XCopyArea(display.display, sptr->mask, display.copy, GCN(GC_MASK),
736 	0, 0, sptr->size.x, sptr->size.y, x, y);
737   XCopyArea(display.display, sptr->image, display.copy, GCN(GC_OR),
738       0, 0, sptr->size.x, sptr->size.y, x, y);
739   return;
740 }
741 /*}}}*/
742 /*{{{  unsigned do_option(gw, start, place, cptr, current)*/
743 static unsigned do_option
744 FUNCARG((gw, start, place, cptr, current),
745 	GardenWidget gw
746 ARGSEP  unsigned  start   /* first action of sequence */
747 ARGSEP  unsigned  place   /* place mask on cell */
748 ARGSEP  char      *cptr   /* char pointer to map (calculated if NULL) */
749 ARGSEP  unsigned  current /* current walls (calculated if cptr is NULL) */
750 )
751 /* perform an option on the current garden cell,
752  * using the specified place mask.
753  * the caller must deal with tracking
754  * returns flag which specifies if the icon needs refreshing
755  */
756 {
757   unsigned long redraw;
758 
759   redraw = 0;
760   assert(gw->garden.option >= 0);
761   if(!cptr)
762     current = get_paths(gw->garden.cell.x, gw->garden.cell.y, &cptr);
763   /*{{{  perform the option*/
764   switch(gw->garden.option)
765   {
766     /*{{{  case OPTION_APPLES:*/
767     case OPTION_APPLES:
768     {
769       if(!(start || place & 0x10))
770 	/* EMPTY */;
771       else if(gw->garden.onoff)
772 	{
773 	  redraw = change_path(gw->garden.cell.x, gw->garden.cell.y, 0x10,
774 	      1, cptr, current);
775 	  if(*cptr == GARDEN_CHERRY)
776 	    force_blank(gw->garden.cell.x, gw->garden.cell.y, cptr);
777 	  redraw |= set_random(gw->garden.cell.x, gw->garden.cell.y, cptr);
778 	  if(!ISAPPLE(*cptr))
779 	    *cptr = GARDEN_APPLE;
780 	  if(!(GARDENAPPLE(*cptr) & (1 << state.apple)))
781 	    {
782 	      *cptr += 1 << state.apple;
783 	      adjust_count(COUNT_APPLES + state.apple, 1);
784 	    }
785 	  redraw |= 0x100;
786 	}
787       else if(ISAPPLE(*cptr) && GARDENAPPLE(*cptr) & (1 << state.apple))
788 	{
789 	  *cptr -= 1 << state.apple;
790 	  adjust_count(COUNT_APPLES + state.apple, -1);
791 	  if(*cptr == GARDEN_APPLE)
792 	    *cptr = GARDEN_RANDOM;
793 	  changed_flag |= state.change;
794 	  redraw = 0x100;
795 	}
796       break;
797     }
798     /*}}}*/
799     /*{{{  case OPTION_RANDOM;*/
800     case OPTION_RANDOM:
801     {
802       if(!(start || place & 0x10))
803 	/* EMPTY */;
804       else if(gw->garden.onoff)
805 	{
806 	  redraw = change_path(gw->garden.cell.x, gw->garden.cell.y,
807 	      0x5F, 1, cptr, current);
808 	  redraw |= set_random(gw->garden.cell.x, gw->garden.cell.y, cptr);
809 	}
810       else if(ISAPPLE(*cptr) || *cptr == GARDEN_RANDOM)
811 	redraw = force_blank(gw->garden.cell.x, gw->garden.cell.y, cptr);
812       break;
813     }
814     /*}}}*/
815     /*{{{  case OPTION_CHERRY:*/
816     case OPTION_CHERRY:
817     {
818       if(!(start || place & 0x10))
819 	/* EMPTY */;
820       else if(gw->garden.onoff)
821 	{
822 	  if(*cptr == GARDEN_CHERRY || ISPATHCHERRY(*cptr))
823 	    /* EMPTY */;
824 	  else
825 	    {
826 	      redraw = force_blank(gw->garden.cell.x, gw->garden.cell.y, cptr);
827 	      if(ISPATH(*cptr))
828 		*cptr += GARDEN_PATH_CHERRY - GARDEN_PATH_BLANK;
829 	      else
830 		*cptr = GARDEN_CHERRY;
831 	      adjust_count(COUNT_CHERRY, 1);
832 	      redraw |= 0x100;
833 	      changed_flag |= state.change;
834 	    }
835 	}
836       else if(*cptr == GARDEN_CHERRY || ISPATHCHERRY(*cptr))
837 	{
838 	  redraw = force_blank(gw->garden.cell.x, gw->garden.cell.y, cptr);
839 	  redraw |= set_random(gw->garden.cell.x, gw->garden.cell.y, cptr);
840 	}
841       break;
842     }
843     /*}}}*/
844     /*{{{  case OPTION_PATH:*/
845     case OPTION_PATH:
846     {
847       redraw = change_path(gw->garden.cell.x, gw->garden.cell.y,
848 	  place | 0x20, gw->garden.onoff, cptr, current);
849       break;
850     }
851     /*}}}*/
852     /*{{{  case OPTION_DEN:*/
853     case OPTION_DEN:
854     {
855       if(!(start || place & 0x10))
856 	/* EMPTY */;
857       else if(gw->garden.onoff)
858 	{
859 	  if(ISPATHDEN(*cptr))
860 	    /* EMPTY */;
861 	  else
862 	    {
863 	      redraw = change_path(gw->garden.cell.x, gw->garden.cell.y,
864 		  0x50, 0, cptr, current);
865 	      assert(ISPATHBLANK(*cptr));
866 	      *cptr += GARDEN_PATH_DEN - GARDEN_PATH_BLANK;
867 	      adjust_count(COUNT_DEN, 1);
868 	      redraw |= 0x100;
869 	      changed_flag |= state.change;
870 	    }
871 	}
872       else if(ISPATHDEN(*cptr))
873 	redraw = force_blank(gw->garden.cell.x, gw->garden.cell.y, cptr);
874       break;
875     }
876     /*}}}*/
877     /*{{{  case OPTION_PLAYER:*/
878     case OPTION_PLAYER:
879     {
880       if(!(start || place & 0x10))
881 	/* EMPTY */;
882       else if(gw->garden.onoff)
883 	{
884 	  if(ISPATHPLAYER(*cptr))
885 	    /* EMPTY */;
886 	  else
887 	    {
888 	      redraw = change_path(gw->garden.cell.x, gw->garden.cell.y,
889 		  0x50, 0, cptr, current);
890 	      assert(ISPATHBLANK(*cptr));
891 	      *cptr += GARDEN_PATH_PLAYER - GARDEN_PATH_BLANK;
892 	      adjust_count(COUNT_PLAYER, 1);
893 	      redraw |= 0x100;
894 	      changed_flag |= state.change;
895 	    }
896 	}
897       else if(ISPATHPLAYER(*cptr))
898 	redraw = force_blank(gw->garden.cell.x, gw->garden.cell.y, cptr);
899       break;
900     }
901     /*}}}*/
902     default:
903       assert(0);
904   }
905   /*}}}*/
906   /*{{{  update display.copy*/
907   {
908     /*{{{  typedef struct Entry*/
909     typedef struct Entry
910     {
911       COORD     offset;   /* cell offset */
912       unsigned  mask;     /* top edge mask */
913     } ENTRY;
914     /*}}}*/
915     /*{{{  static ENTRY CONST table[] =*/
916     static ENTRY CONST table[] =
917     {
918       {{0, -1}, 0x200},
919       {{0, 1},  0x000},
920       {{-1, 0}, 0x400},
921       {{1, 0},  0x800},
922     };
923     /*}}}*/
924     /*{{{  static COORD CONST offsets[] =*/
925     static COORD CONST offsets[] =
926     {
927       { 0, -2},
928       {-1, -1},
929       { 1, -1},
930     };
931     /*}}}*/
932     unsigned  ix;
933     ENTRY CONST *tptr;
934     COORD CONST *optr;
935 
936     if(redraw & 0x10F)
937       paint_cell(gw->garden.cell.x, gw->garden.cell.y,
938 	  (redraw & 0xF) | ((redraw >> 4) & 0x10), 1);
939     for(tptr = &table[XtNumber(table) - 1], ix = XtNumber(table); ix--; tptr--)
940       if(redraw & (0x10 << ix))
941 	paint_cell(gw->garden.cell.x + tptr->offset.x,
942 	    gw->garden.cell.y + tptr->offset.y,
943 	    redraw & tptr->mask ? 0x11 : 0x10, 1);
944     for(optr = &offsets[XtNumber(offsets) - 1], ix = XtNumber(offsets);
945 	ix--; optr--)
946       if(redraw & (0x2000 << ix))
947 	paint_cell(gw->garden.cell.x + optr->x, gw->garden.cell.y + optr->y,
948 	    0x10, 1);
949     draw_board_icon(state.edit, gw->garden.cell.x - ((redraw & 0x44000) != 0),
950 	gw->garden.cell.y - ((redraw & 0x10000) != 0),
951 	1 + ((redraw & 0x44000) != 0) + ((redraw & 0x88000) != 0),
952 	1 + ((redraw & 0x10000) != 0) + ((redraw & 0x20000) != 0));
953   }
954   /*}}}*/
955   return redraw;
956 }
957 /*}}}*/
958 /*{{{  void draw_board_icon(dptr, x, y, w, h)*/
959 static VOIDFUNC draw_board_icon
960 FUNCARG((dptr, x, y, w, h),
961 	DESCRIPTOR CONST *dptr
962 ARGSEP  int       x
963 ARGSEP  int       y
964 ARGSEP  unsigned  w
965 ARGSEP  unsigned  h
966 )
967 /* draws the specified board icon cells and their edges
968  */
969 {
970   BOARD     *board;
971   Pixmap    pixmap;
972   char      *cptr;
973   unsigned  row;
974   unsigned  column;
975   unsigned  ix;
976   XGCValues gcv;
977 
978   assert(dptr && dptr->type == DESCRIPTOR_GARDEN && dptr->board);
979   board = dptr->board;
980   pixmap = dptr->pixmap;
981   gcv.fill_style = FillSolid;
982   gcv.foreground = data.mono != False ? display.black :
983       colors[backgrounds[board->colors][1]].pixel;
984   XChangeGC(display.display, GCN(GC_BOARD), GCFillStyle | GCForeground, &gcv);
985   if(x)
986     {
987       x--;
988       w++;
989     }
990   if(y)
991     {
992       y--;
993       h++;
994     }
995   XFillRectangle(display.display, pixmap, GCN(GC_BOARD),
996       x * 3, y * 3 + 3, w * 3, h * 3);
997   for(row = y; h--; row++)
998     for(cptr = &board->map[row][x], column = x, ix = w; ix--; column++, cptr++)
999       /*{{{  do a cell*/
1000       {
1001 	XRectangle rectangles[3];
1002 	unsigned  n;
1003 	unsigned  mask;
1004 	char      c;
1005 
1006 	c = *cptr;
1007 	if(ISPATH(c))
1008 	  mask = 4 | (GARDENPATH(c) & 3);
1009 	else
1010 	  mask = 0;
1011 	if(mask == 0x7 && ISPATH(cptr[1]) && GARDENPATH(cptr[1]) & 1 &&
1012 	    ISPATH(cptr[CELLS_ACROSS + 1]) &&
1013 	      GARDENPATH(cptr[CELLS_ACROSS + 1]) & 2)
1014 	  mask |= 8;
1015 	n = 0;
1016 	/*{{{  all blank?*/
1017 	if(mask == 0xF)
1018 	  {
1019 	    rectangles[n].x = column * 3;
1020 	    rectangles[n].y = row * 3 + 3;
1021 	    rectangles[n].width = 3;
1022 	    rectangles[n].height = 4;
1023 	    n++;
1024 	    mask = 0;
1025 	  }
1026 	/*}}}*/
1027 	/*{{{  visit and right?*/
1028 	if((mask & 0x6) == 0x6)
1029 	  {
1030 	    rectangles[n].x = column * 3;
1031 	    rectangles[n].y = row * 3 + 3;
1032 	    rectangles[n].width = 3;
1033 	    rectangles[n].height = 2;
1034 	    n++;
1035 	    mask ^= 0x6;
1036 	  }
1037 	/*}}}*/
1038 	/*{{{  visit and down?*/
1039 	if((mask & 0x5) == 0x5)
1040 	  {
1041 	    rectangles[n].x = column * 3;
1042 	    rectangles[n].y = row * 3 + 3;
1043 	    rectangles[n].width = 2;
1044 	    rectangles[n].height = 3;
1045 	    n++;
1046 	    mask ^= 0x5;
1047 	  }
1048 	/*}}}*/
1049 	/*{{{  visit?*/
1050 	if(mask & 0x4)
1051 	  {
1052 	    rectangles[n].x = column * 3;
1053 	    rectangles[n].y = row * 3 + 3;
1054 	    rectangles[n].width = 2;
1055 	    rectangles[n].height = 2;
1056 	    n++;
1057 	    mask ^= 0x4;
1058 	  }
1059 	/*}}}*/
1060 	/*{{{  down?*/
1061 	if(mask & 0x1)
1062 	  {
1063 	    rectangles[n].x = column * 3;
1064 	    rectangles[n].y = row * 3 + 3 + 2;
1065 	    rectangles[n].width = 2;
1066 	    rectangles[n].height = 1;
1067 	    n++;
1068 	    mask ^= 0x1;
1069 	  }
1070 	/*}}}*/
1071 	XFillRectangles(display.display, pixmap, GCN(GC_CLEAR),
1072 	    rectangles, n);
1073 	assert(!mask);
1074 	/*{{{  cherry?*/
1075 	if(c == GARDEN_CHERRY || ISPATHCHERRY(c))
1076 	  {
1077 	    XPoint points[2];
1078 
1079 	    points[0].x = column * 3;
1080 	    points[0].y = row * 3 + 3 + 1;
1081 	    points[1].x = 1;
1082 	    points[1].y = -1;
1083 	    XDrawPoints(display.display, pixmap,
1084 		GCN(ISPATH(c) ? GC_SET : GC_CLEAR), points, 2,
1085 		CoordModePrevious);
1086 	  }
1087 	/*}}}*/
1088       }
1089       /*}}}*/
1090   return;
1091 }
1092 /*}}}*/
1093 /*{{{  unsigned long force_blank(x, y, cptr*/
1094 static unsigned long force_blank
1095 FUNCARG((x, y, cptr),
1096 	unsigned  x
1097 ARGSEP  unsigned  y
1098 ARGSEP  char      *cptr
1099 )
1100 /* sets the cell to blank path or noapple hedge
1101  * adjusts the counts appropriately
1102  * returns mask of parts to redraw, a la change_path
1103  */
1104 {
1105   unsigned long redraw;
1106 
1107   redraw = 0;
1108   if(ISPATH(*cptr) && !ISPATHBLANK(*cptr))
1109     /*{{{  set to blank path*/
1110     {
1111       unsigned  count;
1112       unsigned  delta;
1113 
1114       if(ISPATHCHERRY(*cptr))
1115 	{
1116 	  count = COUNT_CHERRY;
1117 	  delta = GARDEN_PATH_CHERRY - GARDEN_PATH_BLANK;
1118 	  redraw = 0x100;
1119 	}
1120       else if(ISPATHDEN(*cptr))
1121 	{
1122 	  count = COUNT_DEN;
1123 	  delta = GARDEN_PATH_DEN - GARDEN_PATH_BLANK;
1124 	  redraw = 0x10F;
1125 	  if(y && ISPATHDEN(cptr[-1 - CELLS_ACROSS]))
1126 	    redraw |= 0x10;
1127 	  else if(y != CELLS_DOWN - 1 && ISPATHDEN(cptr[1 + CELLS_ACROSS]))
1128 	    redraw |= 0x20;
1129 	  if(x && ISPATHDEN(cptr[-1]))
1130 	    redraw |= 0x40;
1131 	  else if(x != CELLS_ACROSS - 1 && ISPATHDEN(cptr[1]))
1132 	    redraw |= 0x80;
1133 	}
1134       else if(ISPATHPLAYER(*cptr))
1135 	{
1136 	  count = COUNT_PLAYER;
1137 	  delta = GARDEN_PATH_PLAYER - GARDEN_PATH_BLANK;
1138 	  redraw = 0x100;
1139 	}
1140       else
1141 	{
1142 	  assert(0);
1143 	  delta = count = 0; /* keep compiler happy */
1144 	}
1145       adjust_count(count, -1);
1146       *cptr -= delta;
1147       changed_flag |= state.change;
1148     }
1149     /*}}}*/
1150   else if(*cptr == GARDEN_CHERRY)
1151     /*{{{  set to noapple hedge*/
1152     {
1153       *cptr = GARDEN_NOAPPLE;
1154       adjust_count(COUNT_CHERRY, -1);
1155       changed_flag |= state.change;
1156       redraw = 0x100;
1157     }
1158     /*}}}*/
1159   else if(ISAPPLE(*cptr))
1160     /*{{{  set to random hedge*/
1161     {
1162       unsigned  ix;
1163       unsigned  mask;
1164 
1165       mask = GARDENAPPLE(*cptr);
1166       for(ix = 4; ix--;)
1167 	if(mask & (1 << ix))
1168 	  adjust_count(COUNT_APPLES + ix, -1);
1169       *cptr = GARDEN_RANDOM;
1170       changed_flag |= state.change;
1171       redraw = 0x100;
1172     }
1173     /*}}}*/
1174   if(*cptr == GARDEN_RANDOM)
1175     /*{{{  set to noapple hedge*/
1176     {
1177       *cptr = GARDEN_NOAPPLE;
1178       adjust_count(COUNT_SPACES, -1);
1179       if(y != CELLS_DOWN && ISPATH(cptr[CELLS_ACROSS + 1]))
1180 	adjust_count(COUNT_FALL, -1);
1181       changed_flag |= state.change;
1182       redraw = 0x100;
1183     }
1184     /*}}}*/
1185   return redraw;
1186 }
1187 /*}}}*/
1188 /*{{{  unsigned get_cell(gw, event, cell_ptr)*/
1189 static unsigned get_cell
1190 FUNCARG((gw, event, cptr),
1191 	GardenWidget gw
1192 ARGSEP  XEvent    *event
1193 ARGSEP  COORD     *cptr
1194 )
1195 /* get the cell and offset from window coordinate
1196  * returns mask of edges we're on and 0x10 for center
1197  */
1198 {
1199   COORD     coord;
1200   unsigned  place;
1201 
1202   /*{{{  get coordinate*/
1203   switch(event->type)
1204   {
1205     case ButtonPress:
1206     case ButtonRelease:
1207       coord.x = event->xbutton.x;
1208       coord.y = event->xbutton.y;
1209       break;
1210     case MotionNotify:
1211       coord.x = event->xmotion.x;
1212       coord.y = event->xmotion.y;
1213       break;
1214     default:
1215       assert(0);
1216   }
1217   /*}}}*/
1218   coord.x -= gw->garden.x + BORDER_LEFT + GAP_WIDTH / 2;
1219   coord.y -= gw->garden.y + BORDER_TOP + GAP_HEIGHT / 2;
1220   place = 0;
1221   /*{{{  clip x*/
1222   if(coord.x < 0)
1223     {
1224       coord.x = 0;
1225       place = 0x10;
1226     }
1227   else if(coord.x >= (CELL_WIDTH + GAP_WIDTH) * CELLS_ACROSS)
1228     {
1229       coord.x = (CELL_WIDTH + GAP_WIDTH) * CELLS_ACROSS - 1;
1230       place = 0x10;
1231     }
1232   /*}}}*/
1233   /*{{{  clip y*/
1234   if(coord.y < 0)
1235     {
1236       coord.y = 0;
1237       place = 0x10;
1238     }
1239   else if(coord.y >= (CELL_HEIGHT + GAP_HEIGHT) * CELLS_DOWN)
1240     {
1241       coord.y = (CELL_HEIGHT + GAP_HEIGHT) * CELLS_DOWN - 1;
1242       place = 0x10;
1243     }
1244   /*}}}*/
1245   cptr->x = coord.x / (CELL_WIDTH + GAP_WIDTH);
1246   cptr->y = coord.y / (CELL_HEIGHT + GAP_HEIGHT);
1247   coord.x %= (CELL_WIDTH + GAP_WIDTH);
1248   coord.y %= (CELL_HEIGHT + GAP_HEIGHT);
1249   if(coord.x < GAP_WIDTH / 2 + WIDTH_EXPAND)
1250     place |= 0x04;
1251   else if(coord.x >= GAP_WIDTH / 2 + CELL_WIDTH - WIDTH_EXPAND)
1252     place |= 0x08;
1253   if(coord.y < GAP_HEIGHT / 2 + HEIGHT_EXPAND)
1254     place |= 0x01;
1255   else if(coord.y >= GAP_HEIGHT / 2 + CELL_HEIGHT - HEIGHT_EXPAND)
1256     place |= 0x02;
1257   if(place & 0x10)
1258     place = 0;
1259   else if(!place)
1260     place = 0x10;
1261   return place;
1262 }
1263 /*}}}*/
1264 /*{{{  unsigned get_paths(x, y, cptrptr)*/
1265 static unsigned get_paths
1266 FUNCARG((x, y, cptrptr),
1267 	unsigned  x           /* the cell coordinate */
1268 ARGSEP  unsigned  y
1269 ARGSEP  char      **cptrptr   /* return for cell mapp pointer */
1270 )
1271 /* return the current cell walls and pointer to cell map character
1272  */
1273 {
1274   unsigned  current;
1275   char      *cptr;
1276 
1277   cptr = &state.edit->board->map[y][x];
1278   if(cptrptr)
1279     *cptrptr = cptr;
1280   current = 0;
1281   /*{{{  set current paths*/
1282   if(ISPATH(*cptr))
1283     {
1284       current |= 0x10;
1285       current |= (GARDENPATH(*cptr) & 1) << 1;
1286       current |= (GARDENPATH(*cptr) & 2) << 2;
1287       if(y && ISPATH(cptr[-1 - CELLS_ACROSS]))
1288 	current |= GARDENPATH(cptr[-1 - CELLS_ACROSS]) & 1;
1289       if(x && ISPATH(cptr[-1]))
1290 	current |= (GARDENPATH(cptr[-1]) & 2) << 1;
1291     }
1292   /*}}}*/
1293   return current;
1294 }
1295 /*}}}*/
1296 /*{{{  void install_garden(root)*/
1297 extern VOIDFUNC install_garden
1298 FUNCARG((root),
1299 	Widget    root
1300 )
1301 {
1302   garden = root;
1303   return;
1304 }
1305 /*}}}*/
1306 /*{{{  void paint_cell(x, y, mask, copy)*/
1307 static VOIDFUNC paint_cell
1308 FUNCARG((x, y, mask, copy),
1309 	unsigned  x       /* cell coordinates */
1310 ARGSEP  unsigned  y
1311 ARGSEP  unsigned  mask    /* parts to paint
1312 			   * 0-3 edges, 4 center
1313 			   */
1314 ARGSEP  unsigned  copy    /* copy to window */
1315 )
1316 /* paints the specified cell from scratch,
1317  * updates display.copy
1318  * this will have to be copied to display.window
1319  */
1320 {
1321   char CONST *cptr;
1322   COORD     pixel;
1323   unsigned  walls;
1324 
1325   assert(x < CELLS_ACROSS && y < CELLS_DOWN);
1326   if(!mask)
1327     return;
1328   pixel.x = PIXELX(x, 0);
1329   pixel.y = PIXELY(y, 0);
1330   cptr = &state.edit->board->map[y][x];
1331   /*{{{  set board GC*/
1332   {
1333     XGCValues gcv;
1334 
1335     gcv.fill_style = FillOpaqueStippled;
1336     gcv.background = data.mono != False ? display.white :
1337 	colors[backgrounds[state.edit->board->colors][0]].pixel;
1338     gcv.foreground = data.mono != False ? display.black :
1339 	colors[backgrounds[state.edit->board->colors][1]].pixel;
1340     gcv.stipple = fills[state.edit->board->fill].mask;
1341     XChangeGC(display.display, GCN(GC_BOARD),
1342 	GCFillStyle | GCForeground | GCBackground | GCStipple, &gcv);
1343   }
1344   /*}}}*/
1345   /*{{{  set walls*/
1346   {
1347     unsigned  temp;
1348     unsigned  paths;
1349 
1350     paths = 0;
1351     if(ISPATH(cptr[0]))
1352       {
1353 	temp = GARDENPATH(cptr[0]) & 3;
1354 	paths |= (temp & 2) << 2 | (temp & 1) << 1;
1355       }
1356     if(x && ISPATH(cptr[-1]))
1357       {
1358 	temp = GARDENPATH(cptr[-1]) & 3;
1359 	paths |= (temp & 2) << 1 | (temp & 1) << 10;
1360       }
1361     if(y && ISPATH(cptr[-1 - CELLS_ACROSS]))
1362       {
1363 	temp = GARDENPATH(cptr[-1 - CELLS_ACROSS]) & 3;
1364 	paths |= (temp & 1) | (temp & 2) << 7;
1365       }
1366     if(y && x && ISPATH(cptr[-2 - CELLS_ACROSS]))
1367       {
1368 	temp = GARDENPATH(cptr[-2 - CELLS_ACROSS]) & 3;
1369 	paths |= (temp & 1) << 6 | (temp & 2) << 3;
1370       }
1371     if(x != CELLS_ACROSS - 1 && ISPATH(cptr[1]))
1372       {
1373 	temp = GARDENPATH(cptr[1]) & 3;
1374 	paths |= (temp & 1) << 11;
1375       }
1376     if(y != CELLS_DOWN - 1 && ISPATH(cptr[CELLS_ACROSS + 1]))
1377       {
1378 	temp = GARDENPATH(cptr[CELLS_ACROSS + 1]) & 3;
1379 	paths |= (temp & 2) << 8;
1380       }
1381     if(x && y != CELLS_DOWN - 1 && ISPATH(cptr[CELLS_ACROSS]))
1382       {
1383 	temp = GARDENPATH(cptr[CELLS_ACROSS]) & 3;
1384 	paths |= (temp & 2) << 4;
1385       }
1386     if(y && x != CELLS_ACROSS - 1 && ISPATH(cptr[-CELLS_ACROSS]))
1387       {
1388 	temp = GARDENPATH(cptr[-CELLS_ACROSS]) & 3;
1389 	paths |= (temp & 1) << 7;
1390       }
1391     if(!y && INRANGE(x, 4, 8) && ISPATH(*cptr))
1392       {
1393 	paths |= 0x1;
1394 	if(x < 7 && paths & 0x8)
1395 	  {
1396 	    paths |= 0x100;
1397 	    if(ISPATH(cptr[1]))
1398 	      paths |= 0x080;
1399 	  }
1400 	if(x > 4 && paths & 0x4)
1401 	  {
1402 	    paths |= 0x010;
1403 	    if(ISPATH(cptr[-1]))
1404 	      paths |= 0x040;
1405 	  }
1406       }
1407     walls = paths ^ 0xFFF;
1408   }
1409   /*}}}*/
1410   /*{{{  paint center?*/
1411   if(mask & 0x10)
1412     {
1413       XFillRectangle(display.display, display.copy, GCN(GC_BOARD),
1414 	pixel.x, pixel.y, CELL_WIDTH, CELL_HEIGHT);
1415       if(ISPATH(*cptr))
1416 	{
1417 	  /*{{{  typedef struct Entry*/
1418 	  typedef struct Entry
1419 	  {
1420 	    COORD     offset;
1421 	    unsigned  mask;
1422 	  } ENTRY;
1423 	  /*}}}*/
1424 	  /*{{{  static CONST ENTRY table[] =*/
1425 	  static CONST ENTRY table[] =
1426 	  {
1427 	    {{0, 0}, 0x5},
1428 	    {{CELL_WIDTH >> 1, 0}, 0x9},
1429 	    {{CELL_WIDTH >> 1, CELL_HEIGHT >> 1}, 0xA},
1430 	    {{0, CELL_HEIGHT >> 1}, 0x6},
1431 	  };
1432 	  /*}}}*/
1433 	  SPRITE    *sptr;
1434 	  unsigned  ix;
1435 	  ENTRY CONST *tptr;
1436 
1437 	  sptr = &sprites[SPRITE_CENTER_BASE];
1438 	  for(tptr = table, ix = 4; ix--; tptr++)
1439 	    cut_back(tptr->offset.x, tptr->offset.y,
1440 		CELL_WIDTH >> 1, CELL_HEIGHT >> 1,
1441 		pixel.x + tptr->offset.x, pixel.y + tptr->offset.y,
1442 		&sptr[(walls & tptr->mask) != tptr->mask]);
1443 	}
1444     }
1445   /*}}}*/
1446   /*{{{  set edges*/
1447   {
1448     /*{{{  typedef struct Entry*/
1449     typedef struct Entry
1450     {
1451       COORD start;
1452       COORD size;
1453     } ENTRY;
1454     /*}}}*/
1455     /*{{{  static CONST ENTRY table[] =*/
1456     static CONST ENTRY table[] =
1457     {
1458       {{-GAP_WIDTH, -GAP_HEIGHT}, {GAP_WIDTH * 2 + CELL_WIDTH, GAP_HEIGHT}},
1459       {{-GAP_WIDTH, CELL_HEIGHT}, {GAP_WIDTH * 2 + CELL_WIDTH, GAP_HEIGHT}},
1460       {{-GAP_WIDTH, -GAP_HEIGHT}, {GAP_WIDTH, GAP_HEIGHT * 2 + CELL_HEIGHT}},
1461       {{CELL_WIDTH, -GAP_HEIGHT}, {GAP_WIDTH, GAP_HEIGHT * 2 + CELL_HEIGHT}},
1462     };
1463     /*}}}*/
1464     unsigned  ix;
1465     ENTRY CONST *tptr;
1466 
1467     for(tptr = &table[3], ix = 4; ix--; tptr--)
1468       if(mask & (1 << ix))
1469 	XFillRectangle(display.display, display.copy, GCN(GC_BOARD),
1470 	    pixel.x + tptr->start.x, pixel.y + tptr->start.y,
1471 	    tptr->size.x, tptr->size.y);
1472   }
1473   /*}}}*/
1474   if(!(mask & 0x10))
1475     /* EMPTY */;
1476   else if(*cptr == GARDEN_CHERRY || ISPATH(*cptr))
1477     /*{{{  draw sprite*/
1478     {
1479       if(*cptr == GARDEN_CHERRY || ISPATHCHERRY(*cptr))
1480 	cut_sprite(SPRITE_CHERRY, pixel.x, pixel.y);
1481       else if(ISPATHDEN(*cptr))
1482 	{
1483 	  XFillRectangle(display.display, display.copy, GCN(GC_CLEAR),
1484 	      pixel.x - DEN_WIDTH, pixel.y - DEN_HEIGHT,
1485 	      CELL_WIDTH + DEN_HEIGHT * 2, CELL_HEIGHT + DEN_HEIGHT * 2);
1486 	  cut_sprite(SPRITE_DEN, pixel.x, pixel.y);
1487 	}
1488       else if(ISPATHPLAYER(*cptr))
1489 	cut_sprite(SPRITE_PLAYER, pixel.x, pixel.y);
1490     }
1491     /*}}}*/
1492   else if(*cptr == GARDEN_RANDOM || ISAPPLE(*cptr))
1493     /*{{{  draw apples*/
1494     {
1495       unsigned  value;
1496       unsigned  fall;
1497 
1498       value = state.mode == MODE_RANDOM ? 0 :
1499 	  *cptr == GARDEN_RANDOM ? 0 : GARDENAPPLE(*cptr);
1500       fall = y != CELLS_DOWN - 1 && ISPATH(cptr[CELLS_ACROSS + 1]);
1501       if(state.mode == MODE_SEPARATE || !value)
1502 	{
1503 	  unsigned  solid;
1504 
1505 	  solid = value & (1 << state.apple);
1506 	  cut_sprite(SPRITE_BIG_APPLE + !solid, pixel.x, pixel.y);
1507 	  if(fall)
1508 	    cut_sprite(SPRITE_BIG_ARROW + !solid, pixel.x, pixel.y);
1509 	}
1510       else
1511 	{
1512 	  unsigned  ix;
1513 
1514 	  for(ix = 4; ix--;)
1515 	    {
1516 	      COORD     offset;
1517 	      unsigned  solid;
1518 
1519 	      solid = !(value & 1 << ix);
1520 	      offset.x = pixel.x + CELL_WIDTH / 2 * (ix & 1);
1521 	      offset.y = pixel.y + CELL_HEIGHT / 4 * (ix & 2);
1522 	      cut_sprite(SPRITE_SMALL_APPLE + solid, offset.x, offset.y);
1523 	      if(fall)
1524 		cut_sprite(SPRITE_SMALL_ARROW + solid, offset.x, offset.y);
1525 	    }
1526 	}
1527     }
1528     /*}}}*/
1529   /*{{{  cut edges*/
1530   {
1531     mask &= 0xF;
1532     mask |= mask << 4;
1533     /*{{{  set extra bits to cut*/
1534     {
1535       /*{{{  typedef struct Entry*/
1536       typedef struct Entry
1537       {
1538 	unsigned  mask;
1539 	unsigned  value;
1540 	unsigned  current;
1541 	unsigned  extra;
1542       } ENTRY;
1543       /*}}}*/
1544       /*{{{  static CONST ENTRY table[] =*/
1545       static CONST ENTRY table[] =
1546       {
1547 	{0x055, 0x004, 0x01, 0x04},
1548 	{0x055, 0x010, 0x01, 0x04},
1549 	{0x055, 0x001, 0x04, 0x01},
1550 	{0x055, 0x040, 0x04, 0x01},
1551 	{0x189, 0x008, 0x10, 0x08},
1552 	{0x189, 0x100, 0x10, 0x08},
1553 	{0x189, 0x001, 0x08, 0x10},
1554 	{0x189, 0x080, 0x08, 0x10},
1555 	{0x426, 0x004, 0x02, 0x40},
1556 	{0x426, 0x020, 0x02, 0x40},
1557 	{0x426, 0x002, 0x40, 0x02},
1558 	{0x426, 0x400, 0x40, 0x02},
1559 	{0xA0A, 0x008, 0x20, 0x80},
1560 	{0xA0A, 0x200, 0x20, 0x80},
1561 	{0xA0A, 0x002, 0x80, 0x20},
1562 	{0xA0A, 0x800, 0x80, 0x20},
1563       };
1564       /*}}}*/
1565       unsigned  ix;
1566       ENTRY CONST *tptr;
1567 
1568       for(tptr = table, ix = XtNumber(table); ix--; tptr++)
1569 	if(mask & tptr->current && (walls & tptr->mask) == tptr->value)
1570 	  mask |= tptr->extra;
1571     }
1572     /*}}}*/
1573     /*{{{  cut all the edges*/
1574     {
1575       /*{{{  typedef struct Entry*/
1576       typedef struct Entry
1577       {
1578 	COORD     offset;   /* place on copy pixmap */
1579 	unsigned  mask;     /* bits we're interested in */
1580 	char      base[16]; /* base of edge mask */
1581       } ENTRY;
1582       /*}}}*/
1583       /*{{{  static CONST ENTRY table[8] =*/
1584       static CONST ENTRY table[8] =
1585       {
1586 	/*{{{  top left horizontal*/
1587 	{
1588 	  {-GAP_WIDTH, -GAP_HEIGHT},
1589 	  0x055,
1590 	  {
1591 	    4, 7, 0, 5,
1592 	    0, 6, 0, 8,
1593 	    3, 8, 1, 8,
1594 	    2, 8, 0, 8,
1595 	  }
1596 	},
1597 	/*}}}*/
1598 	/*{{{  bottom left horizontal*/
1599 	{
1600 	  {-GAP_WIDTH, CELL_HEIGHT},
1601 	  0x426,
1602 	  {
1603 	    4, 7, 0, 6,
1604 	    0, 5, 0, 8,
1605 	    3, 8, 2, 8,
1606 	    1, 8, 0, 8,
1607 	  }
1608 	},
1609 	/*}}}*/
1610 	/*{{{  top left vertical*/
1611 	{
1612 	  {-GAP_WIDTH, -GAP_HEIGHT},
1613 	  0x055,
1614 	  {
1615 	    4, 0, 7, 5,
1616 	    3, 1, 8, 8,
1617 	    0, 0, 6, 8,
1618 	    2, 0, 8, 8,
1619 	  }
1620 	},
1621 	/*}}}*/
1622 	/*{{{  top right vertical*/
1623 	{
1624 	  {CELL_WIDTH, -GAP_HEIGHT},
1625 	  0x189,
1626 	  {
1627 	    4, 0, 7, 6,
1628 	    0, 0, 5, 8,
1629 	    3, 2, 8, 8,
1630 	    1, 0, 8, 8,
1631 	  }
1632 	},
1633 	/*}}}*/
1634 	/*{{{  top right horizontal*/
1635 	{
1636 	  {CELL_WIDTH / 2, -GAP_HEIGHT},
1637 	  0x189,
1638 	  {
1639 	    4, 7, 0, 5,
1640 	    3, 8, 1, 8,
1641 	    0, 6, 0, 8,
1642 	    2, 8, 0, 8,
1643 	  }
1644 	},
1645 	/*}}}*/
1646 	/*{{{  bottom right horizontal*/
1647 	{
1648 	  {CELL_WIDTH / 2, CELL_HEIGHT},
1649 	  0xA0A,
1650 	  {
1651 	    4, 7, 0, 6,
1652 	    0, 5, 0, 8,
1653 	    3, 8, 2, 8,
1654 	    1, 8, 0, 8,
1655 	  }
1656 	},
1657 	/*}}}*/
1658 	/*{{{  bottom left vertical*/
1659 	{
1660 	  {-GAP_WIDTH, CELL_HEIGHT / 2},
1661 	  0x426,
1662 	  {
1663 	    4, 0, 7, 5,
1664 	    3, 1, 8, 8,
1665 	    0, 0, 6, 8,
1666 	    2, 0, 8, 8,
1667 	  }
1668 	},
1669 	/*}}}*/
1670 	/*{{{  bottom right vertical*/
1671 	{
1672 	  {CELL_WIDTH, CELL_HEIGHT / 2},
1673 	  0xA0A,
1674 	  {
1675 	    4, 0, 7, 6,
1676 	    3, 2, 8, 8,
1677 	    0, 0, 5, 8,
1678 	    1, 0, 8, 8,
1679 	  }
1680 	},
1681 	/*}}}*/
1682       };
1683       /*}}}*/
1684       unsigned  ix;
1685       ENTRY CONST *tptr;
1686 
1687       for(tptr = &table[XtNumber(table) - 1], ix = XtNumber(table);
1688 	  ix--; tptr--)
1689 	if(mask & (1 << ix))
1690 	  {
1691 	    unsigned  mask;
1692 	    unsigned  bits;
1693 	    unsigned  bit;
1694 
1695 	    mask = 0;
1696 	    /*{{{  compact the interesting bits*/
1697 	    for(bits = tptr->mask, bit = 0; bit != 4; bit++)
1698 	      {
1699 		unsigned  bitmask;
1700 
1701 		assert(bits);
1702 		bitmask = bits & -bits;
1703 		if(walls & bitmask)
1704 		  mask |= 1 << bit;
1705 		bits ^= bitmask;
1706 	      }
1707 	    /*}}}*/
1708 	    /*{{{  top edge specials*/
1709 	    if(!y && INRANGE(x, 3, 9) && mask == 0xC)
1710 	      {
1711 		if(ix == 0 || ix == 4)
1712 		  mask |= 0x2;
1713 		else if(ix == 2 || ix == 3)
1714 		  mask |= 0x1;
1715 	      }
1716 	    /*}}}*/
1717 	    if(tptr->base[mask] == 8)
1718 	      /* EMPTY */;
1719 	    else if(ix & 2)
1720 	      cut_back(tptr->base[mask] * GAP_WIDTH,
1721 		  ix & 4 ? GAP_HEIGHT + CELL_HEIGHT / 2 : 0,
1722 		  GAP_WIDTH, GAP_HEIGHT + CELL_HEIGHT / 2,
1723 		  pixel.x + tptr->offset.x, pixel.y + tptr->offset.y,
1724 		  &sprites[SPRITE_EDGE_BASE + 0]);
1725 	    else
1726 	      cut_back(ix & 4 ? GAP_WIDTH + CELL_WIDTH / 2 : 0,
1727 		  tptr->base[mask] * GAP_HEIGHT,
1728 		  GAP_WIDTH + CELL_WIDTH / 2, GAP_HEIGHT,
1729 		  pixel.x + tptr->offset.x, pixel.y + tptr->offset.y,
1730 		  &sprites[SPRITE_EDGE_BASE + 1]);
1731 	  }
1732     }
1733     /*}}}*/
1734   }
1735   /*}}}*/
1736   if(!y && mask & 0xD)
1737     XDrawLine(display.display, display.copy, GCN(GC_BORDER),
1738 	pixel.x - GAP_WIDTH, pixel.y - GAP_HEIGHT,
1739 	pixel.x + CELL_WIDTH + GAP_WIDTH, pixel.y - GAP_HEIGHT);
1740   if(copy)
1741     RedrawRect(garden, pixel.x - GAP_WIDTH, pixel.y - GAP_HEIGHT,
1742 	CELL_WIDTH + 2 * GAP_HEIGHT, CELL_HEIGHT + 2 * GAP_HEIGHT);
1743   return;
1744 }
1745 /*}}}*/
1746 /*{{{  void paint_garden_icon(dptr)*/
1747 extern VOIDFUNC paint_garden_icon
1748 FUNCARG((dptr),
1749 	DESCRIPTOR *dptr
1750 )
1751 {
1752   XFillRectangle(display.display, dptr->pixmap, GCN(GC_CLEAR), 0, 0,
1753       ICON_WIDTH, 3);
1754   XDrawLine(display.display, dptr->pixmap, GCN(GC_BORDER), 0, 2,
1755       ICON_WIDTH, 2);
1756   XDrawLine(display.display, dptr->pixmap, GCN(GC_BORDER), 4 * 3 - 1, 0,
1757       4 * 3 - 1, 2);
1758   XDrawLine(display.display, dptr->pixmap, GCN(GC_BORDER), 8 * 3 - 1, 0,
1759       8 * 3 - 1, 2);
1760   draw_board_icon(dptr, 0, 0, CELLS_ACROSS, CELLS_DOWN);
1761   return;
1762 }
1763 /*}}}*/
1764 /*{{{  void paint_garden_image()*/
1765 extern VOIDFUNC paint_garden_image FUNCARGVOID
1766 {
1767   unsigned  ix;
1768   unsigned  iy;
1769 
1770   XFillRectangle(display.display, display.copy, GCN(GC_CLEAR),
1771       0, 0, WINDOW_WIDTH, PIXELY(0, 0));
1772   XDrawLine(display.display, display.copy, GCN(GC_BORDER),
1773       BORDER_LEFT, BORDER_TOP, BORDER_LEFT + BOARD_WIDTH, BORDER_TOP);
1774   XDrawLine(display.display, display.copy, GCN(GC_BORDER),
1775       PIXELX(4, -1), PIXELY(-1, 0), PIXELX(4, -1), PIXELY(-1, CELL_HEIGHT));
1776   XDrawLine(display.display, display.copy, GCN(GC_BORDER),
1777       PIXELX(4, XTRA_SPACING * 4 + CELL_WIDTH), PIXELY(-1, 0),
1778       PIXELX(4, XTRA_SPACING * 4 + CELL_WIDTH), PIXELY(-1, CELL_HEIGHT));
1779   /*{{{  blat on the extra*/
1780   {
1781     unsigned  ix;
1782 
1783     for(ix = 5; ix--;)
1784       {
1785 	int       x;
1786 	SPRITE    *lptr;
1787 
1788 	lptr = &sprites[SPRITE_EXTRA];
1789 	x = PIXELX(4, ix * XTRA_SPACING);
1790 	XCopyArea(display.display, lptr->mask,
1791 	    display.copy, GCN(GC_MASK), (int)ix * (CELL_WIDTH / 2), 0,
1792 	    CELL_WIDTH / 2, CELL_HEIGHT / 2,
1793 	    x + XTRA_LETTER_X, PIXELY(-1, XTRA_LETTER_Y));
1794 	XCopyArea(display.display, lptr->image,
1795 	    display.copy, GCN(GC_OR), (int)ix * (CELL_WIDTH / 2), 0,
1796 	    CELL_WIDTH / 2, CELL_HEIGHT / 2,
1797 	    x + XTRA_LETTER_X, PIXELY(-1, XTRA_LETTER_Y));
1798       }
1799   }
1800   /*}}}*/
1801   XDrawImageString(display.display, display.copy, GCN(GC_TEXT),
1802       PIXELX(4, -GAP_WIDTH) - 3 * font.width,
1803       PIXELY(-1, CELL_HEIGHT / 2) + font.center, "0", (int)1);
1804   for(iy = CELLS_DOWN; iy--;)
1805     for(ix = CELLS_ACROSS; ix--;)
1806       paint_cell(ix, iy, 0x1A | (iy ? 0x0 : 0x1) | (ix ? 0x0 : 0x4), 0);
1807   paint_garden_source();
1808   RedrawRect(garden, 0, 0, WINDOW_WIDTH, WINDOW_HEIGHT);
1809   return;
1810 }
1811 /*}}}*/
1812 /*{{{  void paint_garden_source()*/
1813 extern VOIDFUNC paint_garden_source FUNCARGVOID
1814 {
1815   char CONST *text;
1816 
1817   switch(state.source)
1818   {
1819     /*{{{  case SOURCE_UNIQUE:*/
1820     case SOURCE_UNIQUE:
1821     {
1822       text = "Unique";
1823       break;
1824     }
1825     /*}}}*/
1826     /*{{{  case SOURCE_BUFFER:*/
1827     case SOURCE_BUFFER:
1828     {
1829       text = "Buffer";
1830       break;
1831     }
1832     /*}}}*/
1833     /*{{{  default:*/
1834     default:
1835     {
1836       static char string[] = "Garden 000";
1837 
1838       itoa(string + 7, (unsigned long)state.source, 0);
1839       text = string;
1840       break;
1841     }
1842     /*}}}*/
1843   }
1844   XFillRectangle(display.display, display.copy, GCN(GC_CLEAR),
1845       PIXELX(8, 0), PIXELY(-1, 0),
1846       (CELLS_ACROSS - 8) * (CELL_WIDTH + GAP_WIDTH), CELL_HEIGHT);
1847   XDrawImageString(display.display, display.copy, GCN(GC_TEXT),
1848       PIXELX(8, (int)font.width * 2),
1849       PIXELY(-1, CELL_HEIGHT / 2 + font.center), text, strlen(text));
1850   RedrawRect(garden, PIXELX(8, 0), PIXELY(-1, 0),
1851       (CELLS_ACROSS - 8) * (CELL_WIDTH + GAP_WIDTH), CELL_HEIGHT);
1852   menu_garden(text);
1853   return;
1854 }
1855 /*}}}*/
1856 /*{{{  unsigned long set_random(x, y, cptr)*/
1857 static unsigned long set_random
1858 FUNCARG((x, y, cptr),
1859 	unsigned  x
1860 ARGSEP  unsigned  y
1861 ARGSEP  char      *cptr
1862 )
1863 /* sets a random apple possiblilty
1864  * if currently noapple
1865  * returns mask of bits to redraw a la change_path
1866  */
1867 {
1868   unsigned long redraw;
1869 
1870   redraw = 0;
1871   if(*cptr == GARDEN_NOAPPLE)
1872     {
1873       *cptr = GARDEN_RANDOM;
1874       adjust_count(COUNT_SPACES, 1);
1875       if(y != CELLS_DOWN && ISPATH(cptr[CELLS_ACROSS + 1]))
1876 	adjust_count(COUNT_FALL, 1);
1877       changed_flag |= state.change;
1878       redraw = 0x100;
1879     }
1880   return redraw;
1881 }
1882 /*}}}*/
1883