ftp://ftp.linux.ncsu.edu/mirror/ftp.redhat.com/pub/redhat/linux/enterprise/4/en/os...
[xscreensaver] / hacks / penrose.c
index ff8791e6f64076b99a62acbad1a5430624a2e788..a15f0009e13ee34e62374063893995755dcf606c 100644 (file)
@@ -1,11 +1,29 @@
-/* -*- Mode: C; tab-width: 4 -*-
- * penrose --- quasiperiodic tilings.
+/* -*- Mode: C; tab-width: 4 -*- */
+/* penrose --- quasiperiodic tilings */
+
+/*  As reported in News of the Weird:
+
+         In April, Sir Roger Penrose, a British math professor who has worked
+         with Stephen Hawking on such topics as relativity, black holes, and
+         whether time has a beginning, filed a copyright-infringement lawsuit
+         against the Kimberly-Clark Corporation, which Penrose said copied a
+         pattern he created (a pattern demonstrating that "a nonrepeating
+         pattern could exist in nature") for its Kleenex quilted toilet paper.
+         Penrose said he doesn't like litigation but, "When it comes to the
+         population of Great Britain being invited by a multinational to wipe
+         their bottoms on what appears to be the work of a Knight of the
+         Realm, then a last stand must be taken."
+
+                                NOTW #491, 4-jul-1997, by Chuck Shepherd.
+                                http://www.nine.org/notw/notw.html
  */
-#if !defined( lint ) && !defined( SABER )
-static const char sccsid[] = "@(#)penrose.c    4.00 97/01/01 xlockmore";
+
+#if 0
+static const char sccsid[] = "@(#)penrose.c    5.00 2000/11/01 xlockmore";
 #endif
 
-/* Copyright (c) 1996 by Timo Korvola <tkorvola@dopey.hut.fi>
+/*-
+ * Copyright (c) 1996 by Timo Korvola <tkorvola@dopey.hut.fi>
  *
  * Permission to use, copy, modify, and distribute this software and its
  * documentation for any purpose and without fee is hereby granted,
@@ -20,8 +38,9 @@ static const char sccsid[] = "@(#)penrose.c   4.00 97/01/01 xlockmore";
  * other special, indirect and consequential damages.
  *
  * Revision History:
- * 10-May-97: jwz@netscape.com: turned into a standalone program.
- * 09-Sep-96: Written.
+ * 01-Nov-2000: Allocation checks
+ * 10-May-1997: Jamie Zawinski <jwz@jwz.org> compatible with xscreensaver
+ * 09-Sep-1996: Written.
  */
 
 /*-
@@ -45,7 +64,7 @@ If one of these are hit penrose will reinitialize.
  * untiled area.  Whenever this is in danger of happening, we just
  * do not add the tile, hoping for a better random choice the next
  * time.  Second, when choosing a vertex randomly, we will take
- * one that lies withing the viewport if available.  If this seems to
+ * one that lies within the viewport if available.  If this seems to
  * cause enclosures in the forced rule case, we will allow invisible
  * vertices to be chosen.
  *
@@ -55,61 +74,78 @@ If one of these are hit penrose will reinitialize.
  * horizontally or vertically or forced rule choice has failed 100
  * times due to areas about to become enclosed.
  *
+ * Introductory info:
+ * Science News March 23 1985 Vol 127, No. 12
+ * Science News July 16 1988 Vol 134, No. 3
+ * The Economist Sept 17 1988 pg. 100
+ *
  */
 
 #ifdef STANDALONE
-# define PROGCLASS                                     "Penrose"
-# define HACK_INIT                                     init_penrose
-# define HACK_DRAW                                     draw_penrose
-# define penrose_opts                          xlockmore_opts
-# define DEFAULTS      "*delay:                10000 \n"                       \
-                                       "*size:                 40    \n"                       \
-                                       "*ncolors:              64   \n"
-# include "xlockmore.h"                                /* from the xscreensaver distribution */
-#else  /* !STANDALONE */
-# include "xlock.h"                                    /* from the xlockmore distribution */
+#define MODE_penrose
+#define PROGCLASS "Penrose"
+#define HACK_INIT init_penrose
+#define HACK_DRAW draw_penrose
+#define penrose_opts xlockmore_opts
+#define DEFAULTS "*delay: 10000 \n" \
+ "*size: 40 \n" \
+ "*ncolors: 64 \n"
+#include "xlockmore.h"         /* from the xscreensaver distribution */
+#else /* !STANDALONE */
+#include "xlock.h"             /* from the xlockmore distribution */
 #endif /* !STANDALONE */
 
