You are on page 1of 7

x_lastclick,

y_lastclick; Window managers also may need these events. Many window managers assign input focus (which
Time t lastc~ck; 1* Time of click (milliseconds)*1 designates the window that gets all keyboard events) to the window that currently contains the pointer.
Time DblClickTime 500; 1* Two clicks < .5 sec apart *1 Such window managers would want to change the focus as the pointer enters a window.
int DblClickspace = 10; 1* Clicks < 10 pixels apart *1

switch (theEvent.type)
EnterNot~ and LeaveNotify'Events
{
case ButtonPress: --=----
button_pressed True; You select these window crossing events by specifying EnterWindowMask and LeClveWindowMask
break; in the call to XSelectlnput. When EnterWindowMask is specified for a window, you receive an
EnterNotify event when the pointer enters the specified window. With LeaveWindowMask, a
LeaveNoti fy event is sent when the pointer leaves the window Thus, changing the cursor, for in-
1* If button was clicked once, this may be a double-click. stance, becomes easy. You change the cursor with an XDefineCursor in response to an EnterNoti fy
* We will call it a double-click if both clicks occurred
* within 0.5 sec of each other and their locations are
event.
* within a 10-pixel-by-10-pixel area
The server reports the information for EnterNotify and LeaveNotify events in the field named
*1
if (button_clicked) xcrossing in the XEvent union. This is an XCrossingEvent data strUcture, defined in <X11·/Xlib. h>
{ as the following:
button_pressed = Falsej
button_clicked = Falsej typedef struct
if«(theEvent.xbutton.time t_lastclick) <= {
DblClickTime) && int type; 1* EnterNotify or LeaveNotify *1
(abs(theEvent.xbutton.x - x_lastclick) <= unsigned long serial; 1* Last processed request number *1
DblClickSpace) && Bool send_event; 1* True = if from a SendEvent *1
(abs(theEvent.xbutton.y y_lastclick) <= Display *display; 1* Display where event occurred *1
DblClickSpace) ) Window window; 1* Window to which event reported*1
{ Window root; 1* Root window in that screen *1
1* It's a double-click. Call appropriate function to handle it *1 Window subwindowj 1* Child window involved in event*1
Time time; 1* Time in milliseconds *I
int x, y'; 1* Final position ·in event window*1
int x_root, 1* Pointer's final position in *1
YJoqtj 1* root window's coordinate frame*1
int mode; 1* One of the three constants:
1* If button was pressed, generate an XUI_BUTTON_CLICK message *1 NotifyNormal, NotifyGrab,
if (button_pressed) NotifyUngrab *I
{ int detail; 1* One of the five'constants:
1* A button click occurred. Remember position and time. *1 NotifyAncestor, NotifyVirtual,
t_lastclick theEvent.xbutton.time; NotifyInferior, NotifyNonLinear,
x_lastclick = theEvent.xbutton.Xj NotifyNonLinearVirtual *1
y_lastclick = theEvent.xbutton.y; Bool same_screen;l* Pointer/window in same screen?*1
button_clicked True; Bool focusj 1* Input focus on this window? *1
button_pressed = False; unsigned int state; I * State of key and buttons *I
} XCrossingEvent;
breakj
Most of the fields in the XCrossingEvent structure are similar to those in XButtonEvent. The x,
y coordinates al~ays represent the final position of the pointer after the crossing event, and state
gives you the state of the buttons and shift keys just before the event.
Wmdow Entry or Exit
The focus field is True if the window identified by the field window or one of its ancestors owns
Notification of the pointer entering and leaving a window is necessary for many tasks in a user in-
the keyboard focus. This can be useful if you want to indicate (perhaps by changing the border
terface. In a menu, you may want to somehow highlight the item (perhaps by displaying that win-
color) which of yout application's windows has the input focus (assuming your application uses
dow in reverse video) that currently contains the pointer. In an editor, you may change the cursor
multiple windows).
in the window displaying the text. For such tasks, you want to know when the mouse enters or
leaves a window.
XGetMotionEvents allocates an array of structures of type XTimeCoord, fills it with information
received from the X server, and returns the address of that buffer to the calling program. The
XTimeCoord structure is defined in <X11/Xlib. h> as follows:
typedef struct
{
Time time; /* Milliseconds since the X server started */
short x, y; /* Position relative to window specified in
the call to XGetMotionEvents */

The last argument you provide to XGetMotionEvents is the address of an integer, wherein it re-
turns the number of elements in the array holding the motion history. Each element of this array
givesyou the pointer's position at a certain time during its motion. The positions are relative to the
origin of the window you specifYin the second argument to XGetMotionEvents.
After you have used the positions reported in the motion history buffer, you have to remember to
releasethe memo sed by the buffer. You can do this by calling XFree.

~
/'

0 ¥-ents" (It'~'~I q I

mouse is important for the point-an -click parts of a GUI; the keyboard is indispensable for
text entry. Whether it is annotating a figure or typing in a program listing, a program must handle
keyboard input to get the job done.
As with the mouse burtonpress and button-release events, the X server generates a KeyPress event
when a key is pressed and a KeyRelease event when the key is released. All keys generate these
events (including, for instance, Shift, Ctr!, Alt, Caps Lock, and the functio keys)..AlI workstations
generate KeyPress events; some systems may not reliably report the KeyRelease event.. Because
there is usually no need to detect a KeyRelease, you should process keyboard input using the
KeyPress event only. That enables the application to work with all X servers.

Keys such as Shift, Ctrl, Alt, and Caps Lock (or Shift Lock) keys are known as~eys
because they modifY the meaning of the other keys. X supports up to five syst~
dent modifier keys:

Keyboard Event Processing


Unlike the mouse, which has a pointer to indicate the window in which a pointer event occurs, the
keyboardhas to rely on the concept of a focus window that gets the keyboard events. How the focus
isassignedana how to ensure that the designated su~indow in your application gets the keystrokes
arediscussed in later sections. For now, you can proceed under the assumption that somehow the
window of your choice has the input focus.
press A while the Shift key is down, the result should be an uppercase A. The keysym
You can request KeyPress events for the window by using KeyPressMask as the event mask when
differentiates between these cases and assigns the names XK_aand XK_A,respectively, for
calling XSelectInput.
lowercase aad uppercase A.
2. Convert the keysym to an ASCII text string that you can use for displaying (and for saving
XKeyEvent Data Structure in files or buffers). For most keys, this would be a string with a single character, but
Once selected, KeyPress and KeyRelease events are reported in an XKeyEvent data structure, which function keys, especially programmable ones, may generate a multicharacter string.
is defined in <X11/ Xlib. h> as follows:

I
Figure 8.4.
typedef struct .. F1F2
Tramlating keycode to F3 7 8 9
{
int type; 1* Event's type *I keysym and ASC.lI . , 4 5 6
unsigned long serial; 1* Last processed request number *1 characters. 1 2 3
I
Bool send_event; 1* True = if from a SendEvent *1 0
Display *display; 1* Display where event occurred *1
Windqw window; 1* Window to which event reported*1
Window root; 1* Root window in that screen *1 (1)KeyPress
Window sUbwindow; 1* Child window involved in event*1 event
Time time; 1* Time in milliseconds *I reports: theEvent,xkey.state = ShiftMask

t
int x, y; 1* Position in event window *1
int x_root, 1* Pointer's position in the *1
y_root; /* root window's coordinate frame*1
unsigned int state j 1* State of key and buttons *1
unsigned int keycode; 1* detail * I char bufferI10]:
int bufsize = 10;
Bool same_screen; 1* Pointer/window in same screen?*1 (2)Translate
keycode Keysym ks;
XKeyEvent; toKeysym and string XComposeStatus CS i

You can access the fields ofXKeyEvent through the xkey member of the XEvent union. For ex-
ample, if the event is received in a variable named theEvent of type XEvent, refer to the keycode
nchars = 1 - ••------ Number ofcharacters
resulting
after a KeyPress event as theEvent . xkey . key code. fromtranslating
thekey
Most of the fields in the XKeyEvent structure have the same meaning as identically named mem- bufferl0J =' 3 -••
------ ASCIIcharacters
corresponding
tothekey
bers in other event data structures. The two most important fields for processing keystrokes are state
and keycode. The state indicates the state (pressed or released) of the modifier keys, such as Shift, ks = XK_3 -••------- Device-independent
standardname forthekey
Ctrl, and Alt, as well as any system-dependent modifiers. The keycode is an integer with values
between 8 and 255 that uniquely identifies the key. You have to translate this code into ASCII
characters (or the ISO Latin-1 code of which ASCII is a subset) before using it. Keycode Translation with XLookupString
You can use a single function, XLookupString, to complete both steps of translating a keycode to
Keycode Mapping to keysym and Character String a keysym as well as to an ASCII text string (see Figure 8.4). A typical use of this 'functioriroight be -
as follows:
The X server decides the keycode to generate for a specific physical key. Each key, including the
modifiers, has a unique keycode. Although the keycode generated for the common alphanumeric
keys-may be the same for many workstations, it is not guaranteed to be so. Therefore, applications XEvent theEvent;
do not use the raw keycode. Instead, this server-dependent keycode is translated to meaningful char xlat[20j; 1* Room for string *1
characters by a two-step process: int nchar = 20; 1* Size of xlat *1
int count; 1* Chars. returned *1
1. As shown in Figure 8.4, the first step involves translating the keycode to a symbolic name, KeySym key; 1* keysym on return *1
XComposeStatus cs; 1* Compose key status· I
known as keysym. All meaningful combinations of a key and the modifiers have unique
keysyms, which are constants defined in the header file <X11/ keysym. h>. The keysym
resulting from a single keypress depends on the state of the modifier keys as well as the key
itself. For example, if you press the A key alone, you should get a lowercase a, but if you
Translation of keysym to String
With the function XRebindKeysym, you can assign an arbitrary ASCII text string to any key. Note
sWitch(theEvent.type) that this binding of a string to a key is local to your application. It does not ~ffect the other appli-
{ cations sharing the display. Therefore, you can use it without any apprehenSIOn.
case KeyPress:
{ Here is an example of binding the string HELPto the function key F1 and HELP INDEXto Ctrl-F1:
count =
XLookupString(&theEvent, xlat, nchar
&key, &cs); Display *theDisplay;
/* Add null byte to make [sqjxlat[sq] a valid C string */ KeySym modkeys[2];
xlat[count] = [sq]\0[sq];

/* Print out translated string ... */ /* Bind "HELP" to the F1 key */


printf("Keypress %s received on window %x, XRebindKeysym (theDisplay, XK_F1, NULL, 0, "HELP", 4);
subwindow %x\n", xlat, theEvent.xkey.window,
theEvent.xkey.subwindow); /* Bind "HELP INDEX" to Control-F1 */
modkeys[0j = XK_Control_L;
/* Keysym may be used like this ... */ XRebindKeysym(theDisplay, XK_F1, modkeys, 1,
if(key == XK_Q :: key == XK_q) "HELP INDEX", 10);
{
/* User has pressed [sqjq[sqj. Exit? */ As you can see, the second argument to XRebindKeysym is the key (indicated b?,its keYSym!whose
string equivalent is being changed. Next comes an array of keysyms representing the ~odlfiers to
be associated with this binding. Use NULLif no modifiers are needed. The number of modifier keysyms
comes next. The last two arguments are the string and its length.
}
breakj
Keyboard Mapping Refresh
/* Other events ... */
}
The X server manages the translation of keycodes to keysym. Applications can change .this n:apping
To use XLookupString, you have to have a buffer for the characters (xlat in the example). You by calling the function XChangeKeyboardMapping. An application shoul~ not do thiS on ItSown,
have to pass the address of the event that contains the keycode you want translated. The event must but the user may run a utility program such as xmodmapto alter the mappIng of one or mo~e keys.
be of type KeyPress or KeyRelease. XLookupString takes into account the state of the Shift key When this happens, your application will receive a MappingNoti fy event. Future mapPIng~ of
when translating the keycode to a keysym. keycodes to keysymswill work propedy as long as your application calls XRefreshKeyboardMapplng
each time it receives a MappingNoti fy event. .
The string returned by XLookupString is not null-terminated. To convert it to a null-terminated
C-style string, you should insert a null character as shown in the example.

What you do with the keysym or the text string generated by XLookupString is up to your appli-
cation. If you are accepting text from the user, you will probably use the text string only, perhaps
saving it in a buffer. On the other hand, if you want the application to quit when the user presses
Input Focus
The server can associate a pointer eyent with a specific windo~ by noting;fhich window contains
the pointer. There is no g06a way to decide the win do:, that s~_ouldreceive the key1?oardevents.
?
the Qkey, you can compare the returned keysym with the ones for uppercase and lowercase Q, (in- To solve the problem, X us~ concept ofJops. The WInd~ww:th t~e ~OC\!!i~~ ~resses,
cluding, for instance, Shift, Ctrl, Alt, Caps Lock, and the function keys)-XK_q and XK_Q,respec- regardless of the pointer's location (unless, of course, the pOInter s location alsoCOn:tro1st1le1ocus).
tively.
Because all applications displayi-ngi~ a workstatio~ have to share th,e ke}Lbo-<lr~,
i~ is ~mp?rta.P~to
The XComposeStatus structure passed to XLookupString is used to store certain status informa- have a well-defined mechanism for transfernng the input focus from ~ne applJ(;atl~n s.eWIndowto
another. In fact, such conventions do exist, in the form ofthe Inter-Client Commumcatwn Conven-
tion to support composing multiple key sequences. For example, the user might press a designated
tfomManual (ICCCM). These conventions have been followed earnestly since the release of XII ~4
Compose key, then a backquote ('), and finally an E to construct the capital letter E with the grave
accent. If you do not care about multikey characters, you can provide a NULLpointer in place of the in January 1990. What you need is a window manager tha~ fol.lo,,:"sthe ICC~M. Then you can give
argument. the window manager an appropriate hint about your applIcation s need for Input focus. After that,
your application should get the focus when the user selects your ~pplication' stop-level wi?dow.
The exact mechanism for transferring the focus depends on the WIndow manager. If the wmdow
Display *display; /* Display where event occurred */
manager uses a click-to-type model, the user indicates the focus window by clicking on it. Some Window window; /* Window to which event reported*/
window managers may follow the model whereby the input focus is always on the top-level window int mode; /* One of the three constants:
NotifyNormal, NotifyGrab,
with the pointer in it. NotifyUngrab */
int detail; /* One of the fiva constants:
NotifyAncestor, NotifyVirtual,
Hints to the Window Manager NotifyInferior, NotifyNonLinear,
NotifyNonLinearVirtual */
Application programs that need keyboard input should so indicate by setting the input field of the } XFocusChangeEv~nt;
XWMHintsstructure to True before calling XSetWMHints (or XSetWMProperties). Here is how this
You care about getting the focus because of an assignment by the user; therefore, you should care
might be done:
about only those FocusIn events with mode equal to NotifyNormal ..Furthermore, the focus al-
ways changes frbm another application's window to yo'ih application's top-level window. For this
if((p_xwmh XAllocWMHints(» == NULL)
case, the detail field will be Noti fyNonlinear. Thus, you can assign focus to a selected subwindow
{ of your application:
fprintf(stderr, "Error allocating Window
Manager hints!\n"); Display *theDisplay;
exit (1); Window theMain, kb_win;
XSelectInput(theDisplay, theMain, ExposureMask :
FocusChangeMask)j
/* Tell the window manager to put the window in its normal
* state and to transfer input focus to our window when
* the user does 50. Use XSetWMProperties or XSetWMHints to /* In event handling loop ... */
* inform the window manager. switch (theEvent.type)
*/ {
p_xwmh->flags = (InputHint:StateHint); case FocusIn:
p_xwmh->input = Truej /* Give focus to our keyboard handling subwindow */
p_xwmh->initial_state = NormalStatej if(theEvent.xfocus.mode == NotifyNormal &&
theEvent.xfocus.detail == NotifyNonlinear)
XSetInputFocus(theDisplay, kb_win, RevertToParent,
CurrentTime) j

FocusChange Events /* Handle other events ... */


}
The window manager will take care of setting the focus to your application's top-level window.
Suppose your application handles all text input in a single subwindow and you want to give the Always use the constant RevertToParent as the third argument to XSetInputFocus. This argu-
focus to this subwindow when the top-level window receives the focus. You can do this by request- ment specifies what to do if the subwindow receiving the input focus becomes obscured. The
ing FocusChange events on the topmost window. When you get notification offQcus beipg.9n your constant RevertToParent specifies that, in this case, the focus should revert back to the parent
main window (through a FocusIn event), you can call ~tFOCu~to give the focus to the window.
subwindow that needs it. Having done this, you can place alfcoaetor handling the keyboar~ events
in the section that processes events for that subwindow. This simplifies keyboard inputs in your
application. Keyboard Control
Xlib includes routines to control some aspects of the keyboard, such as the status of the LED (light-
Although the suggested scheme is straightforward, the F~_ In and FocusOut events are some-
emitting diode) lamps on the keyboard, the volume and pitch of the bell, and the rate at which keys
wh
. .,
like the crossing events-a window can get one even i(jtjs·not the u tlma . clplent of the
mput focus. The mc IS to look at the mode and detail fields of the XFocusChangeEvent data
are automatically repeated (if pressed continuously).
structure, which is defined in <X11/ Xlib. h:>:as follows: A structure of type XKeyboardControl is used to specify the values of the parameters. The
typedef struct XKeyboardControl structure is defined in <X11/Xlib. h> as follows:
{
typedef struct
int type; /* FocusIn or FocusOut */ {
unsigned long serial; /* Last processed request number */ int key_click_percentj /* 0 (silent) to 100 (loudest) */
Bool send_event; /* True = if from a SendEvent */ int.bell-percentj /* 0 (silent) to 100 {loudest) */
int bell_pitch; /* 20 Hz to 20 Khz •. DestroyNotify. This event reports the destruction of a window.
int bell_duration; /* milliseconds for bell
•. GravityNotify. If a window is moved because its parent's size has changed, the server
int led; /* Which LED? (1 to 32)
int led_mode; /* LedModeOn or LedModeOff sends this event.
int key; /* Keycode for next field
•. MapNotify. This event is generated when the window is mapped.
int auto_repeat_mode; /* 0 (off) or 1 (on)
XKeyboardControl; •. ReparentNoti fy. The server sends this event when a window's parent is changed by a call
The approach used to specify the values in this structure is similar to that used in specifying other to the function XReparentWindow. When a window manager places its own decorative
X data structures. You set up the fields you want, set up a bit mask to indicate which fields are being frame around a window, it reparents your application's top-level window.
changed, and call XChangeKeyboardControl with the mask and a pointer to the XKeyboardControl •. UnmapNoti fy. This event is sent to indicate that the window is unmapped.
structure. Note that a -1 as value sets the parameter back to its default. •. Visibili tyNoti fy. If the window's visibility changes (it becomes visible or invisible), the
Applications should not have any reason to change the keyboard parameters. These items are usu- server sends this 'event.
ally set by the user by running the utility program xset. You Cansee the current settings by typing
xset q at the shell prompt in an xterm window. ConfigureNotifY Events
Of the events that notify you about changes in a window's state, the ConfigureNotify event may
Other X Events '----~ be the most important. This event reports changes in position, size, and stacking order of a win-
dow. You probably care most about the report in the change of size of your application's top-level
In addition to the mouse and keyboard events, the X server also sends events to alert your applica-
window because this may require changes to other subwindows.
tion of situations that may require some action. Of these events, the Expose event is the most im-
portant. As explained earlier in this chapter, the server sends Expose events when any of your If you have a simple hierarchy of windows, you should request the Conf igureNoti fy event by using
application's windows that were previously obscured become visible again. To handle this event, the event mask StructureNotifyMask for your program's main window. When you receive the
your application should draw whatever was being displayed in that window. If you display any- ConfigureNotify events, you can resize the subwindows manually.
thing in a window, you must request and process Expose events; otherwise, the contents of the
If you want to receive a ConfigureNotify event when any of the subwindows are reconfigured,
windows will not be maintained when the user moves around any overlapping windows.
you can also use the event mask SubStructureNotifyMask for the main window.
Several notification events are generated when windows are moved and resized, and their hierarchy
and stacking order are changed. As usual, there are many more events than you may care to handle
(at least, in the beginning), but all the possible notification events are there, in case someone needs
XConfigureEvent Data Structure
them. The information for·a ConfigureNotify event is reported in a data structure of type
XConfigureEvent, which is present as the member xconfigure in the XEvent union. The
XConfigu reEvent data structure is defined in <X11/ Xlib. h> as follows:
typedef struct
Nine events report changes in a window's position, size, and other state. Here is a summary of these {
events: int type; /* ConfigureNoti fy */
unsigned long serial; /* Last processed request number */
•. CirculateNotify. This event indicates that the window's position in the stacking order Bool send_event; /* True = if from a SendEvent */
of its siblings has changed. Display *display; /* Display where event occurred */
Window event; /* Window to which event reported*/
•. ConfigureNotify. The server sends this event when the window is moved, resized, or its Window Window; /* Window that was reconfigured */
position in the stacking order is changed. You have to request this event if you want to int x, y; /* New position in parent's frame*/
int width, /* New width of window */
resize the windows in your application when the user alters the size of the top window. height; /* New height of window */
Use StructureNotifyMask in the call to XSelectInput. int border width;/* New border width of window */
Window above;- /* Set to: Sibling or None */
•. CreateNotify. The server sends this event when a subwindow is created. You can request override_redirect; /* If True, window manager
Bool
this event using the SUbStructureNotifyMask when selecting events for your top-level will not intervene with the
window to get reports of creation of subwindows. This event is used by window managers reconfiguration of this window*/
because they can request this event for the root window and get notification when an
application creates its top-level window.
As you can see from the comments in the definition of the structure, most of the fields have straight- '
fOIWardmeaning. The last field, override_redirect has a special significance. The value in this Events for Graphics and Interdient Communications
field comes from the override_redirect attribute of the window that was reconfigured. Window There are several other X events that play important roles in X applications. Of these, the
managers usually intercept the Conf igureNoti fy events so that they can decide the placement and ColorMapNotify event reports installation of a new colormap for a window. Applications can re-
size of each application's top-level windows. When window managers see that the quest this event by specifYing the mask ColormapChangeMask. Also, if the X server encounters cer-
override_redirect field is set to True, they do not interfere wirh rhat window's location and tain problems during graphics copy operations, it generates GraphicsExpose events. Both of these
size. Applications use this feature when creating pop-up windows (that are children of the roor). eventS are described in detail in Chapter 9.
They ser the override_redirect attribute of pop-up windows to True. That way, the window
manager does not place any decorative frame around the pop-up, nor does it try to prompt the user The other group of events are meant for exchanging information between X clients. (Your applica-
for placement of the window (only some window managers do this). tion must work with at least one client, the window manager.) The events in this category are
ClientMessage,PropertyNotify,SelectionClear,SelectionNotify,andSelectionRequest.
Another word of caution is in order. In the presence of a window manager, the x and y positions These events are described in Chapter 16.
reported for your application's top-level windows may not be with respect to the root window (which
is whar you probably expect), because the window managers usually reparent the top-level windows.
Example with Menubar and Pull-Down Menus
Window Size Changes You have seen many small fragments of code illustrating various aspects of event handling in X
applications. The next example illustrates how you can implement a sophisticated graphical inter-
The reaction to a change in size of your application's main window depends on the exact nature of
face using the low-level event reporting mechanism ofX. You reuse the routines ini tapp.c (Chapter
the application. For example, if you are developing a text editor, and you have a single subwindow
7, Listing 7.2) and xbutton. c (Listing 8.3) to build the program.
displaying the text, all you have to do is change this window by using the function XResizeWindow.
Assuming that this is the case, the ConfigureNotify event might be selected and handled as fol- The example program implements a menubar with pull-down menus. Keyboard event handling is
lows: not shown here beC<1.use if the application accepts text input, it also must display the text. Chapter
Display *theDisplay; 10 discusses displaying text, and this example is expanded to include text editing and display.
Window w;
XEvent theEvent;

XSelectlnput(theDisplay, w, ExposureMask :
StructureNotifyMaskl; The first step in displaying a menubar is to implement the menu. Each menu shows a number of
items in a window. The program has to initiate some action when the user presses the mouse but-
ton with the pointer inside one of the items. In the beginning of this chapter, you developed a button
window, multiple copies of which can be handled easily, as shown in Listing 8.4. You will reuse
that code and build the menu as an outer window with a number of buttons as subwindows.
The same code can handle a menubar as well as a pull-down menu. The difference between the two
if(theEvent.xany.window == w && is that the individual button windows are laid out horizontally in the menubar, but they are ar-
theEvent.type == ConfigureNotifyl ranged vertically in the pull-down menu.
XResizeWindow(theDisplay, w, theEvent.xconfigure.width, In keeping with the design of xbutton. c (Listing 8.3), the menu has a private data structure in
theEvent.xconfigure.heightl; which information about each copy of the menu is stored. The menu's data structure is divided
into two parts:
Because Conf igu reNot i fy events are generated for a variety of reasons, of which size change is one,
• Infoimation about each menu item is stored in a structure of type D_ITEM.
you should save the old size of the window and compare it with the size reported in the event.
• Information about the entire menu is stored in a structure of type D_MENU.
Listing 8.7 shows the file xmenu. c, which implements the menu window. The data structures D_ITEM
and D_MENU appear in that file.