]> Pileus Git - ~andy/gtk/blobdiff - gdk/x11/gdkdevicemanager-xi2.c
x11: Put function in header, don't declare them extern
[~andy/gtk] / gdk / x11 / gdkdevicemanager-xi2.c
index 5bd5500feacc49ae0a8b6a0f8e6e061e5d3fe848..cfbfe6fc0e1dcc433525b562b1ce4fe465d4c0f3 100644 (file)
@@ -12,9 +12,7 @@
  * Lesser General Public License for more details.
  *
  * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
- * Boston, MA 02111-1307, USA.
+ * License along with this library. If not, see <http://www.gnu.org/licenses/>.
  */
 
 #include "config.h"
@@ -29,8 +27,7 @@
 #include "gdkprivate-x11.h"
 #include "gdkintl.h"
 #include "gdkkeysyms.h"
-
-#ifdef XINPUT_2
+#include "gdkinternals.h"
 
 #include <X11/Xlib.h>
 #include <X11/Xutil.h>
@@ -38,8 +35,6 @@
 
 #include <string.h>
 
-#endif /* XINPUT_2 */
-
 struct _GdkX11DeviceManagerXI2
 {
   GdkX11DeviceManagerCore parent_object;
@@ -49,6 +44,8 @@ struct _GdkX11DeviceManagerXI2
   GList *devices;
 
   gint opcode;
+  gint major;
+  gint minor;
 };
 
 struct _GdkX11DeviceManagerXI2Class
@@ -63,8 +60,6 @@ G_DEFINE_TYPE_WITH_CODE (GdkX11DeviceManagerXI2, gdk_x11_device_manager_xi2, GDK
                                                 gdk_x11_device_manager_xi2_event_translator_init))
 
 
-#ifdef XINPUT_2
-
 #define HAS_FOCUS(toplevel) ((toplevel)->has_focus || (toplevel)->has_pointer_focus)
 
 