+#ifdef MODE_penrose
 
-/*-
- * Annoyingly the ANSI C library people have reserved all identifiers
- * ending with _t for future use.  Hence we use _c as a suffix for
- * typedefs (c for class, although this is not C++).
- */
-
-#define MINSIZE 5
-
-/*-
- * In theory one could fit 10 tiles to a single vertex.  However, the
- * vertex rules only allow at most seven tiles to meet at a vertex.
- */
-
-#define CELEBRATE 31415927     /* This causes a pause, an error occurred. */
-#define COMPLETION 3141593     /* This causes a pause, an error occurred. */
-
-#define MAX_TILES_PER_VERTEX 7
-#define N_VERTEX_RULES 8
-#define ALLOC_NODE( type) ((type *)malloc( sizeof( type)))
 #define DEF_AMMANN  "False"
 
 static Bool ammann;
 
 static XrmOptionDescRec opts[] =
 {
-       {"-ammann", ".penrose.ammann", XrmoptionNoArg, (caddr_t) "on"},
-       {"+ammann", ".penrose.ammann", XrmoptionNoArg, (caddr_t) "off"}
+       {"-ammann", ".penrose.ammann", XrmoptionNoArg, "on"},
+       {"+ammann", ".penrose.ammann", XrmoptionNoArg, "off"}
 };
 static argtype vars[] =
 {
-       {(caddr_t *) & ammann, "ammann", "Ammann", DEF_AMMANN, t_Bool}
+       {&ammann, "ammann", "Ammann", DEF_AMMANN, t_Bool}
 };
 static OptionStruct desc[] =
 {
        {"-/+ammann", "turn on/off Ammann lines"}
 };
 
-ModeSpecOpt penrose_opts = { 2, opts, 1, vars, desc };
+ModeSpecOpt penrose_opts =
+{sizeof opts / sizeof opts[0], opts, sizeof vars / sizeof vars[0], vars, desc};
+
+#ifdef USE_MODULES
+ModStruct   penrose_description =
+{"penrose", "init_penrose", "draw_penrose", "release_penrose",
+ "init_penrose", "init_penrose", (char *) NULL, &penrose_opts,
+ 10000, 1, 1, -40, 64, 1.0, "",
+ "Shows Penrose's quasiperiodic tilings", 0, NULL};
+
+#endif
+
+/*-
+ * Annoyingly the ANSI C library people have reserved all identifiers
+ * ending with _t for future use.  Hence we use _c as a suffix for
+ * typedefs (c for class, although this is not C++).
+ */
+
+#define MINSIZE 5
+
+/*-
+ * In theory one could fit 10 tiles to a single vertex.  However, the
+ * vertex rules only allow at most seven tiles to meet at a vertex.
+ */
+
+#define CELEBRATE 31415                /* This causes a pause, an error occurred. */
+#define COMPLETION 3141                /* This causes a pause, tiles filled up screen. */
 
+#define MAX_TILES_PER_VERTEX 7
+#define N_VERTEX_RULES 8
+#define ALLOC_NODE(type) (type *)malloc(sizeof (type))
 
 /*-
  * These are used to specify directions.  They can also be used in bit
@@ -158,7 +194,7 @@ typedef unsigned char vertex_type_c;
  * to fill.
  *
  * Here we use a doubly chained ring-like structure as vertices often need
- * to be removed or inserted (they are kept in geometrical order 
+ * to be removed or inserted (they are kept in geometrical order
  * circling the tiled area counterclockwise).  The ring is refered to by
  * a pointer to one more or less random node.  When deleting nodes one
  * must make sure that this pointer continues to refer to a valid
@@ -227,11 +263,12 @@ typedef struct {
        fringe_c    fringe;
        forced_pool_c forced;
        int         done, failures;
-       int         thick_color, thin_color;
+       unsigned long thick_color, thin_color;
+       int         busyLoop;
+       Bool        ammann;
 } tiling_c;
 
-static tiling_c *tilings;      /* = {0} */
-
+static tiling_c *tilings = (tiling_c *) NULL;
 
 /* The tiles are listed in counterclockwise order. */
 typedef struct {
@@ -301,14 +338,14 @@ vertex_dir(ModeInfo * mi, fringe_node_c * vertex, unsigned side)
                                return (2 * i + 5) % 10;
                }
        tp->done = True;
