forked from AuroraMiddleware/gtk
b927609e21
This is not a function that can be safely called outside of GTK.
376 lines
15 KiB
XML
376 lines
15 KiB
XML
<?xml version="1.0"?>
|
||
<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.3//EN"
|
||
"http://www.oasis-open.org/docbook/xml/4.3/docbookx.dtd" [
|
||
]>
|
||
<refentry id="chap-input-handling">
|
||
<refmeta>
|
||
<refentrytitle>The GTK Input Model</refentrytitle>
|
||
<manvolnum>3</manvolnum>
|
||
<refmiscinfo>GTK Library</refmiscinfo>
|
||
</refmeta>
|
||
|
||
<refnamediv>
|
||
<refname>The GTK Input Model</refname>
|
||
<refpurpose>
|
||
input and event handling in detail
|
||
</refpurpose>
|
||
</refnamediv>
|
||
|
||
|
||
<refsect1 id="input-overview">
|
||
<title>Overview of GTK input and event handling</title>
|
||
|
||
<para>
|
||
This chapter describes in detail how GTK handles input. If you are interested
|
||
in what happens to translate a key press or mouse motion of the users into a
|
||
change of a GTK widget, you should read this chapter. This knowledge will also
|
||
be useful if you decide to implement your own widgets.
|
||
</para>
|
||
|
||
<refsect2>
|
||
<title>Devices and events</title>
|
||
|
||
<!-- input devices: master/slave, keyboard/pointer/touch -->
|
||
<para>
|
||
The most basic input devices that every computer user has interacted with are
|
||
keyboards and mice; beyond these, GTK supports touchpads, touchscreens and
|
||
more exotic input devices such as graphics tablets. Inside GTK, every such
|
||
input device is represented by a #GdkDevice object.
|
||
</para>
|
||
|
||
<para>
|
||
To simplify dealing with the variability between these input devices, GTK
|
||
has a concept of master and slave devices. The concrete physical devices that
|
||
have many different characteristics (mice may have 2 or 3 or 8 buttons,
|
||
keyboards have different layouts and may or may not have a separate number
|
||
block, etc) are represented as slave devices. Each slave device is
|
||
associated with a virtual master device. Master devices always come in
|
||
pointer/keyboard pairs - you can think of such a pair as a 'seat'.
|
||
</para>
|
||
<para>
|
||
GTK widgets generally deal with the master devices, and thus can be used
|
||
with any pointing device or keyboard.
|
||
</para>
|
||
|
||
<para>
|
||
When a user interacts with an input device (e.g. moves a mouse or presses
|
||
a key on the keyboard), GTK receives events from the windowing system.
|
||
These are typically directed at a specific surface - for pointer events,
|
||
the surface under the pointer (grabs complicate this), for keyboard events,
|
||
the surface with the keyboard focus.
|
||
</para>
|
||
<para>
|
||
GDK translates these raw windowing system events into #GdkEvents.
|
||
Typical input events are:
|
||
<simplelist>
|
||
<member>#GdkEventButton</member>
|
||
<member>#GdkEventMotion</member>
|
||
<member>#GdkEventCrossing</member>
|
||
<member>#GdkEventKey</member>
|
||
<member>#GdkEventFocus</member>
|
||
<member>#GdkEventTouch</member>
|
||
</simplelist>
|
||
</para>
|
||
<para>
|
||
Additionally, GDK/GTK synthesizes other signals to let know whether
|
||
grabs (system-wide or in-app) are taking input away:
|
||
<simplelist>
|
||
<member>#GdkEventGrabBroken</member>
|
||
<member>#GtkWidget::grab-notify</member>
|
||
</simplelist>
|
||
</para>
|
||
<para>
|
||
When GTK creates a GdkSurface, it connects to the ::event signal
|
||
on it, which receives all of these input events. Surfaces have
|
||
have signals and properties, e.g. to deal with window management
|
||
related events.
|
||
</para>
|
||
</refsect2>
|
||
|
||
<refsect2 id="event-propagation">
|
||
<title>Event propagation</title>
|
||
|
||
<para>
|
||
The function which initially receives input events on the GTK
|
||
side is responsible for a number of tasks.
|
||
</para>
|
||
<orderedlist>
|
||
<listitem><para>
|
||
Compress enter/leave notify events. If the event passed build an
|
||
enter/leave pair together with the next event (peeked from GDK), both
|
||
events are thrown away. This is to avoid a backlog of (de-)highlighting
|
||
widgets crossed by the pointer.
|
||
</para></listitem>
|
||
<listitem><para>
|
||
Find the widget which got the event. If the widget can’t be determined
|
||
the event is thrown away unless it belongs to a INCR transaction.
|
||
</para></listitem>
|
||
<listitem><para>
|
||
Then the event is pushed onto a stack so you can query the currently
|
||
handled event with gtk_get_current_event().
|
||
</para></listitem>
|
||
<listitem><para>
|
||
The event is sent to a widget. If a grab is active all events for widgets
|
||
that are not in the contained in the grab widget are sent to the latter
|
||
with a few exceptions:
|
||
<itemizedlist>
|
||
<listitem><para>
|
||
Deletion and destruction events are still sent to the event widget for
|
||
obvious reasons.
|
||
</para></listitem>
|
||
<listitem><para>
|
||
Events which directly relate to the visual representation of the event
|
||
widget.
|
||
</para></listitem>
|
||
<listitem><para>
|
||
Leave events are delivered to the event widget if there was an enter
|
||
event delivered to it before without the paired leave event.
|
||
</para></listitem>
|
||
<listitem><para>
|
||
Drag events are not redirected because it is unclear what the semantics
|
||
of that would be.
|
||
</para></listitem>
|
||
</itemizedlist>
|
||
</para></listitem>
|
||
<listitem><para>
|
||
After finishing the delivery the event is popped from the event stack.
|
||
</para></listitem>
|
||
</orderedlist>
|
||
|
||
<para>
|
||
When a GDK backend produces an input event, it is tied to a #GdkDevice and
|
||
a #GdkSurface, which in turn represents a windowing system surface in the
|
||
backend. If a widget has grabbed the current input device, or all input
|
||
devices, the event is propagated to that #GtkWidget. Otherwise, it is
|
||
propagated to the the #GtkRoot which owns the #GdkSurface receiving the event.
|
||
</para>
|
||
|
||
<para>
|
||
Grabs are implemented for each input device, and globally. A grab for a
|
||
specific input device (gtk_device_grab_add()), is sent events in
|
||
preference to a global grab (gtk_grab_add()). Input grabs only have effect
|
||
within the #GtkWindowGroup containing the #GtkWidget which registered the
|
||
event’s #GdkSurface. If this #GtkWidget is a child of the grab widget, the
|
||
event is propagated to the child — this is the basis for propagating
|
||
events within modal dialogs.
|
||
</para>
|
||
|
||
<para>
|
||
An event is propagated down and up the widget hierarchy in three phases
|
||
(see #GtkPropagationPhase) towards a target widget.
|
||
</para>
|
||
|
||
<para>
|
||
For key events, the top-level window gets a first shot at activating
|
||
mnemonics and accelerators. If that does not consume the events,
|
||
the target widget for event propagation is window's current focus
|
||
widget (see gtk_window_get_focus()).
|
||
</para>
|
||
|
||
<para>
|
||
For pointer events, the target widget is determined by picking
|
||
the widget at the events coordinates (see gtk_window_pick()).
|
||
</para>
|
||
|
||
<para>In the first phase (the “capture” phase) the event is
|
||
delivered to each widget from the top-most (the top-level
|
||
#GtkWindow or grab widget) down to the target #GtkWidget.
|
||
<link linkend="event-controllers-and-gestures">Event
|
||
controllers</link> that are attached with %GTK_PHASE_CAPTURE
|
||
get a chance to react to the event.
|
||
</para>
|
||
|
||
<para>
|
||
After the “capture” phase, the widget that was intended to be the
|
||
destination of the event will run event controllers attached to
|
||
it with %GTK_PHASE_TARGET. This is known as the “target” phase,
|
||
and only happens on that widget.
|
||
</para>
|
||
|
||
<para>
|
||
In the last phase (the “bubble” phase), the event is delivered
|
||
to each widget from the target to the top-most, and event
|
||
controllers attached with %GTK_PHASE_BUBBLE are run.
|
||
</para>
|
||
|
||
<para>
|
||
Events are not delivered to a widget which is insensitive or
|
||
unmapped.
|
||
</para>
|
||
|
||
<para>
|
||
Any time during the propagation phase, a controller may indicate
|
||
that a received event was consumed and propagation should
|
||
therefore be stopped. If gestures are used, this may happen
|
||
when the gesture claims the event touch sequence (or the
|
||
pointer events) for its own. See the “gesture states” section
|
||
below to learn more about gestures and sequences.
|
||
</para>
|
||
</refsect2>
|
||
|
||
<refsect2>
|
||
<title>Touch events</title>
|
||
|
||
<para>
|
||
Touch events are emitted as events of type %GDK_TOUCH_BEGIN,
|
||
%GDK_TOUCH_UPDATE or %GDK_TOUCH_END, those events contain an
|
||
“event sequence” that univocally identifies the physical touch
|
||
until it is lifted from the device.
|
||
</para>
|
||
</refsect2>
|
||
|
||
<refsect2>
|
||
<title>Grabs</title>
|
||
|
||
<para>
|
||
Grabs are a method to claim all input events from a device,
|
||
they happen either implicitly on pointer and touch devices,
|
||
or explicitly. Implicit grabs happen on user interaction, when
|
||
a #GdkEventButtonPress happens, all events from then on, until
|
||
after the corresponding #GdkEventButtonRelease, will be reported
|
||
to the widget that got the first event. Likewise, on touch events,
|
||
every #GdkEventSequence will deliver only events to the widget
|
||
that received its %GDK_TOUCH_BEGIN event.
|
||
</para>
|
||
|
||
<para>
|
||
Explicit grabs happen programatically (both activation and
|
||
deactivation), and can be either system-wide (GDK grabs) or
|
||
application-wide (GTK grabs). On the windowing platforms that
|
||
support it, GDK grabs will prevent any interaction with any other
|
||
application/window/widget than the grabbing one, whereas GTK grabs
|
||
will be effective only within the application (across all its
|
||
windows), still allowing for interaction with other applications.
|
||
</para>
|
||
|
||
<para>
|
||
But one important aspect of grabs is that they may potentially
|
||
happen at any point somewhere else, even while the pointer/touch
|
||
device is already grabbed. This makes it necessary for widgets to
|
||
handle the cancellation of any ongoing interaction. Depending on
|
||
whether a GTK or GDK grab is causing this, the widget will
|
||
respectively receive a #GtkWidget::grab-notify signal, or a
|
||
#GdkEventGrabBroken event.
|
||
</para>
|
||
|
||
<para>
|
||
On gestures, these signals are handled automatically, causing the
|
||
gesture to cancel all tracked pointer/touch events, and signal
|
||
the end of recognition.
|
||
</para>
|
||
</refsect2>
|
||
|
||
<refsect2>
|
||
<title>Keyboard input</title>
|
||
|
||
<para>
|
||
Every #GtkWindow maintains a single focus location (in
|
||
the ::focus-widget property). The focus widget is the
|
||
target widget for key events sent to the window. Only
|
||
widgets which have ::can-focus set to %TRUE can become
|
||
the focus. Typically these are input controls such as
|
||
entries or text fields, but e.g. buttons can take the
|
||
focus too.
|
||
</para>
|
||
|
||
<para>
|
||
Input widgets can be given the focus by clicking on them,
|
||
but focus can also be moved around with certain key
|
||
events (this is known as “keyboard navigation”). GTK
|
||
reserves the Tab key to move the focus to the next location,
|
||
and Shift-Tab to move it back to the previous one. In addition
|
||
many containers allow “directional navigation” with the
|
||
arrow keys.
|
||
</para>
|
||
|
||
<!-- mnemonics, accelerators, bindings -->
|
||
</refsect2>
|
||
|
||
<refsect2 id="event-controllers-and-gestures">
|
||
<title>Event controllers and gestures</title>
|
||
|
||
<para>
|
||
Event controllers are standalone objects that can perform
|
||
specific actions upon received #GdkEvents. These are tied
|
||
to a #GtkWidget, and can be told of the event propagation
|
||
phase at which they will manage the events.
|
||
</para>
|
||
|
||
<para>
|
||
Gestures are a set of specific controllers that are prepared
|
||
to handle pointer and/or touch events, each gesture
|
||
implementation attempts to recognize specific actions out the
|
||
received events, notifying of the state/progress accordingly to
|
||
let the widget react to those. On multi-touch gestures, every
|
||
interacting touch sequence will be tracked independently.
|
||
</para>
|
||
|
||
<para>
|
||
Since gestures are “simple” units, it is not uncommon to tie
|
||
several together to perform higher level actions, grouped
|
||
gestures handle the same event sequences simultaneously, and
|
||
those sequences share a same state across all grouped
|
||
gestures. Some examples of grouping may be:
|
||
|
||
<simplelist>
|
||
<member>
|
||
A “drag” and a “swipe” gestures may want grouping.
|
||
The former will report events as the dragging happens,
|
||
the latter will tell the swipe X/Y velocities only after
|
||
recognition has finished.
|
||
</member>
|
||
<member>
|
||
Grouping a “drag” gesture with a “pan” gesture will only
|
||
effectively allow dragging in the panning orientation, as
|
||
both gestures share state.
|
||
</member>
|
||
<member>
|
||
If “press” and “long press” are wanted simultaneously,
|
||
those would need grouping.
|
||
</member>
|
||
</simplelist>
|
||
</para>
|
||
</refsect2>
|
||
|
||
<refsect2>
|
||
<title>Gesture states</title>
|
||
<para>
|
||
Gestures have a notion of “state” for each individual touch
|
||
sequence. When events from a touch sequence are first received,
|
||
the touch sequence will have “none” state, this means the touch
|
||
sequence is being handled by the gesture to possibly trigger
|
||
actions, but the event propagation will not be stopped.
|
||
</para>
|
||
|
||
<para>
|
||
When the gesture enters recognition, or at a later point in time,
|
||
the widget may choose to claim the touch sequences (individually
|
||
or as a group), hence stopping event propagation after the event
|
||
is run through every gesture in that widget and propagation phase.
|
||
Anytime this happens, the touch sequences are cancelled downwards
|
||
the propagation chain, to let these know that no further events
|
||
will be sent.
|
||
</para>
|
||
|
||
<para>
|
||
Alternatively, or at a later point in time, the widget may choose
|
||
to deny the touch sequences, thus letting those go through again
|
||
in event propagation. When this happens in the capture phase, and
|
||
if there are no other claiming gestures in the widget,
|
||
a %GDK_TOUCH_BEGIN/%GDK_BUTTON_PRESS event will be emulated and
|
||
propagated downwards, in order to preserve consistency.
|
||
</para>
|
||
|
||
<para>
|
||
Grouped gestures always share the same state for a given touch
|
||
sequence, so setting the state on one does transfer the state to
|
||
the others. They also are mutually exclusive, within a widget
|
||
there may be only one gesture group claiming a given sequence.
|
||
If another gesture group claims later that same sequence, the
|
||
first group will deny the sequence.
|
||
</para>
|
||
</refsect2>
|
||
|
||
</refsect1>
|
||
</refentry>
|