Sierpinski.c

This is a demonstration Motif drawing program that I wrote in 1998, when I was still programming in C and using the Motif tool kit. I mention it here only as a curiousity, a relic of the past.

/*
*   Sierpinski.c
*   04-Dec-1998
*
*   Conrad Halling
*   conrad.halling@sphaerula.com
*
*   Based in part by drawing.c, which was...
*
*   "Written by Dan Heller and Paula Ferguson.
*   Copyright 1994, O'Reilly & Associates, Inc.
*
*   The X Consortium, and any party obtaining a copy of these files from
*   the X Consortium, directly or indirectly, is granted, free of charge, a
*   full and unrestricted irrevocable, world-wide, paid up, royalty-free,
*   nonexclusive right and license to deal in this software and
*   documentation files (the "Software"), including without limitation the
*   rights to use, copy, modify, merge, publish, distribute, sublicense,
*   and/or sell copies of the Software, and to permit persons who receive
*   copies from any such party to do so.  This license includes without
*   limitation a license to do the foregoing actions under any patents of
*   the party supplying this software to the X Consortium."
*/


#include <assert.h>
#include <stdlib.h>
#include <unistd.h>

#include <Xm/DrawingA.h>
#include <Xm/Form.h>
#include <Xm/PushB.h>
#include <Xm/Separator.h>

struct Coords
{
    int       x;            /*  X coordinate in the drawing plane               */
    int       y;            /*  Y coordinate in the drawing plane               */
    int       distance;     /*  distance from one point to the next, in pixels  */
    char      orientation;  /*  current orientation (left, up, right, down)     */
    Display   *display;     /*  X11 display                                     */
    Drawable  drawable;     /*  X11 drawable                                    */
    GC        gc;           /*  X11 graphics context                            */
};


/*
*   Function prototypes.
*/

static void     Advance(
                    struct Coords   *pCoords );
static void     DrawCallback(
                    Widget          widget,
                    XtPointer       client_data,
                    XtPointer       call_data );
static void     QuitCallback(
                    Widget          widget,
                    XtPointer       pClientData,
                    XtPointer       pCallData );
static void     TurnLeft(
                    struct Coords   *pCoords );
static void     TurnRight(
                    struct Coords   *pCoords );
static void     Zag(
                    int             interval,
                    struct Coords   *pCoords );
static void     Zig(
                    int             interval,
                    struct Coords   *pCoords );