-       if (MI_WIN_IS_VERBOSE(mi)) {
+       if (MI_IS_VERBOSE(mi)) {
                (void) fprintf(stderr,
-                  "Weirdness in vertex_dir (this has been reported)\n");
+                      "Weirdness in vertex_dir (this has been reported)\n");
                for (i = 0; i < 5; i++)
                        (void) fprintf(stderr, "v2->fived[%d]=%d, vertex->fived[%d]=%d\n",
-                                     i, v2->fived[i], i, vertex->fived[i]);
+                                      i, v2->fived[i], i, vertex->fived[i]);
        }
-       MI_PAUSE(mi) = CELEBRATE;
+       tp->busyLoop = CELEBRATE;
        return 0;
 }
 
@@ -334,8 +371,8 @@ add_unit_vec(angle_c dir, int *fived)
  * This computes screen coordinates from 5D representation.  Note that X
  * uses left-handed coordinates (y increases downwards).
  */
-static      XPoint
-fived_to_loc(int fived[], tiling_c * tp)
+static void
+fived_to_loc(int fived[], tiling_c * tp, XPoint *pt)
 {
        static fcoord_c fived_table[5] =
        {
@@ -343,10 +380,11 @@ fived_to_loc(int fived[], tiling_c * tp)
        float       fifth = 8 * atan(1.) / 5;
        register int i;
        register float r;
-       register fcoord_c offset =
-       {.0, .0};
-       XPoint      pt = tp->origin;
+       register fcoord_c offset;
 
+       *pt = tp->origin;
+       offset.x = 0.0;
+       offset.y = 0.0;
        if (fived_table[0].x == .0)
                for (i = 0; i < 5; i++) {
                        fived_table[i].x = cos(fifth * i);
@@ -357,32 +395,31 @@ fived_to_loc(int fived[], tiling_c * tp)
                offset.x += r * fived_table[i].x;
                offset.y -= r * fived_table[i].y;
        }
-       pt.x += (int) (offset.x + .5);
-       pt.y += (int) (offset.y + .5);
-       return pt;
+       (*pt).x += (int) (offset.x + .5);
+       (*pt).y += (int) (offset.y + .5);
 }
 
 
 /* Mop up dynamic data for one screen. */
 static void
-release_screen(tiling_c * tp)
+free_penrose(tiling_c * tp)
 {
        register fringe_node_c *fp1, *fp2;
        register forced_node_c *lp1, *lp2;
 
-       if (tp->fringe.nodes == 0)
+       if (tp->fringe.nodes == NULL)
                return;
        fp1 = tp->fringe.nodes;
        do {
                fp2 = fp1;
                fp1 = fp1->next;
-               (void) free((char *) fp2);
+               (void) free((void *) fp2);
        } while (fp1 != tp->fringe.nodes);
-       tp->fringe.nodes = 0;
+       tp->fringe.nodes = (fringe_node_c *) NULL;
        for (lp1 = tp->forced.first; lp1 != 0;) {
                lp2 = lp1;
                lp1 = lp1->next;
-               (void) free((char *) lp2);
+               (void) free((void *) lp2);
        }
        tp->forced.first = 0;
 }
@@ -402,10 +439,20 @@ init_penrose(ModeInfo * mi)
                        return;
        }
        tp = &tilings[MI_SCREEN(mi)];
+
+#if 0 /* if you do this, then the -ammann and -no-ammann options don't work.
+         -- jwz */
+       if (MI_IS_FULLRANDOM(mi))
+               tp->ammann = (Bool) (LRAND() & 1);
+       else
+#endif /* 0 */
+               tp->ammann = ammann;
+
        tp->done = False;
+       tp->busyLoop = 0;
        tp->failures = 0;
