Appendix F. Example Mixed-Model Programs With Xlib

This appendix contains two example mixed-model programs that use Xlib. Each example program is shown first in IRIS GL, then in OpenGL.

Example One: iobounce.c

iobounce.c is a simple interactive program that bounces a ball around a 2D surface. Users can use the mouse buttons to change the velocity of the ball. The IRIS GL version of the program is presented first, then the OpenGL version.

IRIS GL Version of iobounce.c

The IRIS GL version of iobounce.c is a “pure” IRIS GL program; it does not contain X calls.

/*                iobounce.c:
 *                "pool" ball that "bounces" around a 2-d "surface". 
 *                RIGHTMOUSE stops ball
 *                MIDDLEMOUSE increases y velocity
 *                LEFTMOUSE increases x velocity
 */

#include <stdio.h>
#include <gl/gl.h>
#include <gl/device.h>

long xmaxscrn, ymaxscrn;  /* maximum size of screen in x and y */

#define XMIN 100
#define YMIN 100
#define XMAX 900
#define YMAX 700

long xvelocity = 0, yvelocity = 0;

main()
{
    Device dev;
    short val;
    long sizex, sizey;

    initialize();

    while (TRUE) {
       while (qtest()) {
          dev = qread(&val);
          switch (dev) {
                case REDRAW:  /* redraw window re: move/resize/push/pop */
                    reshapeviewport();
                    ortho2(XMIN - 0.5, XMAX + 0.5, YMIN - 0.5,
                           YMAX + 0.5);
                    drawball();
                    break;
                case LEFTMOUSE:     /* increase xvelocity */
                    if (xvelocity >= 0)
                        xvelocity++;
                    else
                        xvelocity--;
                    break;
                case MIDDLEMOUSE:   /* increase yvelocity */
                    if (yvelocity >= 0)
                        yvelocity++;
                    else
                        yvelocity--;
                    break;
                case RIGHTMOUSE:     /* stop ball */
                    xvelocity = yvelocity = 0;
                    break;
                case ESCKEY:
                    gexit();
                    exit(0);
            }
        }
        drawball();
    }
}

initialize() {

    xmaxscrn = getgdesc(GD_XPMAX)-1;
    ymaxscrn = getgdesc(GD_YPMAX)-1;
    prefposition(xmaxscrn/4,xmaxscrn*3/4,ymaxscrn/4,ymaxscrn*3/4);
    winopen("iobounce");
    winconstraints();

    doublebuffer();
    gconfig();
    shademodel(FLAT);

    ortho2(XMIN - 0.5, XMAX + 0.5, YMIN - 0.5, YMAX + 0.5);

    qdevice(ESCKEY);
    qdevice(LEFTMOUSE);
    qdevice(MIDDLEMOUSE);
    qdevice(RIGHTMOUSE);
}

drawball() {
    static xpos = 500,ypos = 500;
    long radius = 10;

    color(BLACK);
    clear();
    xpos += xvelocity;
    ypos += yvelocity;
    if (xpos > XMAX - radius ||
        xpos < XMIN + radius) {
        xpos -= xvelocity;
        xvelocity = -xvelocity;
    }
    if (ypos > YMAX - radius ||
        ypos < YMIN + radius) {
        ypos -= yvelocity;
        yvelocity = -yvelocity;
    }
    color(YELLOW);
    circfi(xpos, ypos, radius);
    swapbuffers();
}

OpenGL Version of iobounce.c

Thie example contains the OpenGL version of iobounce.c. Windowing and event handling are now controlled with Xlib, rather than IRIS GL calls.

/*                   iobounce.c:
 *
 *   "pool ball" that "bounces" around a 2-d "surface". 
 *            RIGHTMOUSE stops ball
 *            MIDDLEMOUSE increases y velocity
 *            LEFTMOUSE increases x velocity
 *
 *           (ported from ~4Dgifts/example/grafix/iobounce.c)
 */

#include <GL/glx.h>
#include <GL/gl.h>
#include <GL/glu.h>
#include <stdio.h>
#include <stdlib.h>
#include <X11/keysym.h>
#include <X11/Xlib.h>
#include <X11/Xutil.h>

#define XMIN 100
#define YMIN 100
#define XMAX 900
#define YMAX 700

#define BLACK           0
#define YELLOW          3

#define LEFTMOUSE       3
#define MIDDLEMOUSE     2
#define RIGHTMOUSE      1

