summaryrefslogtreecommitdiff
path: root/docs/reference/gtk/input-handling.xml
blob: 92017072fca7d9d72d01e2e5f3306cf39c038459 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
<?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 Handling Model</refentrytitle>
<manvolnum>3</manvolnum>
<refmiscinfo>GTK Library</refmiscinfo>
</refmeta>

<refnamediv>
<refname>The GTK+ Input Handling Model</refname>
<refpurpose>
    GTK+ input handling in detail
</refpurpose>
</refnamediv>


  <refsect1 id="input-overview">
    <title>Overview of GTK+ input 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>

    <!-- input events: button, touch, key, motion, etc -->
    <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 window - for pointer events,
     the window under the pointer (grabs complicate this), for keyboard events,
     the window 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+ is initialized, it sets up an event handler function with
      gdk_event_handler_set(), which receives all of these input events
      (as well as others, for instance window management related events).
    </para>
  </refsect2>

  <refsect2>
    <title>Event propagation</title>

    <para>
      When GTK+ receives an event, it determines the target widget that
      it is directed to. Unless grabs are involved, this is done by finding
      the widget to which the window of the event belongs.
    </para>

    <para>
      The event is then propagated from the toplevel window down to the 
      target widget. In this phase, which is known as the “capture” phase,
      gestures 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 let run gestures attached to it with
      GTK_PHASE_TARGET. This is known as the “target” phase, and does only
      happen on that widget.
    </para>

    <para>
      Next, the appropriate event signal is emitted for the event in question,
      e.g. “motion-notify-event”. Handling these signals was the primary
      way to handle input in GTK+ widgets before gestures were introduced.
      The signals are emitted from the target widget up to the toplevel,
      until a signal handler indicates that it has handled the event, by
      returning GDK_EVENT_STOP.
    </para>

    <para>
      The default handlers for the event signals send the event
      to gestures that are attached with GTK_PHASE_BUBBLE. Therefore,
      gestures in the “bubble” phase are only used if the widget does
      not have its own event handlers, or takes care to chain up to the
      default GtkWidget handlers.
    </para>

    <para>
      Anytime during the propagation phase, a widget may indicate that a
      received event was consumed and propagation should therefore be stopped.
      In traditional event handlers, this is hinted by returning GDK_EVENT_STOP,
      if gestures are used, this may happen when the widget tells the gesture
      to claim the event touch sequence (or the pointer events) for its own. See the
      "gesture states" section below to know more of the latter.
    </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>

    <para>
      On some windowing platforms, multitouch devices perform pointer emulation, this works
      by granting a “pointer emulating” hint to one of the currently interacting touch
      sequences, which will be reported on every GdkEventTouch event from that sequence. By
      default, if a widget didn't request touch events by setting GDK_TOUCH_MASK on its
      event mask and didn't override GtkWidget::touch-event, GTK+ will transform these
      “pointer emulating” events into semantically similar GdkEventButton and GdkEventMotion
      events. Depending on GDK_TOUCH_MASK being in the event mask or not, non-pointer-emulating
      sequences could still trigger gestures or just get filtered out, regardless of the widget
      not handling those directly.
    </para>

    <para>
      If the widget sets GDK_TOUCH_MASK on its event mask and doesn't chain up on
      GtkWidget::touch-event, only touch events will be received, and no pointer emulation
      will be performed.
    </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>

    <!-- focus, tab, directional navigation -->
    <!-- mnemonics, accelerators, bindings -->
  </refsect2>

  <refsect2>
    <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 gestures 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>
      Being gestures “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 gesture 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>