int main(
    int             argc,
    char            *argv[] )
{
    XtAppContext    app;
    struct Coords   coords;
    Widget          drawButton;
    Widget          drawingArea;
    Widget          form;
    GC              gc;
    XGCValues       gcv;
    Widget          quitButton;
    Widget          separator;
    Widget          toplevel;

    String          fallbacks[] =
        {
          "*fontList: -adobe-helvetica-bold-r-normal-*-*-120-75-75-*-*-iso8859-1",
          "*background: gray70",
          "*foregound: black",
          NULL
        };

    XtSetLanguageProc( NULL, NULL, NULL );

    toplevel = XtVaAppInitialize(
        &app, "Demos", NULL, 0,
        &argc, argv, fallbacks,
        NULL );

    /*
    *   Create a form in which to place the other widgets.
    */

    form = XtVaCreateWidget(
        "Form", xmFormWidgetClass, toplevel,
        NULL );

    /*
    *   Add a pushbutton the user can use to Quit the program.
    */

    quitButton = XtVaCreateManagedWidget(
        "Quit", xmPushButtonWidgetClass, form,
        XmNrightAttachment,     XmATTACH_FORM,
        XmNrightOffset,         10,
        XmNbottomAttachment,    XmATTACH_FORM,
        XmNbottomOffset,        10,
        NULL );

    /*
    *   Add a pushbutton the user can use to clear the canvas.
    */

    drawButton = XtVaCreateManagedWidget(
        "Draw", xmPushButtonWidgetClass, form,
        XmNrightAttachment,     XmATTACH_WIDGET,
        XmNrightWidget,         quitButton,
        XmNrightOffset,         10,
        XmNbottomAttachment,    XmATTACH_FORM,
        XmNbottomOffset,        10,
        NULL );

    /*
    *   Place a separator above the buttons.
    */

    separator = XtVaCreateManagedWidget(
        "Separator", xmSeparatorWidgetClass, form,
        XmNleftAttachment,      XmATTACH_FORM,
        XmNleftOffset,          0,
        XmNrightAttachment,     XmATTACH_FORM,
        XmNrightOffset,         0,
        XmNbottomAttachment,    XmATTACH_WIDGET,
        XmNbottomWidget,        quitButton,
        XmNbottomOffset,        10,
        NULL );

    /*
    *   Create a DrawingArea widget above the separator.
    */

    drawingArea = XtVaCreateManagedWidget(
        "DrawingArea", xmDrawingAreaWidgetClass, form,
        XmNleftAttachment,      XmATTACH_FORM,
        XmNleftOffset,          1,
        XmNtopAttachment,       XmATTACH_FORM,
        XmNtopOffset,           1,
        XmNrightAttachment,     XmATTACH_FORM,
        XmNrightOffset,         1,
        XmNbottomAttachment,    XmATTACH_WIDGET,
        XmNbottomWidget,        separator,
        XmNbottomOffset,        1,
        XmNwidth,               400,
        XmNheight,              300,
        XtVaTypedArg, XmNbackground, XmRString, "gray90", 7,
        NULL );

    /*
    *   Since we're going to be drawing, we will be using Xlib routines
    *   and therefore need a graphics context.  Create a GC and attach
    *   to the DrawingArea's XmNuserData to avoid having to make global
    *   variable.
    */

    gcv.foreground = BlackPixelOfScreen( XtScreen( drawingArea ) );
    gc = XCreateGC(
        XtDisplay( drawingArea ),
        RootWindowOfScreen( XtScreen( drawingArea ) ),
        GCForeground,
        &gcv );
    XtVaSetValues(
        drawingArea,
        XmNuserData,            gc,
        NULL );

    /*
    *   Add callbacks for the Draw and Quit buttons.
    */

    XtAddCallback( drawButton, XmNactivateCallback, DrawCallback, &coords );
    XtAddCallback( quitButton, XmNactivateCallback, QuitCallback, NULL );

    /*
    *   Display the window.
    */

    XtManageChild( form );
    XtRealizeWidget( toplevel );

    /*
    *   Initialize some of the fields in coords. The rest are initialized
    *   in DrawCallback(). Calling XtWindow() does not work until we have
    *   called XtRealizeWidget(); otherwise, a window does not yet exist.
    */

    coords.display = XtDisplay( drawingArea );
    coords.drawable = XtWindow( drawingArea );
    coords.gc = gc;

    /*
    *   Handle events.
    */

    XtAppMainLoop( app );

    /*
    *   Not reached. The program exits from QuitCallback().
    */

    return ( EXIT_SUCCESS );
}


void DrawCallback(
    Widget                          widget,
    XtPointer                       client_data,
    XtPointer                       call_data )
{
    XmDrawingAreaCallbackStruct     *cbs;
    struct Coords                   *pCoords;

    cbs = ( XmDrawingAreaCallbackStruct * ) call_data;
    pCoords = ( struct Coords * ) client_data;

    if ( cbs->reason == XmCR_ACTIVATE )
    {
        /*
        *   Activated by pushbutton -- clear parent's window.
        */

        XClearWindow( pCoords->display, pCoords->drawable );

        /*
        *   Draw into the window.
        */

        pCoords->x = 300;
        pCoords->y = 230;
        pCoords->distance = 5;
        pCoords->orientation = 'R';

        Zig( 16, pCoords );
    }
}


void QuitCallback(
    Widget                  widget,
    XtPointer               pClientData,
    XtPointer               pCallData )
{
    exit( EXIT_SUCCESS );
}


void Zig(
    int             interval,
    struct Coords   *pCoords )
{
    assert( 0 != interval );
    assert( NULL != pCoords );