#define TRUE            1
#define FALSE           0

long xmaxscrn, ymaxscrn; /* maximum size of screen /*
                         /* in x and y */

Display *dpy;            /* The X server connection */
Atom del_atom;           /* WM_DELETE_WINDOW atom   */
Window glwin;            /* handle to the GL window */
XEvent event;

static void openwindow(char *);
static void drawball(void);
static void clean_exit(void);

long xvelocity = 0, yvelocity = 0;

main(int argc, char *argv[])
{
    int myExpose, myConfigure,
        myButtRelease, myKeyPress,
        myButtonNumber;   /* store which events occur */
    long xsize, ysize;



    myExpose = myConfigure = myButtRelease = myKeyPress =
               myButtonNumber = FALSE;

    openwindow(argv[0]);

    while (TRUE) {

        KeySym keysym;
        char buf[4];

    /* this "do while" loop does the `get events' half of the     */
    /* "get events,process events" action of the infinite while.  */
    /* This is to ensure the event queue is always drained before */
    /* the events that have come in are processed.                */
    while (XEventsQueued(dpy,QueuedAfterReading)) { /* end "do { } while"
                       * XEventsQueued(dpy,QueuedAfterReading)
                       * is like qtest()--it only tells you if 
                       * there're any events presently in the 
                       * queue.it does not disturb the event
                       * queue's contents in any way.       */

            XNextEvent(dpy, &event);
            switch (event.type) {

            /* "Expose" events are sort of like "REDRAW" in gl-speak in
             * terms of when a window or a previously invisible part
             * becomes visible */
                case Expose:   /* Exposures */
                    myExpose = TRUE;
                    break;

            /* "ConfigNotify" events are like "REDRAW" in terms of changes to
             * a window's size or position.*/
                case ConfigureNotify:  /* Resize GL manually */
                    xsize = event.xconfigure.width;
                    ysize = event.xconfigure.height;
                    myConfigure = TRUE;
                    break;

            /* Wait for "ButtonRelease" events so the queue doesn't fill up
             * the way it would if the user sits on ButtonPresss. */
                case ButtonRelease:
                    if (event.xbutton.button == Button1) {  
                        myButtonNumber = LEFTMOUSE;        
                        myButtRelease = TRUE;             
                    } else if (event.xbutton.button ==
                               Button2) {
                        myButtonNumber = MIDDLEMOUSE;
                        myButtRelease = TRUE;      
                    } else if (event.xbutton.button ==
                               Button3) {
                        myButtonNumber = RIGHTMOUSE;
                        myButtRelease = TRUE; 
                    }  /* twirl the green sphere */
                    break;

            /* "ClientMessage" is generated if the WM itself is dying
             * and sends an exit signal to any running prog. */
                case ClientMessage:
                    if (event.xclient.data.l[0] == del_atom)
                        clean_exit();
                    break;

            /* "KeyPress" events are those that would be generated before
             * whenever queueing up any KEYBD key via qdevice. */
                case KeyPress:
                   /* save out which unmodified key (i.e. the key was not
                    * modified w/something like "Shift", "Ctrl", or "Alt")
                    * got pressed for use below. */
                    XLookupString((XKeyEvent *)&event, buf, 4, &keysym, 0);
                    myKeyPress = TRUE;
                    break;

            }  /* end switch (event.type) */
        }

    /* On an "Expose" event, redraw the affected pop'd or
     * de-iconized window
     */
        if (myExpose) {
            drawball();       /* draw the GL stuff */
            myExpose = FALSE; /* reset flag--queue now empty */
        }

    /* On a "ConfigureNotify" event, the GL window has either
     * been moved or resized.  Respond accordingly and then
     * redraw its contents.
     */

        if (myConfigure) {
            glViewport(0, 0, xsize, ysize);
            glLoadIdentity();
            gluOrtho2D(XMIN-0.5, XMAX+0.5, YMIN-0.5, YMAX+0.5);
            drawball();           /* draw the GL stuff */
            myConfigure = FALSE;  /* reset flag--queue now
                                   * empty */
        }

    /* On a "ButtonRelease" event, myButtonNumber stores which
     * mouse button was pressed/released and then we update
     * x/yvelocity accordingly
     * /
        if (myButtRelease) {
            if (myButtonNumber == LEFTMOUSE) {  /* increase
                                                   xvelocity */
                if (xvelocity >= 0)
                    xvelocity += 3;
                else
                    xvelocity -= 3;
            } else if (myButtonNumber == MIDDLEMOUSE) {
                                                 /* increase yvelocity*/
                if (yvelocity >= 0)
                    yvelocity += 3;
                else
                    yvelocity -= 3;
            } else if (myButtonNumber == RIGHTMOUSE) {
                                               /* stop ball */
                xvelocity = yvelocity = 0;
            } else {
                fprintf(stderr,"ERROR: %s thinks 
                        mouse button # ");
                fprintf(stderr,"%d was
                        pressed(?)\n",argv[0],myButtonNumber);
            }
            drawball();
            myButtRelease = FALSE;
        }

        /* On a keypress of Esc key, exit program. */
        if (myKeyPress) {
            if (keysym == XK_Escape)
                clean_exit();
        }

        drawball();

    }
}

