Events

Events are sent to your application to indicate changes in a GdkWindow or user actions you might be interested in. All events are associated with a GdkWindow. They also come to be associated with a GtkWidget; the GTK+ main loop passes events from GDK to the GTK+ widget tree.

Types of Event

There are many kinds of events; the GdkEvent union can represent any of them. A special event type, GdkEventAny, contains the three fields common to all events; any event can be cast to GdkEventAny. The first field in GdkEventAny is a type marker, GdkEventType; GdkEventType is also included in the GdkEvent union. Confused yet? Seeing the code should help. Here is GdkEventAny:


struct _GdkEventAny
{
  GdkEventType type;
  GdkWindow *window;
  gint8 send_event;
};

and GdkEvent:


union _GdkEvent
{
  GdkEventType              type;
  GdkEventAny               any;
  GdkEventExpose            expose;
  GdkEventNoExpose          no_expose;
  GdkEventVisibility        visibility;
  GdkEventMotion            motion;
  GdkEventButton            button;
  GdkEventKey               key;
  GdkEventCrossing          crossing;
  GdkEventFocus             focus_change;
  GdkEventConfigure         configure;
  GdkEventProperty          property;
  GdkEventSelection         selection;
  GdkEventProximity         proximity;
  GdkEventClient            client;
  GdkEventDND               dnd;
};

Every event type has the three members of GdkEventAny as its first three members. Thus, the type of an event can be referred to in many ways (assume a GdkEvent* called event):

You'll probably see all these in GTK+ source code. Of course, each event subtype has its own unique members; the type field tells you which subtype is valid.

The window field of GdkEventAny is the GdkWindow the event was sent to. If the send_event flag is TRUE, then the event was synthesized by another (or your own) application; if FALSE, it originated with the X server. GDK does not export the X interface for sending events (XSendEvent()). However, GTK+ often "makes up" an event by declaring a static event struct, filling it in, then emitting the event's corresponding widget signal. These synthesized events will have send_event set to TRUE. the section called Receiving GDK Events in GTK+ explains how GTK+ associates events with widget signals.

There are more possible values for GdkEventType than there are members in the GdkEvent union. Many event types share the same data; for example, GDK_BUTTON_PRESS and GDK_BUTTON_RELEASE both use the button member of GdkEvent, since the same information is conveyed when mouse buttons are pressed and released. Table 2 shows all possible values of the GdkEventType enumeration, and the corresponding GdkEvent member. The meaning of each event type is described later in this section.

Table 2. GdkEventType Values

Value GdkEvent Member
GDK_NOTHING none [PD] footnote!
GDK_DELETE GdkEventAny
GDK_DESTROY GdkEventAny
GDK_EXPOSE GdkEventExpose
GDK_MOTION_NOTIFY GdkEventMotion
GDK_BUTTON_PRESS GdkEventButton
GDK_2BUTTON_PRESS GdkEventButton
GDK_3BUTTON_PRESS GdkEventButton
GDK_BUTTON_RELEASE GdkEventButton
GDK_KEY_PRESS GdkEventKey
GDK_KEY_RELEASE GdkEventKey
GDK_ENTER_NOTIFY GdkEventCrossing
GDK_LEAVE_NOTIFY GdkEventCrossing
GDK_FOCUS_CHANGE GdkEventFocus
GDK_CONFIGURE GdkEventConfigure
GDK_MAP GdkEventAny
GDK_UNMAP GdkEventAny
GDK_PROPERTY_NOTIFY GdkEventProperty
GDK_SELECTION_CLEAR GdkEventSelection
GDK_SELECTION_REQUEST GdkEventSelection
GDK_SELECTION_NOTIFY GdkEventSelection
GDK_PROXIMITY_IN GdkEventProximity
GDK_PROXIMITY_OUT GdkEventProximity
GDK_DRAG_ENTER GdkEventDND
GDK_DRAG_LEAVE GdkEventDND
GDK_DRAG_MOTION GdkEventDND
GDK_DRAG_STATUS GdkEventDND
GDK_DROP_START GdkEventDND
GDK_DROP_FINISHED GdkEventDND
GDK_CLIENT_EVENT GdkEventClient
GDK_VISIBILITY_NOTIFY GdkEventVisibility
GDK_NO_EXPOSE GdkEventNoExpose

The Event Mask

Each GdkWindow has an associated event mask which determines which events on that window the X server will forward to your application. You specify the event mask when a GdkWindow is created, as part of the GdkWindowAttr struct (the section called GdkWindow Attributes). You can access and change the event mask later using gdk_window_set_events() and gdk_window_get_events(). If the GdkWindow in question belongs to a widget, you should not change the event mask directly; rather, call gtk_widget_set_events() or gtk_widget_add_events(). gtk_widget_set_events() should be used after a widget is realized; gtk_widget_add_events() can be used to add events to the existing mask at any time. Figure 5 and Figure 6 show these functions.

