by Miguel A Sepulveda
About the author:
Graduated from the University of Washington (USA) in 1993. Work in scientific research in Spain, Israel, Japan and the US. Discovered Linux for the first time around kernel 0.98 (Love at first sight). Now is Editor-in-Chief of the LinuxFocus magazine in his free time.
GLUT Programming: Windows Management
This article explains the usage of the GLUT windows management
This article continues our series on GLUT, the GL Utility Toolkit written by Mark Kilgard for OpenGL applications. As we mentioned in our previous article (Windows and Animations) GLUT is a very interesting and useful toolkit for any OpenGL developer because it lets you write portable code. GLUT hides the developer from the dirty details of the underlying windows manager and GUI interface.
GLUT is divided into a number of subAPIs. In this month's issue we will describe the Windows Management subAPI. As its name indicates, it takes care of tasks related to the windows used by your OpenGL application: creating, destroying, iconifying a window; pushing, popping, hiding, moving; and setting titles, positions, etc..
Here is a full list of the functions supported by the windows management in GLUT (As of version 3.6):
|int glutCreateWindow(char *name)||Creates a new top-level window|
|int glutCreateSubWindow(int win,
int x, int y, int width, int height)
|Creates a sub-window|
|void glutSetWindow(int winId)||Set the window with ID winId as current window|
|int glutGetWindow(void)||Requests the identifier for the current window|
|void glutDestroyWindow(int winId)||Destroys the window specified by winId|
|void glutPostRedisplay(void)||Tells the GLUT event processor that the current window needs to be redisplayed|
|void glutSwapBuffers(void)||Swaps the buffers for the current window|
|void glutPositionWindow(int x, int y)||Requests a change in the position of the window|
|void glutReshapeWindow(int width, int height)||Requests a change in the size of the window|
|void glutFullScreen()||Requests that the current window be made full screen|
|Push or Pop the current window relative to others in the stack|
|Show, hide or iconify the current window|
|void glutSetWindowTitle(char *name)
void glutSetIconTitle(char *name)
|Set title bar in window or iconified window|
The use of most of the functions above is very simple. Some were reviewed in our first article: glutPostRedisplay, glutCreateWindow, glutPositionWindow, glutSwapBuffers,..etc. Others although new are trivial to use and the above descriptions pretty much say what they do, like glutSetIconTitle, glutFullScreen, ...etc. However the use of sub-Windows is not that simple so we decided to give you next a simple example and discuss the details of its implementation.
Here is the source code for a little OpenGL-GLUT demo (example1.c, Makefile). Its purpose is to show you: (a) How to handle a subwindow, (b) How to use your keyboard to interact with your OpenGL application (c) How to render text in an OpenGL window.(Please print out or have the source code for ../../common/March1998/example1.c at hand while we follow the discussion.)
First let us take a look at the main() function. It starts as any other GLUT application with initializations: parsing the command line options, selecting the display mode and setting the position and size of the initial window. Since our application is going to handle more than one window it is necessary to store the integer window ID returned by the glutCreateWindow statement. The variable winIdMain is a window handle. Then it is time to set up the callback functions for the events associated window winIdMain; several callback functions are defined and all perform a task on the main window: a display function (mainDisplay) that draws the scene, a reshape function (mainReshape) that handles any transformation of the window frame, keyboard that handles the actions triggered by the keyboard and idle for managing the animation when no other events are pending (see Windows and Animations for a more detailed description of idle's role);
The first important thing to know is that in a GLUT application there can only be one idle callback function. The idle function is global for all the windows in the application. So take this into account when designing your idle() functions, they must take care of refreshing all windows and subwindows in your application.
Next in the code comes the creation a subwindow (winIDSub). To make a sub-window you must provide the ID for the top level window, in the present case winIDMain, the x and y coordinates in pixels for the subwindow relative to the internal coordinates of winIDMain, and the width and height in pixels of the requested sub-window. After creating the subwindow GLUT returns a handle to our program and we are ready to set up the appropriate callback functions for winIDSub. In our demo we set two callback functions: a display (subDisplay) and a reshape (subReshape).
When GLUT opens a subwindow it provides it with a full OpenGL context. There is then a performance penalty on using subwindows because the driver of the graphics card has to refresh the memory area for each of the windows on separate passes. Thanks to having independent OpenGL contexts, each window has its own system of coordinates, for example. In ../../common/March1998/example1.c the coordinate systems are set in mainDisplay() and subDisplay() respectively. Now go and examine these two display functions, they are quite simple and if you followed our January article on OpenGL ("Simple Polygon Rendering") you will have no trouble understanding it.
The function mainDisplay() draws a triangle with Red, Green and Blue vertices. OpenGL interpolates colors between the three vertices to fill the polygon. Before rendering the triangle we have added a glRotate statement that rotates the triangle around the z-axis (perpendicular to the window screen), the angle of rotation (spin) is slowly increased in idle() to give the illusion of a rotating figure.
As for the display function associated with winIdSub it is also very straight forward. First it clears the background with a grey color, then it draws a green border around the subwindow and finally it renders some text. Later we will explain how text rendering works under GLUT. For the moment just notice that glRasterPos2f(x, y) sets the position where text will be drawn, also notice that the x, y coordinates used are relative to the coordinate system of the sub-window (defined in subReshape()).
The subwindow is acting as a text board for data coming out of the animation. It is a silly application; we could have just drawn the text board on the main window and achieved the same result (even more efficiently). Under certain circumstances however it makes sense to open a subwindow for text output. For example, when the animation is in 3D with lights and environmental effects and you do not wish to deform your text board with annoying lights, perspective effects, shadows or fog etc. Under these circumstances a subwindow comes handy because it is completely isolated from the 3D animation.
There is crucial difference beteween the reshape callback function for a top level window and for a subwindow. When a reshape event is triggered, only the top level window reshape callback function gets invoked, in our example mainReshape(). We must call the reshape callback function subReshape from within mainReshape. It clearly makes sense because the location and shape of the subwindows is constrained to the size and shape of its top level window. So if you read now the code for mainReshape() you will see that we first set the projection matrix for the top level window, then switch to the winIDsub subwindow and subsequently invoke the subwindow's reshape function with the width and height relative to winIDMain we wish to use.
Earlier it was already mentioned that the idle() callback function must update all the top level and subwindows in the OpenGL application. In our example, idle() first updates the animation state variables (time and spin) and then it requests both main and subwindow to be redisplayed.
I have added two active keys to the program. By pushing the "i"-key you can switch on or off the text board and with the "q"-key you quit the application. Try them :)
Any time you strike a key in your keyboard, GLUT's event processing driver registers a keyboard event. These events are handled by the keyboard callback functions. In principle every window has its own callback function. When the mouse is in location (x, y) within a given window (or subwindow) and a keyboard event is triggered then the keyboard callback function associated with that window is invoked. This callback function takes as arguments the ASCII unsigned char associated with the key and the x, y location of the cursor at that moment. In ../../common/March1998/example2.c there is no use for x, y but I am sure you can think of applications where you can take advantage of this nice feature.
Only the top level window in our demo has a keyboard callback. If you try to press the keys "i" or "q" while the cursor is inside the subwindow you will notice nothing happens. By default when a window is created and no keyboard callback is registered then all key strokes are ignored. Have this in mind in the future if you use multiple windows and want and active keyboard.
Finally mention that the generation of keyboard callbacks can be disable by passing NULL to the glutKeyBoardFunc().
Rendering text under OpenGL and GLUT sucks!. Sorry to say it but it is true. I am not completely sure why text rendering has been neglected from the OpenGL library. The old GL library from SGI had a few high level functions to handle text rendering in graphics mode and there was an aditional auxiliary library for changing fonts. OpenGL only provides very primitive directives for rendering bitmaps, and that means you have to make your own library of bitmaps for every character, take care of resolation, scaling of fonts....you name it!
GLUT solves the dilemma of using text with OpenGL somewhat. It provides glutBitmapCharacter that renders a single character onto screen at the location specified by the glRasterPos. I have added a couple of functions, drawString(), and drawStringBig() that make your life a bit easier rendering character string.
This concludes a very simple introduction to the use of GLUT subwindows. At this point I have to mention that although it is good to experiment with them and test them under various platforms unfortunately GLUT subwidows are not fully operational in all environments. Users with 3Dfx based cards will find that subwindows are not functional yet due to hardware limitations. Also I have found a big performance penalty when using subwindows under certain platforms. For instance, under my Linux Alpha with a 2Mb Matrox Millenium a subwindow makes the application run about twice as slow, probably because unfortunately the X server for Alphas still doesn't support any hardware acceleration. On the other hand, the same application under windows 95 with a 2Mb ATI RageII with SGI's OpenGL driver runs wonderfully.
Since Linux development moves so fast it is possible that in the near future many of this performance problems and incompatibilities will go away. For the moment simply be aware they exist, so use multiple windows cautiously.
Of course, advanced users can always find a way around using subwindows by playing with the matrix stack but since we haven't studied that yet forgive me if I leave for latter.. ;)).
Webpages maintained by the LinuxFocus Editor team
© Miguel A Sepulveda, FDL
2002-11-02, generated by lfparser version 2.34