static int attributeList[] = { GLX_DOUBLEBUFFER, 
                               None };
GLUquadricObj *qobj; 

static Bool WaitForNotify(Display *d, XEvent *e, char *arg) {
    return (e->type == MapNotify) && (e->xmap.window ==
                                      (Window)arg);
}

static void openwindow(char *progname) {

    int scrnnum;       /* X screen number  */    
    int xorig, yorig;  /* window (upper-left) origin */
    XVisualInfo *vi;
    GLXContext cx;
    Colormap cmap;
    XSizeHints Winhints;/* used to fix window size */
    XSetWindowAttributes swa;
    XColor colorstruct;

    /* Connect to the X server and get screen info */
    if ((dpy = XOpenDisplay(NULL)) == NULL) {
        fprintf(stderr, "%s: cannot connect to X server %s\n",
                                 progname, XDisplayName(NULL));
        exit(1);
    }
    scrnnum = DefaultScreen(dpy);
    ymaxscrn = DisplayHeight(dpy, scrnnum);
    xmaxscrn = DisplayWidth(dpy, scrnnum);

    /* get an appropriate visual */
    vi = glXChooseVisual(dpy, DefaultScreen(dpy),
                         attributeList);
    if (vi == NULL) {
        printf("Couldn't get visual.\n");
        exit(0);
    }

    /* create a GLX context */
    cx = glXCreateContext(dpy, vi, None, GL_TRUE);

    if (cx == NULL) {
        printf("Couldn't get context.\n");
        exit(0);
    }

    /* create a colormap */
    cmap = XCreateColormap(dpy, RootWindow(dpy, vi->screen),
                           vi->visual, AllocAll);

    XSync(dpy, 0);
    /* create a window */
    swa.colormap = cmap;
    swa.border_pixel = 0;

    /* express interest in certain events */
    swa.event_mask = StructureNotifyMask | KeyPressMask |
                     ButtonPressMask |
                     ButtonReleaseMask | ExposureMask;
    glwin = XCreateWindow(dpy, RootWindow(dpy, vi->screen), 
                          10, 10, 300, 300,
                          0, vi->depth, InputOutput,
                          vi->visual,
                          CWBorderPixel|CWColormap|CWEventMask, &swa);

    XMapWindow(dpy, glwin);
    XIfEvent(dpy, &event, WaitForNotify, (char*)glwin);

    /* connect the context to the window */
    glXMakeCurrent(dpy, glwin, cx);

   /* express interest in WM killing this app */
    if ((del_atom = XInternAtom(dpy, "WM_DELETE_WINDOW",
                                True)) != None)
        XSetWMProtocols(dpy, glwin, &del_atom, 1);

    colorstruct.pixel = BLACK;    
    colorstruct.red   = 0;
    colorstruct.green = 0;
    colorstruct.blue  = 0;
    colorstruct.flags = DoRed | DoGreen | DoBlue;
    XStoreColor(dpy, cmap, &colorstruct);
    colorstruct.pixel = YELLOW;
    colorstruct.red   = 65535;
    colorstruct.green = 65535;
    colorstruct.blue  = 0;
    colorstruct.flags = DoRed | DoGreen | DoBlue;
    XStoreColor(dpy, cmap, &colorstruct);

    glLoadIdentity();
    gluOrtho2D(XMIN - 0.5,  XMAX + 0.5,  YMIN - 0.5,  YMAX + 0.5);

    /* clear the buffer */
    glClearIndex((GLfloat)BLACK);
    qobj = gluNewQuadric(); 
    gluQuadricDrawStyle(qobj,GLU_FILL);
    glFlush();
}