@@ -96,7 +91,9 @@ static GdkWindow *  gdk_x11_device_manager_xi2_get_window           (GdkEventTra
 
 enum {
   PROP_0,
-  PROP_OPCODE
+  PROP_OPCODE,
+  PROP_MAJOR,
+  PROP_MINOR
 };
 
 static void
@@ -120,6 +117,20 @@ gdk_x11_device_manager_xi2_class_init (GdkX11DeviceManagerXI2Class *klass)
                                                      P_("Opcode for XInput2 requests"),
                                                      0, G_MAXINT, 0,
                                                      G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+  g_object_class_install_property (object_class,
+                                   PROP_MAJOR,
+                                   g_param_spec_int ("major",
+                                                     P_("Major"),
+                                                     P_("Major version number"),
+                                                     0, G_MAXINT, 0,
+                                                     G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+  g_object_class_install_property (object_class,
+                                   PROP_MINOR,
+                                   g_param_spec_int ("minor",
+                                                     P_("Minor"),
+                                                     P_("Minor version number"),
+                                                     0, G_MAXINT, 0,
+                                                     G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
 }
 
 static void
@@ -148,8 +159,10 @@ _gdk_x11_device_manager_xi2_select_events (GdkDeviceManager *device_manager,
 static void
 translate_valuator_class (GdkDisplay          *display,
                           GdkDevice           *device,
-                          XIValuatorClassInfo *info,
-                          gint                 n_valuator)
+                          Atom                 valuator_label,
+                          gdouble              min,
+                          gdouble              max,
+                          gdouble              resolution)
 {
   static gboolean initialized = FALSE;
   static Atom label_atoms [GDK_AXIS_LAST] = { 0 };
@@ -170,24 +183,19 @@ translate_valuator_class (GdkDisplay          *display,
 
   for (i = GDK_AXIS_IGNORE; i < GDK_AXIS_LAST; i++)
     {
-      if (label_atoms[i] == info->label)
+      if (label_atoms[i] == valuator_label)
         {
           use = i;
           break;
         }
     }
 
-  if (info->label != None)
-    label = gdk_x11_xatom_to_atom_for_display (display, info->label);
+  if (valuator_label != None)
+    label = gdk_x11_xatom_to_atom_for_display (display, valuator_label);
   else
     label = GDK_NONE;
 
-  _gdk_device_add_axis (device,
-                        label,
-                        use,
-                        info->min,
-                        info->max,
-                        info->resolution);
+  _gdk_device_add_axis (device, label, use, min, max, resolution);
 }
 
 static void
@@ -196,7 +204,7 @@ translate_device_classes (GdkDisplay      *display,
                           XIAnyClassInfo **classes,
                           guint            n_classes)
 {
-  gint i, n_valuator = 0;
+  gint i;
 
   g_object_freeze_notify (G_OBJECT (device));
 
@@ -218,11 +226,40 @@ translate_device_classes (GdkDisplay      *display,
           }
           break;
         case XIValuatorClass:
-          translate_valuator_class (display, device,
-                                    (XIValuatorClassInfo *) class_info,
-                                    n_valuator);
-          n_valuator++;
+          {
+            XIValuatorClassInfo *valuator_info = (XIValuatorClassInfo *) class_info;
+            translate_valuator_class (display, device,
+                                      valuator_info->label,
+                                      valuator_info->min,
+                                      valuator_info->max,
+                                      valuator_info->resolution);
+          }
           break;
+#ifdef XINPUT_2_2
+        case XIScrollClass:
+          {
+            XIScrollClassInfo *scroll_info = (XIScrollClassInfo *) class_info;
+            GdkScrollDirection direction;
+
+            if (scroll_info->scroll_type == XIScrollTypeVertical)
+              direction = GDK_SCROLL_DOWN;
+            else
+              direction = GDK_SCROLL_RIGHT;
+
+            GDK_NOTE (INPUT,
+                      g_message ("\n\tscroll valuator %d: %s, increment %f",
+                                 scroll_info->number,
+                                 scroll_info->scroll_type == XIScrollTypeVertical
+                                                ? "vertical"
+                                                : "horizontal",
+                                 scroll_info->increment));
+
+            _gdk_x11_device_xi2_add_scroll_valuator (GDK_X11_DEVICE_XI2 (device),
+                                                     scroll_info->number,
+                                                     direction,
+                                                     scroll_info->increment);
+          }
+#endif /* XINPUT_2_2 */
         default:
           /* Ignore */
           break;
@@ -232,18 +269,58 @@ translate_device_classes (GdkDisplay      *display,
   g_object_thaw_notify (G_OBJECT (device));
 }
 
+static gboolean
+is_touch_device (XIAnyClassInfo **classes,
+                 guint            n_classes,
+                 GdkInputSource  *device_type,
+                 gint            *num_touches)
+{
+#ifdef XINPUT_2_2
+  guint i;
+
+  for (i = 0; i < n_classes; i++)
+    {
+      XITouchClassInfo *class = (XITouchClassInfo *) classes[i];
+
+      if (class->type != XITouchClass)
+        continue;
+
+      if (class->num_touches > 0)
+        {
+          if (class->mode == XIDirectTouch)
+            *device_type = GDK_SOURCE_TOUCHSCREEN;
+          else if (class->mode == XIDependentTouch)
+            *device_type = GDK_SOURCE_TOUCHPAD;
+          else
+            continue;
+
+          *num_touches = class->num_touches;
+
+          return TRUE;
+        }
+    }
+#endif
+
+  return FALSE;
+}
+
 static GdkDevice *
 create_device (GdkDeviceManager *device_manager,
                GdkDisplay       *display,
                XIDeviceInfo     *dev)
 {
   GdkInputSource input_source;
+  GdkInputSource touch_source;
   GdkDeviceType type;
   GdkDevice *device;
   GdkInputMode mode;
+  gint num_touches = 0;
 
   if (dev->use == XIMasterKeyboard || dev->use == XISlaveKeyboard)
     input_source = GDK_SOURCE_KEYBOARD;
+  else if (dev->use == XISlavePointer &&
+           is_touch_device (dev->classes, dev->num_classes, &touch_source, &num_touches))
+    input_source = touch_source;
   else
     {
       gchar *tmp_name;
@@ -282,6 +359,20 @@ create_device (GdkDeviceManager *device_manager,
       break;
     }
 
+  GDK_NOTE (INPUT,
+            ({
+              const gchar *type_names[] = { "master", "slave", "floating" };
+              const gchar *source_names[] = { "mouse", "pen", "eraser", "cursor", "keyboard", "direct touch", "indirect touch" };
+              const gchar *mode_names[] = { "disabled", "screen", "window" };
+              g_message ("input device:\n\tname: %s\n\ttype: %s\n\tsource: %s\n\tmode: %s\n\thas cursor: %d\n\ttouches: %d",
+                         dev->name,
+                         type_names[type],
+                         source_names[input_source],
+                         mode_names[mode],
+                         dev->use == XIMasterPointer,
+                         num_touches);
+            }));
+
   device = g_object_new (GDK_TYPE_X11_DEVICE_XI2,
                          "name", dev->name,
                          "type", type,
@@ -408,6 +499,8 @@ gdk_x11_device_manager_xi2_constructed (GObject *object)
   display = gdk_device_manager_get_display (GDK_DEVICE_MANAGER (object));
   xdisplay = GDK_DISPLAY_XDISPLAY (display);
 
+  g_assert (device_manager->major == 2);
+
   masters = g_hash_table_new (NULL, NULL);
   slaves = g_hash_table_new (NULL, NULL);
 
@@ -417,6 +510,10 @@ gdk_x11_device_manager_xi2_constructed (GObject *object)
   for (i = 0; i < ndevices; i++)
     {
       dev = &info[i];
+
+      if (!dev->enabled)
+             continue;
+
       add_device (device_manager, dev, FALSE);
 
       if (dev->use == XIMasterPointer ||
@@ -529,6 +626,12 @@ gdk_x11_device_manager_xi2_set_property (GObject      *object,
     case PROP_OPCODE:
       device_manager->opcode = g_value_get_int (value);
       break;
+    case PROP_MAJOR:
+      device_manager->major = g_value_get_int (value);
+      break;
+    case PROP_MINOR:
+      device_manager->minor = g_value_get_int (value);
+      break;
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
       break;
@@ -550,6 +653,12 @@ gdk_x11_device_manager_xi2_get_property (GObject    *object,
     case PROP_OPCODE:
       g_value_set_int (value, device_manager->opcode);
       break;
+    case PROP_MAJOR:
+      g_value_set_int (value, device_manager->major);
+      break;
+    case PROP_MINOR:
+      g_value_set_int (value, device_manager->minor);
+      break;
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
       break;
@@ -582,9 +691,14 @@ handle_hierarchy_changed (GdkX11DeviceManagerXI2 *device_manager,
     {
       if (ev->info[i].flags & XIDeviceEnabled)
         {
+          gdk_x11_display_error_trap_push (display);
           info = XIQueryDevice (xdisplay, ev->info[i].deviceid, &ndevices);
-          add_device (device_manager, &info[0], TRUE);
-          XIFreeDeviceInfo (info);
+          gdk_x11_display_error_trap_pop_ignored (display);
+          if (info)
+            {
+              add_device (device_manager, &info[0], TRUE);
+              XIFreeDeviceInfo (info);
+            }
         }
       else if (ev->info[i].flags & XIDeviceDisabled)
         remove_device (device_manager, ev->info[i].deviceid);
@@ -596,6 +710,9 @@ handle_hierarchy_changed (GdkX11DeviceManagerXI2 *device_manager,
           slave = g_hash_table_lookup (device_manager->id_table,
                                        GINT_TO_POINTER (ev->info[i].deviceid));
 
+          if (!slave)
+            continue;
+
           /* Remove old master info */
           master = gdk_device_get_associated_device (slave);
 
@@ -610,15 +727,23 @@ handle_hierarchy_changed (GdkX11DeviceManagerXI2 *device_manager,
           /* Add new master if it's an attachment event */
           if (ev->info[i].flags & XISlaveAttached)
             {
+              gdk_x11_display_error_trap_push (display);
               info = XIQueryDevice (xdisplay, ev->info[i].deviceid, &ndevices);
-
-              master = g_hash_table_lookup (device_manager->id_table,
-                                            GINT_TO_POINTER (info->attachment));
-
-              _gdk_device_set_associated_device (slave, master);
-              _gdk_device_add_slave (master, slave);
-
-              g_signal_emit_by_name (device_manager, "device-changed", master);
+              gdk_x11_display_error_trap_pop_ignored (display);
+              if (info)
+                {
+                  master = g_hash_table_lookup (device_manager->id_table,
+                                                GINT_TO_POINTER (info->attachment));
+                  XIFreeDeviceInfo (info);
+                }
+
+              if (master)
+                {
+                  _gdk_device_set_associated_device (slave, master);
+                  _gdk_device_add_slave (master, slave);
+
+                  g_signal_emit_by_name (device_manager, "device-changed", master);
+                }
             }
 
           g_signal_emit_by_name (device_manager, "device-changed", slave);
@@ -631,16 +756,24 @@ handle_device_changed (GdkX11DeviceManagerXI2 *device_manager,
                        XIDeviceChangedEvent   *ev)
 {
   GdkDisplay *display;
-  GdkDevice *device;
+  GdkDevice *device, *source_device;
 
   display = gdk_device_manager_get_display (GDK_DEVICE_MANAGER (device_manager));
   device = g_hash_table_lookup (device_manager->id_table,
                                 GUINT_TO_POINTER (ev->deviceid));
+  source_device = g_hash_table_lookup (device_manager->id_table,
+                                       GUINT_TO_POINTER (ev->sourceid));
 
-  _gdk_device_reset_axes (device);
-  translate_device_classes (display, device, ev->classes, ev->num_classes);
+  if (device)
+    {
+      _gdk_device_reset_axes (device);
+      translate_device_classes (display, device, ev->classes, ev->num_classes);
+
+      g_signal_emit_by_name (G_OBJECT (device), "changed");
+    }
 
-  g_signal_emit_by_name (G_OBJECT (device), "changed");
+  if (source_device)
+    _gdk_device_xi2_reset_scroll_valuators (GDK_X11_DEVICE_XI2 (source_device));
 }
 
 static GdkCrossingMode
@@ -716,91 +849,6 @@ set_user_time (GdkEvent *event)
     gdk_x11_window_set_user_time (window, time);
 }
 
-static void
-generate_focus_event (GdkWindow *window,
-                      GdkDevice *device,
-                      GdkDevice *source_device,
-                      gboolean   in)
-{
-  GdkEvent *event;
-
-  event = gdk_event_new (GDK_FOCUS_CHANGE);
-  event->focus_change.window = g_object_ref (window);
-  event->focus_change.send_event = FALSE;
-  event->focus_change.in = in;
-  gdk_event_set_device (event, device);
-  gdk_event_set_source_device (event, source_device);
-
-  gdk_event_put (event);
-  gdk_event_free (event);
-}
-
-static void
-handle_focus_change (GdkWindow *window,
-                     GdkDevice *device,
-                     GdkDevice *source_device,
-                     gint       detail,
-                     gint       mode,
-                     gboolean   in)
-{
-  GdkToplevelX11 *toplevel;
-  gboolean had_focus;
-
-  toplevel = _gdk_x11_window_get_toplevel (window);
-
-  if (!toplevel)
-    return;
-
-  had_focus = HAS_FOCUS (toplevel);
-
-  switch (detail)
-    {
-    case NotifyAncestor:
-    case NotifyVirtual:
-      /* When the focus moves from an ancestor of the window to
-       * the window or a descendent of the window, *and* the
-       * pointer is inside the window, then we were previously
-       * receiving keystroke events in the has_pointer_focus
-       * case and are now receiving them in the
-       * has_focus_window case.
-       */
-      if (toplevel->has_pointer &&
-          mode != NotifyGrab &&
-          mode != NotifyUngrab)
-        toplevel->has_pointer_focus = (in) ? FALSE : TRUE;
-
-      /* fall through */
-    case NotifyNonlinear:
-    case NotifyNonlinearVirtual:
-      if (mode != NotifyGrab &&
-          mode != NotifyUngrab)
-        toplevel->has_focus_window = (in) ? TRUE : FALSE;
-      /* We pretend that the focus moves to the grab
-       * window, so we pay attention to NotifyGrab
-       * NotifyUngrab, and ignore NotifyWhileGrabbed
-       */
-      if (mode != NotifyWhileGrabbed)
-        toplevel->has_focus = (in) ? TRUE : FALSE;
-      break;
-    case NotifyPointer:
-      /* The X server sends NotifyPointer/NotifyGrab,
-       * but the pointer focus is ignored while a
-       * grab is in effect
-       */
-      if (mode != NotifyGrab &&
-          mode != NotifyUngrab)
-        toplevel->has_pointer_focus = (in) ? TRUE :FALSE;
-      break;
-    case NotifyInferior:
-    case NotifyPointerRoot:
-    case NotifyDetailNone:
-      break;
-    }
-
-  if (HAS_FOCUS (toplevel) != had_focus)
-    generate_focus_event (window, device, source_device, (in) ? TRUE : FALSE);
-}
-
 static gdouble *
 translate_axes (GdkDevice       *device,
                 gdouble          x,
@@ -885,6 +933,11 @@ get_event_window (GdkEventTranslator *translator,
     case XI_ButtonPress:
     case XI_ButtonRelease:
     case XI_Motion:
+#ifdef XINPUT_2_2
+    case XI_TouchUpdate:
+    case XI_TouchBegin:
+    case XI_TouchEnd:
+#endif /* XINPUT_2_2 */
       {
         XIDeviceEvent *xev = (XIDeviceEvent *) ev;
 
@@ -1003,6 +1056,47 @@ gdk_x11_device_manager_xi2_translate_core_event (GdkEventTranslator *translator,
   return TRUE;
 }
 
+static gboolean
+scroll_valuators_changed (GdkX11DeviceXI2 *device,
+                          XIValuatorState *valuators,
+                          gdouble         *dx,
+                          gdouble         *dy)
+{
+  gboolean has_scroll_valuators = FALSE;
+  GdkScrollDirection direction;
+  guint n_axes, i, n_val;
+  gdouble *vals;
+
+  n_axes = gdk_device_get_n_axes (GDK_DEVICE (device));
+  vals = valuators->values;
+  *dx = *dy = 0;
+  n_val = 0;
+
+  for (i = 0; i < MIN (valuators->mask_len * 8, n_axes); i++)
+    {
+      gdouble delta;
+
+      if (!XIMaskIsSet (valuators->mask, i))
+        continue;
+
+      if (_gdk_x11_device_xi2_get_scroll_delta (device, i, vals[n_val],
+                                                &direction, &delta))
+        {
+          has_scroll_valuators = TRUE;
+
+          if (direction == GDK_SCROLL_UP ||
+              direction == GDK_SCROLL_DOWN)
+            *dy = delta;
+          else
+            *dx = delta;
+        }
+
+      n_val++;
+    }
+
+  return has_scroll_valuators;
+}
+
 static gboolean
 gdk_x11_device_manager_xi2_translate_event (GdkEventTranslator *translator,
                                             GdkDisplay         *display,
@@ -1025,6 +1119,9 @@ gdk_x11_device_manager_xi2_translate_event (GdkEventTranslator *translator,
 
   ev = (XIEvent *) cookie->data;
 
+  if (!ev)
+    return FALSE;
+
   window = get_event_window (translator, ev);
 
   if (window && GDK_WINDOW_DESTROYED (window))
@@ -1063,10 +1160,10 @@ gdk_x11_device_manager_xi2_translate_event (GdkEventTranslator *translator,
 
         event->key.time = xev->time;
         event->key.state = _gdk_x11_device_xi2_translate_state (&xev->mods, &xev->buttons, &xev->group);
-        event->key.group = _gdk_x11_get_group_for_state (display, event->key.state);
+        event->key.group = xev->group.effective;
 
         event->key.hardware_keycode = xev->detail;
-        event->key.is_modifier = _gdk_x11_keymap_key_is_modifier (keymap, event->key.hardware_keycode);
+        event->key.is_modifier = gdk_x11_keymap_key_is_modifier (keymap, event->key.hardware_keycode);
 
         device = g_hash_table_lookup (device_manager->id_table,
                                       GUINT_TO_POINTER (xev->deviceid));
@@ -1106,51 +1203,49 @@ gdk_x11_device_manager_xi2_translate_event (GdkEventTranslator *translator,
         XIDeviceEvent *xev = (XIDeviceEvent *) ev;
         GdkDevice *source_device;
 
-        switch (xev->detail)
+        if (ev->evtype == XI_ButtonRelease &&
+            (xev->detail >= 4 && xev->detail <= 7))
+          return FALSE;
+        else if (ev->evtype == XI_ButtonPress &&
+                 (xev->detail >= 4 && xev->detail <= 7))
+          {
+            /* Button presses of button 4-7 are scroll events */
+            event->scroll.type = GDK_SCROLL;
+
+            if (xev->detail == 4)
+              event->scroll.direction = GDK_SCROLL_UP;
+            else if (xev->detail == 5)
+              event->scroll.direction = GDK_SCROLL_DOWN;
+            else if (xev->detail == 6)
+              event->scroll.direction = GDK_SCROLL_LEFT;
+            else
+              event->scroll.direction = GDK_SCROLL_RIGHT;
+
+            event->scroll.window = window;
+            event->scroll.time = xev->time;
+            event->scroll.x = (gdouble) xev->event_x;
+            event->scroll.y = (gdouble) xev->event_y;
+            event->scroll.x_root = (gdouble) xev->root_x;
+            event->scroll.y_root = (gdouble) xev->root_y;
+            event->scroll.delta_x = 0;
+            event->scroll.delta_y = 0;
+
+            event->scroll.device = g_hash_table_lookup (device_manager->id_table,
+                                                        GUINT_TO_POINTER (xev->deviceid));
+
+            source_device = g_hash_table_lookup (device_manager->id_table,
+                                                 GUINT_TO_POINTER (xev->sourceid));
+            gdk_event_set_source_device (event, source_device);
+
+            event->scroll.state = _gdk_x11_device_xi2_translate_state (&xev->mods, &xev->buttons, &xev->group);
+
+#ifdef XINPUT_2_2
+            if (xev->flags & XIPointerEmulated)
+              _gdk_event_set_pointer_emulated (event, TRUE);
+#endif
+          }
+        else
           {
-          case 4:
-          case 5:
-          case 6:
-          case 7:
-             /* Button presses of button 4-7 are scroll events */
-            if (ev->evtype == XI_ButtonPress)
-              {
-                event->scroll.type = GDK_SCROLL;
-
-                if (xev->detail == 4)
-                  event->scroll.direction = GDK_SCROLL_UP;
-                else if (xev->detail == 5)
-                  event->scroll.direction = GDK_SCROLL_DOWN;
-                else if (xev->detail == 6)
-                  event->scroll.direction = GDK_SCROLL_LEFT;
-                else
-                  event->scroll.direction = GDK_SCROLL_RIGHT;
-
-                event->scroll.window = window;
-                event->scroll.time = xev->time;
-                event->scroll.x = (gdouble) xev->event_x;
-                event->scroll.y = (gdouble) xev->event_y;
-                event->scroll.x_root = (gdouble) xev->root_x;
-                event->scroll.y_root = (gdouble) xev->root_y;
-
-                event->scroll.device = g_hash_table_lookup (device_manager->id_table,
-                                                            GUINT_TO_POINTER (xev->deviceid));
-
-                source_device = g_hash_table_lookup (device_manager->id_table,
-                                                     GUINT_TO_POINTER (xev->sourceid));
-                gdk_event_set_source_device (event, source_device);
-
-                event->scroll.state = _gdk_x11_device_xi2_translate_state (&xev->mods, &xev->buttons, &xev->group);
-                break;
-              }
-            /* Button presses of button 4-7 are scroll events, so ignore the release */
-            else if (ev->evtype == XI_ButtonRelease)
-              {
-                return_val = FALSE;
-                break;
-              }
-            /* else (XI_ButtonRelease) fall thru */
-          default:
             event->button.type = (ev->evtype == XI_ButtonPress) ? GDK_BUTTON_PRESS : GDK_BUTTON_RELEASE;
 
             event->button.window = window;
@@ -1183,9 +1278,15 @@ gdk_x11_device_manager_xi2_translate_event (GdkEventTranslator *translator,
               }
 
             event->button.state = _gdk_x11_device_xi2_translate_state (&xev->mods, &xev->buttons, &xev->group);
+
             event->button.button = xev->detail;
           }
 
+#ifdef XINPUT_2_2
+        if (xev->flags & XIPointerEmulated)
+          _gdk_event_set_pointer_emulated (event, TRUE);
+#endif
+
         if (return_val == FALSE)
           break;
 
@@ -1200,30 +1301,78 @@ gdk_x11_device_manager_xi2_translate_event (GdkEventTranslator *translator,
 
         break;
       }
+
     case XI_Motion:
       {
         XIDeviceEvent *xev = (XIDeviceEvent *) ev;
-        GdkDevice *source_device;
+        GdkDevice *source_device, *device;
+        gdouble delta_x, delta_y;
 
-        event->motion.type = GDK_MOTION_NOTIFY;
+        source_device = g_hash_table_lookup (device_manager->id_table,
+                                             GUINT_TO_POINTER (xev->sourceid));
+        device = g_hash_table_lookup (device_manager->id_table,
+                                      GUINT_TO_POINTER (xev->deviceid));
 
-        event->motion.window = window;
+        /* When scrolling, X might send events twice here; once with both the
+         * device and the source device set to the physical device, and once
+         * with the device set to the master device.
+         * Since we are only interested in the latter, and
+         * scroll_valuators_changed() updates the valuator cache for the
+         * source device, we need to explicitly ignore the first event in
+         * order to get the correct delta for the second.
+         */
+        if (gdk_device_get_device_type (device) != GDK_DEVICE_TYPE_SLAVE &&
+            scroll_valuators_changed (GDK_X11_DEVICE_XI2 (source_device),
+                                      &xev->valuators, &delta_x, &delta_y))
+          {
+            event->scroll.type = GDK_SCROLL;
+            event->scroll.direction = GDK_SCROLL_SMOOTH;
+
+            GDK_NOTE(EVENTS,
+                     g_message ("smooth scroll: %s\n\tdevice: %u\n\tsource device: %u\n\twindow %ld\n\tdeltas: %f %f",
+#ifdef XINPUT_2_2
+                                (xev->flags & XIPointerEmulated) ? "emulated" : "",
+#else
+                                 "",
+#endif
+                                xev->deviceid, xev->sourceid,
+                                xev->event, delta_x, delta_y));
+
+
+            event->scroll.window = window;
+            event->scroll.time = xev->time;
+            event->scroll.x = (gdouble) xev->event_x;
+            event->scroll.y = (gdouble) xev->event_y;
+            event->scroll.x_root = (gdouble) xev->root_x;
+            event->scroll.y_root = (gdouble) xev->root_y;
+            event->scroll.delta_x = delta_x;
+            event->scroll.delta_y = delta_y;
+
+            event->scroll.device = device;
+            gdk_event_set_source_device (event, source_device);
 
+            event->scroll.state = _gdk_x11_device_xi2_translate_state (&xev->mods, &xev->buttons, &xev->group);
+            break;
+          }
+
+        event->motion.type = GDK_MOTION_NOTIFY;
+        event->motion.window = window;
         event->motion.time = xev->time;
         event->motion.x = (gdouble) xev->event_x;
         event->motion.y = (gdouble) xev->event_y;
         event->motion.x_root = (gdouble) xev->root_x;
         event->motion.y_root = (gdouble) xev->root_y;
 
-        event->motion.device = g_hash_table_lookup (device_manager->id_table,
-                                                    GINT_TO_POINTER (xev->deviceid));
-
-        source_device = g_hash_table_lookup (device_manager->id_table,
-                                             GUINT_TO_POINTER (xev->sourceid));
+        event->motion.device = device;
         gdk_event_set_source_device (event, source_device);
 
         event->motion.state = _gdk_x11_device_xi2_translate_state (&xev->mods, &xev->buttons, &xev->group);
 
+#ifdef XINPUT_2_2
+        if (xev->flags & XIPointerEmulated)
+          _gdk_event_set_pointer_emulated (event, TRUE);
+#endif
+
         /* There doesn't seem to be motion hints in XI */
         event->motion.is_hint = FALSE;
 
@@ -1243,6 +1392,137 @@ gdk_x11_device_manager_xi2_translate_event (GdkEventTranslator *translator,
           }
       }
       break;
+
+#ifdef XINPUT_2_2
+    case XI_TouchBegin:
+    case XI_TouchEnd:
+      {
+        XIDeviceEvent *xev = (XIDeviceEvent *) ev;
+        GdkDevice *source_device;
+
+        GDK_NOTE(EVENTS,
+                 g_message ("touch %s:\twindow %ld\n\ttouch id: %u\n\tpointer emulating: %d",
+                            ev->evtype == XI_TouchBegin ? "begin" : "end",
+                            xev->event,
+                            xev->detail,
+                            xev->flags & XITouchEmulatingPointer));
+
+        if (ev->evtype == XI_TouchBegin)
+          event->touch.type = GDK_TOUCH_BEGIN;
+        else if (ev->evtype == XI_TouchEnd)
+          event->touch.type = GDK_TOUCH_END;
+
+        event->touch.window = window;
+        event->touch.time = xev->time;
+        event->touch.x = (gdouble) xev->event_x;
+        event->touch.y = (gdouble) xev->event_y;
+        event->touch.x_root = (gdouble) xev->root_x;
+        event->touch.y_root = (gdouble) xev->root_y;
+
+        event->touch.device = g_hash_table_lookup (device_manager->id_table,
+                                                   GUINT_TO_POINTER (xev->deviceid));
+
+        source_device = g_hash_table_lookup (device_manager->id_table,
+                                             GUINT_TO_POINTER (xev->sourceid));
+        gdk_event_set_source_device (event, source_device);
+
+        event->touch.axes = translate_axes (event->touch.device,
+                                            event->touch.x,
+                                            event->touch.y,
+                                            event->touch.window,
+                                            &xev->valuators);
+
+        if (gdk_device_get_mode (event->touch.device) == GDK_MODE_WINDOW)
+          {
+            GdkDevice *device = event->touch.device;
+
+            /* Update event coordinates from axes */
+            gdk_device_get_axis (device, event->touch.axes, GDK_AXIS_X, &event->touch.x);
+            gdk_device_get_axis (device, event->touch.axes, GDK_AXIS_Y, &event->touch.y);
+          }
+
+        event->touch.state = _gdk_x11_device_xi2_translate_state (&xev->mods, &xev->buttons, &xev->group);
+
+        if (ev->evtype == XI_TouchBegin)
+          event->touch.state |= GDK_BUTTON1_MASK;
+
+        event->touch.sequence = GUINT_TO_POINTER (xev->detail);
+
+        if (xev->flags & XITouchEmulatingPointer)
+          {
+            event->touch.emulating_pointer = TRUE;
+            _gdk_event_set_pointer_emulated (event, TRUE);
+          }
+
+        if (return_val == FALSE)
+          break;
+
+        if (!set_screen_from_root (display, event, xev->root))
+          {
+            return_val = FALSE;
+            break;
+          }
+
+        if (ev->evtype == XI_TouchBegin)
+          set_user_time (event);
+      }
+      break;
+
+    case XI_TouchUpdate:
+      {
+        XIDeviceEvent *xev = (XIDeviceEvent *) ev;
+        GdkDevice *source_device;
+
+        GDK_NOTE(EVENTS,
+                 g_message ("touch update:\twindow %ld\n\ttouch id: %u\n\tpointer emulating: %d",
+                            xev->event,
+                            xev->detail,
+                            xev->flags & XITouchEmulatingPointer));
+
+        event->touch.window = window;
+        event->touch.sequence = GUINT_TO_POINTER (xev->detail);
+        event->touch.type = GDK_TOUCH_UPDATE;
+        event->touch.time = xev->time;
+        event->touch.x = (gdouble) xev->event_x;
+        event->touch.y = (gdouble) xev->event_y;
+        event->touch.x_root = (gdouble) xev->root_x;
+        event->touch.y_root = (gdouble) xev->root_y;
+
+        event->touch.device = g_hash_table_lookup (device_manager->id_table,
+                                                   GINT_TO_POINTER (xev->deviceid));
+
+        source_device = g_hash_table_lookup (device_manager->id_table,
+                                             GUINT_TO_POINTER (xev->sourceid));
+        gdk_event_set_source_device (event, source_device);
+
+        event->touch.state = _gdk_x11_device_xi2_translate_state (&xev->mods, &xev->buttons, &xev->group);
+
+        event->touch.state |= GDK_BUTTON1_MASK;
+
+        if (xev->flags & XITouchEmulatingPointer)
+          {
+            event->touch.emulating_pointer = TRUE;
+            _gdk_event_set_pointer_emulated (event, TRUE);
+          }
+
+        event->touch.axes = translate_axes (event->touch.device,
+                                            event->touch.x,
+                                            event->touch.y,
+                                            event->touch.window,
+                                            &xev->valuators);
+
+        if (gdk_device_get_mode (event->touch.device) == GDK_MODE_WINDOW)
+          {
+            GdkDevice *device = event->touch.device;
+
+            /* Update event coordinates from axes */
+            gdk_device_get_axis (device, event->touch.axes, GDK_AXIS_X, &event->touch.x);
+            gdk_device_get_axis (device, event->touch.axes, GDK_AXIS_Y, &event->touch.y);
+          }
+      }
+      break;
+#endif  /* XINPUT_2_2 */
+
     case XI_Enter:
     case XI_Leave:
       {
@@ -1268,6 +1548,7 @@ gdk_x11_device_manager_xi2_translate_event (GdkEventTranslator *translator,
         source_device = g_hash_table_lookup (device_manager->id_table,
                                              GUINT_TO_POINTER (xev->sourceid));
         gdk_event_set_source_device (event, source_device);
+        _gdk_device_xi2_reset_scroll_valuators (GDK_X11_DEVICE_XI2 (source_device));
 
         event->crossing.mode = translate_crossing_mode (xev->mode);
         event->crossing.detail = translate_notify_type (xev->detail);
@@ -1286,9 +1567,13 @@ gdk_x11_device_manager_xi2_translate_event (GdkEventTranslator *translator,
         source_device = g_hash_table_lookup (device_manager->id_table,
                                              GUINT_TO_POINTER (xev->sourceid));
 
-        handle_focus_change (window, device, source_device,
-                             xev->detail, xev->mode,
-                             (ev->evtype == XI_FocusIn) ? TRUE : FALSE);
+        _gdk_device_manager_core_handle_focus (window,
+                                               xev->event,
+                                               device,
+                                               source_device,
+                                               (ev->evtype == XI_FocusIn) ? TRUE : FALSE,
+                                               xev->detail,
+                                               xev->mode);
 
         return_val = FALSE;
       }
@@ -1336,7 +1621,8 @@ gdk_x11_device_manager_xi2_get_handled_events (GdkEventTranslator *translator)
           GDK_BUTTON2_MOTION_MASK |
           GDK_BUTTON3_MOTION_MASK |
           GDK_BUTTON_MOTION_MASK |
-          GDK_FOCUS_CHANGE_MASK);
+          GDK_FOCUS_CHANGE_MASK |
+          GDK_TOUCH_MASK);
 }
 
 static void
@@ -1350,7 +1636,9 @@ gdk_x11_device_manager_xi2_select_window_events (GdkEventTranslator *translator,
   device_manager = GDK_DEVICE_MANAGER (translator);
 
   event_mask.deviceid = XIAllMasterDevices;
-  event_mask.mask = _gdk_x11_device_xi2_translate_event_mask (evmask, &event_mask.mask_len);
+  event_mask.mask = _gdk_x11_device_xi2_translate_event_mask (GDK_X11_DEVICE_MANAGER_XI2 (device_manager),
+                                                              evmask,
+                                                              &event_mask.mask_len);
 
   _gdk_x11_device_manager_xi2_select_events (device_manager, window, &event_mask);
   g_free (event_mask.mask);
@@ -1374,25 +1662,6 @@ gdk_x11_device_manager_xi2_get_window (GdkEventTranslator *translator,
   return get_event_window (translator, ev);
 }
 
-#else /* XINPUT_2 */
-
-static void
-gdk_x11_device_manager_xi2_class_init (GdkX11DeviceManagerXI2Class *klass)
-{
-}
-
-static void
-gdk_x11_device_manager_xi2_init (GdkX11DeviceManagerXI2 *device_manager)
-{
-}
-
-static void
-gdk_x11_device_manager_xi2_event_translator_init (GdkEventTranslatorIface *iface)
-{
-}
-
-#endif /* XINPUT_2 */
-
 GdkDevice *
 _gdk_x11_device_manager_xi2_lookup (GdkX11DeviceManagerXI2 *device_manager_xi2,
                                     gint                    device_id)