#include <gdk/gdk.h>

GdkEventMask gdk_window_get_events(GdkWindow* window);

void gdk_window_set_events(GdkWindow* window, GdkEventMask event_mask);

Figure 5. GdkWindow Event Mask

#include <gtk/gtkwidget.h>

gint gdk_widget_get_events(GtkWidget* widget);

void gtk_widget_add_events(GtkWidget* widget, gint event_mask);

void gtk_widget_set_events(GtkWidget* widget, gint event_mask);

Figure 6. Widget Event Mask

Table 3 shows which event masks request which events. Some events do not have to be selected to be received; in particular:

Table 3. Event Masks

Mask Event Type
GDK_EXPOSURE_MASK GDK_EXPOSE
GDK_POINTER_MOTION_MASK GDK_MOTION_NOTIFY
GDK_POINTER_MOTION_HINT_MASK N/A (see the section called Mouse Movement Events)
GDK_BUTTON_MOTION_MASK GDK_MOTION_NOTIFY (while a button is pressed)
GDK_BUTTON1_MOTION_MASK GDK_MOTION_NOTIFY (while button 1 is pressed)
GDK_BUTTON2_MOTION_MASK GDK_MOTION_NOTIFY (while button 2 is pressed)
GDK_BUTTON3_MOTION_MASK GDK_MOTION_NOTIFY (while button 3 is pressed)
GDK_BUTTON_PRESS_MASK GDK_BUTTON_PRESS, GDK_2BUTTON_PRESS, GDK_3BUTTON_PRESS
GDK_BUTTON_RELEASE_MASK GDK_BUTTON_RELEASE
GDK_KEY_PRESS_MASK GDK_KEY_PRESS
GDK_KEY_RELEASE_MASK GDK_KEY_RELEASE
GDK_ENTER_NOTIFY_MASK GDK_ENTER_NOTIFY
GDK_LEAVE_NOTIFY_MASK GDK_LEAVE_NOTIFY
GDK_FOCUS_CHANGE_MASK GDK_FOCUS_IN, GDK_FOCUS_OUT
GDK_STRUCTURE_MASK GDK_CONFIGURE, GDK_DESTROY, GDK_MAP, GDK_UNMAP
GDK_PROPERTY_CHANGE_MASK GDK_PROPERTY_NOTIFY
GDK_VISIBILITY_NOTIFY_MASK GDK_VISIBILITY_NOTIFY
GDK_PROXIMITY_IN_MASK GDK_PROXIMITY_IN
GDK_PROXIMITY_OUT_MASK GDK_PROXIMITY_OUT
GDK_SUBSTRUCTURE_MASK Receive GDK_STRUCTURE_MASK events for child windows
GDK_ALL_EVENTS_MASK All events

Receiving GDK Events in GTK+

In a GTK+ program, you will never receive GDK events directly. Instead, all events are passed to a GtkWidget, which emits a corresponding signal. You handle events by connecting handlers to GtkWidget signals.

The X server sends each X client a stream of events. Events are sent and received in the order of their occurrence. GDK converts each XEvent it receives into a GdkEvent, then places events in a queue. GTK+ monitors GDK's event queue; for each event received, it decides which widget (if any) should receive the event. The GtkWidget base class defines signals for most event types (such as "button_press_event"); it also defines a generic "event" signal. The GTK+ main loop calls gtk_widget_event() to deliver an event to a widget; this function first emits the "event" signal, then emits a signal for the specific event type (if appropriate). Some events are handled in special ways; notably, drag-and-drop events do not directly correspond to drag-and-drop signals.

In general, events go to the widget owning the GdkWindow the event occurred on. However, there are certain special cases.

if a widget has the grab (i.e., if gtk_grab_add() was called, see the section called Grabs in the chapter called GTK+ Basics), certain events will only be forwarded to the widget with the grab, or the children of that widget. Events that occur on other widgets are ignored. Only certain user-initiated events such as button events and key events are affected by a grab.

Widget sensitivity (see the section called Sensitivity in the chapter called GTK+ Basics) also affects where events are sent. Events representing user interaction are not forwarded to insensitive widgets.

As you might expect, widgets with no associated GdkWindow do not originate events; X only sends events to windows. There is one exception: containers synthesize expose events for their windowless children.

