Chapter 15. GLX: OpenGL on Linux

by Nick Haemel

WHAT YOU'LL LEARN IN THIS CHAPTER:

How To

Functions You'll Use

Choose appropriate visuals for

glXChooseVisual, glXChooseFBConfig

Manage OpenGL drawing contexts

glXCreateContext, glXDestroyContext, glXMakeCurrent

Create OpenGL windows

glXCreateWindow

Do double-buffered drawing

glXSwapBuffers

Create bitmap text fonts

glXUseXFont

Do offscreen drawing

glXCreateGLXPixmap, glXCreatePbuffer

This chapter discusses GLX, the OpenGL extension that is used to support OpenGL applications through the X Window System on UNIX and Linux. You learn how to create and manage OpenGL contexts as well as how to create OpenGL drawing areas with several of the common GUI toolkits. You also learn how to use GLUT with all the examples in this book.

The Basics

OpenGL on UNIX and Linux uses the OpenGL extension to the X Window System called GLX. All GLX-specific functions start with the prefix glX and are generally included in the GL library. The GLX functions are the “glue” binding OpenGL, X11, and the graphics hardware that provides accelerated rendering.

Using the OpenGL and X11 Libraries

The location of the OpenGL and X11 libraries and header files varies from system to system. Some are in the standard include and library locations, such as /usr/include and /usr/lib, that require only the libraries with the link command:

gcc -o myprogram myprogram.o -lGLU -lGL -lXext -lX11 -lm

Others use a version-specific location, such as /usr/X11R6/include or /usr/X11R6/lib, that requires both compiler and linker options:

gcc -I/usr/X11R6/include -c myprogram.c
gcc -o myprogram myprogram.o -L/usr/X11R6/lib -lGLU -lGL -lXext -lX11 -lm

You can hard-code the compiler and linker options if you are writing an application for a single platform; however, most OpenGL applications see life on many different platforms, so you might want to detect the appropriate options prior to doing a build. One common method is to use the GNU autoconf software to create a configure script that initializes your options for you. Listing 15.1 provides a sample configure.in file for use with autoconf.

Example 15.1. Sample configure.in File for GNU autoconf

dnl Required file in package...
AC_INIT(myprogram.c)

dnl Find the C compiler...
AC_PROG_CC

dnl Find the X Window System...
AC_PATH_XTRA

LIBS="$LIBS -lXext -lX11 $X_EXTRA_LIBS"
CFLAGS="$CFLAGS $X_CFLAGS"
LDFLAGS="$X_LIBS $LDFLAGS"

if test "x$x_includes" != x; then
    ac_cpp="$ac_cpp -I$x_includes"
fi

dnl OpenGL uses the math functions...
AC_SEARCH_LIBS(pow, m)

dnl Some OpenGL implementations use dlopen()...
AC_SEARCH_LIBS(dlopen, dl)

dnl Look for the OpenGL or Mesa libraries...
AC_CHECK_HEADER(GL/gl.h)
AC_CHECK_HEADER(GL/glu.h)
AC_CHECK_HEADER(GL/glx.h)

AC_CHECK_LIB(GL, glXMakeCurrent,
    LIBS="-lGLU -lGL $LIBS",
    AC_CHECK_LIB(MesaGL, glXMakeCurrent,
    LIBS="-lMesaGLU -lMesaGL $LIBS"))

dnl Generate a Makefile for the program
AC_OUTPUT(Makefile)

You build the actual configure script using the following command:

autoconf

The AC_PATH_XTRA and AC_CHECK_LIB macros handle searching for the X11 and OpenGL libraries, and the AC_OUTPUT macro generates any files using what the script found. The Makefile.in file used by the configure script is shown in Listing 15.2.

Example 15.2. Makefile Template File Makefile.in

#
# Sample makefile template for configure script.
#

#
# Compiler and options...
#

CC    =    @CC@
CFLAGS    =    @CFLAGS@
LDFLAGS    =    @LDFLAGS@
LIBS    =    @LIBS@


#
# Program
#

myprogram:    myprogram.o
    $(CC) $(LDFLAGS) -o myprogram myprogram.o $(LIBS)

The configure script substitutes the variables specified in Makefile.in to produce the final makefile to build your application. You specify variables using an at sign (@) before and after the variable name, such as @CC@ for the C compiler, @CFLAGS@ for the compiler options, and so forth.

You run the configure script to generate the makefile and then use the make command to build your program, as follows:

./configure
make

You can use this same technique to create include files for makefiles to include common compiler and linker options for larger, multiple-directory projects, or you can list multiple makefile templates in the AC_OUTPUT macro.

A variation of the configure.in and Makefile.in files is used to build all the examples in this book on UNIX and Linux.

Using the GLUT Library

The GLUT library is not generally provided as a standard part of UNIX or Linux distributions; however, it is available as source code and can be compiled and installed relatively easily from the source that comes on the CD-ROM. Start by copying the glut-3.7 directory to your own hard drive. Then run the following commands:

cd glut-3.7
./mkmkfiles.imake
make
make install

After you install GLUT, simply add the GLUT library to your link command:

gcc -o myprogram myprogram.o -lglut -lGLU -lGL -lXext -lX11 -lm

Or add it to your makefile template:

LIBS    =    -lglut @LIBS@

OpenGL on Linux

Although most commercial versions of UNIX provide OpenGL support out of the box, OpenGL support on Linux depends greatly on the graphics card and Linux distribution you use. Also, there are both free and commercial versions of OpenGL that you can use.

The Xfree86 project provides the most popular free implementation of the X Window System and is provided with every Linux distribution. Xfree86 4.x uses the Direct Rendering Infrastructure (DRI) to provide accelerated OpenGL rendering. The following URLs provide information on each of these projects:

http://www.xfree86.org/

http://dri.sf.net/

OpenGL support is enabled through the XF86Config or XF86Config-4 file, usually installed in /etc/X11. The key area of this file is the Module section, which follows:

Section "Module"
    Load  "GLcore"    # OpenGL support
    Load  "glx"       # OpenGL X protocol interface
    Load  "dri"       # Direct rendering infrastructure

The Load lines load the named modules—GLcore, glx, and dri—and those modules provide OpenGL support. If the underlying driver supports OpenGL, your output will be hardware accelerated. Otherwise, software emulation is used.

Commercial implementations of the X Window System are also available. One popular package that provides strong OpenGL support is Summit from Xi Graphics, available at the following URL:

http://www.xi-graphics.com/

OpenGL Emulation: Mesa

The Mesa library can be used on systems that don't support OpenGL natively, when you want to experiment with new OpenGL features or extensions that are not supported by your graphics card, or when you want to do offline rendering using OpenGL. You can find the Mesa library at the following URL:

http://mesa3d.sf.net/

Aside from its use as a standalone library, Mesa also serves as the core of the Xfree86 implementation of OpenGL.

The OpenGL Extension for the X Window System

The OpenGL extension for the X Window System, GLX, provides the interface between your application, the X Window System, and the graphics driver to provide accelerated rendering. If the underlying graphics hardware does not support a particular feature, it is automatically emulated in software. You can check whether your X server supports this extension by using the xdpyinfo program on the command line:

xdpyinfo | grep GLX

There have been five versions of the GLX extension to date (1.0, 1.1, 1.2, 1.3, and 1.4); by far the most common version in use is 1.2, but this chapter also covers the Pbuffer functionality available in GLX 1.3.

GLX provides several functions that manage visuals, contexts, and drawing surfaces.

X Window System Basics

The X Window System provides a network-transparent interface for creating windows, drawing things on the screen, getting input from the user, and so forth. The program that manages one or more screens, keyboard, mouse, and other assorted input devices is usually called an X server. The connection to the server is managed by a Display pointer. You connect to the server using the XOpenDisplay() function and the display name:

Display *display;

display = XOpenDisplay(getenv("DISPLAY"));

The default display name is, by convention, provided in the DISPLAY environment variable. When you have a display connection, you can create windows, draw graphics, and collect input from the user.

Choosing a Visual

Each window on the screen has a visual associated with it. Each visual has an associated class—DirectGray, DirectColor, PseudoColor, or TrueColor—that defines its properties. In general, most OpenGL rendering uses TrueColor visuals, which support arbitrary red, green, and blue color values. The other visual types map color numbers or indices to specific RGB values, making them useful only for specialized applications.

The glXChooseVisual() function determines the correct visual to use for a specific screen and combination of OpenGL features. The function accepts a Display pointer, screen number (usually 0), and attribute list. The attribute list is an array of integers. Each attribute consists of a token name (for example, GLX_RED_SIZE) and, for some attributes, a value. Table 15.1 lists the attribute list tokens that are defined.

Table 15.1. GLX Visual Attribute List Constants

Constant

Description

GLX_ACCUM_ALPHA_SIZE

The number that follows specifies the minimum number of alpha bits that are required in the accumulation buffer.

GLX_ACCUM_BLUE_SIZE

The number that follows specifies the minimum number of blue bits that are required in the accumulation buffer.

GLX_ACCUM_GREEN_SIZE

The number that follows specifies the minimum number of green bits that are required in the accumulation buffer.

GLX_ACCUM_RED_SIZE

The number that follows specifies the minimum number of red bits that are required in the accumulation buffer.

GLX_ALPHA_SIZE

The number that follows specifies the minimum number of alpha bits that are required.

GLX_AUX_BUFFERS

The number that follows specifies the number of auxiliary buffers that are required.

GLX_BLUE_SIZE

The number that follows specifies the minimum number of blue bits that are required.

GLX_BUFFER_SIZE

The number that follows specifies the number of color index bits that are desired.

GLX_DEPTH_SIZE

The number that follows specifies the minimum number of depth bits that are required.

GLX_DOUBLEBUFFER

This token specifies that a double-buffered visual is desired.

GLX_GREEN_SIZE

The number that follows specifies the minimum number of green bits that are required.

GLX_LEVEL

This token specifies the buffer level in the number that follows; 0 is the main buffer, 1 is the first overlay buffer, –1 is the first underlay buffer, and so forth.

GLX_RED_SIZE

The number that follows specifies the minimum number of red bits that are required.

GLX_RGBA

This token specifies that an RGBA visual is desired.

GLX_STENCIL_SIZE

The number that follows specifies the minimum number of stencil bits that are required.

GLX_STEREO

This token specifies that a stereo visual is desired; stereo visuals provide separate left and right eye images.

GLX_USE_GL

This token specifies that OpenGL visuals are desired. This attribute is ignored because glXChooseVisual() returns only OpenGL visuals.

You might use the following code to find a double-buffered RGB visual:

int attributes[] = {
  GLX_RGBA,
  GLX_DOUBLEBUFFER,
  GLX_RED_SIZE, 4,
  GLX_GREEN_SIZE, 4,
  GLX_BLUE_SIZE, 4,
  GLX_DEPTH_SIZE, 16,
  0
};
Display *display;
XVisualInfo *vinfo;

vinfo = glXChooseVisual(display, DefaultScreen(display), attributes);

An XVisualInfo pointer is returned; it provides information on the correct visual to use. If no matching visual can be found, a NULL pointer is returned, and you should retry with different attributes or inform the users that the window cannot be displayed on their system.

When you have found the correct visual to use, you can create an OpenGL context to use with a window or pixmap.

Managing OpenGL Contexts