-       tp->width = MI_WIN_WIDTH(mi);
-       tp->height = MI_WIN_HEIGHT(mi);
+       tp->width = MI_WIDTH(mi);
+       tp->height = MI_HEIGHT(mi);
        if (MI_NPIXELS(mi) > 2) {
                tp->thick_color = NRAND(MI_NPIXELS(mi));
                /* Insure good contrast */
@@ -427,36 +474,48 @@ init_penrose(ModeInfo * mi)
        tp->origin.x = (tp->width / 2 + NRAND(tp->width)) / 2;
        tp->origin.y = (tp->height / 2 + NRAND(tp->height)) / 2;
        tp->fringe.n_nodes = 2;
-       if (tp->fringe.nodes != 0)
-               release_screen(tp);
-       if (tp->fringe.nodes != 0 || tp->forced.first != 0) {
-               if (MI_WIN_IS_VERBOSE(mi)) {
+       if (tp->fringe.nodes != NULL)
+               free_penrose(tp);
+       if (tp->fringe.nodes != NULL || tp->forced.first != 0) {
+               if (MI_IS_VERBOSE(mi)) {
                        (void) fprintf(stderr, "Weirdness in init_penrose()\n");
-                       (void) fprintf(stderr, "tp->fringe.nodes = 0 && tp->forced.first = 0\n");
+                       (void) fprintf(stderr, "tp->fringe.nodes = NULL && tp->forced.first = 0\n");
                }
-               release_screen(tp);     /* Try again */
+               free_penrose(tp);       /* Try again */
                tp->done = True;
        }
        tp->forced.n_nodes = tp->forced.n_visible = 0;
-       fp = tp->fringe.nodes = ALLOC_NODE(fringe_node_c);
+       if ((fp = tp->fringe.nodes = ALLOC_NODE(fringe_node_c)) == NULL) {
+               free_penrose(tp);
+               return;
+       }
        if (fp == 0) {
-               if (MI_WIN_IS_VERBOSE(mi)) {
+               if (MI_IS_VERBOSE(mi)) {
                        (void) fprintf(stderr, "Weirdness in init_penrose()\n");
                        (void) fprintf(stderr, "fp = 0\n");
                }
-               fp = tp->fringe.nodes = ALLOC_NODE(fringe_node_c);
+               if ((fp = tp->fringe.nodes = ALLOC_NODE(fringe_node_c)) == NULL) {
+                       free_penrose(tp);
+                       return;
+               }
                tp->done = True;
        }
        /* First vertex. */
        fp->rule_mask = (1 << N_VERTEX_RULES) - 1;
        fp->list_ptr = 0;
-       fp->prev = fp->next = ALLOC_NODE(fringe_node_c);
+       if  ((fp->prev = fp->next = ALLOC_NODE(fringe_node_c)) == NULL) {
+               free_penrose(tp);
+               return;
+       }
        if (fp->next == 0) {
-               if (MI_WIN_IS_VERBOSE(mi)) {
+               if (MI_IS_VERBOSE(mi)) {
                        (void) fprintf(stderr, "Weirdness in init_penrose()\n");
                        (void) fprintf(stderr, "fp->next = 0\n");
                }
-               fp->prev = fp->next = ALLOC_NODE(fringe_node_c);
+               if ((fp->prev = fp->next = ALLOC_NODE(fringe_node_c)) == NULL) {
+                       free_penrose(tp);
+                       return;
+               }
                tp->done = True;
        }
        fp->n_tiles = 0;
@@ -471,7 +530,7 @@ init_penrose(ModeInfo * mi)
        fp = fp->next;
        i = NRAND(5);
        fp->fived[i] = 2 * NRAND(2) - 1;
-       fp->loc = fived_to_loc(fp->fived, tp);
+       fived_to_loc(fp->fived, tp, &(fp->loc));
        /* That's it!  We have created our first edge. */
 }
 
@@ -622,12 +681,12 @@ draw_tile(fringe_node_c * v1, fringe_node_c * v2,
                else
                        XSetForeground(display, gc, MI_PIXEL(mi, tp->thin_color));
        } else
-               XSetForeground(display, gc, MI_WIN_WHITE_PIXEL(mi));
+               XSetForeground(display, gc, MI_WHITE_PIXEL(mi));
        XFillPolygon(display, window, gc, pts, 4, Convex, CoordModeOrigin);
-       XSetForeground(display, gc, MI_WIN_BLACK_PIXEL(mi));
+       XSetForeground(display, gc, MI_BLACK_PIXEL(mi));
        XDrawLines(display, window, gc, pts, 5, CoordModeOrigin);
 
-       if (ammann) {
+       if (tp->ammann) {
                /* Draw some Ammann lines for debugging purposes.  This will probably
                   fail miserably on a b&w display. */
 
@@ -642,7 +701,7 @@ draw_tile(fringe_node_c * v1, fringe_node_c * v2,
                        if (MI_NPIXELS(mi) > 2)
                                XSetForeground(display, gc, MI_PIXEL(mi, tp->thin_color));
                        else {
-                               XSetForeground(display, gc, MI_WIN_WHITE_PIXEL(mi));
+                               XSetForeground(display, gc, MI_BLACK_PIXEL(mi));
                                XSetLineAttributes(display, gc, 1, LineOnOffDash, CapNotLast, JoinMiter);
                        }
                        XDrawLine(display, window, gc,
@@ -656,7 +715,7 @@ draw_tile(fringe_node_c * v1, fringe_node_c * v2,
                        if (MI_NPIXELS(mi) > 2)
                                XSetForeground(display, gc, MI_PIXEL(mi, tp->thick_color));
                        else {
-                               XSetForeground(display, gc, MI_WIN_WHITE_PIXEL(mi));
+                               XSetForeground(display, gc, MI_BLACK_PIXEL(mi));
                                XSetLineAttributes(display, gc, 1, LineOnOffDash, CapNotLast, JoinMiter);
                        }
                        XDrawLine(display, window, gc,
@@ -683,7 +742,7 @@ draw_tile(fringe_node_c * v1, fringe_node_c * v2,
  * might get called with an untileable vertex, causing ( n <= 1).
  * (This is what the tp->done checks for).
  *
- * A MI_PAUSE celebrates the dislocation.
+ * A delayLoop celebrates the dislocation.
  */
 static void
 check_vertex(ModeInfo * mi, fringe_node_c * vertex, tiling_c * tp)
@@ -694,10 +753,10 @@ check_vertex(ModeInfo * mi, fringe_node_c * vertex, tiling_c * tp)
 
        if (vertex->rule_mask == 0) {
                tp->done = True;
-               if (MI_WIN_IS_VERBOSE(mi)) {
-                       (void) fprintf(stderr, "Dislocation occured!\n");
+               if (MI_IS_VERBOSE(mi)) {
+                       (void) fprintf(stderr, "Dislocation occurred!\n");
                }
-               MI_PAUSE(mi) = CELEBRATE;       /* Should be able to recover */
+               tp->busyLoop = CELEBRATE;       /* Should be able to recover */
        }
        if (1 == find_completions(vertex, hits, n_hits, S_LEFT, 0 /*, False */ ))
                forced_sides |= S_LEFT;
@@ -710,7 +769,7 @@ check_vertex(ModeInfo * mi, fringe_node_c * vertex, tiling_c * tp)
                        *vertex->list_ptr = node->next;
                        if (node->next != 0)
                                node->next->vertex->list_ptr = vertex->list_ptr;
-                       free(node);
+                       (void) free((void *) node);
                        tp->forced.n_nodes--;
                        if (!vertex->off_screen)
                                tp->forced.n_visible--;
@@ -720,7 +779,8 @@ check_vertex(ModeInfo * mi, fringe_node_c * vertex, tiling_c * tp)
                forced_node_c *node;
 
                if (vertex->list_ptr == 0) {
-                       node = ALLOC_NODE(forced_node_c);
+                       if ((node = ALLOC_NODE(forced_node_c)) == NULL)
+                               return;
                        node->vertex = vertex;
                        node->next = tp->forced.first;
                        if (tp->forced.first != 0)
@@ -748,11 +808,11 @@ delete_vertex(ModeInfo * mi, fringe_node_c * vertex, tiling_c * tp)
 {
        if (tp->fringe.nodes == vertex) {
                tp->done = True;
-               if (MI_WIN_IS_VERBOSE(mi)) {
+               if (MI_IS_VERBOSE(mi)) {
                        (void) fprintf(stderr, "Weirdness in delete_penrose()\n");
                        (void) fprintf(stderr, "tp->fringe.nodes == vertex\n");
                }
-               MI_PAUSE(mi) = CELEBRATE;
+               tp->busyLoop = CELEBRATE;
        }
        if (vertex->list_ptr != 0) {
                forced_node_c *node = *vertex->list_ptr;
@@ -760,19 +820,21 @@ delete_vertex(ModeInfo * mi, fringe_node_c * vertex, tiling_c * tp)
                *vertex->list_ptr = node->next;
                if (node->next != 0)
                        node->next->vertex->list_ptr = vertex->list_ptr;
-               free(node);
+               (void) free((void *) node);
                tp->forced.n_nodes--;
                if (!vertex->off_screen)
                        tp->forced.n_visible--;
        }
        if (!vertex->off_screen)
                tp->fringe.n_nodes--;
-       free(vertex);
+       (void) free((void *) vertex);
 }
 
 
-/* Check whether the addition of a tile of type vtype would completely fill *
-   the space available at vertex. */
+/*-
+ * Check whether the addition of a tile of type vtype would completely fill
+ * the space available at vertex.
+ */
 static int
 fills_vertex(ModeInfo * mi, vertex_type_c vtype, fringe_node_c * vertex)
 {
@@ -810,7 +872,7 @@ fringe_changes(ModeInfo * mi, fringe_node_c * vertex,
               fringe_node_c ** right, fringe_node_c ** far,
               fringe_node_c ** left)
 {
-       fringe_node_c *v, *f = NULL;
+       fringe_node_c *v, *f = (fringe_node_c *) NULL;
        unsigned    result = FC_NEW_FAR;        /* We clear this later if necessary. */
 
        if (far)
@@ -884,19 +946,19 @@ add_vtype(fringe_node_c * vertex, unsigned side, vertex_type_c vtype)
 static fringe_node_c *
 alloc_vertex(ModeInfo * mi, angle_c dir, fringe_node_c * from, tiling_c * tp)
 {
-       fringe_node_c *v = ALLOC_NODE(fringe_node_c);
+       fringe_node_c *v;
 
-       if (v == 0) {
+       if ((v = ALLOC_NODE(fringe_node_c)) == NULL) {
                tp->done = True;
-               if (MI_WIN_IS_VERBOSE(mi)) {
-                       (void) fprintf(stderr, "Weirdness in alloc_vertex()\n");
-                       (void) fprintf(stderr, "v = 0\n");
+               if (MI_IS_VERBOSE(mi)) {
+                       (void) fprintf(stderr, "No memory in alloc_vertex()\n");
                }
-               MI_PAUSE(mi) = CELEBRATE;
+               tp->busyLoop = CELEBRATE;
+               return v;
        }
        *v = *from;
        add_unit_vec(dir, v->fived);
-       v->loc = fived_to_loc(v->fived, tp);
+       fived_to_loc(v->fived, tp, &(v->loc));
        if (v->loc.x < 0 || v->loc.y < 0
            || v->loc.x >= tp->width || v->loc.y >= tp->height) {
                v->off_screen = True;
@@ -913,7 +975,7 @@ alloc_vertex(ModeInfo * mi, angle_c dir, fringe_node_c * from, tiling_c * tp)
        return v;
 }
 
-/* 
+/*-
  * Add a tile described by vtype to the side of vertex.  This must be
  * allowed by the rules -- we do not check it here.  New vertices are
  * allocated as necessary.  The fringe and the forced vertex pool are updated.
@@ -930,9 +992,9 @@ add_tile(ModeInfo * mi,
        tiling_c   *tp = &tilings[MI_SCREEN(mi)];
 
        fringe_node_c
-               * left = 0,
-               *right = 0,
-               *far = 0,
+               *left = (fringe_node_c *) NULL,
+               *right = (fringe_node_c *) NULL,
+               *far = (fringe_node_c *) NULL,
                *node;
        unsigned    fc = fringe_changes(mi, vertex, side, vtype, &right, &far, &left);
 
@@ -947,25 +1009,29 @@ add_tile(ModeInfo * mi,
        /* This should never occur. */
        if (fc & FC_BAG) {
                tp->done = True;
-               if (MI_WIN_IS_VERBOSE(mi)) {
+               if (MI_IS_VERBOSE(mi)) {
                        (void) fprintf(stderr, "Weirdness in add_tile()\n");
                        (void) fprintf(stderr, "fc = %d, FC_BAG = %d\n", fc, FC_BAG);
                }
        }
        if (side == S_LEFT) {
-               if (right == 0)
-                       right = alloc_vertex(mi,
-                                            vertex_dir(mi, vertex, S_LEFT) - vtype_angle(vtype), vertex, tp);
-               if (far == 0)
-                       far = alloc_vertex(mi,
-                                          vertex_dir(mi, left, S_RIGHT) + vtype_angle(ltype), left, tp);
+               if (right == NULL)
+                       if ((right = alloc_vertex(mi, vertex_dir(mi, vertex, S_LEFT) -
+                                       vtype_angle(vtype), vertex, tp)) == NULL)
+                               return False;
+               if (far == NULL)
+                       if ((far = alloc_vertex(mi, vertex_dir(mi, left, S_RIGHT) +
+                                       vtype_angle(ltype), left, tp)) == NULL)
+                               return False;
        } else {
-               if (left == 0)
-                       left = alloc_vertex(mi,
-                                           vertex_dir(mi, vertex, S_RIGHT) + vtype_angle(vtype), vertex, tp);
-               if (far == 0)
-                       far = alloc_vertex(mi,
-                                          vertex_dir(mi, right, S_LEFT) - vtype_angle(rtype), right, tp);
+               if (left == NULL)
+                       if ((left = alloc_vertex(mi, vertex_dir(mi, vertex, S_RIGHT) +
+                                       vtype_angle(vtype), vertex, tp)) == NULL)
+                               return False;
+               if (far == NULL)
+                       if ((far = alloc_vertex(mi, vertex_dir(mi, right, S_LEFT) -
+                                       vtype_angle(rtype), right, tp)) == NULL)
+                               return False;
        }
 
        /* Having allocated the new vertices, but before joining them with
@@ -989,7 +1055,7 @@ add_tile(ModeInfo * mi,
        } while (node != tp->fringe.nodes);
 
        /* Rechain. */
-       if (!(fc & FC_CUT_THIS))
+       if (!(fc & FC_CUT_THIS)) {
                if (side == S_LEFT) {
                        vertex->next = right;
                        right->prev = vertex;
@@ -997,6 +1063,7 @@ add_tile(ModeInfo * mi,
                        vertex->prev = left;
                        left->next = vertex;
                }
+       }
        if (!(fc & FC_CUT_FAR)) {
                if (!(fc & FC_CUT_LEFT)) {
                        far->next = left;
@@ -1067,7 +1134,7 @@ add_forced_tile(ModeInfo * mi, forced_node_c * node)
        n = find_completions(node->vertex, hits, n, side, &vtype /*, True */ );
        if (n <= 0) {
                tp->done = True;
-               if (MI_WIN_IS_VERBOSE(mi)) {
+               if (MI_IS_VERBOSE(mi)) {
                        (void) fprintf(stderr, "Weirdness in add_forced_tile()\n");
                        (void) fprintf(stderr, "n = %d\n", n);
                }
@@ -1120,14 +1187,14 @@ add_random_tile(fringe_node_c * vertex, ModeInfo * mi)
                tp->thin_color = (NRAND(2 * MI_NPIXELS(mi) / 3) + tp->thick_color +
                                  MI_NPIXELS(mi) / 6) % MI_NPIXELS(mi);
        } else
-               tp->thick_color = tp->thin_color = MI_WIN_WHITE_PIXEL(mi);
+               tp->thick_color = tp->thin_color = MI_WHITE_PIXEL(mi);
        n_hits = match_rules(vertex, hits, False);
        side = NRAND(2) ? S_LEFT : S_RIGHT;
        n = find_completions(vertex, hits, n_hits, side, vtypes /*, False */ );
        /* One answer would mean a forced tile. */
        if (n <= 0) {
                tp->done = True;
-               if (MI_WIN_IS_VERBOSE(mi)) {
+               if (MI_IS_VERBOSE(mi)) {
                        (void) fprintf(stderr, "Weirdness in add_random_tile()\n");
                        (void) fprintf(stderr, "n = %d\n", n);
                }
@@ -1138,7 +1205,7 @@ add_random_tile(fringe_node_c * vertex, ModeInfo * mi)
                fc = fringe_changes(mi, vertex, side, vtypes[i], &right, &far, &left);
                if (fc & FC_BAG) {
                        tp->done = True;
-                       if (MI_WIN_IS_VERBOSE(mi)) {
+                       if (MI_IS_VERBOSE(mi)) {
                                (void) fprintf(stderr, "Weirdness in add_random_tile()\n");
                                (void) fprintf(stderr, "fc = %d, FC_BAG = %d\n", fc, FC_BAG);
                        }
@@ -1169,7 +1236,7 @@ add_random_tile(fringe_node_c * vertex, ModeInfo * mi)
        }
        if (n_good <= 0) {
                tp->done = True;
-               if (MI_WIN_IS_VERBOSE(mi)) {
+               if (MI_IS_VERBOSE(mi)) {
                        (void) fprintf(stderr, "Weirdness in add_random_tile()\n");
                        (void) fprintf(stderr, "n_good = %d\n", n_good);
                }
@@ -1179,13 +1246,12 @@ add_random_tile(fringe_node_c * vertex, ModeInfo * mi)
                while (no_good & (1 << j))
                        j++;
 
-       i = add_tile(mi, vertex, side, vtypes[j - 1]);
-       if (!i) {
+       if (!add_tile(mi, vertex, side, vtypes[j - 1])) {
                tp->done = True;
-               if (MI_WIN_IS_VERBOSE(mi)) {
+               if (MI_IS_VERBOSE(mi)) {
                        (void) fprintf(stderr, "Weirdness in add_random_tile()\n");
-                       (void) fprintf(stderr, "i = %d\n", i);
                }
+               free_penrose(tp);
        }
 }
 
@@ -1193,26 +1259,40 @@ add_random_tile(fringe_node_c * vertex, ModeInfo * mi)
 void
 draw_penrose(ModeInfo * mi)
 {
-       tiling_c   *tp = &tilings[MI_SCREEN(mi)];
        int         i = 0, n;
-       forced_node_c *p = tp->forced.first;
+       forced_node_c *p;
+       tiling_c   *tp;
 
+       if (tilings == NULL)
+               return;
+       tp = &tilings[MI_SCREEN(mi)];
+       if (tp->fringe.nodes == NULL)
+               return;
+
+       MI_IS_DRAWN(mi) = True;
+       p = tp->forced.first;
+       if (tp->busyLoop > 0) {
+               tp->busyLoop--;
+               return;
+       }
        if (tp->done || tp->failures >= 100) {
                init_penrose(mi);
                return;
        }
        /* Check for the initial "2-gon". */
        if (tp->fringe.nodes->prev == tp->fringe.nodes->next) {
-               vertex_type_c vtype = VT_TOTAL_MASK & LRAND();
+               vertex_type_c vtype = (unsigned char) (VT_TOTAL_MASK & LRAND());
+
+               MI_CLEARWINDOW(mi);
 
-               XClearWindow(MI_DISPLAY(mi), MI_WINDOW(mi));
-               (void) add_tile(mi, tp->fringe.nodes, S_LEFT, vtype);
+               if (!add_tile(mi, tp->fringe.nodes, S_LEFT, vtype))
+                       free_penrose(tp);
                return;
        }
        /* No visible nodes left. */
        if (tp->fringe.n_nodes == 0) {
                tp->done = True;
-               MI_PAUSE(mi) = COMPLETION;      /* Just finished drawing */
+               tp->busyLoop = COMPLETION;      /* Just finished drawing */
                return;
        }
        if (tp->forced.n_visible > 0 && tp->failures < 10) {
@@ -1230,15 +1310,15 @@ draw_penrose(ModeInfo * mi)
                while (i++ < n)
                        p = p->next;
        } else {
-               fringe_node_c *p = tp->fringe.nodes;
+               fringe_node_c *fringe_p = tp->fringe.nodes;
 
                n = NRAND(tp->fringe.n_nodes);
                i = 0;
                for (; i <= n; i++)
                        do {
-                               p = p->next;
-                       } while (p->off_screen);
-               add_random_tile(p, mi);
+                               fringe_p = fringe_p->next;
+                       } while (fringe_p->off_screen);
+               add_random_tile(fringe_p, mi);
                tp->failures = 0;
                return;
        }
@@ -1256,12 +1336,11 @@ release_penrose(ModeInfo * mi)
        if (tilings != NULL) {
                int         screen;
 
-               for (screen = 0; screen < MI_NUM_SCREENS(mi); screen++) {
-                       tiling_c   *tp = &tilings[screen];
-
-                       release_screen(tp);
-               }
+               for (screen = 0; screen < MI_NUM_SCREENS(mi); screen++)
+                       free_penrose(&tilings[screen]);
                (void) free((void *) tilings);
-               tilings = NULL;
+               tilings = (tiling_c *) NULL;
        }
 }
+
+#endif /* MODE_penrose */