The GTK+ main loop propagates certain events from child widgets to their parent containers. That is, for each event, a signal is emitted first from a child widget, then from its immediate parent, then from the parent's parent, and so on. For example, if you click a GtkMenuItem, it ignores the button press and lets the menu it's a part of handle it. Some events are not propagated; Table 4 gives details.

Event propagation ends once a widget "handles" the event. This ensures that only one user-visible change results from any user action. Handlers for GtkWidget's event signals must return a gint value. Recall that the last signal handler to run determines the return value of a signal emission---see the section called Emitting A Signal in the chapter called The GTK+ Object and Type System. All event signals are GTK_RUN_LAST, so the return value will come from:

If the emission of an event signal returns TRUE, the GTK+ main loop will stop propagating the current event. If it returns FALSE, the main loop will propagate the event to the widget's parent. Recall that each event results in two signal emissions: a generic "event" signal and a specific signal (such as "button_press_event" or "key_press_event"). If either emission returns TRUE, event propagation ends. The return value from the generic "event" signal has one additional effect: if TRUE, the second, more specific signal will not be emitted.

Table 4 summarizes how GtkWidget signals correspond to event types, which events are affected by an active grab, and which events are propagated from parent to child. Signal handlers for all event signals should return a gint and take three arguments: the widget emitting the signal, the event which triggered the signal, and a user data pointer.

Table 4. GtkWidget Events

Event Type GtkWidget Signal Propagated? Grabbed?
GDK_DELETE "delete_event" No No
GDK_DESTROY "destroy_event" No No
GDK_EXPOSE "expose_event" No No
GDK_MOTION_NOTIFY "motion_notify_event" Yes Yes
GDK_BUTTON_PRESS "button_press_event" Yes Yes
GDK_2BUTTON_PRESS "button_press_event" Yes Yes
GDK_3BUTTON_PRESS "button_press_event" Yes Yes
GDK_BUTTON_RELEASE "button_release_event" Yes Yes
GDK_KEY_PRESS "key_press_event" Yes Yes
GDK_KEY_RELEASE "key_release_event" Yes Yes
GDK_ENTER_NOTIFY "enter_notify_event" No Yes
GDK_LEAVE_NOTIFY "leave_notify_event" No Yes [PD] footnote!
GDK_FOCUS_CHANGE "focus_in_event", "focus_out_event" No No
GDK_CONFIGURE "configure_event" No No
GDK_MAP "map_event" No No
GDK_UNMAP "unmap_event" No No
GDK_PROPERTY_NOTIFY "property_notify_event" No No
GDK_SELECTION_CLEAR "selection_clear_event" No No
GDK_SELECTION_REQUEST "selection_request_event" No No
GDK_SELECTION_NOTIFY "selection_notify_event" No No
GDK_PROXIMITY_IN "proximity_in_event" Yes Yes
GDK_PROXIMITY_OUT "proximity_out_event" Yes Yes
GDK_CLIENT_EVENT "client_event" No No
GDK_VISIBILITY_NOTIFY "visibility_notify_event" No No
GDK_NO_EXPOSE "no_expose_event" No No

Button Events

Four different event types result in a GdkEventButton:

If you click three times quickly, on the same GdkWindow, the following events are received in order:

  1. GDK_BUTTON_PRESS

  2. GDK_BUTTON_RELEASE

  3. GDK_BUTTON_PRESS

  4. GDK_2BUTTON_PRESS

  5. GDK_BUTTON_RELEASE

  6. GDK_BUTTON_PRESS

  7. GDK_3BUTTON_PRESS

  8. GDK_BUTTON_RELEASE