OpenGL contexts are used to manage drawing in a single window or pixmap. Contexts are identified using the GLXContext data type. The glXCreateContext() function creates a new context and accepts the Display pointer, an XVisualInfo pointer, a GLXContext to share with, and a boolean value indicating whether to create a direct or indirect context:

GLXContext context;

context = glXCreateContext(display, vinfo, 0, True);

The display and visual information pointers are as initialized by the previous code examples. The third argument specifies a GLXContext with which to share display lists, texture objects, and other stored data. You'll likely specify this argument when creating multiple OpenGL windows for your application. If you have no other contexts defined, pass a value of 0 for the context.

The fourth argument specifies whether a direct (True) or indirect (False) context is created. Direct contexts allow the OpenGL library to talk directly to the graphics hardware, providing the highest speed rendering. Indirect contexts send OpenGL drawing commands through the X server allowing for remote display over a network. Direct contexts cannot share with indirect contexts, and vice versa. This has the greatest impact on some types of offscreen rendering. Normally, you create a direct context.

When you are finished with an OpenGL context, call the glXDestroyContext() function to free the resources it uses:

glXDestroyContext(display, context);

Creating an OpenGL Window

When you have a Display pointer and GLXContext, you can use the XCreateWindow() function to create a window:

Window window;
XSetWindowAttributes winattrs;

winattrs.event_mask   = ExposureMask | VisibilityChangeMask |
                        StructureNotifyMask | ButtonPressMask |
                        ButtonReleaseMask | PointerMotionMask;
winattrs.border_pixel = 0;
winattrs.bit_gravity  = StaticGravity;
winmask               = CWBorderPixel | CWBitGravity | CWEventMask;

window                = XCreateWindow(display, DefaultRootWindow(display),
                        0, 0, 400, 400, 0, vinfo->depth, InputOutput,
                        vinfo->visual, winmask, &winattrs);

After you create the window, you can show it using the XMapWindow() function and bind the OpenGL context to the window using the glXMakeCurrent() function:

XMapWindow(display, window);
glXMakeCurrent(display, window, context);

If you have multiple OpenGL windows, you need to call the glXMakeCurrent() function before drawing in each window. Otherwise, you can call it once for the life of the program.

Before you can actually draw in the window, you must wait for a MapNotify event, which tells your application that the X server has displayed your window on the screen. You can use the XNextEvent() function to wait for a MapNotify event or track whether you have seen the event in the normal event loop in your application.

Double-Buffered Windows

You create double-buffered windows by using a double-buffered visual. You get this type of visual by using the glXChooseVisual() function with the GLX_DOUBLEBUFFER attribute.

After the window has been created, the glXSwapBuffers() function will swap the front and back buffer for the window:

glXSwapBuffers(display, window);

The glXSwapBuffers() function flushes any pending OpenGL drawing commands and may block until the next vertical retrace.

Putting It All Together

Figure 15.1 shows an OpenGL application written entirely using the Xlib and GLX functions covered in this section; this application displays a spinning cube. The user can click any mouse button to stop the rotation or drag the mouse to rotate the cube in any axis. The program responds to ConfigureNotify events to track when the window is resized and MapNotify and UnmapNotify events when the window is shown or iconified. Listing 15.3 shows the source for this application.

Example 15.3. The xlib.c Sample Program

/*
 * Include necessary headers...
 */

#include <stdio.h>
#include <stdlib.h>
#include <X11/Xlib.h>
#include <X11/Xatom.h>
#include <GL/glx.h>
#include <GL/gl.h>

#include <sys/select.h>
#include <sys/types.h>
#include <sys/time.h>


/*
 * Globals...
 */

float           CubeRotation[3],        /* Rotation of cube */
                CubeRate[3];            /* Rotation rate of cube */
int             CubeMouseButton,        /* Button that was pressed */
                CubeMouseX,             /* Start X position of mouse */
                CubeMouseY;             /* Start Y position of mouse */
int             CubeWidth,              /* Width of window */
                CubeHeight;             /* Height of window */


/*
 * Functions...
 */

void            DisplayFunc(void);
void            IdleFunc(void);
void            MotionFunc(int x, int y);
void            MouseFunc(int button, int state, int x, int y);
void            ReshapeFunc(int width, int height);


/*
 * 'main()' - Main entry for example program.
 */

int                                     /* O - Exit status */
main(int  argc,                         /* I - Number of command-line args */
     char *argv[])                      /* I - Command-line args */
{
  Bool                  mapped;         /* Is the window mapped? */
  GLXContext            context;        /* OpenGL context */
  Display               *display;       /* X display connection */
  Window                window;         /* X window */
  XVisualInfo           *vinfo;         /* X visual information */
  XSetWindowAttributes  winattrs;       /* Window attributes */
  int                   winmask;        /* Mask for attributes */
  XEvent                event;          /* Event data */
  XWindowAttributes     windata;        /* Window data */
  struct timeval        timeout;        /* Timeout interval for select() */
  fd_set                input;          /* Input set for select() */
  int                   ready;          /* Event ready? */
  static int            attributes[] =  /* OpenGL attributes */
                        {
                          GLX_RGBA,
                          GLX_DOUBLEBUFFER,
                          GLX_RED_SIZE, 8,
                          GLX_GREEN_SIZE, 8,
                          GLX_BLUE_SIZE, 8,
                          GLX_DEPTH_SIZE, 16,
                          0
                        };


 /*
  * Open a connection to the X server...
  */

  display = XOpenDisplay(getenv("DISPLAY"));

 /*
  * Find the proper visual for an OpenGL window...
  */

  vinfo = glXChooseVisual(display, DefaultScreen(display), attributes);

 /*
  * Create the window...
  */

  winattrs.event_mask   = ExposureMask | VisibilityChangeMask |
                          StructureNotifyMask | ButtonPressMask |
                          ButtonReleaseMask | PointerMotionMask;
  winattrs.border_pixel = 0;
  winattrs.bit_gravity  = StaticGravity;
  winmask               = CWBorderPixel | CWBitGravity | CWEventMask;

  window  = XCreateWindow(display, DefaultRootWindow(display),
                          0, 0, 400, 400, 0, vinfo->depth, InputOutput,
                          vinfo->visual, winmask, &winattrs);

  XChangeProperty(display, window, XA_WM_NAME, XA_STRING, 8, 0,
                  (unsigned char *)argv[0], strlen(argv[0]));
  XChangeProperty(display, window, XA_WM_ICON_NAME, XA_STRING, 8, 0,
                  (unsigned char *)argv[0], strlen(argv[0]));

  XMapWindow(display, window);

 /*
  * Create the OpenGL context...
  */

  context = glXCreateContext(display, vinfo, 0, True);
  glXMakeCurrent(display, window, context);

 /*
  * Setup remaining globals...
  */

  CubeWidth       = 400;
  CubeHeight      = 400;
  CubeRotation[0] = 45.0f;
  CubeRotation[1] = 45.0f;
  CubeRotation[2] = 45.0f;
  CubeRate[0]     = 1.0f;
  CubeRate[1]     = 1.0f;
  CubeRate[2]     = 1.0f;

 /*
  * Loop forever...
  */

  mapped = False;

  for (;;)
  {
   /*
    * Use select() to respond asynchronously to events; when the window is
    * not mapped, we wait indefinitely; otherwise we'll timeout after 20ms
    * to rotate the cube...
    */

    if (mapped)
    {
      FD_ZERO(&input);
      FD_SET(ConnectionNumber(display), &input);

      timeout.tv_sec  = 0;
      timeout.tv_usec = 20000;

      ready = select(ConnectionNumber(display) + 1, &input, NULL, NULL,
                     &timeout);
    }
    else
      ready = 1;

    if (ready)
    {
     /*
      * An event is ready, handle it...
      */

      XNextEvent(display, &event);

      switch (event.type)
      {
        case MapNotify :
            mapped = True;

        case ConfigureNotify :
            XGetWindowAttributes(display, window, &windata);

            ReshapeFunc(windata.width, windata.height);
            break;

        case UnmapNotify :
            mapped = False;
            break;

        case ButtonPress :
            MouseFunc(event.xbutton.button, 0, event.xbutton.x,
                      event.xbutton.y);
            break;

        case ButtonRelease :
            MouseFunc(event.xbutton.button, 1, event.xbutton.x,
                      event.xbutton.y);
            break;

        case MotionNotify :
            if (event.xmotion.state & (Button1Mask | Button2Mask | Button3Mask))
              MotionFunc(event.xmotion.x, event.xmotion.y);
            break;
      }
    }

   /*
    * Redraw if the window is mapped...
    */

    if (mapped)
    {
     /*
      * Update the cube rotation...
      */

      IdleFunc();

     /*
      * Draw the cube...
      */

      DisplayFunc();

     /*
      * Swap the front and back buffers...
      */

      glXSwapBuffers(display, window);
    }
  }
}


/*
 * 'DisplayFunc()' - Draw a cube.
 */

void
DisplayFunc(void)
{
  int                   i, j;           /* Looping vars */
  static const GLfloat  corners[8][3] = /* Corner vertices */
                        {
                          {  1.0f,  1.0f,  1.0f },      /* Front top right */
                          {  1.0f, -1.0f,  1.0f },      /* Front bottom right */
                          { -1.0f, -1.0f,  1.0f },      /* Front bottom left */
                          { -1.0f,  1.0f,  1.0f },      /* Front top left */
                          {  1.0f,  1.0f, -1.0f },      /* Back top right */
                          {  1.0f, -1.0f, -1.0f },      /* Back bottom right */
                          { -1.0f, -1.0f, -1.0f },      /* Back bottom left */
                          { -1.0f,  1.0f, -1.0f }       /* Back top left */
                        };
  static const int      sides[6][4] =   /* Sides */
                        {
                          { 0, 1, 2, 3 },               /* Front */
                          { 4, 5, 6, 7 },               /* Back */
                          { 0, 1, 5, 4 },               /* Right */
                          { 2, 3, 7, 6 },               /* Left */
                          { 0, 3, 7, 4 },               /* Top */
                          { 1, 2, 6, 5 }                /* Bottom */
                        };
  static const GLfloat  colors[6][3] =  /* Colors */
                        {
                          { 1.0f, 0.0f, 0.0f },         /* Red */
                          { 0.0f, 1.0f, 0.0f },         /* Green */
                          { 1.0f, 1.0f, 0.0f },         /* Yellow */
                          { 0.0f, 0.0f, 1.0f },         /* Blue */
                          { 1.0f, 0.0f, 1.0f },         /* Magenta */
                          { 0.0f, 1.0f, 1.0f }          /* Cyan */
                        };


 /*
  * Clear the window...
  */

  glViewport(0, 0, CubeWidth, CubeHeight);
  glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
  glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

 /*
  * Setup the matrices...
  */

  glMatrixMode(GL_PROJECTION);
  glLoadIdentity();
  glOrtho(-2.0f, 2.0f,
          -2.0f * CubeHeight / CubeWidth, 2.0f * CubeHeight / CubeWidth,
          -2.0f, 2.0f);

  glMatrixMode(GL_MODELVIEW);
  glLoadIdentity();
  glRotatef(CubeRotation[0], 1.0f, 0.0f, 0.0f);
  glRotatef(CubeRotation[1], 0.0f, 1.0f, 0.0f);
  glRotatef(CubeRotation[2], 0.0f, 0.0f, 1.0f);

 /*
  * Draw the cube...
  */

  glEnable(GL_DEPTH_TEST);

  glBegin(GL_QUADS);

  for (i = 0; i < 6; i ++)
  {
    glColor3fv(colors[i]);
    for (j = 0; j < 4; j ++)
      glVertex3fv(corners[sides[i][j]]);
  }

  glEnd();
}


