The first two programs demonstrated how to draw and create animated scenes, respectively. The next program demonstrates how to obtain input and illustrates the concept of an event loop.
The event loop is critical to writing applications for a windowing system. An event loop allows a program to respond to events occurring in the system that are beyond the control of the application program: for example, when a user picks up a window and moves it, has a window obscured and then unobscured by other windows, or resizes a window.
At this point, return to the first program, change the sleep time to 50 seconds, then recompile and rerun the program. While it is running, pick up another window (for instance, the xclock), drop it on the HI THERE window, pick it up again, and remove it. Notice that the original Hello, World! display was destroyed. This occurred because the other window overwrote the pixel data in the HI THERE window, and the overwritten data was not saved (GL does not support backing store or save-under).
When the contents of a window are destroyed in this fashion, the application itself must redraw the window. GL provides an event that indicates that a window may have to be redrawn. The application must test for this event, and redraw the window whenever the event is received. The discussion after the following program explains how the testing and redrawing is done.
#include <gl/gl.h> #include <gl/device.h>
/* This subroutine draws stuff */ drawstuff (int xxx, int yyy) { color (BLACK); clear (); color (GREEN); cmov2i (xxx, yyy); charstr ("Hello, World!"); swapbuffers (); }
main () { int ox, oy; int ix = 150, iy = 200; int update = TRUE;
/* Create and configure window */ prefsize (400, 400); winopen ("HI THERE"); doublebuffer (); gconfig ();
/* get window origin */ getorigin (&ox, &oy);
/* queue up input devices */ qdevice (REDRAW); /* window needs to be redrawn */ qdevice (WINQUIT); /* user selected "close" from window menu */ qdevice (MOUSEX); /* mouse x position, in pixels */ qdevice (MOUSEY); /* mouse y position, in pixels */ qdevice (ESCKEY); /* user pressed escape key */ qdevice (RIGHTMOUSE);/* user pressed right mouse button */
/* enter event loop */ while (TRUE) { long dev; short value;
/* if there aren't any events, and data has changed, redraw */ if (!qtest() & update) { drawstuff (ix, iy); update = FALSE; }
/* get the next event */ dev = qread (&value);
/* dispatch the next event */ switch (dev) { case MOUSEX: ix = value - ox; /* update x location */ update = TRUE; break; case MOUSEY: iy = value - oy; /* update y location */ update = TRUE; break; case REDRAW: /* redraw it */ getorigin (&ox, &oy); /* get window origin */ update = TRUE; break; case ESCKEY: /* if user presses escape key, quit */ case RIGHTMOUSE: /* if user presses right mouse button, quit */ case WINQUIT: /* if "close" selected from window menu */ exit(); default: break; } } }
When you run this program, you will find that the character string Hello, World! follows the cursor around. You can can pick up the window, move it, obscure it, and uncover it, but it will always appear correctly. The following discussion examines the operation of this complicated program in detail.
First, this program includes a new header file: gl/device.h. This file contains definitions and type definitions (typedefs) pertaining to GL devices. To make the program easier to read, the drawing section has been put into its own subroutine, drawstuff, which contains a set of GL subroutines. When you want the program to draw, call this subroutine.
The program begins as before: the first step opens a window. Next, the program obtains the window origin with the getorigin subroutine, which will be needed later. Then, a number of devices are queued up. These devices, the REDRAW device, the MOUSEX device, and so on, generate events and place them on a queue. The program reads events from the bottom of the queue and processes them according to what came in. The qdevice subroutine itself does not generate or process events, but only initializes these devices and readies them for use.
Next, the program enters the event loop. Although this looks like an infinite loop, if the user presses either the escape key or the right mouse button, or else chooses the Close option from the window menu, the program ends.
In the loop, the program tests to see if there are any events on the event queue. If the queue is empty and the picture needs to be redrawn, the program begins to draw at this time. If the queue is not empty, then the program reads the next event and processes it. The qread subroutine returns the device that generated the event and a value associated with that event.
Depending on the device, the switch statement branches to the correct code to handle the event. If, for instance, the event is a mouse-motion event, the new x or y coordinate (or both) is recorded. If the event is an Escape key press, then the program is ended. After a nonterminating event is processed, the program returns to the beginning of the event loop and processes the next event.
If the user moves the window, a REDRAW event is generated. In this case, the window origin is obtained, so that the character string can be drawn in the right place later.
To learn more about devices and events, see "Controlling Queues and Devices".