The X server automatically causes a pointer grab when a button is pressed, and releases it when it is released. This means that the button release event always goes to the same window that received the button press event. Xlib allows you to change this behavior, but GDK does not. (In the Xlib documentation, this automatic grab is referred to as a "passive" grab. It's distinct from an "active" grab initiated with gdk_pointer_grab(), described in the section called Grabbing the Pointer.)

A button event is defined as follows:


typedef struct _GdkEventButton GdkEventButton;

struct _GdkEventButton
{
  GdkEventType type;
  GdkWindow *window;
  gint8 send_event;
  guint32 time;
  gdouble x;
  gdouble y;
  gdouble pressure;
  gdouble xtilt;
  gdouble ytilt;
  guint state;
  guint button;
  GdkInputSource source;
  guint32 deviceid;
  gdouble x_root, y_root;
};

Button events are marked with a time stamp (time) by the X server. The time is given in milliseconds of "server time"; every few weeks the integer overflows and timestamps begin again at 0. Thus, you should not rely on the value as an absolute measure of time; it is intended only to determine relative time between events.

The mouse pointer's X and Y coordinates (relative to the window the event occurred in) are included in GdkEventButton. Keep in mind that the pointer may be outside the window (if a pointer grab is in effect; see the section called The Mouse Pointer). If the pointer is outside the window, its coordinates could be negative or larger than the window's size. Coordinates are given as doubles rather than integers, because some input devices such as graphics tablets have sub-pixel resolution. For most purposes, you will want to cast the doubles to integers. pressure, xtilt, and ytilt are also special features of some input devices; they can be ignored almost all the time.

The state member of GdkEventButton indicates which modifier keys or mouse buttons were held down an instant before the button was pressed. It is a bitfield, with one or more of the flags in Table 5 set. Since the modifiers are read just before the button press, it follows that button press events do not have the pressed button in state, but button release events do have it.

Be careful to check for the presence of certain bit masks, rather than the exact value of state. That is, prefer this:


 if ( (state & GDK_SHIFT_MASK) == GDK_SHIFT_MASK )

and avoid this:


 if ( state == GDK_SHIFT_MASK )

If you check the exact value of state, your application will mysteriously stop working if the user has Num Lock or some other obscure modifier turned on.

Table 5. Modifier Masks for Key and Button Events

Modifier Mask Meaning
GDK_SHIFT_MASK Shift
GDK_LOCK_MASK Caps Lock
GDK_CONTROL_MASK Control
GDK_MOD1_MASK Mod1 (often Meta or Alt)
GDK_MOD2_MASK Mod2
GDK_MOD3_MASK Mod3
GDK_MOD4_MASK Mod4
GDK_MOD5_MASK Mod5
GDK_BUTTON1_MASK Button 1
GDK_BUTTON2_MASK Button 2
GDK_BUTTON3_MASK Button 3
GDK_BUTTON4_MASK Button 4
GDK_BUTTON5_MASK Button 5
GDK_RELEASE_MASK Key releases

The button member of GdkEventButton indicates which button triggered the event (i.e., the button which was pressed or released). Buttons are numbered from one to five; most of the time, button one is the left button, button two is the middle button, and button three is the right button. Left-handed users might reverse these. Button four and five events are generated by some scroll wheel mice when you spin the scroll wheel; GTK+ attempts to send capture these events and move nearby scroll bars. You should probably ignore any events you receive for buttons four or five.

The three standard mouse buttons have conventional meanings in Gnome. Button one is used for selection, drag and drop, and operating widgets: the most common tasks. Button three typically activates a pop-up menu. Button two is traditionally used to move objects, such as the panel. Sometimes button one moves objects also; for example, desktop icons can be moved with either button one or two. It is a good idea to be consistent with other applications whenever possible.

The source and deviceid members are used to determine which device triggered the event; the user may have a graphics tablet and a mouse connected simultaneously, for example. You can ignore these fields unless you are writing an application that can take advantage of non-mouse devices.

The last two members of GdkEventButton, x_root and y_root, are the x and y coordinates translated to be relative to the root window rather than the window receiving the event. You can use these as "absolute" coordinates to compare events from two different windows.

Keyboard Events

There are only two types of key event: GDK_KEY_PRESS and GDK_KEY_RELEASE. Some hardware does not generate key release events; you should not write code that depends on GDK_KEY_RELEASE events, though your code should respond sanely if one is received.

Here are the contents of a key event:


typedef struct _GdkEventKey GdkEventKey;

struct _GdkEventKey
{
  GdkEventType type;
  GdkWindow *window;
  gint8 send_event;
  guint32 time;
  guint state;
  guint keyval;
  gint length;
  gchar *string;
};

The first three members are the standard members from GdkEventAny; the time and state members are identical to those found in GdkEventButton.

keyval contains a keysym. The X server keeps a global translation table which converts combinations of physical keys and modifiers to keysyms. For example, the key marked "A" on the keyboard typically generates the keysym GDK_a with no modifiers, and GDK_A with shift held down. Users can change the physical-key-to-keysym mapping; for example, they may rearrange their keys to create a Dvorak keyboard (more commonly, they might swap Control and Caps Lock, or use the Alt key as a Meta key). Keysyms are defined in gdk/gdkkeysyms.h. You will need to include this file to use the keyval field.

Keysyms are matched with a string representation. For example, the GDK_a keysym typically maps to the string "a". (However, X allows the keysym-to-string mapping to be modified.) The string member of GdkEventKey contains a keysym's string representation, and the length member contains the string's length. Keep in mind that the length may be 0 (many non-alphanumeric keys have no string representation by default). (If you're familiar with Xlib, the string field is simply the result of XLookupString() or XmbLookupString(). Even if you aren't familiar with Xlib, the man pages for these functions may be helpful.)

In general, if you are reading key events in order to create a textual representation of what the user is typing, you should use the string field of GdkEventKey. GtkEntry and GtkText use the string field, for example. A word processor would also read this field. If you're reading key events for some other reason (such as keyboard shortcuts), or if you are interested in keys with no string representation by default (such as function keys or arrow keys), you will need to use the keyval field and the keysyms defined in gdk/gdkkeysyms.h.

Here is a sample key event callback demonstrating how to extract information from a key event. It would be suitable for connection to the "key_press_event" signal of any GtkWidget:


static gint 
key_press_cb(GtkWidget* widget, GdkEventKey* event, gpointer data)
{
  if (event->length > 0)
    printf("The key event's string is `%s'\n", event->string);

  printf("The name of this keysym is `%s'\n", 
         gdk_keyval_name(event->keyval));

  switch (event->keyval)
    {
    case GDK_Home:
      printf("The Home key was pressed.\n");
      break;
    case GDK_Up:
      printf("The Up arrow key was pressed.\n");
      break;
    default:
      break;
    }

  if (gdk_keyval_is_lower(event->keyval))
    {
      printf("A non-uppercase key was pressed.\n");
    }
  else if (gdk_keyval_is_upper(event->keyval))
    {
      printf("An uppercase letter was pressed.\n");
    }
}

gdk_keyval_name() is useful for debugging; it returns the name of the keysym without the GDK_ prefix. For example, it returns "Home" if passed the value GDK_Home. The string is statically allocated. gdk_keyval_is_lower() returns FALSE if the keysym has an uppercase equivalent. Thus it returns TRUE for lowercase letters, numbers, and all non-alphanumeric characters. It returns FALSE only for uppercase letters. gdk_keyval_is_upper() returns the opposite values.

Mouse Movement Events

Events are emitted to let you track the mouse as it moves around the screen. Motion events are emitted as the pointer moves inside a window; crossing events are emitted when the pointer enters or leaves a GdkWindow. The type field for motion events is GDK_MOTION_NOTIFY. There are two kinds of crossing events: GDK_ENTER_NOTIFY and GDK_LEAVE_NOTIFY.

There are two ways to track motion events. If you specify GDK_POINTER_MOTION_MASK in the event mask for a window, you will receive as many motion events as the X server can generate. If the user moves the pointer rapidly, you will be deluged in motion events; you must handle them quickly, or your application may become unresponsive while it processes the backlog. If you also specify GDK_POINTER_MOTION_HINT_MASK, motion events are sent one at a time. At most one event will be sent until you call gdk_window_get_pointer(), the pointer leaves and re-enters the window, or a button or key event occurs. Thus, each time you receive a motion event, you must call gdk_window_get_pointer() to get the current pointer position and signal the server that you are ready for another event. See the section called The Mouse Pointer for details on gdk_window_get_pointer().

Which mode you choose depends on the application. If you need to trace the exact trajectory of the pointer, you will want to get all motion events. If you only care about the most recent pointer position, you will want to include GDK_POINTER_MOTION_HINT_MASK in your window's event mask to minimize network traffic and maximize responsiveness. One caveat: gdk_window_get_pointer() requires a server round-trip to obtain the pointer position; so it does place some maximum limit on your application's responsiveness. If you can handle motion events quickly enough to keep them from backlogging, your application will probably seem faster without GDK_POINTER_MOTION_HINT_MASK. Motion events are unlikely to come more often than a couple hundred per second --- so if you can handle them in less than 5 milliseconds, you should be OK.

You can ask to receive motion events only while one or more mouse buttons are held down. To receive motion events while any button is down, use GDK_BUTTON_MOTION_MASK in place of GDK_POINTER_MOTION_MASK. You can use GDK_POINTER_MOTION_HINT_MASK with GDK_BUTTON_MOTION_MASK to limit the number of events received, just as you can use it with GDK_POINTER_MOTION_MASK. If you are only interested in motion events while a certain button is pressed, you can use the more specific GDK_BUTTON1_MOTION_MASK, GDK_BUTTON2_MOTION_MASK, and GDK_BUTTON3_MOTION_MASK. Any combination of these three is allowed. They can also be combined with GDK_POINTER_MOTION_HINT_MASK to limit the number of events.

In sum, you can select which motion events to receive along the "button state" dimension using these five masks:

By default, you are deluged with events as quickly as the X server can generate them; adding GDK_POINTER_MOTION_HINT_MASK to the event mask enables one-at-a-time behavior.

Motion events are represented by GdkEventMotion:


typedef struct _GdkEventMotion GdkEventMotion;

struct _GdkEventMotion
{
  GdkEventType type;
  GdkWindow *window;
  gint8 send_event;
  guint32 time;
  gdouble x;
  gdouble y;
  gdouble pressure;
  gdouble xtilt;
  gdouble ytilt;
  guint state;
  gint16 is_hint;
  GdkInputSource source;
  guint32 deviceid;
  gdouble x_root, y_root;
};

Most of these fields should be familiar to you from GdkEventButton; in fact, the only field unique to GdkEventMotion is the is_hint flag. If this field is TRUE, GDK_POINTER_MOTION_HINT_MASK was selected. You might use this flag if you are writing a widget for other people to use, and you want to let them choose how to receive motion events. In your motion event handler, you could do this:


  double x, y;

  x = event->motion.x;
  y = event->motion.y;

  if (event->motion.is_hint)
    gdk_window_get_pointer(event->window, &x, &y, NULL);

That is, you call gdk_window_get_pointer() only if necessary. If you are using GDK_POINTER_MOTION_HINT_MASK, you should prefer the results from gdk_window_get_pointer() to the coordinates given in the event, because they are more recent. (If you are receiving every event, it makes no sense to call gdk_window_get_pointer() because it is relatively slow and will worsen the backlog---you're getting every event eventually anyway.)

Crossing events occur when the mouse pointer enters or leaves a window. If you move the mouse pointer rapidly across your application, GDK generates these events for every window you pass through. However, GTK+ will try to remove the events "in the middle" and forward only the first leave event and the last enter event to widgets. If you feel you should be getting enter/leave events when you aren't, this optimization is a likely cause.

Here is GdkEventCrossing:


typedef struct _GdkEventCrossing GdkEventCrossing;

struct _GdkEventCrossing
{
  GdkEventType type;
  GdkWindow *window;
  gint8 send_event;
  GdkWindow *subwindow;
  guint32 time;
  gdouble x;
  gdouble y;
  gdouble x_root;
  gdouble y_root;
  GdkCrossingMode mode;
  GdkNotifyType detail;
  gboolean focus;
  guint state;
};

Again, many of the fields should be familiar; coordinates relative to the event window and the root window, a time stamp, a state bitfield indicating which buttons and modifiers are active, and the standard three fields from GdkEventAny. However, there are several new fields.

The standard window field contains a pointer to the window the pointer is entering or leaving; x and y are relative to this window. However, the pointer may have been in a child of the window receiving the event before a leave event occurred; the pointer may end up in a child window when an enter event occurs. In these cases, subwindow is set to the child window. Otherwise subwindow is NULL. Note that the child window will receive its own enter and leave events, if it GDK_ENTER_NOTIFY_MASK or GDK_LEAVE_NOTIFY_MASK are in its event mask.

The mode field indicates whether the event occurred normally, or as part of a pointer grab. When the pointer is grabbed or ungrabbed (see the section called The Mouse Pointer), the pointer may be moved; crossing events caused by a grab have the GDK_CROSSING_GRAB mode, those caused by an ungrab have GDK_CROSSING_UNGRAB, and all others have GDK_CROSSING_NORMAL. This field appears to be completely useless; some quick greps through GTK+ and Gnome reveal no examples of its use.

The detail field is rarely used. It gives information about the relative tree positions of the window being left and the window being entered. It has two simple and useful values:

Several other values are also possible: GDK_NOTIFY_VIRTUAL, GDK_NOTIFY_INFERIOR, GDK_NOTIFY_NONLINEAR, GDK_NOTIFY_NONLINEAR_VIRTUAL, and GDK_NOTIFY_UNKNOWN. However, they are never used and are too complicated to explain here.

Keyboard Focus

The focus field in GdkEventCrossing indicates whether the event window or one of its ancestors has the keyboard input focus. Keyboard focus is an X concept, used to determine which window should receive key events. The window manager decides which toplevel window has the focus (usually the focused window is highlighted and brought to the top; most window managers let you choose between "focus follows mouse" and "click to focus" modes). When an application has the focus, it is free to move it among its subwindows---perhaps different text entry fields. However, GTK+ does not use the X focus mechanism for subwindows. Toplevel GtkWindow widgets are the only ones which receive the X focus. Thus, they receive all raw key events from X (by way of GDK). GTK+ implements its own concept of widget focus, which is analagous to X's window focus, but in reality entirely distinct. When a toplevel GtkWindow widget receives key events, it forwards them to the widget with GTK+'s focus.

In short, this means the focus flag will be TRUE if the toplevel GtkWindow containing the event window currently has the X focus. The focus flag is unrelated to GTK+'s widget focus concept.

Focus Change Events

The previous section explained the difference between GTK+'s concept of keyboard focus and the X/GDK concept. This makes focus events a little bit confusing. There is only one type of focus event, GDK_FOCUS_CHANGE, which is received whenever a window gains or loses the keyboard focus. As we just said, only toplevel GtkWindow widgets ever gain or lose the focus from GDK's point of view; so this event may not seem useful. However, each GtkWindow maintains a current "focus widget" and forwards key events to that widget. It also synthesizes GDK-style focus events as the focus widget changes. Thus, even though GDK-style focus is not being used, widgets receive events in the same way they would if it were being used. There are subtle differences: for example, widgets receive focus events whether or not their GdkWindow's event mask includes GDK_FOCUS_CHANGE_MASK. Only toplevel widgets need to specify this mask.

Focus events themselves are very simple. When a widget gains the keyboard focus, it receives a focus event with its in member set to TRUE (a "focus in event") and when a widget loses the focus it receives a focus event with the in member set to FALSE (a "focus out event"). Otherwise, focus events contain only the three standard fields from GdkEventAny:


typedef struct _GdkEventFocus GdkEventFocus;

struct _GdkEventFocus
{
  GdkEventType type;
  GdkWindow *window;
  gint8 send_event;
  gint16 in;
};

Expose Events

Expose events are received when a previously-obscured region of a window becomes visible. GdkWindow contents are not recorded; that is, if you draw to a GdkWindow, then the X server places another window on top of it, the graphics in the bottom window will be lost. When the top window is moved away, the bottom window will receive an expose event indicating the region that needs to be re-drawn. Expose events are also sent when a window first appears on-screen. (Incidentally, you should not draw into a GdkWindow until you receive the first expose event. The first expose event is your signal that the window is actually on-screen.)

Expose events have a unique feature: GTK+ synthesizes them for windowless widgets. This is the only kind of event GTK_NO_WINDOW widgets will receive.

Expose events are very simple:


typedef struct _GdkEventExpose GdkEventExpose;

struct _GdkEventExpose
{
  GdkEventType type;
  GdkWindow *window;
  gint8 send_event;
  GdkRectangle area;
  gint count;
};

area is the area that has been exposed and should be redrawn. count is the number of expose events that follow this one; conceivably you might want to compress successive events into a single redraw. However, GDK already makes a reasonable effort to do this, so adding another pass is unlikely to gain much. GdkRectangle is defined as follows:


typedef struct _GdkRectangle GdkRectangle;

struct _GdkRectangle
{
  gint16 x;
  gint16 y;
  guint16 width;
  guint16 height;
};

There is one other time you will receive expose events. If you call gdk_window_copy_area() to copy part of one window into another, the source window region may be partially or completely obscured. If it is, X will be unable to copy something sensible from the obscured region. By default, expose events will be generated for the areas of the destination window X was unable to copy something to. Your program's standard redraw routine can then refresh these areas of the destination window by hand. You can turn this behavior off by calling gdk_gc_set_exposures() on the graphics context passed to gdk_window_copy_area(). (Graphics contexts are discussed in the section called Graphics Contexts.)

If the source window region is not obscured, gdk_window_copy_area() can successfully draw the entire destination region. In this case, no expose events are generated; a single "no expose" event is generated instead. This event contains no information beyond the standard event fields:


typedef struct _GdkEventNoExpose GdkEventNoExpose;

struct _GdkEventNoExpose
{
  GdkEventType type;
  GdkWindow *window;
  gint8 send_event;
};

Window Change Events

As a GdkWindow is shown, hidden, resized, or destroyed, events are emitted.

Configure events indicate that the size or position of the event window has changed. They include the new size and positon of the window:


typedef struct _GdkEventConfigure GdkEventConfigure;

struct _GdkEventConfigure
{
  GdkEventType type;
  GdkWindow *window;
  gint8 send_event;
  gint16 x, y;
  gint16 width;
  gint16 height;
};

All widgets receive this event (since GDK_STRUCTURE_MASK is automatically in the event mask), but the widget size allocation system already conveys the necessary information. That is, most widgets resize their GdkWindow themselves in response to a size allocation; configure events just report back the resize---not very useful. There are two notable exceptions. First, the toplevel GtkWindow widget is in charge of initiating the size allocation process, and has no parent to get an allocation from, so it monitors configure events to determine its size allocation. When you resize a GtkWindow using its window manager decorations, it will receive configure events and act accordingly. The second exception is GtkDrawingArea. GtkDrawingArea sends itself a configure event when it receives its size allocation. This is convenient, since you will usually want to repaint the contents of the drawing area if it is resized. Like all "fake" events GTK+ creates, send_event will be TRUE for this configure event.

Other changes in a GdkWindow are signalled with GdkEventAny; these events contain no special information, they just tell you that something has occurred. They are distinguished by their type field:

Little-used Events

There are a few other event types that aren't very useful, so this book does not cover them in detail. This section gives a brief description of each one, for completeness. You can safely skip this section.

Visibility Events

Visibility events occur when a window is obscured or unobscured. They are not very useful, because other events already give you the information you need. In particular, when a window is unobscured you receive expose events indicating which newly-visible areas should be redrawn. Here is the event:


typedef struct _GdkEventVisibility GdkEventVisibility;

struct _GdkEventVisibility
{
  GdkEventType type;
  GdkWindow *window;
  gint8 send_event;
  GdkVisibilityState state;
};

state is an enumeration value indicating whether the window was obscured or unobscured.

Property Events

X associates properties with windows. These are basically key-value pairs used for interclient communication; most commonly, they relay some information about a toplevel window to the window manager. GTK+ provides a high-level interface for all the important properties, so you should not need to deal with them directly. Property events are sent when a property's value changes.


typedef struct _GdkEventProperty GdkEventProperty;

struct _GdkEventProperty
{
  GdkEventType type;
  GdkWindow *window;
  gint8 send_event;
  GdkAtom atom;
  guint32 time;
  guint state;
};

Selection Events

X has a simple cut-and-paste mechanism (typically, you select some text with button 1 and paste with button 2). The current contents of the "clipboard" are called the selection. You only need to know about this if you are writing a widget like GtkText or GtkEntry that allows you to cut and paste text. Selection events indicate that the current selection has changed, and look like this:


typedef struct _GdkEventSelection GdkEventSelection;

struct _GdkEventSelection
{
  GdkEventType type;
  GdkWindow *window;
  gint8 send_event;
  GdkAtom selection;
  GdkAtom target;
  GdkAtom property;
  guint32 requestor;
  guint32 time;
};

Most GTK+ widgets deal with the selection using a higher-level interface, found in gtk/gtkselection.h. If you're interested in writing selection-handling code, have a look at this header.

Client Events

Client events are an arbitrary chunk of data sent from one application to another. However, there are some conventional "messages" that can be sent, mostly defined in the Inter-Client Communication Conventions Manual (ICCCM). The ICCCM is free with the X Window System distribution from the Open Group. Client events are mostly used for communication between applications and the window manager. (The Xlib event being wrapped is called ClientMessage, so look for ClientMessage in the ICCCM.) An important ClientMessage event is sent from the window manager to applications, asking for windows to be deleted. However, GDK converts these events to a GdkEventAny with type GDK_DELETE, so a GdkEventClient will not be received. Some events involved in drag-and-drop are also ClientMessage events, but GDK translates these to GdkEventDND, so a GdkEventClient will not be received for them either. GdkEventClient will only be received if some other client sends your application an unconventional message GDK and GTK+ are not prepared to understand. Most of the common messages are nicely wrapped in a high-level interface.

Just for reference, here is the event:


typedef struct _GdkEventClient GdkEventClient;

struct _GdkEventClient
{
  GdkEventType type;
  GdkWindow *window;
  gint8 send_event;
  GdkAtom message_type;
  gushort data_format;
  union {
    char b[20];
    short s[10];
    long l[5];
  } data;
};

The union at the end is used to hold the contents of the message. send_event is always TRUE, since this event is always sent from one client to another and does not come from the X server.

Drag and Drop Events

Drag and drop events are actually pretty useful if you're implementing a drag and drop feature in your application. However, since few applications need to do this and the topic is fairly involved, this books glosses over the topic. GTK+ comes with some documentation on drag and drop and several examples.


typedef struct _GdkEventDND GdkEventDND;

struct _GdkEventDND {
  GdkEventType type;
  GdkWindow *window;
  gint8 send_event;
  GdkDragContext *context;

  guint32 time;
  gshort x_root, y_root;
};

Proximity Events

Proximity events are generated when using GDK's wrapper for the XInput extension. The XInput extension is an add-on for standard X that allows you to use nonstandard devices such as graphics tablets. A proximity event indicates that the stylus has moved in or out of contact with the tablet, or perhaps that the user's finger has moved in or out of contact with a touch screen. The X distribution comes with some documentation for the XInput extension, and Owen Taylor's gsumi application contains example GDK code.


typedef struct _GdkEventProximity GdkEventProximity;

struct _GdkEventProximity
{
  GdkEventType type;
  GdkWindow *window;
  gint8 send_event;
  guint32 time;
  GdkInputSource source;
  guint32 deviceid;
};