/*
 * 'IdleFunc()' - Rotate and redraw the cube.
 */

void
IdleFunc(void)
{
  CubeRotation[0] += CubeRate[0];
  CubeRotation[1] += CubeRate[1];
  CubeRotation[2] += CubeRate[2];
}


/*
 * 'MotionFunc()' - Handle mouse pointer motion.
 */

void
MotionFunc(int x,                       /* I - X position */
           int y)                       /* I - Y position */
{
 /*
  * Get the mouse movement...
  */

  x -= CubeMouseX;
  y -= CubeMouseY;

 /*
  * Update the cube rotation rate based upon the mouse movement and
  * button...
  */

  switch (CubeMouseButton)
  {
    case 0 :                            /* Button 1 */
        CubeRate[0] = 0.01f * y;
        CubeRate[1] = 0.01f * x;
        CubeRate[2] = 0.0f;
        break;

    case 1 :                            /* Button 2 */
        CubeRate[0] = 0.0f;
        CubeRate[1] = 0.01f * y;
        CubeRate[2] = 0.01f * x;
        break;

    default :                           /* Button 3 */
        CubeRate[0] = 0.01f * y;
        CubeRate[1] = 0.0f;
        CubeRate[2] = 0.01f * x;
        break;
  }
}


/*
 * 'MouseFunc()' - Handle mouse button press/release events.
 */

void
MouseFunc(int button,                   /* I - Button that was pressed */
          int state,                    /* I - Button state (1 = down) */
          int x,                        /* I - X position */
          int y)                        /* I - Y position */
{
 /*
  * Only respond to button presses...
  */

  if (state)
    return;

 /*
  * Save the mouse state...
  */

  CubeMouseButton = button;
  CubeMouseX      = x;
  CubeMouseY      = y;

 /*
  * Zero-out the rotation rates...
  */

  CubeRate[0] = 0.0f;
  CubeRate[1] = 0.0f;
  CubeRate[2] = 0.0f;
}


/*
 * 'ReshapeFunc()' - Resize the window.
 */

void
ReshapeFunc(int width,                  /* I - Width of window */
            int height)                 /* I - Height of window */
{
  CubeWidth  = width;
  CubeHeight = height;
}
The Xlib spinning cube example.

Figure 15.1. The Xlib spinning cube example.

Creating Bitmap Fonts for OpenGL

The GLX extension provides a single function called glXUseXFont() for converting an X font to OpenGL bitmaps. Each bitmap is placed in a display list, allowing you to display a string of text using the glCallLists() function. You start by looking up an X font using the XLoadQueryFont() function:

XFontStruct *font;

font = XLoadQueryFont(display, "-*-courier-bold-r-normal--14-*-*-*-*-*-*-*");

The sample code loads a 14-pixel Courier Bold font; however, any X font can be used. After you load the X font, you call glGenLists() to create display lists for the number of characters you want to use and glXUseXFont() to load the display list bitmaps. In the following sample code, characters from the space (32) to delete (127) are loaded into 96 display lists:

GLuint listbase;

listbase = glGenLists(96);
glXUseXFont(font->fid, ' ', 96, listbase);

Then you can draw text using a combination of the glRasterPos(), glPushAttrib(), glListBase(), glCallLists(), and glPopAttrib() functions, as follows:

char *s = "Hello, World!";

glPushAttrib(GL_LIST_BIT);
  glListBase(CubeFont - ' '),

  glRasterPos3f(0.0f, 0.0f, 0.0f);
  glCallLists(strlen(s), GL_BYTE, s);
glPopAttrib();

The example in Listing 15.4 uses this code to draw the names of each side of the cube. Figure 15.2 shows the result.

Example 15.4. The xlibfonts.c Sample Program

/*
 * Include necessary headers...
 */

#include <stdio.h>
#include <stdlib.h>
#include <X11/Xlib.h>
#include <X11/Xatom.h>
#include <GL/glx.h>
#include <GL/gl.h>

#include <sys/select.h>
#include <sys/types.h>
#include <sys/time.h>


/*
 * Globals...
 */

float           CubeRotation[3],        /* Rotation of cube */
                CubeRate[3];            /* Rotation rate of cube */
int             CubeMouseButton,        /* Button that was pressed */
                CubeMouseX,             /* Start X position of mouse */
                CubeMouseY;             /* Start Y position of mouse */
int             CubeWidth,              /* Width of window */
                CubeHeight;             /* Height of window */
GLuint          CubeFont;               /* Display list base for font */


/*
 * Functions...
 */

void            DisplayFunc(void);
void            IdleFunc(void);
void            MotionFunc(int x, int y);
void            MouseFunc(int button, int state, int x, int y);
void            ReshapeFunc(int width, int height);


/*
 * 'main()' - Main entry for example program.
 */

int                                     /* O - Exit status */
main(int  argc,                         /* I - Number of command-line args */
     char *argv[])                      /* I - Command-line args */
{
  Bool                  mapped;         /* Is the window mapped? */
  GLXContext            context;        /* OpenGL context */
  Display               *display;       /* X display connection */
  Window                window;         /* X window */
  XVisualInfo           *vinfo;         /* X visual information */
  XFontStruct           *font;          /* X font information */
  XSetWindowAttributes  winattrs;       /* Window attributes */
  int                   winmask;        /* Mask for attributes */
  XEvent                event;          /* Event data */
  XWindowAttributes     windata;        /* Window data */
  struct timeval        timeout;        /* Timeout interval for select() */
  fd_set                input;          /* Input set for select() */
  int                   ready;          /* Event ready? */
  static int            attributes[] =  /* OpenGL attributes */
                        {
                          GLX_RGBA,
                          GLX_DOUBLEBUFFER,
                          GLX_RED_SIZE, 8,
                          GLX_GREEN_SIZE, 8,
                          GLX_BLUE_SIZE, 8,
                          GLX_DEPTH_SIZE, 16,
                          0
                        };


 /*
  * Open a connection to the X server...
  */

  display = XOpenDisplay(getenv("DISPLAY"));

 /*
  * Find the proper visual for an OpenGL window...
  */

  vinfo = glXChooseVisual(display, DefaultScreen(display), attributes);

 /*
  * Create the window...
  */

  winattrs.event_mask   = ExposureMask | VisibilityChangeMask |
                          StructureNotifyMask | ButtonPressMask |
                          ButtonReleaseMask | PointerMotionMask;
  winattrs.border_pixel = 0;
  winattrs.bit_gravity  = StaticGravity;
  winmask               = CWBorderPixel | CWBitGravity | CWEventMask;

  window  = XCreateWindow(display, DefaultRootWindow(display),
                          0, 0, 400, 400, 0, vinfo->depth, InputOutput,
                          vinfo->visual, winmask, &winattrs);

  XChangeProperty(display, window, XA_WM_NAME, XA_STRING, 8, 0,
                  (unsigned char *)argv[0], strlen(argv[0]));
  XChangeProperty(display, window, XA_WM_ICON_NAME, XA_STRING, 8, 0,
                  (unsigned char *)argv[0], strlen(argv[0]));

  XMapWindow(display, window);

 /*
  * Create the OpenGL context...
  */

  context = glXCreateContext(display, vinfo, 0, True);
  glXMakeCurrent(display, window, context);

 /*
  * Setup remaining globals...
  */

  CubeWidth       = 400;
  CubeHeight      = 400;
  CubeRotation[0] = 45.0f;
  CubeRotation[1] = 45.0f;
  CubeRotation[2] = 45.0f;
  CubeRate[0]     = 1.0f;
  CubeRate[1]     = 1.0f;
  CubeRate[2]     = 1.0f;

 /*
  * Setup font...
  */

  font     = XLoadQueryFont(display,
                            "-*-courier-bold-r-normal--14-*-*-*-*-*-*-*");
  CubeFont = glGenLists(96);

  glXUseXFont(font->fid, ' ', 96, CubeFont);

 /*
  * Loop forever...
  */

  mapped = False;

  for (;;)
  {
   /*
    * Use select() to respond asynchronously to events; when the window is
    * not mapped, we wait indefinitely; otherwise we'll timeout after 20ms
    * to rotate the cube...
    */

    if (mapped)
    {
      FD_ZERO(&input);
      FD_SET(ConnectionNumber(display), &input);

      timeout.tv_sec  = 0;
      timeout.tv_usec = 20000;

      ready = select(ConnectionNumber(display) + 1, &input, NULL, NULL,
                     &timeout);
    }
    else
      ready = 1;

    if (ready)
    {
     /*
      * An event is ready, handle it...
      */

      XNextEvent(display, &event);

      switch (event.type)
      {
        case MapNotify :
            mapped = True;

        case ConfigureNotify :
            XGetWindowAttributes(display, window, &windata);

            ReshapeFunc(windata.width, windata.height);
            break;

        case UnmapNotify :
            mapped = False;
            break;

        case ButtonPress :
            MouseFunc(event.xbutton.button, 0, event.xbutton.x,
                      event.xbutton.y);
            break;

        case ButtonRelease :
            MouseFunc(event.xbutton.button, 1, event.xbutton.x,
                      event.xbutton.y);
            break;

        case MotionNotify :
            if (event.xmotion.state & (Button1Mask | Button2Mask | Button3Mask))
              MotionFunc(event.xmotion.x, event.xmotion.y);
            break;
      }
    }

   /*
    * Redraw if the window is mapped...
    */

    if (mapped)
    {
     /*
      * Update the cube rotation...
      */

      IdleFunc();

     /*
      * Draw the cube...
      */

      DisplayFunc();

     /*
      * Swap the front and back buffers...
      */

      glXSwapBuffers(display, window);
    }
  }
}


/*
 * 'DisplayFunc()' - Draw a cube.
 */

