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:
-
GDK_POINTER_MOTION_MASK: all motion events
regardless of button state.
-
GDK_BUTTON_MOTION_MASK: all motion events
while any button is held.
-
GDK_BUTTON1_MOTION_MASK: all motion events
while button 1 is held.
-
GDK_BUTTON2_MOTION_MASK: all motion events
while button 2 is held.
-
GDK_BUTTON3_MOTION_MASK: all motion events
while button 3 is held.
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:
-
GDK_NOTIFY_INFERIOR
marks a crossing event received by a parent window
when the pointer moves into or out of a child
window.
-
GDK_NOTIFY_ANCESTOR
marks a crossing event received by a child window
when the pointer moves into or out of its parent
window.
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.