static void drawball(void) {
    static int xpos = 500, ypos = 500;
    GLdouble radius = 14.0;

    glClear(GL_COLOR_BUFFER_BIT);
    xpos += xvelocity;
    ypos += yvelocity;
    if (xpos > XMAX - radius || xpos < XMIN + radius) {
        xpos -= xvelocity;
        xvelocity = -xvelocity;
    }
    if (ypos > YMAX - radius || ypos < YMIN + radius) {
        ypos -= yvelocity;
        yvelocity = -yvelocity;
    }

    glIndexi(YELLOW);
    glPushMatrix(); 
    glTranslatef(xpos,  ypos, 0.); 
    gluDisk( qobj, 0., radius, 10, 1); 
    glPopMatrix(); 
    glXSwapBuffers(dpy, glwin);
}

/*  clean_exit  --  Clean up before exiting */
static void clean_exit(void)
{
    gluDeleteQuadric(qobj); 
    XCloseDisplay(dpy);
    exit(0);
}

Example Two: zrgb.c

The zrgb.c example program includes depth buffering. This program won't work on 8-bit IRIS workstations. The IRIS GL version is presented first.

IRIS GL Version of zrgb.c

This is the IRIS GL version of zrgb.c. Like iobounce.c, this is a pure IRIS GL program.

/*                  zrgb.c
 *
 *  This program demostrates zbuffering 3 intersecting RGB
 *  polygons while in doublebuffer mode where,movement of the
 *  mouse with the LEFTMOUSE button depressed will, rotate the 3
 *  polygons. This is done via compound rotations allowing
 *  continuous screen-oriented rotations. (See orient(),
 *  and draw_scene() below).  Notice the effective way there
 *  is no wasted CPU usage when the user moves the mouse out
 *  of the window without holding down LEFTMOUSE--there is no
 *  qtest being performed and so the program simply blocks on
 *  the qread statement. Press the "Esc"[ape] key to exit.
 *  Please note that this program will not work on any 8-bit
 *  IRIS machine.
 *                                          ratman - 1989
 */

#include <stdio.h>
#include <gl/gl.h>
#include <gl/device.h>

Matrix objmat = {
    {1.0, 0.0, 0.0, 0.0},
    {0.0, 1.0, 0.0, 0.0},
    {0.0, 0.0, 1.0, 0.0},
    {0.0, 0.0, 0.0, 1.0},
};

Matrix idmat = {
    {1.0, 0.0, 0.0, 0.0},
    {0.0, 1.0, 0.0, 0.0},
    {0.0, 0.0, 1.0, 0.0},
    {0.0, 0.0, 0.0, 1.0},
};

/* Modes the program can be in */
#define NOTHING 0
#define ORIENT 1

int mode = 0;
int omx, mx, omy, my;  /* old and new mouse position */
float scrnaspect;      /* aspect ratio value         */
long zfar;             /* holds specific machine's   */
                       /* maximum Z depth value      */

main(argc, argv)
int argc;
char *argv[];
{
    long dev;
    short val;
    int redrawneeded=TRUE;  /* Is true when the scene */
                            /* needs redrawing */

    initialize(argv[0]);

    while (TRUE) {

        if (redrawneeded) {
            draw_scene();
            redrawneeded=FALSE;
        }

        while (qtest() || (!redrawneeded)) {

            switch(dev=qread(&val)) {

            case ESCKEY:   /* exit when key is going up, */
                           /* not down                   */
                if (val)   /* this avoids the scenario   */
                           /* where a window             */
                    break; /* underneath this program's  */
                           /* window--say                */
                exit(0);   /* another copy of this       */
                           /* program--getting the       */
                           /* ESC UP event and exiting   */
                           /* also.                      */
            case REDRAW:
                reshapeviewport();
                redrawneeded=TRUE;
                break;

            case LEFTMOUSE:
                if (val) {
                    mode = ORIENT;
                    omx = mx;
                    omy = my;
                } else 
                    mode = NOTHING;
                break;

            case MOUSEX:
                omx = mx; 
                mx = val;
                if (mode == ORIENT) {
                    update_scene();
                    redrawneeded=TRUE;
                }
                break;

            case MOUSEY:
                omy = my;
                my = val;
                if (mode == ORIENT) {
                    update_scene();
                    redrawneeded=TRUE;
                }
                break;
            }
        }
    }
}


initialize(progname)
char *progname;
{

    long xscrnsize;   /* size of screen in x used
                       * to set globals  */    
    long testifZinst;

    /*
     * This program requires the following to run:
     *  -- z buffer
     *  -- ability to do double-buffered RGB mode
     */
    /* Test for Z buffer */
    testifZinst = getgdesc(GD_BITS_NORM_ZBUFFER);
    if (testifZinst == FALSE) {
         fprintf(stderr,"BUMmer!--%s won't work on ",
                         progname);
         fprintf(stderr,"this machine--zbuffer option not
                         installed.\n");
         exit(0);
    }
    /* Test for double-buffered RGB */
    if (getgdesc(GD_BITS_NORM_DBL_RED) == 0) {
         fprintf(stderr,"BUMmer!--%s won't work on ",
                         progname);
         fprintf(stderr,"this machine--not enough
                         bitplanes.\n");
         exit(0);
        
    }

    /* Code to keep same aspec ratio as the screen */
    keepaspect(getgdesc(GD_XMMAX), getgdesc(GD_YMMAX));
    scrnaspect =
           (float)getgdesc(GD_XMMAX)/(float)getgdesc(GD_YMMAX);

    winopen(progname);
    wintitle("Zbuffered RGB #1");

    doublebuffer();
    RGBmode();
    gconfig();
    zbuffer(TRUE);
    glcompat(GLC_ZRANGEMAP, 0);
    zfar = getgdesc(GD_ZMAX);

    qdevice(ESCKEY);
    qdevice(LEFTMOUSE);
    qdevice(MOUSEX);
    qdevice(MOUSEY);
}

update_scene() {

    switch (mode) {

        case ORIENT:
            orient();
            break;
    }
}

orient () {

    pushmatrix();

    loadmatrix(idmat);

    rotate(mx-omx, 'y');
    rotate(omy-my, 'x');

    multmatrix(objmat);
    getmatrix(objmat);
    popmatrix();
}

draw_scene() {

    czclear(0x00C86428, zfar);

    perspective(400, scrnaspect, 30.0, 60.0);
    translate(0.0, 0.0, -40.0);
    multmatrix(objmat);
    rotate(-580, 'y');   /* skews original view 
                          * to show all polygons */
    draw_polys();

    swapbuffers();
}
float polygon1[3][3] = { {-10.0, -10.0,   0.0,},
                         { 10.0, -10.0,   0.0,},
                         {-10.0,  10.0,   0.0,} };

float polygon2[3][3] = { {  0.0, -10.0, -10.0,},
                         {  0.0, -10.0,  10.0,},
                         {  0.0,   5.0, -10.0,} };

float polygon3[4][3] = { {-10.0,   6.0,   4.0,},
                         {-10.0,   3.0,   4.0,},
                         {  4.0,  -9.0, -10.0,},
                         {  4.0,  -6.0, -10.0,} };

draw_polys() {

    bgnpolygon();
    cpack(0x00000000);
    v3f(&polygon1[0][0]);
    cpack(0x007F7F7F);
    v3f(&polygon1[1][0]);
    cpack(0x00FFFFFF);
    v3f(&polygon1[2][0]);
    endpolygon();
    bgnpolygon();
    cpack(0x0000FFFF);
    v3f(&polygon2[0][0]);
    cpack(0x007FFF00);
    v3f(&polygon2[1][0]);
    cpack(0x00FF0000);
    v3f(&polygon2[2][0]);
    endpolygon();

    bgnpolygon();
    cpack(0x0000FFFF);
    v3f(&polygon3[0][0]);
    cpack(0x00FF00FF);
    v3f(&polygon3[1][0]);
    cpack(0x00FF0000);
    v3f(&polygon3[2][0]);
    cpack(0x00FF00FF);
    v3f(&polygon3[3][0]);
    endpolygon();
}

OpenGL Version of zrgb.c

This is the OpenGL version of zrgb.c.

/*
 *                     zrgb.c
 */
#include <GL/glx.h>
/*
#include <GL/gl.h>
#include <GL/glu.h>
*/
#include <stdio.h>
#include <stdlib.h>
#include <X11/keysym.h>
#include <X11/Xlib.h>
#include <X11/Xutil.h>

#define TRUE            1
#define FALSE           0

Display *dpy;         /* The X server connection */
Atom del_atom;        /* WM_DELETE_WINDOW atom   */
Window glwin;         /* handle to the GL window */
XEvent event;

/* function declarations */

static void openwindow(char *);
static void resize_buffer(void);
static void clean_exit(void);
void initGL(void);
void orient(void);
void drawScene(void);
void drawPolys(void);

static float objmat[16] = {
         1.0, 0.0, 0.0, 0.0,
         0.0, 1.0, 0.0, 0.0,
         0.0, 0.0, 1.0, 0.0,
         0.0, 0.0, 0.0, 1.0,
};

short ax, ay, az;     /* angles for the "twirling" green
                       * sphere to ride on */
long xsize, ysize;    /* current size-of-window keepers */
long zfar;            /* used in czclear for the machine's
                       * zbuffer max */
long buffermode;      /* flag tracks current window
                       * (single or double) */
double scrnaspect;    /* aspect ratio value */
int xpos, ypos, oxpos, oypos; /* old and new mouse position */


main(argc,argv)
int argc;
char **argv;
{
    int myExpose, myConfigure, myButtPress, myKeyPress; 
    int needToDraw = 0;  /* don't set this to true until
                          * we get our first Expose event */

    myExpose = myConfigure = myButtPress = myKeyPress = FALSE;

    openwindow(argv[0]);
   
  
   /* start out making the singlebuffer window be 
    * our current GL window */
    initGL();     /* do GL init stuff */

    /*
     * The event loop.
     */
    while (1) {      /* standard logic:  get event(s), 
                      * process event(s) */

        XEvent event;
        KeySym keysym;
        char buf[4];

    /* this "do while" loop does the `get events' 
     * half of the "get events, process events" action 
     * of the infinite while. this is to ensure
     * the event queue is always drained before the events
     * that have come in are processed.
     */

        do {

            XNextEvent(dpy, &event);
                switch (event.type) {

            /* "Expose" events are sort of like "REDRAW" in
             * gl-speak in terms of when a window becomes
             * visible, or a previously
             * invisible part becomes visible.
             */
                case Expose:   /* Exposures */
                    needToDraw = myExpose = TRUE;
                    break;

            /* "ConfigNotify" events are like "REDRAW" in
             * terms of changes to a window's size or position.
             */
                case ConfigureNotify: /* Resize GL manually */
                    xsize = event.xconfigure.width;
                    ysize = event.xconfigure.height;
                    needToDraw = myConfigure = TRUE;
                    break;

            /* Wait for "MotionNotify" events so the 
             * queue doesn't fill up
             */
                case MotionNotify:                        
                    myButtPress = TRUE;
                    xpos = event.xmotion.x;
                    ypos = event.xmotion.y;
                    break;

            /* "ClientMessage" is generated if the WM itself
             * is being gunned down and sends an exit signal
             * to any running prog.
             */
                case ClientMessage:
                    if (event.xclient.data.l[0] == del_atom)
                        clean_exit();
                    break;

            /* "KeyPress" events are those that would be
             *  generated before whenever queueing up any
                KEYBD key via qdevice.
             */

                case KeyPress:
                   /* save out which unmodified key (i.e. the
                    * key was not modified w/something like
                    * "Shift", "Ctrl", or "Alt") got pressed
                    * for use below.
                    */
                    XLookupString((XKeyEvent *)&event, buf, 4,
                                   &keysym, 0);
                    myKeyPress = TRUE;
                    break;

              }  /* end switch (event.type) */

        } while (XPending(dpy));   /* end "do { } while".
                                    * XPending() is like
                                    * qtest()--it only
                                    * tells you if there're
                                    * any events presently in
                                    * the queue. it does not
                                    * disturb queue's contents
                                    * in any way.
                                    */

    /* On an "Expose" event, redraw the affected pop'd or
     * de-iconized window
     */
        if (myExpose) {
            resize_buffer();   
            myExpose = FALSE; /* reset flag--queue now empty */
        }

    /* On a "ConfigureNotify" event, the GL window has either
     * been moved or resized. Respond accordingly and then
     * redraw its contents.
     */
        if (myConfigure) {
            oxpos = xpos;
            oypos = ypos;
            resize_buffer();   
            myConfigure = FALSE;  /* reset flag--queue now
                                   * empty */
        }

        if (needToDraw) {
            drawScene();
            needToDraw = FALSE;
        }

        /* On a keypress of Esc key, exit program.
         */
        if (myKeyPress) {
            if (keysym == XK_Escape)
                clean_exit();
        }

        if (myButtPress) {
            orient();
            drawScene();
            myButtPress = FALSE;
        }
    }      /* end while(1) */

}      /* end main */

static int attributeList[] = { GLX_RGBA, 
                               GLX_DOUBLEBUFFER, 
                               GLX_RED_SIZE, 1, 
                               GLX_GREEN_SIZE, 1, 
                               GLX_BLUE_SIZE, 1,
                               GLX_DEPTH_SIZE, 1,
                               None };
static int attributeList2[] = { GLX_RGBA,
                               GLX_RED_SIZE, 1,
                               GLX_GREEN_SIZE, 1,
                               GLX_BLUE_SIZE, 1,
                               GLX_DEPTH_SIZE, 1,
                               None };


static Bool WaitForNotify(Display *d, XEvent *e, char *arg) {
    return (e->type == MapNotify) && (e->xmap.window ==
                                      (Window)arg);
}

XSizeHints Winhints;          /* used to fix window size */

/* openwindow - establish connection to X server, get screen info, specify the
 * attributes we want the WM to try to provide, and create the GL window */
static void openwindow(char *progname) {

    XVisualInfo *vi;
    GLXContext cx;
    Colormap cmap;
    XSizeHints Winhints;       /* used to fix window size*/ 
    XSetWindowAttributes swa;
    int scrnnum;               /* X screen number */
    int xorig, yorig;          /* window (upper-left) origin */
    long scrnheight;

   /* define window initial size */
    xorig = 50;  yorig = 40;
    xsize = 300; ysize = 240;
    scrnaspect = xsize / (double) ysize;

   /* Connect to the X server and get screen info */
    if ((dpy = XOpenDisplay(NULL)) == NULL) {
        fprintf(stderr, "%s: cannot connect to X server %s\n",
                                 progname, XDisplayName(NULL));
        exit(1);
    }

    scrnnum = DefaultScreen(dpy);
    scrnheight = DisplayHeight(dpy, scrnnum);

        /* get an appropriate visual */
    vi = glXChooseVisual(dpy, DefaultScreen(dpy),
                         attributeList);
    if (vi == NULL) {
        fprintf(stderr, "Unable to obtain visual
                         Doublebuffered visual\n");
        vi = glXChooseVisual(dpy, DefaultScreen(dpy),
                             attributeList2);
    }
    if (vi == NULL) {
        printf("Unable to obtain Singlebuffered
               VISUAL(????)\n");
        exit(0);
    }

    /* create a GLX context */
    cx = glXCreateContext(dpy, vi, None, GL_TRUE);

    /* create a colormap */
    cmap = XCreateColormap(dpy, RootWindow(dpy, vi->screen),
                           vi->visual, AllocNone);

    /* create a window */
    swa.colormap = cmap;
    swa.border_pixel = 0;
    swa.event_mask = StructureNotifyMask  | ButtonPressMask |
                     ExposureMask |
                     Button1MotionMask | 
                     KeyPressMask;    /* express interest in
                                       * events */;
    glwin = XCreateWindow(dpy, RootWindow(dpy, vi->screen),
                          xorig, yorig, xsize, ysize, 
                          0, vi->depth, InputOutput,
                          vi->visual,
                          CWBorderPixel|CWColormap|CWEventMask, &swa);

    XMapWindow(dpy, glwin);
    XIfEvent(dpy, &event, WaitForNotify, (char*)glwin);

    /* connect the context to the window */
    glXMakeCurrent(dpy, glwin, cx);

    if (!(glwin)) {
        fprintf(stderr,"%s: couldn't create `parent' X
                        window\n",progname);
        exit(1);
    }

   /* define string that will show up in the window title bar
    * (and icon) */
    XStoreName(dpy, glwin, "z-buffered rgb program");

   /* specify the values for the Window Size Hints we want to
    * enforce: this window's aspect ratio needs to stay at
    * 1:1, constrain min and max window size, and specify the
    * initial size of the window.
    */
    Winhints.width  = xsize;   /* specify desired x/y size of
                                * window */
    Winhints.height = ysize;
    Winhints.min_width = xorig;  /* define min and max */
    Winhints.max_width = scrnheight-1;  /* width and height */
    Winhints.min_height = yorig;
    Winhints.max_height = scrnheight-1;
    Winhints.min_aspect.x = xsize;    /* keep aspect to a xsize:ysize ratio */
    Winhints.max_aspect.x = xsize;
    Winhints.min_aspect.y = ysize;
    Winhints.max_aspect.y = ysize;
    /* set the corresponding flags */                                              
    Winhints.flags = USSize|PMaxSize|PMinSize|PAspect;
    XSetNormalHints(dpy, glwin, &Winhints);

    /* express interest in WM killing this app */
    if ((del_atom = XInternAtom(dpy, "WM_DELETE_WINDOW",
                                True)) != None)
        XSetWMProtocols(dpy, glwin, &del_atom, 1);

    return ;
}

/*  window has been moved or resized so update viewport & CTM stuff. */
static void resize_buffer() {

    XSync(dpy, False);  /* STILL NEED THIS????? */
                        /* Need before GL reshape */
    scrnaspect = xsize / (double) ysize;
    glViewport(0, 0, (short) (xsize-1), (short) (ysize-1));
}

/*  clean up before exiting */
static void clean_exit(void)
{
    XCloseDisplay(dpy);
    exit(0);
}

/* setup all necessary GL initialzation parameters. */
void initGL()
{
    glEnable(GL_DEPTH_TEST);
    glClearColor(0.16, 0.39, 0.78, 0.0);
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    glLoadIdentity();
    gluPerspective(400.0, scrnaspect, 30.0, 1000.0);
}

void orient()
{
    float dx, dy;
    glPushMatrix();
    dx = xpos-oxpos;
    dy = oypos-ypos;
    glLoadIdentity();
    glRotatef((float) (0.03*(xpos-oxpos)), 1.0, 0.0, 0.0);
    glRotatef((float) (0.03*(oypos-ypos)), 0.0, 1.0, 0.0);
    glMultMatrixf(objmat);
    glGetFloatv(GL_MODELVIEW_MATRIX, objmat);

    glPopMatrix();
}

void drawScene() 
{
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

    glPushMatrix();
    glTranslatef(0.0, 0.0, -40.0);
    glMultMatrixf(objmat);
    glRotatef(-220.0, 0.0, 1.0, 0.0);  /* skews orig view to
                                        * show all polys */
    drawPolys();
    glPopMatrix();
    glFlush ();
    glXSwapBuffers(dpy, glwin);
}

float polygon1[3][3] = { {-10.0, -10.0,   0.0,},
                         { 10.0, -10.0,   0.0,},
                         {-10.0,  10.0,   0.0,} };

float polygon2[3][3] = { {  0.0, -10.0, -10.0,},
                         {  0.0, -10.0,  10.0,},
                         {  0.0,   5.0, -10.0,} };

float polygon3[4][3] = { {-10.0,   6.0,   4.0,},
                         {-10.0,   3.0,   4.0,},
                         {  4.0,  -9.0, -10.0,},
                         {  4.0,  -6.0, -10.0,} };

void drawPolys()
{
    glBegin(GL_POLYGON);
    glColor4f(0.0, 0.0, 0.0, 0.0);
    glVertex3fv(&polygon1[0][0]);
    glColor4f(0.5, 0.5, 0.5, 0.0);
    glVertex3fv(&polygon1[1][0]);
    glColor4f(1.0, 1.0, 1.0, 0.0);
    glVertex3fv(&polygon1[2][0]);
    glEnd();

    glBegin(GL_POLYGON);
    glColor4f(1.0, 1.0, 0.0, 0.0);
    glVertex3fv(&polygon2[0][0]);
    glColor4f(0.0, 1.0, 0.5, 0.0);
    glVertex3fv(&polygon2[1][0]);
    glColor4f(0.0, 0.0, 1.0, 0.0);
    glVertex3fv(&polygon2[2][0]);
    glEnd();

    glBegin(GL_POLYGON);
    glColor4f(1.0, 1.0, 0.0, 0.0);
    glVertex3fv(&polygon3[0][0]);
    glColor4f(1.0, 0.0, 1.0, 0.0);
    glVertex3fv(&polygon3[1][0]);
    glColor4f(0.0, 0.0, 1.0, 0.0);
    glVertex3fv(&polygon3[2][0]);
    glColor4f(1.0, 0.0, 1.0, 0.0);
    glVertex3fv(&polygon3[3][0]);
    glEnd();

}