void
DisplayFunc(void)
{
  int                   i, j;           /* Looping vars */
  static const GLfloat  corners[8][3] = /* Corner vertices */
                        {
                          {  1.0f,  1.0f,  1.0f },      /* Front top right */
                          {  1.0f, -1.0f,  1.0f },      /* Front bottom right */
                          { -1.0f, -1.0f,  1.0f },      /* Front bottom left */
                          { -1.0f,  1.0f,  1.0f },      /* Front top left */
                          {  1.0f,  1.0f, -1.0f },      /* Back top right */
                          {  1.0f, -1.0f, -1.0f },      /* Back bottom right */
                          { -1.0f, -1.0f, -1.0f },      /* Back bottom left */
                          { -1.0f,  1.0f, -1.0f }       /* Back top left */
                        };
  static const int      sides[6][4] =   /* Sides */
                        {
                          { 0, 1, 2, 3 },               /* Front */
                          { 4, 5, 6, 7 },               /* Back */
                          { 0, 1, 5, 4 },               /* Right */
                          { 2, 3, 7, 6 },               /* Left */
                          { 0, 3, 7, 4 },               /* Top */
                          { 1, 2, 6, 5 }                /* Bottom */
                        };
  static const GLfloat  colors[6][3] =  /* Colors */
                        {
                          { 1.0f, 0.0f, 0.0f },         /* Red */
                          { 0.0f, 1.0f, 0.0f },         /* Green */
                          { 1.0f, 1.0f, 0.0f },         /* Yellow */
                          { 0.0f, 0.0f, 1.0f },         /* Blue */
                          { 1.0f, 0.0f, 1.0f },         /* Magenta */
                          { 0.0f, 1.0f, 1.0f }          /* Cyan */
                        };


 /*
  * Clear the window...
  */

  glViewport(0, 0, CubeWidth, CubeHeight);
  glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
  glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

 /*
  * Setup the matrices...
  */

  glMatrixMode(GL_PROJECTION);
  glLoadIdentity();
  glOrtho(-2.0f, 2.0f,
          -2.0f * CubeHeight / CubeWidth, 2.0f * CubeHeight / CubeWidth,
          -2.0f, 2.0f);

  glMatrixMode(GL_MODELVIEW);
  glLoadIdentity();
  glRotatef(CubeRotation[0], 1.0f, 0.0f, 0.0f);
  glRotatef(CubeRotation[1], 0.0f, 1.0f, 0.0f);
  glRotatef(CubeRotation[2], 0.0f, 0.0f, 1.0f);

 /*
  * Draw the cube...
  */

  glEnable(GL_DEPTH_TEST);

  glBegin(GL_QUADS);

  for (i = 0; i < 6; i ++)
  {
    glColor3fv(colors[i]);
    for (j = 0; j < 4; j ++)
      glVertex3fv(corners[sides[i][j]]);
  }

  glEnd();

 /*
  * Draw lines coming out of the cube...
  */

  glColor3f(1.0f, 1.0f, 1.0f);

  glBegin(GL_LINES);
    glVertex3f(0.0f, 0.0f, -1.5f);
    glVertex3f(0.0f, 0.0f, 1.5f);

    glVertex3f(-1.5f, 0.0f, 0.0f);
    glVertex3f(1.5f, 0.0f, 0.0f);

    glVertex3f(0.0f, 1.5f, 0.0f);
    glVertex3f(0.0f, -1.5f, 0.0f);
  glEnd();

 /*
  * Draw text for each side...
  */

  glPushAttrib(GL_LIST_BIT);
    glListBase(CubeFont - ' '),

    glRasterPos3f(0.0f, 0.0f, -1.5f);
    glCallLists(4, GL_BYTE, "Back");

    glRasterPos3f(0.0f, 0.0f, 1.5f);
    glCallLists(5, GL_BYTE, "Front");

    glRasterPos3f(-1.5f, 0.0f, 0.0f);
    glCallLists(4, GL_BYTE, "Left");

    glRasterPos3f(1.5f, 0.0f, 0.0f);
    glCallLists(5, GL_BYTE, "Right");

    glRasterPos3f(0.0f, 1.5f, 0.0f);
    glCallLists(3, GL_BYTE, "Top");

    glRasterPos3f(0.0f, -1.5f, 0.0f);
    glCallLists(6, GL_BYTE, "Bottom");
  glPopAttrib();
}


/*
 * 'IdleFunc()' - Rotate and redraw the cube.
 */

void
IdleFunc(void)
{
  CubeRotation[0] += CubeRate[0];
  CubeRotation[1] += CubeRate[1];
  CubeRotation[2] += CubeRate[2];
}


/*
 * 'MotionFunc()' - Handle mouse pointer motion.
 */

void
MotionFunc(int x,                       /* I - X position */
           int y)                       /* I - Y position */
{
 /*
  * Get the mouse movement...
  */

  x -= CubeMouseX;
  y -= CubeMouseY;

 /*
  * Update the cube rotation rate based upon the mouse movement and
  * button...
  */

  switch (CubeMouseButton)
  {
    case 0 :                            /* Button 1 */
        CubeRate[0] = 0.01f * y;
        CubeRate[1] = 0.01f * x;
        CubeRate[2] = 0.0f;
        break;

    case 1 :                            /* Button 2 */
        CubeRate[0] = 0.0f;
        CubeRate[1] = 0.01f * y;
        CubeRate[2] = 0.01f * x;
        break;

    default :                           /* Button 3 */
        CubeRate[0] = 0.01f * y;
        CubeRate[1] = 0.0f;
        CubeRate[2] = 0.01f * x;
        break;
  }
}


/*
 * 'MouseFunc()' - Handle mouse button press/release events.
 */

void
MouseFunc(int button,                   /* I - Button that was pressed */
          int state,                    /* I - Button state (1 = down) */
          int x,                        /* I - X position */
          int y)                        /* I - Y position */
{
 /*
  * Only respond to button presses...
  */

  if (state)
    return;

 /*
  * Save the mouse state...
  */

  CubeMouseButton = button;
  CubeMouseX      = x;
  CubeMouseY      = y;

 /*
  * Zero-out the rotation rates...
  */

  CubeRate[0] = 0.0f;
  CubeRate[1] = 0.0f;
  CubeRate[2] = 0.0f;
}


/*
 * 'ReshapeFunc()' - Resize the window.
 */

void
ReshapeFunc(int width,                  /* I - Width of window */
            int height)                 /* I - Height of window */
{
  CubeWidth  = width;
  CubeHeight = height;
}
The Xlib spinning cube with text example.

Figure 15.2. The Xlib spinning cube with text example.

Offscreen Rendering

GLX supports two types of offscreen rendering, each with its own advantage: GLX pixmaps and Pbuffers.

Using GLX Pixmaps

GLX pixmaps are the original type of offscreen rendering and generally support TrueColor and PseudoColor visuals. They are generally used when portability is desired over performance; although GLX pixmaps are available on all platforms, GLX pixmaps are not hardware accelerated. GLX pixmaps also often support larger bit depths and dimensions than the graphics hardware, making them ideal for offline rendering of images when graphics card memory is limited.

As with OpenGL windows, GLX pixmaps start with a call to the glXChooseVisual() function to find an appropriate visual. Because some systems and graphics cards provide only double-buffered OpenGL visuals, you have to check for both single- and double-buffered visuals:

Display *display;
XVisualInfo *vinfo;
static int attributes[] =
{
  GLX_RGBA,
  GLX_RED_SIZE, 8,
  GLX_GREEN_SIZE, 8,
  GLX_BLUE_SIZE, 8,
  GLX_DEPTH_SIZE, 16,
  0,        /* Save space for GLX_DOUBLEBUFFER */
  0
};

display = XOpenDisplay(getenv("DISPLAY"));
vinfo = glXChooseVisual(display, DefaultScreen(display), attributes);
if (!vinfo)
{
 /*
  * If no single-buffered visual is available, try a double-buffered one...
  */

  attributes[9] = GLX_DOUBLEBUFFER;
  vinfo         = glXChooseVisual(display, DefaultScreen(display),
                                  attributes);
}

When you have an appropriate visual, you can create an X Pixmap using the XCreatePixmap() function; this pixmap will hold the actual pixels for your offscreen buffer. It is then bound to GLX for OpenGL rendering using the glXCreateGLXPixmap() function:

Pixmap pixmap;
GLXPixmap glxpixmap;

pixmap    = XCreatePixmap(display, DefaultRootWindow(display),
                          1024, 1024, vinfo->depth);
glxpixmap = glXCreateGLXPixmap(display, vinfo, pixmap);

Finally, you call the glXCreateContext() function to create a context for the GLX pixmap, specifying a value of False for the fourth parameter for an indirect rendering context:

GLXContext context;

context = glXCreateContext(display, vinfo, 0, False);
glXMakeCurrent(display, glxpixmap, context);

You can then draw into the pixmap using OpenGL functions and read the results back using the glReadPixels() function. Listing 15.5 shows a variation of the previous sample program that creates a GLX pixmap, draws a cube, reads the image using glReadPixels(), and writes the result to a PPM image file called glxpixmap.ppm.

Example 15.5. GLX Pixmap Sample Program

/*
 * Include necessary headers...
 */

#include <stdio.h>
#include <stdlib.h>
#include <X11/Xlib.h>
#include <X11/Xatom.h>
#include <GL/glx.h>
#include <GL/gl.h>


/*
 * Globals...
 */

float           CubeRotation[3],        /* Rotation of cube */
                CubeRate[3];            /* Rotation rate of cube */
int             CubeWidth,              /* Width of window */
                CubeHeight;             /* Height of window */


/*
 * Functions...
 */

void            DisplayFunc(void);


/*
 * 'main()' - Main entry for example program.
 */

