by Nick Haemel
WHAT YOU'LL LEARN IN THIS CHAPTER:
How To | Functions You'll Use |
---|---|
Choose appropriate visuals for |
|
Manage OpenGL drawing contexts |
|
Create OpenGL windows |
|
Do double-buffered drawing |
|
Create bitmap text fonts |
|
Do offscreen drawing |
|
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.
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.
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.
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.
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@
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:
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:
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:
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, 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.
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.
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
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.
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);
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.
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.
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 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; }
GLX supports two types of offscreen rendering, each with its own advantage: GLX pixmaps and Pbuffers.
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(); }
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(); }
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
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 |
---|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| None; specifies the GLX attribute list directly |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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 |
---|---|
| The redraw callback |
| The initialization callback |
| The callback for mouse and keyboard input |
| 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 |
---|---|---|
|
| The X event associated with the input or expose callback |
|
| The new height of the widget for expose and resize callbacks |
|
| The reason for the 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 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 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 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.
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);
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); }
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.
glXChooseFBConfig | |
---|---|
Purpose: | Gets a list of matching framebuffer configurations. |
Include File: |
|
Syntax: | |
GLXFBConfig *glXChooseFBConfig(Display *dpy, int screen, const int *attribList, int *nelements); | |
Description: | This function finds a list of framebuffer configurations that match the specified GLX attributes. |
Parameters: | |
| The X display connection |
| The screen to query |
| The NULL-terminated list of GLX attributes |
| 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 |
See Also: |
|
glXChooseVisual | |
---|---|
Purpose: | Selects an X visual to use for OpenGL rendering. |
Include File: |
|
Syntax: | |
XVisualInfo *glXChooseVisual(Display *dpy, int screen, 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: | |
| The X display connection |
| The screen number |
| The zero-terminated attribute list |
Returns: | A pointer to a matching X visual information structure or |
See Also: |
|
glXCreateContext | |
---|---|
Purpose: | Creates an OpenGL drawing context. |
Include File: |
|
Syntax: | |
GLXContext glXCreateContext(Display *dpy, XVisualInfo *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: | |
| The X display connection |
| The X visual to use |
| An OpenGL context for sharing display lists, textures, and so on |
|
|
Returns: | A new OpenGL context or |
See Also: |
|
glXCreateGLXPixmap | |
---|---|
Purpose: | Creates an offscreen pixmap for OpenGL rendering. |
Include File: |
|
Syntax: | |
GLXPixmap glXCreateGLXPixmap(Display *dpy, XVisualInfo *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: | |
| The X display connection |
| The X visual to use |
| The X pixmap to use |
Returns: | The new GLX pixmap. |
See Also: |
|
glXCreateNewContext | |
---|---|
Purpose: | Creates a new OpenGL context. |
Include File: |
|
Syntax: | |
GLXContext glXCreateNewContext(Display *dpy, GLXFBConfig config, int renderType, GLXContext shareList, Bool direct); | |
Description: | This function creates a context for OpenGL rendering and is functionally equivalent to |
Parameters: | |
| The X display connection |
| The framebuffer configuration to use |
| The color type of the context: |
| An OpenGL context for sharing display lists, textures, and so on |
|
|
Returns: | A new OpenGL context or |
See Also: |
|
glXCreatePbuffer | |
---|---|
Purpose: | Creates an offscreen pixel buffer for OpenGL rendering. |
Include File: |
|
Syntax: | |
GLXPbuffer glXCreatePbuffer(Display *dpy, 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 |
Parameters: | |
| The X display connection |
| The framebuffer configuration |
| The zero-terminated list of GLX attributes |
Returns: | A new Pbuffer or |
See Also: |
|
glXDestroyContext | |
---|---|
Purpose: | Destroys an OpenGL context. |
Include File: |
|
Syntax: | |
void glXDestroyContext(Display *dpy, GLXContext ctx); | |
Description: | This function destroys an OpenGL rendering context, freeing any system resources associated with it. |
Parameters: | |
| The X display connection |
| The OpenGL context |
Returns: | Nothing. |
See Also: |
|
glXDestroyGLXPixmap | |
---|---|
Purpose: | Destroys a GLX pixmap. |
Include File: |
|
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: | |
| The X display connection |
| The GLX pixmap |
Returns: | Nothing. |
See Also: |
|
glXDestroyPbuffer | |
---|---|
Purpose: | Destroys an offscreen pixel buffer. |
Include File: |
|
Syntax: | |
void glXDestroyPbuffer(Display *dpy, GLXPbuffer pbuf); | |
Description: | This function releases all resources used for the specified Pbuffer. |
Parameters: | |
| The X display connection |
| The Pbuffer |
Returns: | Nothing. |
See Also: |
|
glXGetFBConfigs | |
---|---|
Purpose: | Gets a list of supported framebuffer configurations. |
Include File: |
|
Syntax: | |
GLXFBConfig *glXGetFBConfigs(Display *dpy, int screen, int *nelements); | |
Description: | This function gets a list of supported framebuffer configurations for the specified display and screen. |
Parameters: | |
| The X display connection |
| The screen to query |
| 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 |
See Also: |
|
glXGetVisualFromFBConfig | |
---|---|
Purpose: | Gets the X visual for a specific framebuffer configuration. |
Include File: |
|
Syntax: | |
XVisualInfo *glXGetVisualFromFBConfig(Display *dpy , GLXFBConfig config); | |
Description: | This function finds the X visual that corresponds to the given framebuffer configuration. |
Parameters: | |
| The X display connection |
| The framebuffer configuration |
Returns: | A pointer to the |
See Also: |
|
glXMakeCurrent | |
---|---|
Purpose: | Sets the current OpenGL context for rendering. |
Include File: |
|
Syntax: | |
Bool glXMakeCurrent(Display *dpy, GLXDrawable 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 |
Parameters: | |
| The X display connection |
| The OpenGL context |
| The window or pixmap |
Returns: |
|
See Also: |
|
glXSwapBuffers | |
---|---|
Purpose: | Swaps the front and back display buffers. |
Include File: |
|
Syntax: | |
void glXSwapBuffers(Display *dpy, GLXDrawable drawable); | |
Description: | This function swaps the back buffer with the front buffer, synchronizing with the vertical retrace of the screen as necessary. |
Parameters: | |
| The X display connection |
| The window or pixmap |
Returns: | Nothing. |
See Also: |
|
glXUseXFont | |
---|---|
Purpose: | Creates a collection of bitmap display lists. |
Include File: |
|
Syntax: | |
void glXUseXFont(Font font, int first, int count, int listbase); | |
Description: | This function creates |
Parameters: | |
| Specifies the font to use |
| Specifies the first character in the font to use |
| Specifies the number of characters to use from the font |
| Specifies the first display list to use as returned by |
Returns: | Nothing. |
See Also: |
|