diff --git a/docs/reference/gtk/Makefile.am b/docs/reference/gtk/Makefile.am
index 5099c337a3..5abfcea60c 100644
--- a/docs/reference/gtk/Makefile.am
+++ b/docs/reference/gtk/Makefile.am
@@ -119,6 +119,7 @@ content_files = \
running.sgml \
building.sgml \
compiling.sgml \
+ device-interaction-patterns.xml \
drawing-model.xml \
glossary.xml \
migrating-2to3.xml \
@@ -142,6 +143,7 @@ content_files = \
overview.xml
expand_content_files = \
+ device-interaction-patterns.xml \
drawing-model.xml \
getting_started.xml \
glossary.xml \
diff --git a/docs/reference/gtk/device-interaction-patterns.xml b/docs/reference/gtk/device-interaction-patterns.xml
new file mode 100644
index 0000000000..0e028c3556
--- /dev/null
+++ b/docs/reference/gtk/device-interaction-patterns.xml
@@ -0,0 +1,578 @@
+
+
+
+ Multitouch and other device interaction patterns
+
+
+ Depending on the platform, GTK+ is able to handle a wide range of input
+ devices. Those are offered to applications in a 2-level hierarchy, with
+ virtual devices (or master devices) representing the visual cursors
+ displayed in the screen, which are each controlled by a number of physical
+ devices (or slave devices). Those devices can respectively be retrieved
+ from an input event with gdk_event_get_device() and
+ gdk_event_get_source_device().
+
+
+
+ In X11, GTK+ uses XInput2 for input events, which caters for a fully dynamic
+ device hierarchy, and support for multiple virtual pointer/keyboard pairs.
+
+
+
+ Listing and modifying the device hierarchy
+
+ carlos@sacarino:~$ xinput list
+ ⎡ Virtual core pointer id=2 [master pointer (3)]
+ ⎜ ↳ Virtual core XTEST pointer id=4 [slave pointer (2)]
+ ⎜ ↳ Wacom ISDv4 E6 Pen stylus id=10 [slave pointer (2)]
+ ⎜ ↳ Wacom ISDv4 E6 Finger touch id=11 [slave pointer (2)]
+ ⎜ ↳ SynPS/2 Synaptics TouchPad id=13 [slave pointer (2)]
+ ⎜ ↳ TPPS/2 IBM TrackPoint id=14 [slave pointer (2)]
+ ⎜ ↳ Wacom ISDv4 E6 Pen eraser id=16 [slave pointer (2)]
+ ⎣ Virtual core keyboard id=3 [master keyboard (2)]
+ ↳ Virtual core XTEST keyboard id=5 [slave keyboard (3)]
+ ↳ Power Button id=6 [slave keyboard (3)]
+ ↳ Video Bus id=7 [slave keyboard (3)]
+ ↳ Sleep Button id=8 [slave keyboard (3)]
+ ↳ Integrated Camera id=9 [slave keyboard (3)]
+ ↳ AT Translated Set 2 keyboard id=12 [slave keyboard (3)]
+ ↳ ThinkPad Extra Buttons id=15 [slave keyboard (3)]
+
+ carlos@sacarino:~$ xinput create-master eek
+ carlos@sacarino:~$ xinput list
+ ...
+ ⎡ eek pointer id=17 [master pointer (18)]
+ ⎜ ↳ eek XTEST pointer id=19 [slave pointer (17)]
+ ⎣ eek keyboard id=18 [master keyboard (17)]
+ ↳ eek XTEST keyboard id=20 [slave keyboard (18)]
+
+ carlos@sacarino:~$ xinput reattach 10 17
+ carlos@sacarino:~$ xinput list
+ ...
+ ⎡ eek pointer id=17 [master pointer (18)]
+ ⎜ ↳ Wacom ISDv4 E6 Pen stylus id=10 [slave pointer (17)]
+ ⎜ ↳ eek XTEST pointer id=19 [slave pointer (17)]
+ ⎣ eek keyboard id=18 [master keyboard (17)]
+ ↳ eek XTEST keyboard id=20 [slave keyboard (18)]
+
+
+
+
+ Anytime a virtual device is added or removed, or a physical device
+ is attached to another virtual device, or left floating (detached
+ from any virtual device), #GdkDeviceManager will emit the corresponding
+ #GdkDeviceManager::device-added, #GdkDeviceManager::device-removed, or
+ #GdkDeviceManager::device-changed signals.
+
+
+
+ The client pointer
+
+
+ In X11, Under the presence of multiple virtual pointers, GDK and XInput2
+ use the "client pointer" principle to allow several legacy applications
+ to interact simultaneously with different virtual pointer/keyboard pairs,
+ it would be usually set by the window manager for a focused window, so
+ different application windows could operate on different client pointers.
+ gdk_device_manager_get_client_pointer() may be called to get the client
+ pointer #GdkDevice
+
+
+
+ Under the hood, X11 uses the client pointer (or its paired keyboard) to
+ satisfy core calls such as XGrabPointer/Keyboard, XQueryPointer and
+ others that have been superseded by XInput2.
+
+
+
+ In platforms without multidevice features, gdk_device_manager_get_client_pointer()
+ will return the only virtual pointer available.
+
+
+
+
+ Simple device handling
+
+
+ There are applications that could have little gain in knowing about
+ multiple devices, although there are situations where a device could
+ be needed (i.e. popping up a menu on the pointer coordinates).
+
+
+
+ For such applications, the client pointer may be a good enough
+ approximation for these operations. Under the presence of multiple
+ device pairs, this gives a behavior that is most similar to that
+ of legacy applications (i.e. gtk+2).
+
+
+
+ Getting the client pointer and keyboard
+
+ GdkDisplay *display;
+ GdkDeviceManager *device_manager;
+ GdkDevice *client_pointer, client_keyboard;
+
+ display = gdk_display_get_default ();
+ device_manager = gdk_display_get_device_manager (display);
+ client_pointer = gdk_device_manager_get_client_pointer (device_manager);
+
+ /* Or if we need a keyboard too */
+ client_keyboard = gdk_device_get_associated_device (client_pointer);
+
+
+
+
+
+ Dealing with multiple devices
+
+
+ There may be several usecases to deal with multiple devices, including,
+ but not limited to:
+
+
+
+
+ Retrieving advanced information from an input device: i.e. stylus pressure/tilt
+ in drawing applications.
+
+
+ Receiving events from a dedicated input device: i.e. joysticks in games.
+
+
+ Collaborative interfaces, handling simultaneous input from multiple users.
+
+
+
+
+ However, the patterns to make them work are very similar.
+
+
+
+ Event handling
+
+
+ Each device will emit its own event stream, this means
+ that you will need to check the GdkEvent you get in
+ your event handlers
+
+
+
+ Reacting differently to devices
+
+ static gboolean
+ my_widget_motion_notify (GtkWidget *widget,
+ GdkEventMotion *event)
+ {
+ GdkDevice *device, *source_device;
+
+ device = gdk_event_get_device ((GdkEvent *) event);
+ source_device = gdk_event_get_source_device ((GdkEvent *) event);
+
+ g_print ("Motion event by '%s', coming from HW device '%s'\n",
+ gdk_device_get_name (device),
+ gdk_device_get_name (source_device));
+
+ /* Handle touch devices differently */
+ if (gdk_device_get_source (source_device) == GDK_SOURCE_TOUCH)
+ {
+ ...
+ }
+ else
+ {
+ ...
+ }
+ return TRUE;
+ }
+
+
+
+
+ The mechanism above could also be used for fine grained event discarding
+ (i.e. so rubberband selection doesn't jump to another pointer entering
+ the widget for example)
+
+
+
+ Reacting differently to devices
+
+ static gboolean
+ my_widget_button_press (Gtkwidget *widget,
+ GdkEventButton *event)
+ {
+ GET_PRIV(widget)->current_pointer = gdk_event_get_device ((GdkEvent *) event);
+ ...
+ }
+
+ static gboolean
+ my_widget_button_release (Gtkwidget *widget,
+ GdkEventButton *event)
+ {
+ GET_PRIV(widget)->current_pointer = NULL;
+ ...
+ }
+
+ static gboolean
+ my_widget_motion_notify (Gtkwidget *widget,
+ GdkEventMotion *event)
+ {
+ if (gdk_event_get_device (event) !=
+ GET_PRIV(widget)->current_pointer)
+ return FALSE;
+
+ ...
+ }
+
+
+
+
+
+ Grabs
+
+
+ Grabs are a mechanism to coerce a device into sending events to
+ a window, but with multidevice there's an other side of the coin,
+ how other devices are supposed to interact while the grab is in
+ effect.
+
+
+
+ The GdkGrabOwnership enum passed to gdk_device_grab() may be used
+ to block other devices' interaction. %GDK_OWNERSHIP_NONE applies
+ no restrictions, allowing other devices to interact, even with
+ the grab window. %GDK_OWNERSHIP_WINDOW blocks other devices from
+ interacting with the grab window, but they'll still be able to
+ interact with the rest of the application, whereas
+ %GDK_OWNERSHIP_APPLICATION will render the whole application
+ insensitive to input from other devices. Different devices may
+ have simultaneous grabs on the same or different windows.
+
+
+
+ Grabbing as a result of an input event
+
+ gboolean
+ my_widget_button_press (GtkWidget *widget,
+ GdkEventButton *event)
+ {
+ GdkDevice *pointer, *keyboard;
+
+ pointer = gdk_event_get_device ((GdkEvent *) event);
+ keyboard = gdk_device_get_associated_device (pointer);
+
+ /* Grab both keyboard/pointer, other devices will be
+ * unable to interact with the widget window meanwhile
+ */
+ gdk_device_grab (pointer,
+ gtk_widget_get_window (widget),
+ GDK_OWNERSHIP_WINDOW,
+ ...);
+ gdk_device_grab (keyboard,
+ gtk_widget_get_window (widget),
+ GDK_OWNERSHIP_WINDOW,
+ ...);
+
+ return FALSE;
+ }
+
+
+
+
+ For GTK+ grabs, there's only a boolean value, equivalent to
+ %GDK_OWNERSHIP_NONE and %GDK_OWNERSHIP_WINDOW, but the mechanism
+ is quite similar.
+
+
+
+ Once the device is grabbed, there may be different situations
+ that could break the grabs, so the widget needs to listen to
+ #GdkGrabBrokenEvent and the #GtkWidget::grab-notify signal to
+ handle these situations.
+
+
+
+ Handling broken grabs
+
+ static gboolean
+ my_widget_grab_broken (GtkWidget *widget,
+ GdkEventGrabBroken *event)
+ {
+ MyWidgetPrivate *priv = GET_PRIV (widget);
+
+ if (gdk_event_get_device (event) == priv->grab_pointer)
+ {
+ /* Undo state */
+ ...
+ priv->grab_pointer = NULL;
+ return TRUE;
+ }
+
+ return FALSE;
+ }
+
+ static void
+ my_widget_grab_notify (GtkWidget *widget,
+ gboolean was_grabbed)
+ {
+ MyWidgetPrivate *priv = GET_PRIV (widget);
+
+ if (gtk_widget_device_is_shadowed (widget, priv->grab_device))
+ {
+ /* Device was "shadowed" by another widget's grab,
+ * release and undo state
+ */
+ ...
+ priv->grab_pointer = NULL;
+ }
+ }
+
+
+
+
+
+ Handling multipointer
+
+
+ Widgets do react by default to every virtual device, although
+ by default they are set in a compatibility mode that makes them
+ behave better with multiple pointers, without necessarily
+ being multipointer aware.
+
+
+
+ This compatibility mode most notably disables per-device
+ enter/leave events, so these are stacked, and the crossing
+ events are only emitted when the first pointer enters the
+ window, and after the last pointer leaves it. This behavior
+ is controlled through gtk_widget_set_support_multidevice()
+
+
+
+
+ Reading device axis values
+
+
+ Button and motion events provide further information about
+ the device axes' current state. Note the device axes are
+ hardware and driver dependent, therefore the set of axes
+ is not set in stone, although there are a few more common ones.
+
+
+
+ Getting to know the axes provided by a device
+
+ carlos@sacarino:~$ xinput list "Wacom ISDv4 E6 Pen stylus" |grep "Label"
+ Label: Abs X
+ Label: Abs Y
+ Label: Abs Pressure
+ Label: Abs Tilt X
+ Label: Abs Tilt Y
+ Label: Abs Wheel
+
+
+
+
+ Getting an axis value
+
+ gboolean
+ my_widget_motion_notify (GtkWidget *widget,
+ GdkEventMotion *event)
+ {
+ GdkAtom *label_atom;
+ gdouble pressure;
+
+ label_atom = gdk_atom_intern_static_string ("Abs Pressure");
+ gdk_device_get_axis_value (gdk_event_get_device ((GdkEvent *) event),
+ event->axes, label_atom, &pressure);
+
+ /* Do something with pressure */
+ ...
+
+ return TRUE;
+ }
+
+
+
+
+ All pointer devices report axes information, master and slave. to
+ achieve this, master pointers modify their list of axes at runtime
+ to reflect those of the currently routed slave, emitting
+ #GdkDevice::changed as the routed slave device changes.
+
+
+
+
+ Dealing with slave (or floating) devices
+
+
+ By default, GTK+ listens to all master devices, and typically
+ all slave devices will be attached to a master device. so
+ gdk_event_get_source_device() is the recommended way to deal
+ with the physical device triggering the event.
+
+
+
+ In more specialized setups, some devices could be floating
+ (i.e. tablets that don't route events through any virtual
+ pointer, but are expected to interact with drawing applications).
+ In that case, such specialized applications could want to interact
+ directly with the device. To do so, the device must be enabled,
+ and the widget wanting its events needs to add the event mask.
+
+
+
+ Enabling events for a slave device
+
+ GdkDevice *device;
+
+ /* Gets the first device found with the given GdkInputSource */
+ device = get_device (gtk_widget_get_display (widget),
+ GDK_SOURCE_PEN);
+ gdk_device_set_mode (device, GDK_MODE_SCREEN);
+ gtk_widget_add_device_events (widget, device,
+ GDK_BUTTON_PRESS_MASK |
+ GDK_BUTTON_RELEASE_MASK |
+ GDK_POINTER_MOTION_MASK);
+
+
+
+
+ After these calls, the widget would specifically receive events
+ from the physical device, regardless of it being floating or
+ connected to a master device. In this second case, and if you
+ want exclusive control of the device, you can temporarily detach
+ the stylus device from its master by doing a GDK grab on it.
+
+
+
+ For events coming directly from slave devices, both
+ gdk_event_get_device() and gdk_event_get_source_device() will
+ return the same device of type %GDK_DEVICE_TYPE_SLAVE or
+ %GDK_DEVICE_TYPE_FLOATING.
+
+
+
+ This is less useful than it used to be in GTK+2/XInput1, at least
+ for attached slaves, as there is gdk_event_get_source_device(),
+ and master devices' events provide axes information.
+
+
+
+
+
+
+
+ Multitouch across a widget hierarchy
+
+
+ Fully touch driven applications might not want to confine multitouch
+ operations within a single widget, but rather offer simultaneous
+ interaction with multiple widgets.
+
+
+ GTK+ is able to provide such experience, although it does not enable
+ %GDK_TOUCH_MASK by default on its stock widgets. If a widget meets the
+ following requirements, it is ready to be used in a multitouch UI:
+
+
+
+
+ The widget handles #GtkWidget::button-press-event, #GtkWidget::button-release-event
+ and #GtkWidget::motion-notify-event, and does something meaningful while the button 1
+ is pressed. If any explicit check on the event type being %GDK_BUTTON_PRESS,
+ %GDK_BUTTON_RELEASE or %GDK_MOTION_NOTIFY is performed, the event types
+ %GDK_TOUCH_PRESS, %GDK_TOUCH_RELEASE or %GDK_TOUCH_MOTION also need to be handled.
+
+
+ The widget relies on the implicit grab as long as the button press/touch is active,
+ GDK or GTK+ grabs would break the implicit grabs other touch sequences may have on
+ other widgets.
+
+
+ The widget does not require (or opts out) keyboard interaction while a touch is
+ active on it. Touch interaction does not necessarily bring the keyboard focus with it.
+
+
+ If the widget is only meant to interact with one touch sequence at a time (i.e.
+ buttons), it has to be able to discern and reject operations on any later touch
+ sequence as long as the touch it is interacting with remains active.
+
+
+
+
+ If a widget meets those requirements, enabling %GDK_TOUCH_MASK on it will suffice
+ to make it handle multitouch events in a way that doesn't disrupt other touch
+ operations.
+
+
+
+ Enabling touch events on a widget
+
+ gtk_widget_add_events (widget, GDK_TOUCH_MASK);
+
+
+
+
+ Not all GTK+ stock widgets are immediately suitable for handling touch
+ events, there could be even design reasons on some of those which render
+ them unsuitable.
+
+
+
+
+ Recommendations
+
+
+
+ Device operations often come up as a result of input events, favor
+ gdk_event_get_device() and gtk_get_current_event_device() before
+ gdk_device_manager_get_client_pointer().
+
+
+
+ Store the devices the widget is currently interacting with, handle
+ GdkEventGrabBroken and #GtkWidget::grab-notify to undo/nullify these.
+
+
+
+
diff --git a/docs/reference/gtk/gtk-docs.sgml b/docs/reference/gtk/gtk-docs.sgml
index 7db9731c3e..bf979f29ba 100644
--- a/docs/reference/gtk/gtk-docs.sgml
+++ b/docs/reference/gtk/gtk-docs.sgml
@@ -68,8 +68,9 @@
-
- Multitouch
+
+ Interacting with input devices
+