int                                     /* O - Exit status */
main(int  argc,                         /* I - Number of command-line args */
     char *argv[])                      /* I - Command-line args */
{
  GLXContext            context;        /* OpenGL context */
  Display               *display;       /* X display connection */
  Pixmap                pixmap;         /* X pixmap */
  GLXPixmap             glxpixmap;      /* GLX pixmap */
  XVisualInfo           *vinfo;         /* X visual information */
  FILE                  *fp;            /* PPM file pointer */
  int                   y;              /* Current row */
  unsigned char         pixels[3072];   /* One line of RGB pixels */
  static int            attributes[] =  /* OpenGL attributes */
                        {
                          GLX_RGBA,
                          GLX_RED_SIZE, 8,
                          GLX_GREEN_SIZE, 8,
                          GLX_BLUE_SIZE, 8,
                          GLX_DEPTH_SIZE, 16,
                          0,            /* Save space for GLX_DOUBLEBUFFER */
                          0
                        };


 /*
  * Open a connection to the X server...
  */

  display = XOpenDisplay(getenv("DISPLAY"));

 /*
  * Find the proper visual for a GLX pixmap...
  */

  vinfo = glXChooseVisual(display, DefaultScreen(display), attributes);
  if (!vinfo)
  {
   /*
    * If no single-buffered visual is available, try a double-buffered one...
    */

    attributes[9] = GLX_DOUBLEBUFFER;
    vinfo         = glXChooseVisual(display, DefaultScreen(display),
                                    attributes);
  }

  if (!vinfo)
  {
    puts("No OpenGL visual available!");
    return (1);
  }

 /*
  * Create the pixmap...
  */

  pixmap    = XCreatePixmap(display, DefaultRootWindow(display),
                            1024, 1024, vinfo->depth);
  glxpixmap = glXCreateGLXPixmap(display, vinfo, pixmap);

 /*
  * Create the OpenGL context...
  */

  context = glXCreateContext(display, vinfo, 0, False);
  glXMakeCurrent(display, glxpixmap, context);

 /*
  * Setup remaining globals...
  */

  CubeWidth       = 1024;
  CubeHeight      = 1024;
  CubeRotation[0] = 45.0f;
  CubeRotation[1] = 45.0f;
  CubeRotation[2] = 45.0f;
  CubeRate[0]     = 1.0f;
  CubeRate[1]     = 1.0f;
  CubeRate[2]     = 1.0f;

 /*
  * Draw a cube...
  */

  DisplayFunc();

 /*
  * Read back the RGB pixels and write the result as a PPM file.
  */

  if ((fp = fopen("glxpixmap.ppm", "wb")) == NULL)
    perror("Unable to create glxpixmap.ppm");
  else
  {
   /*
    * Write a PPM image from top to bottom...
    */

    fputs("P6
1024 1024 255
", fp);

    for (y = 1023; y >= 0; y --)
    {
      glReadPixels(0, y, 1024, 1, GL_RGB, GL_UNSIGNED_BYTE, pixels);
      fwrite(pixels, 1024, 3, fp);
    }

    fclose(fp);
  }

 /*
  * Destroy all resources we used and close the display...
  */

  glXDestroyContext(display, context);
  glXDestroyGLXPixmap(display, glxpixmap);
  XFreePixmap(display, pixmap);
  XCloseDisplay(display);

  return (0);
}


/*
 * 'DisplayFunc()' - Draw a cube.
 */

void
DisplayFunc(void)
{
  int                   i, j;           /* Looping vars */
  static const GLfloat  corners[8][3] = /* Corner vertices */
                        {
                          {  1.0f,  1.0f,  1.0f },      /* Front top right */
                          {  1.0f, -1.0f,  1.0f },      /* Front bottom right */
                          { -1.0f, -1.0f,  1.0f },      /* Front bottom left */
                          { -1.0f,  1.0f,  1.0f },      /* Front top left */
                          {  1.0f,  1.0f, -1.0f },      /* Back top right */
                          {  1.0f, -1.0f, -1.0f },      /* Back bottom right */
                          { -1.0f, -1.0f, -1.0f },      /* Back bottom left */
                          { -1.0f,  1.0f, -1.0f }       /* Back top left */
                        };
  static const int      sides[6][4] =   /* Sides */
                        {
                          { 0, 1, 2, 3 },               /* Front */
                          { 4, 5, 6, 7 },               /* Back */
                          { 0, 1, 5, 4 },               /* Right */
                          { 2, 3, 7, 6 },               /* Left */
                          { 0, 3, 7, 4 },               /* Top */
                          { 1, 2, 6, 5 }                /* Bottom */
                        };
  static const GLfloat  colors[6][3] =  /* Colors */
                        {
                          { 1.0f, 0.0f, 0.0f },         /* Red */
                          { 0.0f, 1.0f, 0.0f },         /* Green */
                          { 1.0f, 1.0f, 0.0f },         /* Yellow */
                          { 0.0f, 0.0f, 1.0f },         /* Blue */
                          { 1.0f, 0.0f, 1.0f },         /* Magenta */
                          { 0.0f, 1.0f, 1.0f }          /* Cyan */
                        };


 /*
  * Clear the window...
  */

  glViewport(0, 0, CubeWidth, CubeHeight);
  glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
  glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

 /*
  * Setup the matrices...
  */

  glMatrixMode(GL_PROJECTION);
  glLoadIdentity();
  glOrtho(-2.0f, 2.0f,
          -2.0f * CubeHeight / CubeWidth, 2.0f * CubeHeight / CubeWidth,
          -2.0f, 2.0f);

  glMatrixMode(GL_MODELVIEW);
  glLoadIdentity();
  glRotatef(CubeRotation[0], 1.0f, 0.0f, 0.0f);
  glRotatef(CubeRotation[1], 0.0f, 1.0f, 0.0f);
  glRotatef(CubeRotation[2], 0.0f, 0.0f, 1.0f);

 /*
  * Draw the cube...
  */

  glEnable(GL_DEPTH_TEST);

  glBegin(GL_QUADS);

  for (i = 0; i < 6; i ++)
  {
    glColor3fv(colors[i]);
    for (j = 0; j < 4; j ++)
      glVertex3fv(corners[sides[i][j]]);
  }

  glEnd();
}

Using Pbuffers

Pbuffers, the second type of offscreen buffer, are supported by GLX 1.3 implementations. Pbuffers use graphics memory instead of X pixmaps and are hardware accelerated, providing faster offscreen rendering. However, the use of graphics memory often limits the maximum size of a Pbuffer, and Pbuffers are not universally supported.

Using Pbuffers, unlike OpenGL windows and GLX pixmaps, you start by choosing a framebuffer configuration using the glXChooseFBConfig() function instead of glXChooseVisual():

Display *display;
int nconfigs;
GLXFBConfig *configs;
static int attributes[] =
{
  GLX_RGBA,
  GLX_RED_SIZE, 8,
  GLX_GREEN_SIZE, 8,
  GLX_BLUE_SIZE, 8,
  GLX_DEPTH_SIZE, 16,
  0,        /* Save space for GLX_DOUBLEBUFFER */
  0
};

display = XOpenDisplay(getenv("DISPLAY"));
configs = glXChooseFBConfig(display, DefaultScreen(display), attributes,
                            &nconfigs);
if (!configs)
{
  attributes[3] = GLX_DOUBLEBUFFER;
  configs       = glXChooseFBConfig(display, DefaultScreen(display),
                                    attributes, &nconfigs);
}

When you have a list of the matching framebuffer configurations, you can create the Pbuffer using the glXCreatePbuffer() function. The function takes a display, framebuffer configuration, and list of Pbuffer attributes:

GLXPbuffer pbuffer;
static int pbattrs[] =
{
  GLX_PBUFFER_WIDTH, 1024,
  GLX_PBUFFER_HEIGHT, 1024,
  0
};
pbuffer = glXCreatePbuffer(display, *configs, pbattrs);

The Pbuffer attribute list consists of the GLX_PBUFFER_WIDTH and GLX_PBUFFER_HEIGHT values specifying the width and height of the Pbuffer.

After you create the Pbuffer, you call the glXCreateNewContext() function to create a context based on the framebuffer configuration for the Pbuffer, specifying a value of True for the fifth parameter for a direct rendering context:

GLXContext context;

context = glXCreateNewContext(display, *configs, GLX_RGBA_BIT, 0, False);
glXMakeCurrent(display, pbuffer, context);

You can then draw into the pixmap using OpenGL functions and read the results back using the glReadPixels() function. Listing 15.6 shows a variation of the previous sample program that creates a Pbuffer, draws a cube, reads the image using glReadPixels(), and writes the result to a PPM image file called pbuffer.ppm.

Example 15.6. Pbuffer Sample Program

/*
 * Include necessary headers...
 */

#include <stdio.h>
#include <stdlib.h>
#include <X11/Xlib.h>
#include <X11/Xatom.h>
#include <GL/glx.h>
#include <GL/gl.h>


/*
 * Globals...
 */

float           CubeRotation[3],        /* Rotation of cube */
                CubeRate[3];            /* Rotation rate of cube */
int             CubeWidth,              /* Width of window */
                CubeHeight;             /* Height of window */


/*
 * Functions...
 */

void            DisplayFunc(void);


/*
 * 'main()' - Main entry for example program.
 */

int                                     /* O - Exit status */
main(int  argc,                         /* I - Number of command-line args */
     char *argv[])                      /* I - Command-line args */
{
  GLXContext            context;        /* OpenGL context */
  Display               *display;       /* X display connection */
  GLXPbuffer            pbuffer;        /* Pbuffer */
  int                   nconfigs;       /* Number of configurations */
  GLXFBConfig           *configs;       /* GLX framebuffer configuration */
  FILE                  *fp;            /* PPM file pointer */
  int                   y;              /* Current row */
  unsigned char         pixels[3072];   /* One line of RGB pixels */
  static int            attributes[] =  /* OpenGL attributes */
                        {
                          GLX_RGBA,
                          GLX_RED_SIZE, 8,
                          GLX_GREEN_SIZE, 8,
                          GLX_BLUE_SIZE, 8,
                          GLX_DEPTH_SIZE, 16,
                          0,            /* Save space for GLX_DOUBLEBUFFER */
                          0
                        };
  static int            pbattrs[] =     /* Pbuffer attributes */
                        {
                          GLX_PBUFFER_WIDTH, 1024,
                          GLX_PBUFFER_HEIGHT, 1024,
                          0
                        };


 /*
  * Open a connection to the X server...
  */

  display = XOpenDisplay(getenv("DISPLAY"));

 /*
  * Get a matching framebuffer configuration...
  */

  configs = glXChooseFBConfig(display, DefaultScreen(display), attributes,
                              &nconfigs);
  if (!configs)
  {
    attributes[3] = GLX_DOUBLEBUFFER;
    configs       = glXChooseFBConfig(display, DefaultScreen(display),
                                      attributes, &nconfigs);
  }

  if (!configs)
  {
    puts("No OpenGL framebuffer configurations available!");
    return (1);
  }

 /*
  * Create the Pbuffer...
  */

  pbuffer = glXCreatePbuffer(display, *configs, pbattrs);

 /*
  * Create the OpenGL context...
  */

  context = glXCreateNewContext(display, *configs, GLX_RGBA_BIT, 0, True);
  glXMakeCurrent(display, pbuffer, context);

 /*
  * Setup remaining globals...
  */

  CubeWidth       = 1024;
  CubeHeight      = 1024;
  CubeRotation[0] = 45.0f;
  CubeRotation[1] = 45.0f;
  CubeRotation[2] = 45.0f;
  CubeRate[0]     = 1.0f;
  CubeRate[1]     = 1.0f;
  CubeRate[2]     = 1.0f;

 /*
  * Draw a cube...
  */

  DisplayFunc();

 /*
  * Read back the RGB pixels and write the result as a PPM file.
  */

  if ((fp = fopen("pbuffer.ppm", "wb")) == NULL)
    perror("Unable to create pbuffer.ppm");
  else
  {
   /*
    * Write a PPM image from top to bottom...
    */

    fputs("P6
1024 1024 255
", fp);

    for (y = 1023; y >= 0; y --)
    {
      glReadPixels(0, y, 1024, 1, GL_RGB, GL_UNSIGNED_BYTE, pixels);
      fwrite(pixels, 1024, 3, fp);
    }

    fclose(fp);
  }

 /*
  * Destroy all resources we used and close the display...
  */

  glXDestroyContext(display, context);
  glXDestroyPbuffer(display, pbuffer);
  XCloseDisplay(display);

  return (0);
}


/*
 * 'DisplayFunc()' - Draw a cube.
 */

void
DisplayFunc(void)
{
  int                   i, j;           /* Looping vars */
  static const GLfloat  corners[8][3] = /* Corner vertices */
                        {
                          {  1.0f,  1.0f,  1.0f },      /* Front top right */
                          {  1.0f, -1.0f,  1.0f },      /* Front bottom right */
                          { -1.0f, -1.0f,  1.0f },      /* Front bottom left */
                          { -1.0f,  1.0f,  1.0f },      /* Front top left */
                          {  1.0f,  1.0f, -1.0f },      /* Back top right */
                          {  1.0f, -1.0f, -1.0f },      /* Back bottom right */
                          { -1.0f, -1.0f, -1.0f },      /* Back bottom left */
                          { -1.0f,  1.0f, -1.0f }       /* Back top left */
                        };
  static const int      sides[6][4] =   /* Sides */
                        {
                          { 0, 1, 2, 3 },               /* Front */
                          { 4, 5, 6, 7 },               /* Back */
                          { 0, 1, 5, 4 },               /* Right */
                          { 2, 3, 7, 6 },               /* Left */
                          { 0, 3, 7, 4 },               /* Top */
                          { 1, 2, 6, 5 }                /* Bottom */
                        };
  static const GLfloat  colors[6][3] =  /* Colors */
                        {
                          { 1.0f, 0.0f, 0.0f },         /* Red */
                          { 0.0f, 1.0f, 0.0f },         /* Green */
                          { 1.0f, 1.0f, 0.0f },         /* Yellow */
                          { 0.0f, 0.0f, 1.0f },         /* Blue */
                          { 1.0f, 0.0f, 1.0f },         /* Magenta */
                          { 0.0f, 1.0f, 1.0f }          /* Cyan */
                        };


 /*
  * Clear the window...
  */

  glViewport(0, 0, CubeWidth, CubeHeight);
  glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
  glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

 /*
  * Setup the matrices...
  */

  glMatrixMode(GL_PROJECTION);
  glLoadIdentity();
  glOrtho(-2.0f, 2.0f,
          -2.0f * CubeHeight / CubeWidth, 2.0f * CubeHeight / CubeWidth,
          -2.0f, 2.0f);

  glMatrixMode(GL_MODELVIEW);
  glLoadIdentity();
  glRotatef(CubeRotation[0], 1.0f, 0.0f, 0.0f);
  glRotatef(CubeRotation[1], 0.0f, 1.0f, 0.0f);
  glRotatef(CubeRotation[2], 0.0f, 0.0f, 1.0f);

 /*
  * Draw the cube...
  */

  glEnable(GL_DEPTH_TEST);

  glBegin(GL_QUADS);

  for (i = 0; i < 6; i ++)
  {
    glColor3fv(colors[i]);
    for (j = 0; j < 4; j ++)
      glVertex3fv(corners[sides[i][j]]);
  }

  glEnd();
}

Using the Motif Library

The Motif library is one of the older toolkits used on UNIX/Linux and is still the standard used for applications developed solely for commercial versions of UNIX. Motif is based on the X Intrinsics (Xt) library, which provides the core support for several other toolkits such as the Athena toolkit (Xaw), the 3D Athena toolkit (Xaw3d), and the neXtaW toolkit, which provides a NextStep look and feel.

Two OpenGL widgets are available: one for generic Xt-based toolkits called GLwDrawingArea and one specifically integrated with Motif called GLwMDrawingArea. Both are functionally equivalent, so we will create an example based on the Motif toolkit and GLwMDrawingArea widget.

The OpenGL widgets are provided in a separate library called GLw. You include them in your application by using the -lGLw linker option. A typical link command for a Motif-based OpenGL application looks like the following:

gcc -o myprogram myprogram.o -lGLw -lGL -lXm -lXt -lXext -lX11

GLwDrawingArea and GLwMDrawingArea: The OpenGL Widgets

Like most Motif and Xt widgets, the OpenGL widgets use abbreviated header filenames. The include file for the generic GlwDrawingArea widget is <GL/GLwDrawA.h>, and the Motif widget is <GL/GLwMDrawA.h>. After you include the appropriate header file, you use the XtVaCreateManagedWidget function to create the OpenGL widget, as follows:

widget = XtVaCreateManagedWidget(
             "name",
             glwMDrawingAreaWidgetClass,
             parent,
             GLwNrgba, True,
             GLwNdoublebuffer, True,
             GLwNdepthSize, 16,
             ... other resource arguments as needed ...
             NULL);

The first argument is the resource name of the widget; it can be used to associate additional X resources with the widget, including the OpenGL resources.

The second argument specifies the widget class; glwMDrawingAreaWidgetClass specifies the Motif OpenGL widget. You use glwDrawingAreaWidgetClass for the generic Xt OpenGL widget.

The third argument specifies the parent widget, which is normally a manager widget like XmForm.

The remaining arguments specify widget resources in name/value pairs along with a trailing NULL pointer to specify the end of the resource argument list. Normally, Motif and Xt applications use hard-coded resources for widget configuration and resource files and fallback resources for labels and basic look-and-feel preferences. In this case, we specify the OpenGL visual attributes so that the correct visual will be used for double-buffered RGB (TrueColor) drawing. Table 15.2 lists the available OpenGL widget resources. Most directly correspond to the GLX attributes and are used to construct a GLX attribute list. The GLwNattribList resource specifies the GLX attribute list directly.

Table 15.2. OpenGL Widget Resources

Resource Name

Resource Type

Corresponding GLX Attribute from Table 15.1

GLwNaccumAlphaSize

int

GLX_ACCUM_ALPHA_SIZE

GLwNaccumBlueSize

int

GLX_ACCUM_BLUE_SIZE

GLwNaccumGreenSize

int

GLX_ACCUM_GREEN_SIZE

GLwNaccumRedSize

int

GLX_ACCUM_RED_SIZE

GLwNalphaSize

int

GLX_ALPHA_SIZE

GLwNattribList

int *

None; specifies the GLX attribute list directly

GLwNauxBuffers

boolean

GLX_AUX_BUFFERS

GLwNblueSize

int

GLX_BLUE_SIZE

GLwNbufferSize

int

GLX_BUFFER_SIZE

GLwNdepthSize

int

GLX_DEPTH_SIZE

GLwNdoublebuffer

boolean

GLX_DOUBLEBUFFER

GLwNgreenSize

int

GLX_GREEN_SIZE

GLwNlevel

int

GLX_LEVEL

GLwNredSize

int

GLX_RED_SIZE

GLwNrgba

boolean

GLX_RGBA

GLwNstencilSize

int

GLX_STENCIL_SIZE

GLwNstereo

boolean

GLX_STEREO

Callbacks

After you create the widget, you must associate several callbacks with one or more callback functions in your application. Table 15.3 lists the callbacks that the OpenGL widgets define.

Table 15.3. OpenGL Widget Callback Resources

Resource Name

Description

GLwNexposeCallback

The redraw callback

GLwNginitCallback

The initialization callback

GLwNinputCallback

The callback for mouse and keyboard input

GLwNresizeCallback

The callback for widget resizes

OpenGL widget callbacks take three arguments: the widget pointer, the user data pointer, and a pointer to the GLwDrawingAreaCallbackStruct data structure. Table 15.4 shows the members of the structure. You can use the reason member of this structure to handle all types of callbacks using a single function.

Table 15.4. GLwDrawingAreaCallbackStruct Members

Name

Type

Description

event

XEvent *

The X event associated with the input or expose callback

height

Dimension

The new height of the widget for expose and resize callbacks

reason

int

The reason for the callbackGLwCR_EXPOSE, GLwCR_GINIT, GLwCR_INPUT, or GLwCR_RESIZE

width

Dimension

The new width of the widget for expose and resize callbacks

The GLwNexposeCallback Callback

The GLwNexposeCallback callback function handles redrawing the widget when the window manager reports that all or part of the widget is exposed and needs to be drawn.

Typically, this function sets the current OpenGL context and draws in the widget. The width and height members of the callback structure contain the current width and height of the widget, and the event member contains any X expose event data that can be used to see whether additional expose events follow or to limit the redraw to the area that needs it.

The GLwNginitCallback Callback

The GLwNginitCallback callback function handles any initialization of the OpenGL widget. Typically, this function creates the OpenGL context, loads textures and fonts, and initializes display lists for common display elements.

The GLwNvisualInfo resource can be queried by the callback to create the OpenGL context. The following code creates an OpenGL context using the resource value:

Widget application_shell;
Widget drawing_area;
XVisualInfo *info;
GLXcontext context;

XtVaGetValues(drawing_area, GLwNvisualInfo, &info, NULL);
context = glXCreateContext(XtDisplay(application_shell), info,
                           NULL, GL_TRUE);

This resource and the OpenGL window for the widget are not created until your callback function is called.

The GLwNinputCallback Callback

The GLwNinputCallback callback function handles user input in the form of button clicks, mouse motion, and keyboard interaction. The event member of the callback data points to the ButtonPress, ButtonRelease, MotionNotify, KeyPress, or KeyRelease event that triggered the callback.

The GLwNresizeCallback Callback

The GLwNresizeCallback callback function is called whenever the application or user resizes the OpenGL widget. The width and height members of the callback data contain the new width and height of the widget and can be used to track changes to the size of the widget. An expose callback with the same information will follow a resize callback, so many applications do not need to use the resize callback.

Functions

The GLw library provides two helper functions that work with both of the OpenGL widgets. The GLwDrawingAreaMakeCurrent() function sets the current OpenGL context for the widget and must be called before drawing to the widget:

GLwDrawingAreaMakeCurrent(drawing_area, context);

When you are done drawing in a double-buffered widget, call the GLwDrawingAreaSwapBuffers() function to swap the front and back buffers:

GLwDrawingAreaSwapBuffers(drawing_area);

Putting It All Together

Listing 15.7 shows a Motif version of the xlibfonts example presented in Listing 15.4, providing identical output. The program starts by creating an Xt “application shell,” which includes the main window. It then adds a Motif XmForm widget to manage the OpenGL widget and the OpenGL widget itself:

Widget          CubeShell;            /* Application shell */
XtAppContext    CubeContext;          /* Application context */
Widget          CubeGLArea;           /* OpenGL drawing area */
...
Widget        form;                   /* Form management widget */
XtAppContext  context;                /* Application context */
static char   *fallback[] =           /* Fallback resources */
                      {
                        "Motif.geometry: 400x400",
                        NULL
                      };


/*
 * Initialize the application window and manager widgets...
 */

CubeShell = XtVaAppInitialize(
                &CubeContext, "Motif", NULL, 0, &argc, argv,
                fallback,
                XmNtitle,    "Motif Example",
                XmNiconName, "Motif",
                NULL);
form  = XtVaCreateManagedWidget(
            "form", xmFormWidgetClass, CubeShell,
            NULL);

/*
 * Create the OpenGL drawing area...
 */

CubeGLArea = XtVaCreateManagedWidget(
                 "drawingArea", glwMDrawingAreaWidgetClass, form,
                 GLwNrgba,            True,
                 GLwNdoublebuffer,    True,
                 GLwNdepthSize,       16,
                 XmNtopAttachment,    XmATTACH_FORM,
                 XmNbottomAttachment, XmATTACH_FORM,
                 XmNleftAttachment,   XmATTACH_FORM,
                 XmNrightAttachment,  XmATTACH_FORM,
                 NULL);

The OpenGL widget is attached to the sides of the form, causing it to occupy the entire window. In a typical application with a menu bar, you would probably attach the top of the OpenGL widget to the menu bar instead.

After you create the widgets, you set the callback functions to use for the OpenGL widget:

XtAddCallback(CubeGLArea, GLwNexposeCallback,
              (XtCallbackProc)DisplayCB, NULL);
XtAddCallback(CubeGLArea, GLwNginitCallback,
              (XtCallbackProc)InitCB, NULL);
XtAddCallback(CubeGLArea, GLwNresizeCallback,
              (XtCallbackProc)ReshapeCB, NULL);
XtAddCallback(CubeGLArea, GLwNinputCallback,
              (XtCallbackProc)InputCB, NULL);

Finally, you “realize” the application shell to show the window and call the XtAppMainLoop() function to start the application event loop:

XtRealizeWidget(CubeShell);
XtAppMainLoop(CubeContext);

The callback functions use the same functions as the xlibfonts example to initialize the OpenGL context and font, draw the cube and text, and handle mouse input. A new TimeOutCB() function is used to rotate the cube once every 20 milliseconds and is registered via XtAppAddTimeOut():

void
TimeOutCB(void)
{
  CubeRotation[0] += CubeRate[0];
  CubeRotation[1] += CubeRate[1];
  CubeRotation[2] += CubeRate[2];

  if (CubeRate[0] || CubeRate[1] || CubeRate[2])
    XmRedisplayWidget(CubeGLArea);

  XtAppAddTimeOut(CubeContext, 20,
                  (XtTimerCallbackProc)TimeOutCB, NULL);
}

The XmRedisplayWidget() function tells the OpenGL widget to redraw itself and can be used by applications to update their display based on new, possibly asynchronous data or user input.

Example 15.7. The Motif Sample Source Code

/*
 * Include necessary headers...
 */

#include <stdio.h>
#include <stdlib.h>
#include <Xm/Xm.h>
#include <Xm/Form.h>
#include <GL/GLwMDrawA.h>


/*
 * Globals...
 */

float           CubeRotation[3],        /* Rotation of cube */
                CubeRate[3];            /* Rotation rate of cube */
int             CubeMouseButton,        /* Button that was pressed */
                CubeMouseX,             /* Start X position of mouse */
                CubeMouseY;             /* Start Y position of mouse */
int             CubeWidth,              /* Width of window */
                CubeHeight;             /* Height of window */
GLuint          CubeFont;               /* Display list base for font */
Widget          CubeShell;              /* Application shell */
XtAppContext    CubeContext;            /* Application context */
Widget          CubeGLArea;             /* OpenGL drawing area */
GLXContext      CubeGLContext;          /* OpenGL drawing context */


/*
 * Functions...
 */

void    DisplayCB(void);
void    InitCB(Widget w, void *ud, GLwDrawingAreaCallbackStruct *cd);
void    InputCB(Widget w, void *ud, GLwDrawingAreaCallbackStruct *cd);
void    ReshapeCB(Widget w, void *ud, GLwDrawingAreaCallbackStruct *cd);
void    TimeOutCB(void);


/*
 * 'main()' - Main entry for example program.
 */

int                                     /* O - Exit status */
main(int  argc,                         /* I - Number of command-line args */
     char *argv[])                      /* I - Command-line args */
{
  Widget        form;                   /* Form management widget */
  XtAppContext  context;                /* Application context */
  static char   *fallback[] =           /* Fallback resources */
                        {
                          "Motif.geometry: 400x400",
                          NULL
                        };


 /*
  * Initialize the application window and manager widgets...
  */

  CubeShell = XtVaAppInitialize(
                  &CubeContext, "Motif", NULL, 0, &argc, argv,
                  fallback,
                  XmNtitle,    "Motif Example",
                  XmNiconName, "Motif",
                  NULL);
  form  = XtVaCreateManagedWidget(
              "form", xmFormWidgetClass, CubeShell,
              NULL);

 /*
  * Create the OpenGL drawing area...
  */

  CubeGLArea = XtVaCreateManagedWidget(
                   "drawingArea", glwMDrawingAreaWidgetClass, form,
                   GLwNrgba,            True,
                   GLwNdoublebuffer,    True,
                   GLwNdepthSize,       16,
                   XmNtopAttachment,    XmATTACH_FORM,
                   XmNbottomAttachment, XmATTACH_FORM,
                   XmNleftAttachment,   XmATTACH_FORM,
                   XmNrightAttachment,  XmATTACH_FORM,
                   NULL);

 /*
  * Set callbacks and timeout processing...
  */

  XtAddCallback(CubeGLArea, GLwNexposeCallback,
                (XtCallbackProc)DisplayCB, NULL);
  XtAddCallback(CubeGLArea, GLwNginitCallback,
                (XtCallbackProc)InitCB, NULL);
  XtAddCallback(CubeGLArea, GLwNresizeCallback,
                (XtCallbackProc)ReshapeCB, NULL);
  XtAddCallback(CubeGLArea, GLwNinputCallback,
                (XtCallbackProc)InputCB, NULL);

 /*
  * Setup remaining globals...
  */

  CubeWidth       = 400;
  CubeHeight      = 400;
  CubeRotation[0] = 45.0f;
  CubeRotation[1] = 45.0f;
  CubeRotation[2] = 45.0f;
  CubeRate[0]     = 1.0f;
  CubeRate[1]     = 1.0f;
  CubeRate[2]     = 1.0f;

 /*
  * Loop forever...
  */

  XtRealizeWidget(CubeShell);
  XtAppMainLoop(CubeContext);

  return (0);
}


/*
 * 'DisplayCB()' - Display callback.
 */

void
DisplayCB(void)
{
  int                   i, j;           /* Looping vars */
  XVisualInfo           *info;          /* Drawing area visual */
  XFontStruct           *font;          /* X font information */
  static const GLfloat  corners[8][3] = /* Corner vertices */
                        {
                          {  1.0f,  1.0f,  1.0f },      /* Front top right */
                          {  1.0f, -1.0f,  1.0f },      /* Front bottom right */
                          { -1.0f, -1.0f,  1.0f },      /* Front bottom left */
                          { -1.0f,  1.0f,  1.0f },      /* Front top left */
                          {  1.0f,  1.0f, -1.0f },      /* Back top right */
                          {  1.0f, -1.0f, -1.0f },      /* Back bottom right */
                          { -1.0f, -1.0f, -1.0f },      /* Back bottom left */
                          { -1.0f,  1.0f, -1.0f }       /* Back top left */
                        };
  static const int      sides[6][4] =   /* Sides */
                        {
                          { 0, 1, 2, 3 },               /* Front */
                          { 4, 5, 6, 7 },               /* Back */
                          { 0, 1, 5, 4 },               /* Right */
                          { 2, 3, 7, 6 },               /* Left */
                          { 0, 3, 7, 4 },               /* Top */
                          { 1, 2, 6, 5 }                /* Bottom */
                        };
  static const GLfloat  colors[6][3] =  /* Colors */
                        {
                          { 1.0f, 0.0f, 0.0f },         /* Red */
                          { 0.0f, 1.0f, 0.0f },         /* Green */
                          { 1.0f, 1.0f, 0.0f },         /* Yellow */
                          { 0.0f, 0.0f, 1.0f },         /* Blue */
                          { 1.0f, 0.0f, 1.0f },         /* Magenta */
                          { 0.0f, 1.0f, 1.0f }          /* Cyan */
                        };


 /*
  * Clear the window...
  */

  glViewport(0, 0, CubeWidth, CubeHeight);
  glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
  glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

 /*
  * Setup the matrices...
  */

  glMatrixMode(GL_PROJECTION);
  glLoadIdentity();
  glOrtho(-2.0f, 2.0f,
          -2.0f * CubeHeight / CubeWidth, 2.0f * CubeHeight / CubeWidth,
          -2.0f, 2.0f);

  glMatrixMode(GL_MODELVIEW);
  glLoadIdentity();
  glRotatef(CubeRotation[0], 1.0f, 0.0f, 0.0f);
  glRotatef(CubeRotation[1], 0.0f, 1.0f, 0.0f);
  glRotatef(CubeRotation[2], 0.0f, 0.0f, 1.0f);

 /*
  * Draw the cube...
  */

  glEnable(GL_DEPTH_TEST);

  glBegin(GL_QUADS);

  for (i = 0; i < 6; i ++)
  {
    glColor3fv(colors[i]);
    for (j = 0; j < 4; j ++)
      glVertex3fv(corners[sides[i][j]]);
  }

  glEnd();

 /*
  * Draw lines coming out of the cube...
  */

  glColor3f(1.0f, 1.0f, 1.0f);

  glBegin(GL_LINES);
    glVertex3f(0.0f, 0.0f, -1.5f);
    glVertex3f(0.0f, 0.0f, 1.5f);

    glVertex3f(-1.5f, 0.0f, 0.0f);
    glVertex3f(1.5f, 0.0f, 0.0f);

    glVertex3f(0.0f, 1.5f, 0.0f);
    glVertex3f(0.0f, -1.5f, 0.0f);
  glEnd();

 /*
  * Draw text for each side...
  */

  glPushAttrib(GL_LIST_BIT);
    glListBase(CubeFont - ' '),

    glRasterPos3f(0.0f, 0.0f, -1.5f);
    glCallLists(4, GL_BYTE, "Back");

    glRasterPos3f(0.0f, 0.0f, 1.5f);
    glCallLists(5, GL_BYTE, "Front");

    glRasterPos3f(-1.5f, 0.0f, 0.0f);
    glCallLists(4, GL_BYTE, "Left");

    glRasterPos3f(1.5f, 0.0f, 0.0f);
    glCallLists(5, GL_BYTE, "Right");

    glRasterPos3f(0.0f, 1.5f, 0.0f);
    glCallLists(3, GL_BYTE, "Top");

    glRasterPos3f(0.0f, -1.5f, 0.0f);
    glCallLists(6, GL_BYTE, "Bottom");
  glPopAttrib();

 /*
  * Swap the back buffer with the front buffer...
  */

  GLwDrawingAreaSwapBuffers(CubeGLArea);
}


/*
 * 'InitCB()' - Initialization callback.
 */

void
InitCB(Widget                       w,  /* I - Widget */
       void                         *ud,/* I - User data */
       GLwDrawingAreaCallbackStruct *cd)/* I - Callback data */
{
  XVisualInfo           *info;          /* Drawing area visual */
  XFontStruct           *font;          /* X font information */


 /*
  * Initialize the OpenGL context for drawing...
  */

  XtVaGetValues(CubeGLArea, GLwNvisualInfo, &info, NULL);
  CubeGLContext = glXCreateContext(XtDisplay(CubeShell), info,
                                   NULL, GL_TRUE);

  if (!CubeGLContext)
  {
    puts("Unable to create OpenGL context!");
    exit(1);
  }

  GLwDrawingAreaMakeCurrent(CubeGLArea, CubeGLContext);

 /*
  * Setup font...
  */

  font     = XLoadQueryFont(XtDisplay(CubeShell),
                            "-*-courier-bold-r-normal--14-*-*-*-*-*-*-*");
  CubeFont = glGenLists(96);

  glXUseXFont(font->fid, ' ', 96, CubeFont);

 /*
  * Activate the timeout procedure to rotate the cube...
  */

  XtAppAddTimeOut(CubeContext, 20,
                  (XtTimerCallbackProc)TimeOutCB, NULL);
}


/*
 * 'InputCB()' - Input callback.
 */

void
InputCB(Widget                       w, /* I - Widget */
        void                         *ud,
                                        /* I - User data */
        GLwDrawingAreaCallbackStruct *cd)
                                        /* I - Callback data */
{
  int   x,                              /* X position */
        y;                              /* Y position */


  switch (cd->event->type)
  {
    case ButtonPress :
       /*
        * Save the initial mouse button + position...
        */

        CubeMouseButton = cd->event->xbutton.button;
        CubeMouseX      = cd->event->xbutton.x;
        CubeMouseY      = cd->event->xbutton.y;

       /*
        * Zero-out the rotation rates...
        */

        CubeRate[0] = 0.0f;
        CubeRate[1] = 0.0f;
        CubeRate[2] = 0.0f;
        break;

    case MotionNotify :
       /*
        * Get the mouse movement...
        */

        x = cd->event->xmotion.x - CubeMouseX;
        y = cd->event->xmotion.y - CubeMouseY;

       /*
        * Update the cube rotation rate based upon the mouse
        * movement and button...
        */

        switch (CubeMouseButton)
        {
          case 0 :                      /* Button 1 */
              CubeRate[0] = 0.01f * y;
              CubeRate[1] = 0.01f * x;
              CubeRate[2] = 0.0f;
              break;

          case 1 :                      /* Button 2 */
              CubeRate[0] = 0.0f;
              CubeRate[1] = 0.01f * y;
              CubeRate[2] = 0.01f * x;
              break;

          default :                     /* Button 3 */
              CubeRate[0] = 0.01f * y;
              CubeRate[1] = 0.0f;
              CubeRate[2] = 0.01f * x;
              break;
        }
        break;
  }
}


/*
 * 'ReshapeCB()' - Resize callback.
 */

void
ReshapeCB(Widget                       w,
                                        /* I - Widget */
          void                         *ud,
                                        /* I - User data */
          GLwDrawingAreaCallbackStruct *cd)
                                        /* I - Callback data */
{
 /*
  * Save the current width and height...
  */

  CubeWidth  = cd->width;
  CubeHeight = cd->height;
}


/*
 * 'TimeOutCB()' - Rotate and redraw the cube.
 */

void
TimeOutCB(void)
{
  CubeRotation[0] += CubeRate[0];
  CubeRotation[1] += CubeRate[1];
  CubeRotation[2] += CubeRate[2];

  if (CubeRate[0] || CubeRate[1] || CubeRate[2])
    XmRedisplayWidget(CubeGLArea);

  XtAppAddTimeOut(CubeContext, 20,
                  (XtTimerCallbackProc)TimeOutCB, NULL);
}

Summary

In this chapter, we have taken the basic OpenGL principles and extended them for use in a Linux environment. You have leaned how to set up an appropriate visual for an application and how rendering contexts are handled in the Linux environment. You now also know how to deal with double buffered contexts. You learned how to generate and use bitmap fonts. Finally, we introduced using pBuffers for offscreen rendering on Linux.

Reference

glXChooseFBConfig

Purpose:

Gets a list of matching framebuffer configurations.

Include File:

<GL/glx.h>

Syntax:

GLXFBConfig *glXChooseFBConfig(Display *dpy, int 
ReferencePbuffersframebuffer configurationbuffersPbuffersframebuffer configurationglXChooseFBConfig functionfunctionsglXChooseFBConfigscreen, const int *attribList, int 
ReferencePbuffersframebuffer configurationbuffersPbuffersframebuffer configurationglXChooseFBConfig functionfunctionsglXChooseFBConfig*nelements);

Description:

This function finds a list of framebuffer configurations that match the specified GLX attributes.

Parameters:

*dpy

The X display connection

screen

The screen to query

*attribList

The NULL-terminated list of GLX attributes

*nelements

Pointer to an integer that will hold the number of framebuffer configurations that are pointed to

Returns:

A pointer to an array of matching framebuffer configurations or NULL if the X display does not support the framebuffer query. Use the XFree() function to free the memory used for the array.

See Also:

glXGetFBConfigs, glXGetVisualFromFBConfig, glXCreatePbuffer

glXChooseVisual

Purpose:

Selects an X visual to use for OpenGL rendering.

Include File:

<GL/glx.h>

Syntax:

XVisualInfo *glXChooseVisual(Display *dpy, int 
ReferencePbuffersframebuffer configurationbuffersPbuffersframebuffer configurationglXChooseFBConfig functionfunctionsglXChooseFBConfigscreen, int *attribList);

Description:

This function finds a visual matching the specified GLX rendering attributes that can be used to create a window or pixmap for OpenGL rendering.

Parameters:

*dpy

The X display connection

screen

The screen number

*attribList

The zero-terminated attribute list

Returns:

A pointer to a matching X visual information structure or NULL.

See Also:

glXCreateContext, glXCreateGLXPixmap

glXCreateContext

Purpose:

Creates an OpenGL drawing context.

Include File:

<GL/glx.h>

Syntax:

GLXContext glXCreateContext(Display *dpy, 
ReferencePbuffersframebuffer configurationbuffersPbuffersframebuffer configurationglXChooseFBConfig functionfunctionsglXChooseFBConfigXVisualInfo *vis, GLXContext shareList, Bool direct);

Description:

This function creates a context for OpenGL rendering. The context can be direct-to-hardware or indirect and can share the display lists, textures, and so forth with other OpenGL contexts of the same type.

Parameters:

*dpy

The X display connection

*vis

The X visual to use

shareList

An OpenGL context for sharing display lists, textures, and so on

direct

True if a direct-to-hardware context is desired; False otherwise

Returns:

A new OpenGL context or NULL if the context cannot be created.

See Also:

glXChooseVisual, glXCreateNewContext

glXCreateGLXPixmap

Purpose:

Creates an offscreen pixmap for OpenGL rendering.

Include File:

<GL/glx.h>

Syntax:

GLXPixmap glXCreateGLXPixmap(Display *dpy, 
ReferencePbuffersframebuffer configurationbuffersPbuffersframebuffer configurationglXChooseFBConfig functionfunctionsglXChooseFBConfigXVisualInfo *vis, Pixmap pixmap);

Description:

This function creates a GLX pixmap that can be used to do offscreen rendering of OpenGL scenes. Only indirect rendering contexts may be used with GLX pixmaps.

Parameters:

*dpy

The X display connection

*vis

The X visual to use

pixmap

The X pixmap to use

Returns:

The new GLX pixmap.

See Also:

glXChooseVisual, glXCreateContext

glXCreateNewContext

Purpose:

Creates a new OpenGL context.

Include File:

<GL/glx.h>

Syntax:

GLXContext glXCreateNewContext(Display *dpy, 
ReferencePbuffersframebuffer configurationbuffersPbuffersframebuffer configurationglXChooseFBConfig functionfunctionsglXChooseFBConfigGLXFBConfig config, int renderType, GLXContext 
ReferencePbuffersframebuffer configurationbuffersPbuffersframebuffer configurationglXChooseFBConfig functionfunctionsglXChooseFBConfigshareList, Bool direct);

Description:

This function creates a context for OpenGL rendering and is functionally equivalent to glXCreateContext(). The context can be direct-to-hardware or indirect and can share the display lists, textures, and so forth with other OpenGL contexts of the same type.

Parameters:

Display *dpy

The X display connection

config

The framebuffer configuration to use

renderType

The color type of the contextGLX_RGBA_TYPE for RGBA rendering or GLX_COLOR_INDEX_TYPE for color indexed rendering

shareList

An OpenGL context for sharing display lists, textures, and so on

direct

True if a direct-to-hardware context is desired; False otherwise

Returns:

A new OpenGL context or NULL if the context cannot be created.

See Also:

glXChooseFBConfig, glXCreateContext, glXDestroyContext, glXGetFBConfigs

glXCreatePbuffer

Purpose:

Creates an offscreen pixel buffer for OpenGL rendering.

Include File:

<GL/glx.h>

Syntax:

GLXPbuffer glXCreatePbuffer(Display *dpy, 
The color type of the context:GLXFBCOnfig config, const int  *attribList);

Description:

This function creates an offscreen pixel buffer for OpenGL rendering. The dimensions of the Pbuffer are specified using the GLX_WIDTH and GLX_HEIGHT attributes.

Parameters:

*dpy

The X display connection

config

The framebuffer configuration

*attribList

The zero-terminated list of GLX attributes

Returns:

A new Pbuffer or NULL if it could not be created.

See Also:

glXGetFBConfigs, glXChooseFBConfigs, glXDestroyPbuffer

glXDestroyContext

Purpose:

Destroys an OpenGL context.

Include File:

<GL/glx.h>

Syntax:

void glXDestroyContext(Display *dpy, GLXContext ctx);

Description:

This function destroys an OpenGL rendering context, freeing any system resources associated with it.

Parameters:

*dpy

The X display connection

ctx

The OpenGL context

Returns:

Nothing.

See Also:

glXCreateContext

glXDestroyGLXPixmap

Purpose:

Destroys a GLX pixmap.

Include File:

<GL/glx.h>

Syntax:

void glXDestroyGLXPixmap(Display *dpy, GLXPixmap pix);

Description:

This function destroys a GLX pixmap resource. You must still destroy the X pixmap resource and OpenGL context separately.

Parameters:

*dpy

The X display connection

pix

The GLX pixmap

Returns:

Nothing.

See Also:

glXCreateGLXPixmap

glXDestroyPbuffer

Purpose:

Destroys an offscreen pixel buffer.

Include File:

<GL/glx.h>

Syntax:

void glXDestroyPbuffer(Display *dpy, GLXPbuffer pbuf);

Description:

This function releases all resources used for the specified Pbuffer.

Parameters:

*dpy

The X display connection

pbuf

The Pbuffer

Returns:

Nothing.

See Also:

glXCreatePbuffer

glXGetFBConfigs

Purpose:

Gets a list of supported framebuffer configurations.

Include File:

<GL/glx.h>

Syntax:

GLXFBConfig *glXGetFBConfigs(Display *dpy, int 
The color type of the context:screen, int *nelements);

Description:

This function gets a list of supported framebuffer configurations for the specified display and screen.

Parameters:

*dpy

The X display connection

screen

The screen to query

*nelements

Pointer to an integer that will hold the number of framebuffer configurations that are pointed to

Returns:

A pointer to an array of framebuffer configurations or NULL if the X display does not support the framebuffer query. Use the XFree() function to free the memory used for the array.

See Also:

glXChooseFBCOnfig, glXGetVisualFromFBConfig, glXCreatePbuffer

glXGetVisualFromFBConfig

Purpose:

Gets the X visual for a specific framebuffer configuration.

Include File:

<GL/glx.h>

Syntax:

XVisualInfo *glXGetVisualFromFBConfig(Display *dpy
The color type of the context:, GLXFBConfig config);

Description:

This function finds the X visual that corresponds to the given framebuffer configuration.

Parameters:

*dpy

The X display connection

config

The framebuffer configuration

Returns:

A pointer to the XVisualInfo structure containing all the X visual information for the given framebuffer configuration.

See Also:

glXGetFBConfigs, glXChooseFBConfigs, glXCreatePbuffer

glXMakeCurrent

Purpose:

Sets the current OpenGL context for rendering.

Include File:

<GL/glx.h>

Syntax:

Bool glXMakeCurrent(Display *dpy, GLXDrawable 
The color type of the context:drawable, GLXContext ctx);

Description:

This function sets the current OpenGL context and drawable (window or pixmap) to use when rendering. If there are any pending OpenGL drawing commands on the previous context, they are flushed prior to changing the current context. Pass None for the drawable and NULL for the context arguments to flush pending OpenGL commands and release the current context.

Parameters:

*dpy

The X display connection

ctx

The OpenGL context

drawable

The window or pixmap

Returns:

True if the context is set successfully; False otherwise.

See Also:

glXCreateContext, glXCreateNewContext, glXSwapBuffers

glXSwapBuffers

Purpose:

Swaps the front and back display buffers.

Include File:

<GL/glx.h>

Syntax:

void glXSwapBuffers(Display *dpy, GLXDrawable 
The color type of the context:drawable);

Description:

This function swaps the back buffer with the front buffer, synchronizing with the vertical retrace of the screen as necessary.

Parameters:

*dpy

The X display connection

drawable

The window or pixmap

Returns:

Nothing.

See Also:

glXCreateContext, glXCreateNewContext, glXMakeCurrent

glXUseXFont

Purpose:

Creates a collection of bitmap display lists.

Include File:

<GL/glx.h>

Syntax:

void glXUseXFont(Font font, int first, int count, 
The color type of the context:int listbase);

Description:

This function creates count display lists containing bitmaps of characters in the specified font. You allocate the display lists for the bitmaps using the glGenLists() function.

Parameters:

font

Specifies the font to use

first

Specifies the first character in the font to use

count

Specifies the number of characters to use from the font

listbase

Specifies the first display list to use as returned by glGenLists()

Returns:

Nothing.

See Also:

glXCreateContext

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset