5.12. Events   

5.12.1. Concepts   

Most modern programming environments are moving towards event driven programming. The idea is that instead of the program telling the user what to do next, the user can tell the program what to do. You will find that this is the case with the Mac, Windows, OS/2, GEM and of course X windows programs.

As you would expect of a decent graphical environment, X provides us with a rich variety of events. Events in X are generated from windows,keyboards, mouse, signals, etc. The X server provides a single queue for each application, in which will be stored all the events generated by any windows belonging to that application.

Events generated from the keyboard will be sent to whichever window currently has the keyboard focus. Depending on your window manager this may determined either by focus follows mouse or by click to type. Some window managers offer you the choice.

If the focused window does not wish to handle the particular type of event received, that event will be passed to its parent. This continues until an event gets accepted by a window, or gets propagated to the root window. In the later case the event will probably be ignored.

Your program can either wait for events (blocking) or it can poll for events. Blocking is more efficient in terms of CPU time and network bandwidth, but results in the program going to sleep. As such it is not suitable when you wish your program to continue with background processing while waiting for an event.

5.12.2. Which Events?   

X is very friendly to the programmer in that it only provides you with the events that you ask for. There are two ways to ask for events. The first is when you create your window. If you refer back to a previous section you will see that the XCreateWindow function takes an attributes parameter which includes an event mask.

Alternatively, you can use the function XSelectInput after the window has been created, as shown below.

     #include <X11/Xlib.h>

Display * display;
Window window;
unsigned long event_mask;
event_mask = .....;
XSelectInput( display, window, event_mask );

XSelectWindow (and XCreateWindow) tell the X server which events that you are interested in receiving. However, your program must explicitly wait or poll for those events in which it is interested. The event_mask variable is set to the bit wise OR of the required masks.


The available masks and associated events are listed below.



Mask Defined Asks for Event Type
Button1MotionMask MotionNotify
Button2MotionMask MotionNotify
Button3MotionMask MotionNotify
Button4MotionMask MotionNotify
Button5MotionMask MotionNotify
ButtonMotionMask MotionNotify (any button)
ButtonPressMask ButtonPress
ButtonReleaseMask ButtonRelease
ColorMapChangeMask ColormapNotify
EnterWindowMask EnterNotify
ExposureMask Expose
FocusChangeMask FocusIn, FocusOut
KeymapStateMask KeymapNotify
KeyPressMask KeyPress
KeyReleaseMask KeyRelease
LeaveWindowMask LeaveNotify
NoEventMask None
OwnerGrabButtonMask None
PointerMotionHintMask None
PointerMotionMask MotionNotify
PropertyChangeMask PropertyNotify
ResizeRedirectMask ResizeRequest
StructureNotifyMask CirculateNotify, Config-
ureNotify, DestroyNotify,
GravityNotify, MapNotify,
ReparentNotify, UnmapNo-
tify
SubstructureNotifyMask CirculateNotify, Config-
ureNotify, CreateNotify,
DestroyNotify, GravityNo-
tify, MapNotify, Repar-
entNotify, UnmapNotify
SubstructureNotifyMask CirculateRequest, Config-
ureRequest, MapRequest
VisibilityChangeMask VisibilityNotify

5.12.3. Waiting for Events   

If your program does not perform any background processing between events, then it should wait for the next event. The function used to achieve this is, naturally enough, called XNextEvent. This checks the program event queue and returns the first event in line. If there is not an event in the queue, then the function waits until an event is placed into the queue.

A more restrictive function is XMaskEvent. This returns the next event for your program that matches the given event mask. More restrictive again is XWindowEvent. XWindowEvent is similar to XEventMask, but only returns an event for the given window, as opposed to the whole application.

XPeekEvent peeks into the event queue. It returns a copy of the event at the head of the queue, but does not remove that event.


The syntax of each of these functions is shown below.

     Display        * display;

Window window;
XEvent event;
unsigned long event_mask;
XNextEvent( display, &event );
XMaskEvent( display, event_mask, &event );
XWindowEvent( display, window, event_mask, &event );
XPeekEvent( display, &event );

In general you would use XNextEvent. An example is given below.

     XNextEvent( display, &event );

switch ( event.type )
{
case Expose: ...
case ButtonPress : ...
case KeyPress : ...
}

5.12.4. Polling for Events   

If your program performs background processing, and you are aware of the overheads (in CPU time and network traffic) then you may wish to poll for events rather than waiting for them.

To check if there are events in your programs internal input queue you can use the function XPending. As only your internal input queue is checked, and the X server may be buffering up events for your application, it is often a good idea to call XFlush first. This ensures that all events stored by the X server are placed in your internal queue.

     Display        * display;

int no_events;
XFlush( display );
no_events = XPending( display );

There are also polling equivalents of some of the functions described in the previous section. XCheckMaskEvent will check for the existence of an event of a particular type. If one is found the function returns the value True, and returns the event. If there is not a matching event in the queue then the function returns False. Similarly with XCheckWindowEvent.

As noted in a previous section, some event masks can result in multiple types of events to be returned. To avoid any ambiguity, the X Window System also has functions similar to XCheckMaskEvent and XCheckWindowEvent which only poll for a particular type of event. All these functions are shown below.

     Display        * display;

Window window;
XEvent event;
int type;
int event_found;
unsigned_long event_mask;
event_found = XCheckMaskEvent( display, event_mask, &event);
event_found = XCheckWindowEvent( display, window, event_mask,
&event);
event_found = XCeckTypedEvent( display, type, &event );
event_found = XCheckTypedWindowEvent( display, window, type,
&event );

5.12.5. Mouse Events   

As X is a graphical windowing system, much of the time the events that we are interested in will be mouse events. When we use the word mouse we could in fact be talking about any pointing device, such as a digitiser, track ball or joystick. X provides support for up to 5 buttons on the mouse. In practice, most mice only have three buttons (though digitiser cursors normally have four buttons) so most software only assumes three buttons.

X mouse button events are generated when :

the user presses a particular mouse button

the user releases a mouse button

the user moves the mouse pointer

the user moves the mouse pointer while holding down a mouse button within an active window or within the child of an active window.

With the X Window System, you have the option of checking for one or more of these events. Normally, though, you would try to avoid asking for mouse movement events as each pixel change will generate a MotionNotify event, an awful CPU and network overhead.

The masks to request mouse pointer events include :



Mask Meaning
ButtonMotionMask The mouse moved while a button is pressed
Button1MotionMask The mouse moved while the left button was pressed.
Button2MotionMask The mouse moved while the middle button was pressed.
Button3MotionMask The mouse moved while the right button was pressed.
ButtonPressMask A mouse button was pressed down.
ButtonReleaseMask A mouse button previously pressed was released.
PointerMotionMask The mouse pointer was moved.

Your program will receive an event of ButtonPress, ButtonRelease or MotionNotify as appropriate.

Having received the event you will typically wish to know which window the event occurred in, the x and y coordinates of the event, and the state of the mouse buttons at the time of the event.

For button events, use the following code

     Window         window;

XEvent event;
int x, y;
window = event.xbutton.window;
x = event.xbutton.x;
y = event.xbutton.y;
if ( event.xbutton.button == 1 )
printf("Left mouse button pressed\n");
if ( event.xbutton.button == 2 )
printf("Middle mouse button pressed\n");
if ( event.xbutton.button == 3 )
printf("Right mouse button pressed\n");

The above assumes a ButtonPress event. If this code was responding to a ButtonRelease event then it would print out which button had been released. With these events, the state field is normally 0 unless a modifier key has been pressed.

For motion events, use the following code

     Window         window;

XEvent event;
int x, y;
window = event.xmotion.window;
x = event.xmotion.x;
y = event.xmotion.y;

For MotionNotify events, the button field is not available. Instead you must rely on the state field. The state field will give you the state of the mouse buttons and also the state of any modifier keys such as Shift, Control or the Meta key (meta key is keyboard dependant, eg. Apple key on apple keyboard).


You can check the state field using one of the following masks.



Mask Meaning
Button1Mask The left mouse button was down
Button2Mask The middle mouse button was down
Button3Mask The right mouse button was down
ShiftMask A shift key was down
ControlMask A control key was down
LockMask Caps lock was down
Mod1Mask Meta key was down.

The sort of code that you would use to check this is given below.

     XEvent         event;

if ( event.xmotion.state & Button1Mask )
/* motion event with left mouse button down */

5.12.6. Keyboard Events   

Even in this age of GUIs, mice and pen input, for many applications you can't go past the keyboard for inputting text. Unfortunately, there are many different types of key boards available. X provides the concept of KeySyms to allow your program to map proprietary keyboard schemes into portable code.

The basic task of a KeySym is to convert a machine-specific key code into a generic letter, such as the letter A. X also allows some keyboards to program a given key to send a string of characters. X does this by having each key send back an ASCII character string, even if the string is just one letter long.

The Xlib function XLookupString converts a KeyPress event into the ASCII character string pressed on the keyboard. In most cases this character string will be a single letter followed by a terminating NULL.

Sample code for reading keyboard events is shown below.

     XKeyEvent event;

char keyb_buffer[20];
XComposeStatus composestatus;
KeySym keysym;
int length, max_bytes;
max_bytes = 1;
length = XLookupString( &event, keyb_buffer, max_bytes, &keysym,
&composestatus );
keyb_buffer[length] = '\0'; /* terminating NULL */
printf( "Key pressed was %s key\n", keyb_buffer);

It is possible to use returned KeySym to check for keys like F1 function key, the Help key or the Page-Down key.

The composestatus is not implemented on all revisions of X11, and we can ignore it.

5.12.7. Other Events   

There are several other events of interest. Probably the most important of these is the expose event. This occurs when a portion of your window which had been obscured becomes uncovered, or exposed, and thus needs redrawing. The X Window System requires your application to redraw the window.

The X server may also send you a ConfigureNotify event. This will occur if the user changes the size or position of your window.

Another type of event you may wish to check for is the MapNotify event. This is raised when the window is mapped to the screen. So far we have assumed that the window was mapped as soon as we called XMapWindow or XMapRaised. In practise the window manager may have intervened and delayed the process. To verify that the window has been mapped (before we start drawing in it) you can ask for a MapNotify event via the StructureNotifyMask. An alternative is to just wait for the first Expose event before starting to draw on the window.