    if ( 1 == interval )
    {
        TurnLeft( pCoords );
        Advance( pCoords );
        TurnLeft( pCoords );
        Advance( pCoords );
    }
    else
    {
        Zig( interval / 2, pCoords );
        Zag( interval / 2, pCoords );
        Zig( interval / 2, pCoords );
        Zag( interval / 2, pCoords );
    }
}


void Zag(
    int             interval,
    struct Coords   *pCoords )
{
    assert( 0 != interval );
    assert( NULL != pCoords );

    if ( 1 == interval )
    {
        TurnRight( pCoords );
        Advance( pCoords );
        TurnRight( pCoords );
        Advance( pCoords );
        TurnLeft( pCoords );
        Advance( pCoords );
    }
    else
    {
        Zag( interval / 2, pCoords );
        Zag( interval / 2, pCoords );
        Zig( interval / 2, pCoords );
        Zag( interval / 2, pCoords );
    }
}


void Advance(
    struct Coords   *pCoords )
{
    /*
    *   Draw a line of distance units.
    */

    int             newX;
    int             newY;

    switch ( pCoords->orientation )
    {
        case 'L':
            newX = pCoords->x - pCoords->distance;
            XDrawLine(
                pCoords->display,
                pCoords->drawable,
                pCoords->gc,
                pCoords->x,
                pCoords->y,
                newX,
                pCoords->y );
            pCoords->x = newX;
            break;

        case 'U':
            newY = pCoords->y - pCoords->distance;
            XDrawLine(
                pCoords->display,
                pCoords->drawable,
                pCoords->gc,
                pCoords->x,
                pCoords->y,
                pCoords->x,
                newY );
            pCoords->y = newY;
            break;

        case 'R':
            newX = pCoords->x + pCoords->distance;
            XDrawLine(
                pCoords->display,
                pCoords->drawable,
                pCoords->gc,
                pCoords->x,
                pCoords->y,
                newX,
                pCoords->y );
            pCoords->x = newX;
            break;

        case 'D':
            newY = pCoords->y + pCoords->distance;
            XDrawLine(
                pCoords->display,
                pCoords->drawable,
                pCoords->gc,
                pCoords->x,
                pCoords->y,
                pCoords->x,
                newY );
            pCoords->y = newY;
            break;

        default:
            assert( 0 );
            break;
    }

    XFlush( pCoords->display );
    usleep( 10 );
}


void TurnLeft(
    struct Coords   *pCoords )
{
    /*
    *   Change orientation.
    */

    switch ( pCoords->orientation )
    {
        case 'L':
            pCoords->orientation = 'D';
            break;

        case 'U':
            pCoords->orientation = 'L';
            break;

        case 'R':
            pCoords->orientation = 'U';
            break;

        case 'D':
            pCoords->orientation = 'R';
            break;

        default:
            assert( 0 );
            break;
    }
}


void TurnRight(
    struct Coords   *pCoords )
{
    /*
    *   Change orientation.
    */

    switch ( pCoords->orientation )
    {
        case 'L':
            pCoords->orientation = 'U';
            break;

        case 'U':
            pCoords->orientation = 'R';
            break;

        case 'R':
            pCoords->orientation = 'D';
            break;

        case 'D':
            pCoords->orientation = 'L';
            break;

        default:
            assert( 0 );
            break;
    }
}

To compile this program, you need OpenMotif or Lesstif installed on your system. I provide separate notes for installing OpenMotif 2.2.3 for Mac OS X.

I compiled this program on my Mac OS X laptop using GCC 4.0.1 with the following command:

gcc \
  -Wall \
  -I/Applications/Darwin/openmotif-2.2.3/include \
  -L/usr/X11R6/lib \
  -L/Applications/Darwin/openmotif-2.2.3/lib \
  Sierpinski.c -lXm -lXt -lSM -lICE -lX11 -o Sierpinski

The program runs correctly, and its output is this:

A Sierpinski fractal

A Sierpinski fractal