Events

Events are the way in which the graphics system notifies your program of asychronous changes in the state of the screen, mouse, or keyboard. Whenever the state changes, your program is notified of this change and can act on it. The word "event" is used both for the actual change that took place, and also for the data that is returned to your program which describes the change.

Events are generated for various different types of changes that may be useful for your program to know. Events directly related to the hardware are the keyboard and mouse events. Keyboard events are generated for each key which is pressed (and released, if possible). The event contains the character which caused the event. Mouse events are generated when a button on the mouse is pressed or released, or when the mouse position moves. The event contains the buttons which are pressed, and the current position of the mouse. Other events are more subtle, and are based on non-physical changes, such as having the mouse move into or out of specific windows.

Events are generally tied to individual windows. Your program can enable or disable which kinds of events it wants for each window. Part of the data associated with an event is the window associated with the event. For example, if a key is pressed on the keyboard, the event for that key will indicate which window that key is for. You program can then act differently for different windows. Events which you have not indicated an interest in are simply discarded.

The keyboard and mouse events can propagate upwards through the window tree and be delivered to some parent window. This occurs if the window does not select for the event, but one of the parent windows does. Part of the information returned about these events is the window that accepted the event, and also the original window which caused the event. Therefore, your program can determine which child window an event was for without having to select for the event for each child. Events other than keyboard and mouse events never propagate.

The window that keyboard events are delivered to depends on the current mouse position or on the "input focus". The input focus is a way of specifying that keyboard events are to be delivered to a particular window, no matter where the mouse is currently pointing. Your program can change the input focus as desired. If the input focus is set to the root window, then the keyboard events will be delivered to the window which contains the mouse pointer (or one of its parents).

Events are returned to your program as a structure containing the information about the event. This information is the event type, the window id which the event is associated with, and other event-specific data. Events are stored in a queue, and are delivered to your program one by one as requested. The order of the events is preserved. Your program can either simply ask for the next available event (waiting for one if none are yet available), or it can check to see if an event is available without waiting. The delivering of events only occurs when you request an event. So even though events themselves are asychronous, the reading of them is synchronous. There are no "interrupts" for events, you must explicitly ask for them.

The important thing about programming with events is that your program should be written to run "upside-down". That is, you do not have a main routine which checks that the mouse has been moved, or the keyboard has been typed on, or which window the mouse is in. Instead, your main routine just waits for an event, and then dispatches on its type and which window it is for. Generally, you must keep some state information to remember what is happening in your program. For example, if the user wants to click the button in a window to indicate where some text should be inserted, then your program cannot simply detect the mouse click, and then wait for the text to be typed. Instead, when the mouse is clicked, it should just remember the position of the mouse and set a flag to indicate that text typing is allowed, When the keyboard event arrives, this saved information then enables you to draw the text at the correct location. Your program basically becomes one large state machine.

One obscure event is the exposure event. This is sent to your program when a window requires redrawing. Due to lack of memory space, the graphics server does not attempt to save the data from the parts of windows which are covered by other windows. Therefore, when the obscured parts of the window are uncovered, your program must be told to redraw those parts. The exposure event contains a rectangular area which requires drawing (which may in fact be larger than the area which was actually uncovered). Your program can either just redraw that area, or if more convenient, redraw the whole window. The area to be redrawn has already been cleared to the window's background color. When a window is mapped, an exposure event is sent for the window. Therefore, you should not explicitly draw into a window when it is first created and mapped, but should instead just wait for the exposure event, and then draw it. In this way, the code to draw the window only resides in one place in your program, and you prevent redundant drawing of the window. If you are drawing the complete window on all exposure events, then it might be useful to use GrPeekEvent to examine the next event too. If it is also an exposure event for the same window, then you can read it by using GrGetNextEvent, and thereby prevent redundant redrawing. Of course, to be able to redraw the window, you may need to save extra data in order to regenerate the drawing commands. (Pixmaps are one way of doing this in the future, but they are not currently implemented.)

The following is a description of the various types of events which are available, and (in parenthesis) the typedef name for the structure that returns the event. Each event has a type field, which can be used to distinguish between the various events. For details on the other data within the structures, refer to graphics.h. The typedef GR_EVENT is a union which contains all of the possible event structures.

To select for events, you use GrSelectEvents, and specify the window which wants to receive the events, and also specify a mask indicating the events you wish to receive. The mask is the logical OR of individual bit values representing the event types. The mask names are the same as the event names, except that the "_TYPE_" string is replaced by "_MASK_". For example, the mask associated with the event GR_EVENT_TYPE_FOCUS_IN is GR_EVENT_MASK_FOCUS_IN.

If you select for both button down and button up events, then the mouse will be implicitly "grabbed" when any button is pressed down in that window. This means that the mouse position and button down and up events will be delivered only to that window, and the cursor shape won't change, even if the mouse leaves that window. The implicit grabbing ends after the last button is released. While this grabbing occurs, the input focus is also not changed as the mouse is moved.