diff --git a/configure.in b/configure.in
index 1e0a0a8..20f873a 100644
--- a/configure.in
+++ b/configure.in
@@ -1568,9 +1568,12 @@ if test "x$gdktarget" = "xx11"; then
   if test "x$with_xinput" = "xxfree" || test "x$with_xinput" = "xyes"; then
     AC_DEFINE(XINPUT_XFREE, 1,
               [Define to 1 if XFree XInput should be used])
-    
+
     if $PKG_CONFIG --exists xi ; then
       X_PACKAGES="$X_PACKAGES xi"
+
+      AC_CHECK_HEADER(X11/extensions/XInput2.h,
+                      have_xinput2=yes; AC_DEFINE(XINPUT_2, 1, [Define to 1 if XInput 2.0 is available]))
     else
       GTK_ADD_LIB(x_extra_libs, Xi)
     fi
@@ -1580,6 +1583,7 @@ if test "x$gdktarget" = "xx11"; then
   fi
 
   AM_CONDITIONAL(XINPUT_XFREE, test x$with_xinput = xxfree || test x$with_xinput = xyes)
+  AM_CONDITIONAL(XINPUT_2, test "x$have_xinput2" = "xyes")
 
   # Check for the RANDR extension
   if $PKG_CONFIG --exists "xrandr >= 1.2.99" ; then
@@ -2103,6 +2107,7 @@ demos/Makefile
 demos/gtk-demo/Makefile
 demos/gtk-demo/geninclude.pl
 tests/Makefile
+tests/multidevice/Makefile
 docs/Makefile
 docs/reference/Makefile
 docs/reference/gdk-pixbuf/Makefile
diff --git a/gdk/Makefile.am b/gdk/Makefile.am
index 9dc9d6e..cebd514 100644
--- a/gdk/Makefile.am
+++ b/gdk/Makefile.am
@@ -67,6 +67,9 @@ gdk_public_h_sources =				\
 	gdkcairo.h				\
 	gdkcolor.h				\
 	gdkcursor.h				\
+	gdkdevice.h				\
+	gdkdevicemanager.h			\
+	gdkdeviceprivate.h			\
 	gdkdisplay.h				\
 	gdkdisplaymanager.h			\
 	gdkdnd.h				\
@@ -107,6 +110,8 @@ gdk_c_sources =                 \
 	gdkcairo.c		\
 	gdkcolor.c		\
 	gdkcursor.c		\
+	gdkdevice.c		\
+	gdkdevicemanager.c	\
 	gdkdisplay.c		\
 	gdkdisplaymanager.c	\
 	gdkdnd.c		\
diff --git a/gdk/gdk.h b/gdk/gdk.h
index d834bb1..dff03c1 100644
--- a/gdk/gdk.h
+++ b/gdk/gdk.h
@@ -33,6 +33,8 @@
 #include <gdk/gdkcairo.h>
 #include <gdk/gdkcolor.h>
 #include <gdk/gdkcursor.h>
+#include <gdk/gdkdevice.h>
+#include <gdk/gdkdevicemanager.h>
 #include <gdk/gdkdisplay.h>
 #include <gdk/gdkdisplaymanager.h>
 #include <gdk/gdkdnd.h>
@@ -112,6 +114,7 @@ gint gdk_input_add	  (gint		     source,
 void gdk_input_remove	  (gint		     tag);
 #endif /* GDK_DISABLE_DEPRECATED */
 
+#ifndef GDK_MULTIDEVICE_SAFE
 GdkGrabStatus gdk_pointer_grab       (GdkWindow    *window,
 				      gboolean      owner_events,
 				      GdkEventMask  event_mask,
@@ -121,18 +124,31 @@ GdkGrabStatus gdk_pointer_grab       (GdkWindow    *window,
 GdkGrabStatus gdk_keyboard_grab      (GdkWindow    *window,
 				      gboolean      owner_events,
 				      guint32       time_);
+#endif /* GDK_MULTIDEVICE_SAFE */
 
-gboolean gdk_pointer_grab_info_libgtk_only (GdkDisplay *display,
-					    GdkWindow **grab_window,
-					    gboolean   *owner_events);
-gboolean gdk_keyboard_grab_info_libgtk_only (GdkDisplay *display,
-					     GdkWindow **grab_window,
-					     gboolean   *owner_events);
+GdkGrabStatus gdk_device_grab        (GdkDevice        *device,
+                                      GdkWindow        *window,
+                                      GdkGrabOwnership  grab_ownership,
+                                      gboolean          owner_events,
+                                      GdkEventMask      event_mask,
+				      GdkCursor        *cursor,
+				      guint32           time_);
+
+void          gdk_device_ungrab      (GdkDevice        *device,
+                                      guint32          time_);
+
+gboolean gdk_device_grab_info_libgtk_only (GdkDisplay  *display,
+                                           GdkDevice   *device,
+                                           GdkWindow  **grab_window,
+                                           gboolean    *owner_events);
 
 #ifndef GDK_MULTIHEAD_SAFE
+
+#ifndef GDK_MULTIDEVICE_SAFE
 void          gdk_pointer_ungrab     (guint32       time_);
 void          gdk_keyboard_ungrab    (guint32       time_);
 gboolean      gdk_pointer_is_grabbed (void);
+#endif /* GDK_MULTIDEVICE_SAFE */
 
 gint gdk_screen_width  (void) G_GNUC_CONST;
 gint gdk_screen_height (void) G_GNUC_CONST;
diff --git a/gdk/gdk.symbols b/gdk/gdk.symbols
index 6e25f87..c891eee 100644
--- a/gdk/gdk.symbols
+++ b/gdk/gdk.symbols
@@ -19,16 +19,6 @@
 #define IN_HEADER(x) 1
 #endif
 
-#if IN_HEADER(__GDK_EVENTS_H__)
-#if IN_FILE(__GDK_EVENTS_X11_C__)
-gdk_add_client_message_filter
-#ifndef GDK_DISABLE_DEPRECATED
-gdk_event_get_graphics_expose
-#endif
-gdk_events_pending
-#endif
-#endif
-
 #if IN_HEADER(__GDK_TEST_UTILS_H__)
 #if IN_FILE(__GDK_TEST_UTILS_X11_C__)
 gdk_test_simulate_button
@@ -61,6 +51,19 @@ gdk_setting_get
 #endif
 #endif
 
+#if IN_HEADER(__GDK_EVENTS_H__)
+#if IN_FILE(__GDK_DISPLAY_X11_C__)
+gdk_add_client_message_filter
+gdk_event_get_graphics_expose
+#endif
+#endif
+
+#if IN_HEADER(__GDK_EVENTS_H__)
+#if IN_FILE(__GDK_EVENT_SOURCE_C__)
+gdk_events_pending
+#endif
+#endif
+
 #if IN_HEADER(__GDK_H__)
 #if IN_FILE(__GDK_MAIN_X11_C__)
 gdk_error_trap_pop
@@ -70,7 +73,6 @@ gdk_get_display
 gdk_get_use_xshm
 gdk_set_use_xshm
 #endif
-gdk_keyboard_grab
 #endif
 #endif
 
@@ -78,17 +80,25 @@ gdk_keyboard_grab
 #if IN_HEADER(__GDK_H__)
 #if IN_FILE(__GDK_DISPLAY_C__)
 gdk_beep
+#ifndef GDK_MULTIDEVICE_SAFE
+#ifndef GDK_DISABLE_DEPRECATED
+#ifndef GDK_MULTIHEAD_SAFE
+gdk_device_get_core_pointer
 gdk_set_pointer_hooks
+#endif
+#endif
 gdk_keyboard_ungrab
 gdk_pointer_is_grabbed
 gdk_pointer_ungrab
+#endif
 gdk_event_send_client_message
 gdk_event_send_clientmessage_toall
-gdk_keyboard_grab_info_libgtk_only
-gdk_pointer_grab_info_libgtk_only
+gdk_device_grab_info_libgtk_only
+#ifndef GDK_MULTIDEVICE_SAFE
 gdk_display_pointer_is_grabbed
 #endif
 #endif
+#endif
 
 #if IN_HEADER(__GDK_H__)
 #if IN_FILE(__GDK_IM_X11_C__)
@@ -101,14 +111,11 @@ gdk_set_locale
 #endif
 
 #if IN_HEADER(__GDK_H__)
-#if IN_FILE(__GDK_EVENTS_X11_C__)
+#if IN_FILE(__GDK_DISPLAY_X11_C__)
+gdk_device_ungrab
+gdk_display_list_devices
 gdk_event_send_client_message_for_display
 gdk_flush
-#endif
-#endif
-
-#if IN_HEADER(__GDK_H__)
-#if IN_FILE(__GDK_DISPLAY_X11_C__)
 gdk_notify_startup_complete
 gdk_notify_startup_complete_with_id
 #endif
@@ -173,6 +180,20 @@ gdk_screen_height_mm G_GNUC_CONST
 #endif
 #endif
 
+#if IN_HEADER(__GDK_H__)
+#if IN_FILE(__GDK_WINDOW_C__)
+#ifndef GDK_MULTIDEVICE_SAFE
+gdk_keyboard_grab
+#endif
+#endif
+#endif
+
+#if IN_HEADER(__GDK_H__)
+#if IN_FILE(__GDK_DEVICE_C__)
+gdk_device_grab
+#endif
+#endif
+
 #if IN_HEADER(__GDK_PROPERTY_H__)
 #if IN_FILE(__GDK_SELECTION_C__)
 gdk_string_to_compound_text
@@ -373,70 +394,68 @@ gdk_cursor_get_image
 #endif
 #endif
 
-#if IN_HEADER(__GDK_INPUT_H__)
-#if IN_FILE(__GDK_INPUT_C__)
+#if IN_HEADER(__GDK_DEVICE_MANAGER_H__)
+#if IN_FILE(__GDK_DEVICE_MANAGER_C__)
+gdk_device_manager_get_display
+gdk_device_manager_get_type G_GNUC_CONST
+#endif
+#endif
+
+#if IN_HEADER(__GDK_DEVICE_H__)
+#if IN_FILE(__GDK_DEVICE_C__)
 gdk_device_free_history
 gdk_device_get_axis
 gdk_device_get_history
 gdk_device_get_type G_GNUC_CONST
+gdk_device_get_state
 gdk_device_set_axis_use
 gdk_device_set_key
+gdk_device_set_mode
 gdk_device_set_source
-gdk_devices_list
-gdk_input_set_extension_events
-#endif
-#endif
-
-#if IN_HEADER(__GDK_INPUT_H__)
-#if IN_FILE(__GDK_DISPLAY_C__)
-gdk_device_get_core_pointer
 #endif
 #endif
 
 #if IN_HEADER(__GDK_INPUT_H__)
-#if IN_FILE(__GDK_INPUT_X11_C__)
-gdk_device_get_state
-#endif
-#endif
-
-#if IN_HEADER(__GDK_INPUT_H__)
-#if IN_FILE(__GDK_INPUT_XFREE_C__)
-gdk_device_set_mode
-#endif
+#if IN_FILE(__GDK_INPUT_C__)
+#ifndef GDK_DISABLE_DEPRECATED
+#ifndef GDK_MULTIDEVICE_SAFE
+#ifndef GDK_MULTIHEAD_SAFE
+gdk_devices_list
 #endif
-
-#if IN_HEADER(__GDK_INPUT_H__)
-#if IN_FILE(__GDK_INPUT_NONE_C__)
-gdk_device_get_state
-gdk_device_set_mode
+gdk_input_set_extension_events
 #endif
 #endif
-
-#if IN_HEADER(__GDK_DISPLAY_H__)
-#if IN_FILE(__GDK_EVENTS_X11_C__)
-gdk_display_add_client_message_filter
 #endif
 #endif
 
 #if IN_HEADER(__GDK_DISPLAY_H__)
 #if IN_FILE(__GDK_DISPLAY_C__)
 gdk_display_close
-gdk_display_get_core_pointer
 gdk_display_get_event
-gdk_display_get_pointer
+gdk_display_get_device_manager
 gdk_display_get_type G_GNUC_CONST
-gdk_display_get_window_at_pointer
 gdk_display_peek_event
 gdk_display_put_event
+#ifndef GDK_MULTIDEVICE_SAFE
+gdk_display_get_pointer
+gdk_display_get_window_at_pointer
+gdk_display_keyboard_ungrab
+gdk_display_pointer_ungrab
 gdk_display_set_pointer_hooks
+#ifndef GDK_DISABLE_DEPRECATED
+gdk_display_get_core_pointer
+#endif
+#endif
 #endif
 #endif
 
 #if IN_HEADER(__GDK_DISPLAY_H__)
 #if IN_FILE(__GDK_WINDOW_X11_C__)
+#ifndef GDK_MULTIDEVICE_SAFE
 gdk_display_warp_pointer
 #endif
 #endif
+#endif
 
 #if IN_HEADER(__GDK_DISPLAY_H__)
 #if IN_FILE(__GDK_DISPLAY_MANAGER_C__)
@@ -455,6 +474,7 @@ gdk_display_supports_cursor_color
 
 #if IN_HEADER(__GDK_DISPLAY_H__)
 #if IN_FILE(__GDK_DISPLAY_X11_C__)
+gdk_display_add_client_message_filter
 gdk_display_beep
 gdk_display_sync
 gdk_display_flush
@@ -463,8 +483,6 @@ gdk_display_get_default_screen
 gdk_display_get_name
 gdk_display_get_n_screens
 gdk_display_get_screen
-gdk_display_pointer_ungrab
-gdk_display_keyboard_ungrab
 gdk_display_open
 gdk_display_request_selection_notification
 gdk_display_store_clipboard
@@ -477,12 +495,6 @@ gdk_display_supports_composite
 #endif
 
 #if IN_HEADER(__GDK_DISPLAY_H__)
-#if IN_FILE(__GDK_INPUT_C__)
-gdk_display_list_devices
-#endif
-#endif
-
-#if IN_HEADER(__GDK_DISPLAY_H__)
 #if IN_FILE(__GDK_C__)
 gdk_display_open_default_libgtk_only
 #endif
@@ -678,7 +690,9 @@ gdk_window_merge_child_input_shapes
 gdk_window_set_static_gravities
 gdk_window_reparent
 gdk_window_add_filter
+#ifndef GDK_MULTIDEVICE_SAFE
 gdk_window_at_pointer
+#endif
 gdk_window_begin_paint_rect
 gdk_window_begin_paint_region
 gdk_window_clear
@@ -694,7 +708,9 @@ gdk_window_freeze_updates
 gdk_window_get_children
 gdk_window_get_internal_paint_info
 gdk_window_get_parent
+#ifndef GDK_MULTIDEVICE_SAFE
 gdk_window_get_pointer
+#endif
 gdk_window_get_position
 gdk_window_get_state
 gdk_window_get_toplevel
@@ -722,7 +738,9 @@ gdk_window_set_user_data
 gdk_window_thaw_toplevel_updates_libgtk_only
 gdk_window_thaw_updates
 gdk_window_set_composited
+#ifndef GDK_MULTIDEVICE_SAFE
 gdk_pointer_grab
+#endif
 gdk_window_beep
 gdk_window_geometry_changed
 gdk_window_ensure_native
@@ -894,12 +912,6 @@ gdk_visual_get_type G_GNUC_CONST
 #endif
 #endif
 
-#if IN_HEADER(__GDK_X_H__)
-#if IN_FILE(__GDK_EVENTS_X11_C__)
-gdk_net_wm_supports
-#endif
-#endif
-
 #if IN_HEADER(__GDK_PANGO_H__)
 #if IN_FILE(__GDK_PANGO_C__)
 gdk_pango_attr_emboss_color_new
@@ -1047,13 +1059,6 @@ gdk_screen_get_rgb_visual
 #endif
 
 #if IN_HEADER(__GDK_SCREEN_H__)
-#if IN_FILE(__GDK_EVENTS_X11_C__)
-gdk_screen_get_setting
-gdk_screen_broadcast_client_message
-#endif
-#endif
-
-#if IN_HEADER(__GDK_SCREEN_H__)
 #if IN_FILE(__GDK_VISUAL_X11_C__)
 gdk_screen_get_system_visual
 gdk_screen_list_visuals
@@ -1068,6 +1073,7 @@ gdk_screen_get_toplevel_windows
 
 #if IN_HEADER(__GDK_SCREEN_H__)
 #if IN_FILE(__GDK_SCREEN_X11_C__)
+gdk_screen_broadcast_client_message
 gdk_screen_get_display
 gdk_screen_get_width
 gdk_screen_get_width_mm
@@ -1075,6 +1081,7 @@ gdk_screen_get_height
 gdk_screen_get_height_mm
 gdk_screen_get_number
 gdk_screen_get_root_window
+gdk_screen_get_setting
 gdk_screen_get_default_colormap
 gdk_screen_set_default_colormap
 gdk_screen_get_n_monitors
@@ -1217,6 +1224,7 @@ gdk_x11_display_ungrab
 gdk_x11_lookup_xdisplay
 gdk_x11_display_broadcast_startup_message
 gdk_x11_display_get_startup_notification_id
+gdk_x11_register_standard_event_type
 #endif
 
 #if IN_FILE(__GDK_DRAWABLE_X11_C__)
@@ -1247,22 +1255,18 @@ gdk_x11_grab_server
 gdk_x11_ungrab_server
 #endif
 
-#if IN_FILE(__GDK_EVENTS_X11_C__)
-gdk_x11_get_server_time
-gdk_x11_register_standard_event_type
-gdk_x11_screen_get_window_manager_name
-gdk_x11_screen_supports_net_wm_hint
-#endif
-
 #if IN_FILE(__GDK_IMAGE_X11_C__)
 gdk_x11_image_get_xdisplay
 gdk_x11_image_get_ximage
 #endif
 
 #if IN_FILE(__GDK_SCREEN_X11_C__)
+gdk_net_wm_supports
 gdk_x11_screen_get_screen_number
+gdk_x11_screen_get_window_manager_name
 gdk_x11_screen_get_xscreen
 gdk_x11_screen_get_monitor_output
+gdk_x11_screen_supports_net_wm_hint
 #endif
 
 #if IN_FILE(__GDK_VISUAL_X11_C__)
@@ -1272,6 +1276,7 @@ gdkx_visual_get
 #endif
 
 #if IN_FILE(__GDK_WINDOW_X11_C__)
+gdk_x11_get_server_time
 gdk_x11_window_set_user_time
 gdk_x11_window_move_to_current_desktop
 #endif
diff --git a/gdk/gdkdevice.c b/gdk/gdkdevice.c
new file mode 100644
index 0000000..2fa58ab
--- /dev/null
+++ b/gdk/gdkdevice.c
@@ -0,0 +1,1052 @@
+/* GDK - The GIMP Drawing Kit
+ * Copyright (C) 2009 Carlos Garnacho <carlosg@gnome.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * 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.
+ */
+
+#include "gdkdevice.h"
+#include "gdkdeviceprivate.h"
+#include "gdkintl.h"
+#include "gdkinternals.h"
+#include "gdkalias.h"
+
+#define GDK_DEVICE_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GDK_TYPE_DEVICE, GdkDevicePrivate))
+
+typedef struct _GdkDevicePrivate GdkDevicePrivate;
+typedef struct _GdkAxisInfo GdkAxisInfo;
+
+struct _GdkAxisInfo
+{
+  GdkAtom label;
+  GdkAxisUse use;
+
+  gdouble min_axis;
+  gdouble max_axis;
+
+  gdouble min_value;
+  gdouble max_value;
+  gdouble resolution;
+};
+
+struct _GdkDevicePrivate
+{
+  GdkDeviceManager *device_manager;
+  GdkDisplay *display;
+  GdkDevice *relative;
+  GdkDeviceType type;
+  GArray *axes;
+};
+
+static void gdk_device_dispose      (GObject      *object);
+static void gdk_device_set_property (GObject      *object,
+                                     guint         prop_id,
+                                     const GValue *value,
+                                     GParamSpec   *pspec);
+static void gdk_device_get_property (GObject      *object,
+                                     guint         prop_id,
+                                     GValue       *value,
+                                     GParamSpec   *pspec);
+
+
+G_DEFINE_ABSTRACT_TYPE (GdkDevice, gdk_device, G_TYPE_OBJECT)
+
+enum {
+  PROP_0,
+  PROP_DISPLAY,
+  PROP_DEVICE_MANAGER,
+  PROP_NAME,
+  PROP_RELATIVE,
+  PROP_TYPE,
+  PROP_INPUT_SOURCE,
+  PROP_INPUT_MODE,
+  PROP_HAS_CURSOR,
+  PROP_N_AXES
+};
+
+
+static void
+gdk_device_class_init (GdkDeviceClass *klass)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+  object_class->dispose = gdk_device_dispose;
+  object_class->set_property = gdk_device_set_property;
+  object_class->get_property = gdk_device_get_property;
+
+  g_object_class_install_property (object_class,
+				   PROP_DISPLAY,
+				   g_param_spec_object ("display",
+                                                        P_("Device Display"),
+                                                        P_("Display to which the device belongs to"),
+                                                        GDK_TYPE_DISPLAY,
+                                                        G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+  g_object_class_install_property (object_class,
+				   PROP_DEVICE_MANAGER,
+				   g_param_spec_object ("device-manager",
+                                                        P_("Device manager"),
+                                                        P_("Device manager to which the device belongs to"),
+                                                        GDK_TYPE_DEVICE_MANAGER,
+                                                        G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+  g_object_class_install_property (object_class,
+				   PROP_NAME,
+				   g_param_spec_string ("name",
+                                                        P_("Device name"),
+                                                        P_("Device name"),
+                                                        NULL,
+                                                        G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+  g_object_class_install_property (object_class,
+                                   PROP_TYPE,
+                                   g_param_spec_enum ("type",
+                                                      P_("Device type"),
+                                                      P_("Device role in the device manager"),
+                                                      GDK_TYPE_DEVICE_TYPE,
+                                                      GDK_DEVICE_TYPE_MASTER,
+                                                      G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+  g_object_class_install_property (object_class,
+				   PROP_RELATIVE,
+				   g_param_spec_object ("relative",
+                                                        P_("Relative device"),
+                                                        P_("Corresponding pointer or keyboard to this device"),
+                                                        GDK_TYPE_DEVICE,
+                                                        G_PARAM_READABLE));
+  g_object_class_install_property (object_class,
+				   PROP_INPUT_SOURCE,
+				   g_param_spec_enum ("input-source",
+                                                      P_("Input source"),
+                                                      P_("Source type for the device"),
+                                                      GDK_TYPE_INPUT_SOURCE,
+                                                      GDK_SOURCE_MOUSE,
+                                                      G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+  g_object_class_install_property (object_class,
+                                   PROP_INPUT_MODE,
+				   g_param_spec_enum ("input-mode",
+                                                      P_("Input mode for the device"),
+                                                      P_("Input mode for the device"),
+                                                      GDK_TYPE_INPUT_MODE,
+                                                      GDK_MODE_DISABLED,
+                                                      G_PARAM_READWRITE));
+  g_object_class_install_property (object_class,
+				   PROP_HAS_CURSOR,
+				   g_param_spec_boolean ("has-cursor",
+                                                         P_("Whether the device has cursor"),
+                                                         P_("Whether there is a visible cursor following device motion"),
+                                                         FALSE,
+                                                         G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+  g_object_class_install_property (object_class,
+				   PROP_N_AXES,
+				   g_param_spec_uint ("n-axes",
+                                                      P_("Number of axes in the device"),
+                                                      P_("Number of axes in the device"),
+                                                      0, G_MAXUINT, 0,
+                                                      G_PARAM_READABLE));
+
+  g_type_class_add_private (object_class, sizeof (GdkDevicePrivate));
+}
+
+static void
+gdk_device_init (GdkDevice *device)
+{
+  GdkDevicePrivate *priv;
+
+  priv = GDK_DEVICE_GET_PRIVATE (device);
+  priv->axes = g_array_new (FALSE, TRUE, sizeof (GdkAxisInfo));
+}
+
+static void
+gdk_device_dispose (GObject *object)
+{
+  GdkDevicePrivate *priv;
+  GdkDevice *device;
+
+  device = GDK_DEVICE (object);
+  priv = GDK_DEVICE_GET_PRIVATE (device);
+
+  if (priv->relative)
+    {
+      _gdk_device_set_relative (priv->relative, NULL);
+      g_object_unref (priv->relative);
+      priv->relative = NULL;
+    }
+
+  if (priv->axes)
+    {
+      g_array_free (priv->axes, TRUE);
+      priv->axes = NULL;
+    }
+
+  g_free (device->name);
+  g_free (device->keys);
+  g_free (device->axes);
+
+  device->name = NULL;
+  device->keys = NULL;
+  device->axes = NULL;
+
+  G_OBJECT_CLASS (gdk_device_parent_class)->dispose (object);
+}
+
+static void
+gdk_device_set_property (GObject      *object,
+                         guint         prop_id,
+                         const GValue *value,
+                         GParamSpec   *pspec)
+{
+  GdkDevice *device = GDK_DEVICE (object);
+  GdkDevicePrivate *priv = GDK_DEVICE_GET_PRIVATE (object);
+
+  switch (prop_id)
+    {
+    case PROP_DISPLAY:
+      priv->display = g_value_get_object (value);
+      break;
+    case PROP_DEVICE_MANAGER:
+      priv->device_manager = g_value_get_object (value);
+      break;
+    case PROP_NAME:
+      if (device->name)
+        g_free (device->name);
+
+      device->name = g_value_dup_string (value);
+      break;
+    case PROP_TYPE:
+      priv->type = g_value_get_enum (value);
+      break;
+    case PROP_INPUT_SOURCE:
+      device->source = g_value_get_enum (value);
+      break;
+    case PROP_INPUT_MODE:
+      gdk_device_set_mode (device, g_value_get_enum (value));
+      break;
+    case PROP_HAS_CURSOR:
+      device->has_cursor = g_value_get_boolean (value);
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+    }
+}
+
+static void
+gdk_device_get_property (GObject    *object,
+                         guint       prop_id,
+                         GValue     *value,
+                         GParamSpec *pspec)
+{
+  GdkDevice *device = GDK_DEVICE (object);
+  GdkDevicePrivate *priv = GDK_DEVICE_GET_PRIVATE (object);
+
+  switch (prop_id)
+    {
+    case PROP_DISPLAY:
+      g_value_set_object (value, priv->display);
+      break;
+    case PROP_DEVICE_MANAGER:
+      g_value_set_object (value, priv->device_manager);
+      break;
+    case PROP_RELATIVE:
+      g_value_set_object (value, priv->relative);
+      break;
+    case PROP_NAME:
+      g_value_set_string (value,
+                          device->name);
+      break;
+    case PROP_TYPE:
+      g_value_set_enum (value, priv->type);
+      break;
+    case PROP_INPUT_SOURCE:
+      g_value_set_enum (value, device->source);
+      break;
+    case PROP_INPUT_MODE:
+      g_value_set_enum (value, device->mode);
+      break;
+    case PROP_HAS_CURSOR:
+      g_value_set_boolean (value,
+                           device->has_cursor);
+      break;
+    case PROP_N_AXES:
+      g_value_set_uint (value, priv->axes->len);
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+    }
+}
+
+void
+gdk_device_get_state (GdkDevice       *device,
+                      GdkWindow       *window,
+                      gdouble         *axes,
+                      GdkModifierType *mask)
+{
+  g_return_if_fail (GDK_IS_DEVICE (device));
+  g_return_if_fail (GDK_IS_WINDOW (window));
+
+  if (GDK_DEVICE_GET_CLASS (device)->get_state)
+    GDK_DEVICE_GET_CLASS (device)->get_state (device, window, axes, mask);
+}
+
+/**
+ * gdk_device_get_history:
+ * @device: a #GdkDevice
+ * @window: the window with respect to which which the event coordinates will be reported
+ * @start: starting timestamp for range of events to return
+ * @stop: ending timestamp for the range of events to return
+ * @events: location to store a newly-allocated array of #GdkTimeCoord, or %NULL
+ * @n_events: location to store the length of @events, or %NULL
+ *
+ * Obtains the motion history for a device; given a starting and
+ * ending timestamp, return all events in the motion history for
+ * the device in the given range of time. Some windowing systems
+ * do not support motion history, in which case, %FALSE will
+ * be returned. (This is not distinguishable from the case where
+ * motion history is supported and no events were found.)
+ *
+ * Return value: %TRUE if the windowing system supports motion history and
+ *  at least one event was found.
+ **/
+gboolean
+gdk_device_get_history (GdkDevice      *device,
+                        GdkWindow      *window,
+                        guint32         start,
+                        guint32         stop,
+                        GdkTimeCoord ***events,
+                        guint          *n_events)
+{
+  g_return_val_if_fail (GDK_IS_DEVICE (device), FALSE);
+  g_return_val_if_fail (GDK_IS_WINDOW (window), FALSE);
+
+  if (!GDK_DEVICE_GET_CLASS (device)->get_history)
+    return FALSE;
+
+  return GDK_DEVICE_GET_CLASS (device)->get_history (device, window,
+                                                     start, stop,
+                                                     events, n_events);
+}
+
+GdkTimeCoord **
+_gdk_device_allocate_history (GdkDevice *device,
+                              guint      n_events)
+{
+  GdkTimeCoord **result = g_new (GdkTimeCoord *, n_events);
+  gint i;
+
+  for (i = 0; i < n_events; i++)
+    result[i] = g_malloc (sizeof (GdkTimeCoord) -
+			  sizeof (double) * (GDK_MAX_TIMECOORD_AXES - device->num_axes));
+  return result;
+}
+
+void
+gdk_device_free_history (GdkTimeCoord **events,
+                         gint           n_events)
+{
+  gint i;
+
+  for (i = 0; i < n_events; i++)
+    g_free (events[i]);
+
+  g_free (events);
+}
+
+void
+gdk_device_set_source (GdkDevice      *device,
+		       GdkInputSource  source)
+{
+  g_return_if_fail (GDK_IS_DEVICE (device));
+
+  device->source = source;
+}
+
+gboolean
+gdk_device_set_mode (GdkDevice    *device,
+                     GdkInputMode  mode)
+{
+  g_return_val_if_fail (GDK_IS_DEVICE (device), FALSE);
+
+  if (device->mode == mode)
+    return TRUE;
+
+  if (mode == GDK_MODE_DISABLED &&
+      gdk_device_get_device_type (device) == GDK_DEVICE_TYPE_MASTER)
+    return FALSE;
+
+  /* FIXME: setting has_cursor when mode is window? */
+
+  device->mode = mode;
+  g_object_notify (G_OBJECT (device), "input-mode");
+
+  if (gdk_device_get_device_type (device) != GDK_DEVICE_TYPE_MASTER)
+    _gdk_input_check_extension_events (device);
+
+  return TRUE;
+}
+
+void
+gdk_device_set_key (GdkDevice      *device,
+		    guint           index,
+		    guint           keyval,
+		    GdkModifierType modifiers)
+{
+  g_return_if_fail (GDK_IS_DEVICE (device));
+  g_return_if_fail (index < device->num_keys);
+
+  device->keys[index].keyval = keyval;
+  device->keys[index].modifiers = modifiers;
+}
+
+void
+gdk_device_set_axis_use (GdkDevice   *device,
+			 guint        index,
+			 GdkAxisUse   use)
+{
+  GdkDevicePrivate *priv;
+  GdkAxisInfo *info;
+
+  g_return_if_fail (GDK_IS_DEVICE (device));
+  g_return_if_fail (index < device->num_axes);
+
+  priv = GDK_DEVICE_GET_PRIVATE (device);
+  info = &g_array_index (priv->axes, GdkAxisInfo, index);
+  info->use = use;
+
+  device->axes[index].use = use;
+
+  switch (use)
+    {
+    case GDK_AXIS_X:
+    case GDK_AXIS_Y:
+      device->axes[index].min = info->min_value = 0;
+      device->axes[index].max = info->max_value = 0;
+      break;
+    case GDK_AXIS_XTILT:
+    case GDK_AXIS_YTILT:
+      device->axes[index].min = info->min_value = -1;
+      device->axes[index].max = info->max_value = 1;
+      break;
+    default:
+      device->axes[index].min = info->min_value = 0;
+      device->axes[index].max = info->max_value = 1;
+      break;
+    }
+}
+
+/**
+ * gdk_device_get_display:
+ * @device: a #GdkDevice
+ *
+ * Returns the #GdkDisplay to which @device pertains.
+ *
+ * Returns: a #GdkDisplay. This memory is owned by GTK+,
+ *          and must not be freed or unreffed.
+ **/
+GdkDisplay *
+gdk_device_get_display (GdkDevice *device)
+{
+  GdkDevicePrivate *priv;
+
+  g_return_val_if_fail (GDK_IS_DEVICE (device), NULL);
+
+  priv = GDK_DEVICE_GET_PRIVATE (device);
+
+  return priv->display;
+}
+
+GdkDevice *
+gdk_device_get_relative (GdkDevice *device)
+{
+  GdkDevicePrivate *priv;
+
+  g_return_val_if_fail (GDK_IS_DEVICE (device), NULL);
+
+  priv = GDK_DEVICE_GET_PRIVATE (device);
+
+  return priv->relative;
+}
+
+void
+_gdk_device_set_relative (GdkDevice *device,
+                          GdkDevice *relative)
+{
+  GdkDevicePrivate *priv;
+
+  g_return_if_fail (GDK_IS_DEVICE (device));
+  g_return_if_fail (GDK_IS_DEVICE (relative));
+
+  priv = GDK_DEVICE_GET_PRIVATE (device);
+
+  if (priv->relative == relative)
+    return;
+
+  if (priv->relative)
+    {
+      g_object_unref (priv->relative);
+      priv->relative = NULL;
+    }
+
+  if (relative)
+    priv->relative = g_object_ref (relative);
+}
+
+/**
+ * gdk_device_get_device_type:
+ * @device: a #GdkDevice
+ *
+ * Returns the device type for @device.
+ *
+ * Returns: the #GdkDeviceType for @device.
+ **/
+GdkDeviceType
+gdk_device_get_device_type (GdkDevice *device)
+{
+  GdkDevicePrivate *priv;
+
+  g_return_val_if_fail (GDK_IS_DEVICE (device), GDK_DEVICE_TYPE_MASTER);
+
+  priv = GDK_DEVICE_GET_PRIVATE (device);
+
+  return priv->type;
+}
+
+/**
+ * gdk_device_list_axes:
+ * @device: a #GdkDevice
+ *
+ * Returns a #GList of #GdkAtom<!-- -->s, containing the labels for
+ * the axes that @device currently has.
+ *
+ * Returns: A #GList of #GdkAtom<!-- -->s, free with g_list_free().
+ **/
+GList *
+gdk_device_list_axes (GdkDevice *device)
+{
+  GdkDevicePrivate *priv;
+  GList *axes = NULL;
+  gint i;
+
+  priv = GDK_DEVICE_GET_PRIVATE (device);
+
+  for (i = 0; i < priv->axes->len; i++)
+    {
+      GdkAxisInfo axis_info;
+
+      axis_info = g_array_index (priv->axes, GdkAxisInfo, i);
+      axes = g_list_prepend (axes, GDK_ATOM_TO_POINTER (axis_info.label));
+    }
+
+  return g_list_reverse (axes);
+}
+
+gboolean
+gdk_device_get_axis_value (GdkDevice *device,
+                           gdouble   *axes,
+                           GdkAtom    axis_label,
+                           gdouble   *value)
+{
+  GdkDevicePrivate *priv;
+  gint i;
+
+  g_return_val_if_fail (GDK_IS_DEVICE (device), FALSE);
+
+  if (axes == NULL)
+    return FALSE;
+
+  priv = GDK_DEVICE_GET_PRIVATE (device);
+
+  for (i = 0; i < priv->axes->len; i++)
+    {
+      GdkAxisInfo axis_info;
+
+      axis_info = g_array_index (priv->axes, GdkAxisInfo, i);
+
+      if (axis_info.label != axis_label)
+        continue;
+
+      if (value)
+        *value = axes[i];
+
+      return TRUE;
+    }
+
+  return FALSE;
+}
+
+/**
+ * gdk_device_get_axis:
+ * @device: a #GdkDevice
+ * @axes: pointer to an array of axes
+ * @use: the use to look for
+ * @value: location to store the found value.
+ *
+ * Interprets an array of double as axis values for a given device,
+ * and locates the value in the array for a given axis use.
+ *
+ * Return value: %TRUE if the given axis use was found, otherwise %FALSE
+ **/
+gboolean
+gdk_device_get_axis (GdkDevice  *device,
+                     gdouble    *axes,
+                     GdkAxisUse  use,
+                     gdouble    *value)
+{
+  GdkDevicePrivate *priv;
+  gint i;
+
+  g_return_val_if_fail (GDK_IS_DEVICE (device), FALSE);
+
+  if (axes == NULL)
+    return FALSE;
+
+  priv = GDK_DEVICE_GET_PRIVATE (device);
+
+  g_return_val_if_fail (priv->axes != NULL, FALSE);
+
+  for (i = 0; i < priv->axes->len; i++)
+    {
+      GdkAxisInfo axis_info;
+
+      axis_info = g_array_index (priv->axes, GdkAxisInfo, i);
+
+      if (axis_info.use != use)
+        continue;
+
+      if (value)
+        *value = axes[i];
+
+      return TRUE;
+    }
+
+  return FALSE;
+}
+
+/**
+ * gdk_device_grab:
+ * @device: a #GdkDevice
+ * @window: the #GdkWindow which will own the grab (the grab window)
+ * @grab_ownership: 
+ * @owner_events: if %FALSE then all device events are reported with respect to
+ *                @window and are only reported if selected by @event_mask. If
+ *                %TRUE then pointer events for this application are reported
+ *                as normal, but pointer events outside this application are
+ *                reported with respect to @window and only if selected by
+ *                @event_mask. In either mode, unreported events are discarded.
+ * @event_mask: specifies the event mask, which is used in accordance with
+ *              @owner_events.
+ * @cursor: the cursor to display while the grab is active if the device is
+ *          a pointer. If this is %NULL then the normal cursors are used for
+ *          @window and its descendants, and the cursor for @window is used
+ *          elsewhere.
+ * @time_: the timestamp of the event which led to this pointer grab. This
+ *         usually comes from the #GdkEvent struct, though %GDK_CURRENT_TIME
+ *         can be used if the time isn't known.
+ *
+ * Grabs the device so that all events coming from this device are passed to
+ * this application until the device is ungrabbed with gdk_device_ungrab(),
+ * or the window becomes unviewable. This overrides any previous grab on the device
+ * by this client.
+ *
+ * Device grabs are used for operations which need complete control over the
+ * given device events (either pointer or keyboard). For example in GTK+ this
+ * is used for Drag and Drop operations, popup menus and such.
+ *
+ * Note that if the event mask of an X window has selected both button press
+ * and button release events, then a button press event will cause an automatic
+ * pointer grab until the button is released. X does this automatically since
+ * most applications expect to receive button press and release events in pairs.
+ * It is equivalent to a pointer grab on the window with @owner_events set to
+ * %TRUE.
+ *
+ * If you set up anything at the time you take the grab that needs to be
+ * cleaned up when the grab ends, you should handle the #GdkEventGrabBroken
+ * events that are emitted when the grab ends unvoluntarily.
+ *
+ * Returns: %GDK_GRAB_SUCCESS if the grab was successful.
+ **/
+GdkGrabStatus
+gdk_device_grab (GdkDevice        *device,
+                 GdkWindow        *window,
+                 GdkGrabOwnership  grab_ownership,
+                 gboolean          owner_events,
+                 GdkEventMask      event_mask,
+                 GdkCursor        *cursor,
+                 guint32           time_)
+{
+  GdkGrabStatus res;
+  GdkWindow *native;
+
+  g_return_val_if_fail (GDK_IS_DEVICE (device), 0);
+  g_return_val_if_fail (GDK_IS_WINDOW (window), 0);
+
+  if (_gdk_native_windows)
+    native = window;
+  else
+    native = gdk_window_get_toplevel (window);
+
+  while (((GdkWindowObject *) native)->window_type == GDK_WINDOW_OFFSCREEN)
+    {
+      native = gdk_offscreen_window_get_embedder (native);
+
+      if (native == NULL ||
+	  (!_gdk_window_has_impl (native) &&
+	   !gdk_window_is_viewable (native)))
+	return GDK_GRAB_NOT_VIEWABLE;
+
+      native = gdk_window_get_toplevel (native);
+    }
+
+  res = _gdk_windowing_device_grab (device,
+                                    window,
+                                    native,
+                                    owner_events,
+                                    event_mask,
+                                    NULL,
+                                    cursor,
+                                    time_);
+
+  if (res == GDK_GRAB_SUCCESS)
+    {
+      GdkDisplay *display;
+      gulong serial;
+
+      display = gdk_drawable_get_display (window);
+      serial = _gdk_windowing_window_get_next_serial (display);
+
+      _gdk_display_add_device_grab (display,
+                                    device,
+                                    window,
+                                    native,
+                                    GDK_OWNERSHIP_NONE,
+                                    owner_events,
+                                    event_mask,
+                                    serial,
+                                    time_,
+                                    FALSE);
+    }
+
+  return res;
+}
+
+/* Private API */
+void
+_gdk_device_reset_axes (GdkDevice *device)
+{
+  GdkDevicePrivate *priv;
+  gint i;
+
+  priv = GDK_DEVICE_GET_PRIVATE (device);
+
+  for (i = priv->axes->len - 1; i >= 0; i--)
+    g_array_remove_index (priv->axes, i);
+
+  g_object_notify (G_OBJECT (device), "n-axes");
+
+  /* This is done for backwards compatibility */
+  g_free (device->axes);
+  device->axes = NULL;
+}
+
+guint
+_gdk_device_add_axis (GdkDevice   *device,
+                      GdkAtom      label_atom,
+                      GdkAxisUse   use,
+                      gdouble      min_value,
+                      gdouble      max_value,
+                      gdouble      resolution)
+{
+  GdkDevicePrivate *priv;
+  GdkAxisInfo axis_info;
+  guint pos;
+
+  priv = GDK_DEVICE_GET_PRIVATE (device);
+
+  axis_info.use = use;
+  axis_info.label = label_atom;
+  axis_info.min_value = min_value;
+  axis_info.max_value = max_value;
+  axis_info.resolution = resolution;
+
+  switch (use)
+    {
+    case GDK_AXIS_X:
+    case GDK_AXIS_Y:
+      axis_info.min_axis = 0.;
+      axis_info.max_axis = 0.;
+      break;
+    case GDK_AXIS_XTILT:
+    case GDK_AXIS_YTILT:
+      axis_info.min_axis = -1.;
+      axis_info.max_axis = 1.;
+      break;
+    default:
+      axis_info.min_axis = 0.;
+      axis_info.max_axis = 1.;
+      break;
+    }
+
+  priv->axes = g_array_append_val (priv->axes, axis_info);
+  device->num_axes = priv->axes->len;
+  pos = device->num_axes - 1;
+
+  /* This is done for backwards compatibility, since the public
+   * struct doesn't actually store the device data.
+   */
+  device->axes = g_realloc (device->axes, sizeof (GdkDeviceAxis) * priv->axes->len);
+  device->axes[pos].use = axis_info.use;
+  device->axes[pos].min = axis_info.min_axis;
+  device->axes[pos].max = axis_info.max_axis;
+
+  g_object_notify (G_OBJECT (device), "n-axes");
+
+  return pos;
+}
+
+void
+_gdk_device_set_keys (GdkDevice *device,
+                      guint      num_keys)
+{
+  if (device->keys)
+    g_free (device->keys);
+
+  device->num_keys = num_keys;
+  device->keys = g_new0 (GdkDeviceKey, num_keys);
+}
+
+GdkAxisInfo *
+find_axis_info (GArray     *array,
+                GdkAxisUse  use)
+{
+  GdkAxisInfo *info;
+  gint i;
+
+  for (i = 0; i < GDK_AXIS_LAST; i++)
+    {
+      info = &g_array_index (array, GdkAxisInfo, i);
+
+      if (info->use == use)
+        return info;
+    }
+
+  return NULL;
+}
+
+GdkAxisUse
+_gdk_device_get_axis_use (GdkDevice *device,
+                          guint      index)
+{
+  GdkDevicePrivate *priv;
+  GdkAxisInfo info;
+
+  priv = GDK_DEVICE_GET_PRIVATE (device);
+
+  info = g_array_index (priv->axes, GdkAxisInfo, index);
+  return info.use;
+}
+
+gboolean
+_gdk_device_translate_window_coord (GdkDevice *device,
+                                    GdkWindow *window,
+                                    guint      index,
+                                    gdouble    value,
+                                    gdouble   *axis_value)
+{
+  GdkDevicePrivate *priv;
+  GdkAxisInfo axis_info;
+  GdkAxisInfo *axis_info_x, *axis_info_y;
+  gdouble device_width, device_height;
+  gdouble x_offset, y_offset;
+  gdouble x_scale, y_scale;
+  gdouble x_min, y_min;
+  gdouble x_resolution, y_resolution;
+  gdouble device_aspect;
+  gint window_width, window_height;
+  GdkWindowObject *window_private;
+
+  priv = GDK_DEVICE_GET_PRIVATE (device);
+
+  if (index >= priv->axes->len)
+    return FALSE;
+
+  axis_info = g_array_index (priv->axes, GdkAxisInfo, index);
+
+  if (axis_info.use != GDK_AXIS_X &&
+      axis_info.use != GDK_AXIS_Y)
+    return FALSE;
+
+  if (axis_info.use == GDK_AXIS_X)
+    {
+      axis_info_x = &axis_info;
+      axis_info_y = find_axis_info (priv->axes, GDK_AXIS_Y);
+    }
+  else
+    {
+      axis_info_x = find_axis_info (priv->axes, GDK_AXIS_X);
+      axis_info_y = &axis_info;
+    }
+
+  device_width = axis_info_x->max_value - axis_info_x->min_value;
+  device_height = axis_info_y->max_value - axis_info_y->min_value;
+
+  if (device_width > 0)
+    x_min = axis_info_x->min_value;
+  else
+    {
+      device_width = gdk_screen_get_width (gdk_drawable_get_screen (window));
+      x_min = 0;
+    }
+
+  if (device_height > 0)
+    y_min = axis_info_y->min_value;
+  else
+    {
+      device_height = gdk_screen_get_height (gdk_drawable_get_screen (window));
+      y_min = 0;
+    }
+
+  window_private = (GdkWindowObject *) window;
+  gdk_drawable_get_size (window, &window_width, &window_height);
+
+  x_resolution = axis_info_x->resolution;
+  y_resolution = axis_info_y->resolution;
+
+  /*
+   * Some drivers incorrectly report the resolution of the device
+   * as zero (in partiular linuxwacom < 0.5.3 with usb tablets).
+   * This causes the device_aspect to become NaN and totally
+   * breaks windowed mode.  If this is the case, the best we can
+   * do is to assume the resolution is non-zero is equal in both
+   * directions (which is true for many devices).  The absolute
+   * value of the resolution doesn't matter since we only use the
+   * ratio.
+   */
+  if (x_resolution == 0 || y_resolution == 0)
+    {
+      x_resolution = 1;
+      y_resolution = 1;
+    }
+
+  device_aspect = (device_height * y_resolution) /
+    (device_width * x_resolution);
+
+  if (device_aspect * window_width >= window_height)
+    {
+      /* device taller than window */
+      x_scale = window_width / device_width;
+      y_scale = (x_scale * x_resolution) / y_resolution;
+
+      x_offset = 0;
+      y_offset = - (device_height * y_scale - window_height) / 2;
+    }
+  else
+    {
+      /* window taller than device */
+      y_scale = window_height / device_height;
+      x_scale = (y_scale * y_resolution) / x_resolution;
+
+      y_offset = 0;
+      x_offset = - (device_width * x_scale - window_width) / 2;
+    }
+
+  if (axis_value)
+    {
+      if (axis_info.use == GDK_AXIS_X)
+        *axis_value = x_offset + x_scale * (value - x_min);
+      else
+        *axis_value = y_offset + y_scale * (value - y_min);
+    }
+
+  return TRUE;
+}
+
+gboolean
+_gdk_device_translate_screen_coord (GdkDevice *device,
+                                    GdkWindow *window,
+                                    gint       window_root_x,
+                                    gint       window_root_y,
+                                    guint      index,
+                                    gdouble    value,
+                                    gdouble   *axis_value)
+{
+  GdkDevicePrivate *priv;
+  GdkAxisInfo axis_info;
+  gdouble axis_width, scale, offset;
+  GdkWindowObject *window_private;
+
+  if (device->mode != GDK_MODE_SCREEN)
+    return FALSE;
+
+  priv = GDK_DEVICE_GET_PRIVATE (device);
+
+  if (index >= priv->axes->len)
+    return FALSE;
+
+  axis_info = g_array_index (priv->axes, GdkAxisInfo, index);
+
+  if (axis_info.use != GDK_AXIS_X &&
+      axis_info.use != GDK_AXIS_Y)
+    return FALSE;
+
+  axis_width = axis_info.max_value - axis_info.min_value;
+  window_private = (GdkWindowObject *) window;
+
+  if (axis_info.use == GDK_AXIS_X)
+    {
+      scale = gdk_screen_get_width (gdk_drawable_get_screen (window)) / axis_width;
+      offset = - window_root_x - window_private->abs_x;
+    }
+  else
+    {
+      scale = gdk_screen_get_height (gdk_drawable_get_screen (window)) / axis_width;
+      offset = - window_root_y - window_private->abs_y;
+    }
+
+  if (axis_value)
+    *axis_value = offset + scale * (value - axis_info.min_value);
+
+  return TRUE;
+}
+
+gboolean
+_gdk_device_translate_axis (GdkDevice *device,
+                            guint      index,
+                            gdouble    value,
+                            gdouble   *axis_value)
+{
+  GdkDevicePrivate *priv;
+  GdkAxisInfo axis_info;
+  gdouble axis_width, out;
+
+  priv = GDK_DEVICE_GET_PRIVATE (device);
+
+  if (index >= priv->axes->len)
+    return FALSE;
+
+  axis_info = g_array_index (priv->axes, GdkAxisInfo, index);
+
+  if (axis_info.use == GDK_AXIS_X ||
+      axis_info.use == GDK_AXIS_Y)
+    return FALSE;
+
+  axis_width = axis_info.max_value - axis_info.min_value;
+  out = (axis_info.max_axis * (value - axis_info.min_value) +
+         axis_info.min_axis * (axis_info.max_value - value)) / axis_width;
+
+  if (axis_value)
+    *axis_value = out;
+
+  return TRUE;
+}
+
+#define __GDK_DEVICE_C__
+#include "gdkaliasdef.c"
diff --git a/gdk/gdkdevice.h b/gdk/gdkdevice.h
new file mode 100644
index 0000000..3f4eae2
--- /dev/null
+++ b/gdk/gdkdevice.h
@@ -0,0 +1,174 @@
+/* GDK - The GIMP Drawing Kit
+ * Copyright (C) 2009 Carlos Garnacho <carlosg@gnome.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * 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.
+ */
+
+#if defined(GTK_DISABLE_SINGLE_INCLUDES) && !defined (__GDK_H_INSIDE__) && !defined (GDK_COMPILATION)
+#error "Only <gdk/gdk.h> can be included directly."
+#endif
+
+#ifndef __GDK_DEVICE_H__
+#define __GDK_DEVICE_H__
+
+#include <gdk/gdktypes.h>
+
+G_BEGIN_DECLS
+
+#define GDK_TYPE_DEVICE         (gdk_device_get_type ())
+#define GDK_DEVICE(o)           (G_TYPE_CHECK_INSTANCE_CAST ((o), GDK_TYPE_DEVICE, GdkDevice))
+#define GDK_IS_DEVICE(o)        (G_TYPE_CHECK_INSTANCE_TYPE ((o), GDK_TYPE_DEVICE))
+
+typedef struct _GdkDevice GdkDevice;
+
+typedef struct _GdkDeviceKey GdkDeviceKey;
+typedef struct _GdkDeviceAxis GdkDeviceAxis;
+typedef struct _GdkTimeCoord GdkTimeCoord;
+
+typedef enum
+{
+  GDK_EXTENSION_EVENTS_NONE,
+  GDK_EXTENSION_EVENTS_ALL,
+  GDK_EXTENSION_EVENTS_CURSOR
+} GdkExtensionMode;
+
+typedef enum
+{
+  GDK_SOURCE_MOUSE,
+  GDK_SOURCE_PEN,
+  GDK_SOURCE_ERASER,
+  GDK_SOURCE_CURSOR,
+  GDK_SOURCE_KEYBOARD
+} GdkInputSource;
+
+typedef enum
+{
+  GDK_MODE_DISABLED,
+  GDK_MODE_SCREEN,
+  GDK_MODE_WINDOW
+} GdkInputMode;
+
+typedef enum
+{
+  GDK_AXIS_IGNORE,
+  GDK_AXIS_X,
+  GDK_AXIS_Y,
+  GDK_AXIS_PRESSURE,
+  GDK_AXIS_XTILT,
+  GDK_AXIS_YTILT,
+  GDK_AXIS_WHEEL,
+  GDK_AXIS_LAST
+} GdkAxisUse;
+
+typedef enum {
+  GDK_DEVICE_TYPE_MASTER,
+  GDK_DEVICE_TYPE_SLAVE,
+  GDK_DEVICE_TYPE_FLOATING
+} GdkDeviceType;
+
+struct _GdkDeviceKey
+{
+  guint keyval;
+  GdkModifierType modifiers;
+};
+
+struct _GdkDeviceAxis
+{
+  GdkAxisUse use;
+  gdouble    min;
+  gdouble    max;
+};
+
+/* We don't allocate each coordinate this big, but we use it to
+ * be ANSI compliant and avoid accessing past the defined limits.
+ */
+#define GDK_MAX_TIMECOORD_AXES 128
+
+struct _GdkTimeCoord
+{
+  guint32 time;
+  gdouble axes[GDK_MAX_TIMECOORD_AXES];
+};
+
+struct _GdkDevice
+{
+  GObject parent_instance;
+
+  /* All fields are read-only */
+  gchar *name;
+  GdkInputSource source;
+  GdkInputMode mode;
+  gboolean has_cursor;	     /* TRUE if the X pointer follows device motion */
+
+  gint num_axes;
+  GdkDeviceAxis *axes;
+
+  gint num_keys;
+  GdkDeviceKey *keys;
+};
+
+GType gdk_device_get_type (void) G_GNUC_CONST;
+
+/* Functions to configure a device */
+void           gdk_device_set_source    (GdkDevice      *device,
+					 GdkInputSource  source);
+
+gboolean       gdk_device_set_mode      (GdkDevice      *device,
+					 GdkInputMode    mode);
+
+void           gdk_device_set_key       (GdkDevice      *device,
+					 guint           index_,
+					 guint           keyval,
+					 GdkModifierType modifiers);
+
+void           gdk_device_set_axis_use  (GdkDevice         *device,
+                                         guint              index_,
+                                         GdkAxisUse         use);
+
+
+void     gdk_device_get_state    (GdkDevice         *device,
+				  GdkWindow         *window,
+				  gdouble           *axes,
+				  GdkModifierType   *mask);
+gboolean gdk_device_get_history  (GdkDevice         *device,
+				  GdkWindow         *window,
+				  guint32            start,
+				  guint32            stop,
+				  GdkTimeCoord    ***events,
+				  guint             *n_events);
+void     gdk_device_free_history (GdkTimeCoord     **events,
+				  gint               n_events);
+
+GList *  gdk_device_list_axes      (GdkDevice       *device);
+gboolean gdk_device_get_axis_value (GdkDevice       *device,
+                                    gdouble         *axes,
+                                    GdkAtom          axis_label,
+                                    gdouble         *value);
+
+gboolean gdk_device_get_axis     (GdkDevice         *device,
+				  gdouble           *axes,
+				  GdkAxisUse         use,
+				  gdouble           *value);
+GdkDisplay * gdk_device_get_display (GdkDevice      *device);
+
+GdkDevice  * gdk_device_get_relative (GdkDevice     *device);
+
+GdkDeviceType gdk_device_get_device_type (GdkDevice *device);
+
+
+G_END_DECLS
+
+#endif /* __GDK_DEVICE_H__ */
diff --git a/gdk/gdkdevicemanager.c b/gdk/gdkdevicemanager.c
new file mode 100644
index 0000000..0f901fc
--- /dev/null
+++ b/gdk/gdkdevicemanager.c
@@ -0,0 +1,230 @@
+/* GDK - The GIMP Drawing Kit
+ * Copyright (C) 2009 Carlos Garnacho <carlosg@gnome.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * 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.
+ */
+
+#include "gdkdevicemanager.h"
+#include "gdkintl.h"
+#include "gdkinternals.h"
+#include "gdkalias.h"
+
+
+static void gdk_device_manager_set_property (GObject      *object,
+                                             guint         prop_id,
+                                             const GValue *value,
+                                             GParamSpec   *pspec);
+static void gdk_device_manager_get_property (GObject      *object,
+                                             guint         prop_id,
+                                             GValue       *value,
+                                             GParamSpec   *pspec);
+
+
+G_DEFINE_ABSTRACT_TYPE (GdkDeviceManager, gdk_device_manager, G_TYPE_OBJECT)
+
+enum {
+  PROP_0,
+  PROP_DISPLAY
+};
+
+enum {
+  DEVICE_ADDED,
+  DEVICE_REMOVED,
+  DEVICE_CHANGED,
+  LAST_SIGNAL
+};
+
+static guint signals [LAST_SIGNAL] = { 0 };
+
+typedef struct GdkDeviceManagerPrivate GdkDeviceManagerPrivate;
+
+struct GdkDeviceManagerPrivate
+{
+  GdkDisplay *display;
+};
+
+#define GDK_DEVICE_MANAGER_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), GDK_TYPE_DEVICE_MANAGER, GdkDeviceManagerPrivate))
+
+static void
+gdk_device_manager_class_init (GdkDeviceManagerClass *klass)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+  object_class->set_property = gdk_device_manager_set_property;
+  object_class->get_property = gdk_device_manager_get_property;
+
+  g_object_class_install_property (object_class,
+				   PROP_DISPLAY,
+				   g_param_spec_object ("display",
+ 							P_("Display"),
+ 							P_("Display for the device manager"),
+							GDK_TYPE_DISPLAY,
+ 							G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+
+  /**
+   * GdkDeviceManager::device-added:
+   * @device_manager: the object on which the signal is emitted
+   * @device: the newly added #GdkDevice.
+   *
+   * The ::device-added signal is emitted either when a new master
+   * pointer is created, or when a slave (Hardware) input device
+   * is plugged in.
+   */
+  signals [DEVICE_ADDED] =
+    g_signal_new (g_intern_static_string ("device-added"),
+                  G_TYPE_FROM_CLASS (klass),
+                  G_SIGNAL_RUN_LAST,
+                  G_STRUCT_OFFSET (GdkDeviceManagerClass, device_added),
+                  NULL, NULL,
+                  g_cclosure_marshal_VOID__OBJECT,
+                  G_TYPE_NONE, 1,
+                  GDK_TYPE_DEVICE);
+
+  /**
+   * GdkDeviceManager::device-removed:
+   * @device_manager: the object on which the signal is emitted
+   * @device: the just removed #GdkDevice.
+   *
+   * The ::device-added signal is emitted either when a master
+   * pointer is removed, or when a slave (Hardware) input device
+   * is unplugged.
+   */
+  signals [DEVICE_REMOVED] =
+    g_signal_new (g_intern_static_string ("device-removed"),
+                  G_TYPE_FROM_CLASS (klass),
+                  G_SIGNAL_RUN_LAST,
+                  G_STRUCT_OFFSET (GdkDeviceManagerClass, device_removed),
+                  NULL, NULL,
+                  g_cclosure_marshal_VOID__OBJECT,
+                  G_TYPE_NONE, 1,
+                  GDK_TYPE_DEVICE);
+
+  /**
+   * GdkDeviceManager::device-changed:
+   * @device_manager: the object on which the signal is emitted
+   * @device: the #GdkDevice that changed.
+   *
+   * The ::device-changed signal is emitted either when some
+   * #GdkDevice has changed the number of either axes or keys.
+   * For example In X this will normally happen when the slave
+   * device routing events through the master device changes,
+   * in that case the master device will change to reflect the
+   * new slave device axes and keys.
+   */
+  signals [DEVICE_CHANGED] =
+    g_signal_new (g_intern_static_string ("device-changed"),
+                  G_TYPE_FROM_CLASS (klass),
+                  G_SIGNAL_RUN_LAST,
+                  G_STRUCT_OFFSET (GdkDeviceManagerClass, device_changed),
+                  NULL, NULL,
+                  g_cclosure_marshal_VOID__OBJECT,
+                  G_TYPE_NONE, 1,
+                  GDK_TYPE_DEVICE);
+
+  g_type_class_add_private (object_class, sizeof (GdkDeviceManagerPrivate));
+}
+
+static void
+gdk_device_manager_init (GdkDeviceManager *manager)
+{
+
+}
+
+static void
+gdk_device_manager_set_property (GObject      *object,
+                                 guint         prop_id,
+                                 const GValue *value,
+                                 GParamSpec   *pspec)
+{
+  GdkDeviceManagerPrivate *priv;
+
+  priv = GDK_DEVICE_MANAGER_GET_PRIVATE (object);
+
+  switch (prop_id)
+    {
+    case PROP_DISPLAY:
+      priv->display = g_value_get_object (value);
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+    }
+}
+
+static void
+gdk_device_manager_get_property (GObject      *object,
+                                 guint         prop_id,
+                                 GValue       *value,
+                                 GParamSpec   *pspec)
+{
+  GdkDeviceManagerPrivate *priv;
+
+  priv = GDK_DEVICE_MANAGER_GET_PRIVATE (object);
+
+  switch (prop_id)
+    {
+    case PROP_DISPLAY:
+      g_value_set_object (value, priv->display);
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+    }
+}
+
+/**
+ * gdk_device_manager_get_display:
+ * @device_manager: a #GdkDeviceManager
+ *
+ * Gets the #GdkDisplay associated to @device_manager.
+ *
+ * Returns: the #GdkDisplay to which @device_manager is
+ *          associated to, or #NULL.
+ **/
+GdkDisplay *
+gdk_device_manager_get_display (GdkDeviceManager *device_manager)
+{
+  GdkDeviceManagerPrivate *priv;
+
+  g_return_val_if_fail (GDK_IS_DEVICE_MANAGER (device_manager), NULL);
+
+  priv = GDK_DEVICE_MANAGER_GET_PRIVATE (device_manager);
+
+  return priv->display;
+}
+
+/**
+ * gdk_device_manager_get_devices:
+ * @device_manager: a #GdkDeviceManager
+ * @type: device type to get.
+ *
+ * Returns the list of devices of type @type currently attached to
+ * @device_manager.
+ *
+ * Returns: a list of #GdkDevice<!-- -->s. The returned list must be
+ *          freed with g_list_free ().
+ **/
+GList *
+gdk_device_manager_get_devices (GdkDeviceManager *device_manager,
+                                GdkDeviceType     type)
+{
+  g_return_val_if_fail (GDK_IS_DEVICE_MANAGER (device_manager), NULL);
+
+  return GDK_DEVICE_MANAGER_GET_CLASS (device_manager)->get_devices (device_manager, type);
+}
+
+#define __GDK_DEVICE_MANAGER_C__
+#include "gdkaliasdef.c"
diff --git a/gdk/gdkdevicemanager.h b/gdk/gdkdevicemanager.h
new file mode 100644
index 0000000..c82971f
--- /dev/null
+++ b/gdk/gdkdevicemanager.h
@@ -0,0 +1,72 @@
+/* GDK - The GIMP Drawing Kit
+ * Copyright (C) 2009 Carlos Garnacho <carlosg@gnome.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * 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.
+ */
+
+#if defined(GTK_DISABLE_SINGLE_INCLUDES) && !defined (__GDK_H_INSIDE__) && !defined (GDK_COMPILATION)
+#error "Only <gdk/gdk.h> can be included directly."
+#endif
+
+#ifndef __GDK_DEVICE_MANAGER_H__
+#define __GDK_DEVICE_MANAGER_H__
+
+#include <gdk/gdktypes.h>
+#include <gdk/gdkdevice.h>
+
+G_BEGIN_DECLS
+
+#define GDK_TYPE_DEVICE_MANAGER         (gdk_device_manager_get_type ())
+#define GDK_DEVICE_MANAGER(o)           (G_TYPE_CHECK_INSTANCE_CAST ((o), GDK_TYPE_DEVICE_MANAGER, GdkDeviceManager))
+#define GDK_DEVICE_MANAGER_CLASS(c)     (G_TYPE_CHECK_CLASS_CAST ((c), GDK_TYPE_DEVICE_MANAGER, GdkDeviceManagerClass))
+#define GDK_IS_DEVICE_MANAGER(o)        (G_TYPE_CHECK_INSTANCE_TYPE ((o), GDK_TYPE_DEVICE_MANAGER))
+#define GDK_IS_DEVICE_MANAGER_CLASS(c)  (G_TYPE_CHECK_CLASS_TYPE ((c), GDK_TYPE_DEVICE_MANAGER))
+#define GDK_DEVICE_MANAGER_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GDK_TYPE_DEVICE_MANAGER, GdkDeviceManagerClass))
+
+typedef struct _GdkDeviceManager GdkDeviceManager;
+typedef struct _GdkDeviceManagerClass GdkDeviceManagerClass;
+
+struct _GdkDeviceManager
+{
+  GObject parent_instance;
+};
+
+struct _GdkDeviceManagerClass
+{
+  GObjectClass parent_class;
+
+  /* Signals */
+  void (* device_added)   (GdkDeviceManager *device_manager,
+                           GdkDevice        *device);
+  void (* device_removed) (GdkDeviceManager *device_manager,
+                           GdkDevice        *device);
+  void (* device_changed) (GdkDeviceManager *device_manager,
+                           GdkDevice        *device);
+
+  /* VMethods */
+  GList * (* get_devices) (GdkDeviceManager *device_manager,
+                           GdkDeviceType     type);
+};
+
+GType gdk_device_manager_get_type (void) G_GNUC_CONST;
+
+GdkDisplay *             gdk_device_manager_get_display      (GdkDeviceManager *device_manager);
+GList *                  gdk_device_manager_get_devices      (GdkDeviceManager *device_manager,
+                                                              GdkDeviceType     type);
+
+G_END_DECLS
+
+#endif /* __GDK_DEVICE_MANAGER_H__ */
diff --git a/gdk/gdkdeviceprivate.h b/gdk/gdkdeviceprivate.h
new file mode 100644
index 0000000..9ce5875
--- /dev/null
+++ b/gdk/gdkdeviceprivate.h
@@ -0,0 +1,135 @@
+/* GDK - The GIMP Drawing Kit
+ * Copyright (C) 2009 Carlos Garnacho <carlosg@gnome.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * 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.
+ */
+
+#if defined(GTK_DISABLE_SINGLE_INCLUDES) && !defined (__GDK_H_INSIDE__) && !defined (GDK_COMPILATION)
+#error "Only <gdk/gdk.h> can be included directly."
+#endif
+
+#ifndef __GDK_DEVICE_PRIVATE_H__
+#define __GDK_DEVICE_PRIVATE_H__
+
+#include <gdk/gdkdevice.h>
+#include <gdk/gdkevents.h>
+
+G_BEGIN_DECLS
+
+#define GDK_DEVICE_CLASS(c)     (G_TYPE_CHECK_CLASS_CAST ((c), GDK_TYPE_DEVICE, GdkDeviceClass))
+#define GDK_IS_DEVICE_CLASS(c)  (G_TYPE_CHECK_CLASS_TYPE ((c), GDK_TYPE_DEVICE))
+#define GDK_DEVICE_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GDK_TYPE_DEVICE, GdkDeviceClass))
+
+typedef struct _GdkDeviceClass GdkDeviceClass;
+
+struct _GdkDeviceClass
+{
+  GObjectClass parent_class;
+
+  gboolean (* get_history) (GdkDevice      *device,
+                            GdkWindow      *window,
+                            guint32         start,
+                            guint32         stop,
+                            GdkTimeCoord ***events,
+                            guint          *n_events);
+
+  void (* get_state) (GdkDevice       *device,
+                      GdkWindow       *window,
+                      gdouble         *axes,
+                      GdkModifierType *mask);
+
+  void (* set_window_cursor) (GdkDevice *device,
+                              GdkWindow *window,
+                              GdkCursor *cursor);
+
+  void (* warp)              (GdkDevice  *device,
+                              GdkScreen  *screen,
+                              gint        x,
+                              gint        y);
+  gboolean (* query_state)   (GdkDevice        *device,
+                              GdkWindow        *window,
+                              GdkWindow       **root_window,
+                              GdkWindow       **child_window,
+                              gint             *root_x,
+                              gint             *root_y,
+                              gint             *win_x,
+                              gint             *win_y,
+                              GdkModifierType  *mask);
+  GdkGrabStatus (* grab)     (GdkDevice        *device,
+                              GdkWindow        *window,
+                              gboolean          owner_events,
+                              GdkEventMask      event_mask,
+                              GdkWindow        *confine_to,
+                              GdkCursor        *cursor,
+                              guint32           time_);
+  void          (*ungrab)    (GdkDevice        *device,
+                              guint32           time_);
+
+  GdkWindow * (* window_at_position) (GdkDevice       *device,
+                                      gint            *win_x,
+                                      gint            *win_y,
+                                      GdkModifierType *mask,
+                                      gboolean         get_toplevel);
+  void (* select_window_events)      (GdkDevice       *device,
+                                      GdkWindow       *window,
+                                      GdkEventMask     event_mask);
+};
+
+void  _gdk_device_set_relative (GdkDevice *device,
+                                GdkDevice *relative);
+
+void  _gdk_device_reset_axes (GdkDevice   *device);
+guint _gdk_device_add_axis   (GdkDevice   *device,
+                              GdkAtom      label_atom,
+                              GdkAxisUse   use,
+                              gdouble      min_value,
+                              gdouble      max_value,
+                              gdouble      resolution);
+
+void _gdk_device_set_keys    (GdkDevice   *device,
+                              guint        num_keys);
+
+GdkAxisUse _gdk_device_get_axis_use (GdkDevice *device,
+                                     guint      index);
+
+gboolean   _gdk_device_translate_window_coord (GdkDevice *device,
+                                               GdkWindow *window,
+                                               guint      index,
+                                               gdouble    value,
+                                               gdouble   *axis_value);
+
+gboolean   _gdk_device_translate_screen_coord (GdkDevice *device,
+                                               GdkWindow *window,
+                                               gint       window_root_x,
+                                               gint       window_root_y,
+                                               guint      index,
+                                               gdouble    value,
+                                               gdouble   *axis_value);
+
+gboolean   _gdk_device_translate_axis         (GdkDevice *device,
+                                               guint      index,
+                                               gdouble    value,
+                                               gdouble   *axis_value);
+
+GdkTimeCoord ** _gdk_device_allocate_history  (GdkDevice *device,
+                                               guint      n_events);
+
+void _gdk_input_check_extension_events (GdkDevice *device);
+
+
+G_END_DECLS
+
+#endif /* __GDK_DEVICE_PRIVATE_H__ */
diff --git a/gdk/gdkdisplay.c b/gdk/gdkdisplay.c
index c2064ba..0c5dd79 100644
--- a/gdk/gdkdisplay.c
+++ b/gdk/gdkdisplay.c
@@ -33,12 +33,44 @@
 #include "gdkalias.h"
 
 enum {
+  OPENED,
   CLOSED,
   LAST_SIGNAL
 };
 
-static void gdk_display_dispose    (GObject         *object);
-static void gdk_display_finalize   (GObject         *object);
+static void gdk_display_dispose     (GObject         *object);
+static void gdk_display_finalize    (GObject         *object);
+
+static void        multihead_get_device_state           (GdkDisplay       *display,
+                                                         GdkDevice        *device,
+                                                         GdkScreen       **screen,
+                                                         gint             *x,
+                                                         gint             *y,
+                                                         GdkModifierType  *mask);
+static GdkWindow * multihead_window_get_device_position (GdkDisplay       *display,
+                                                         GdkDevice        *device,
+                                                         GdkWindow        *window,
+                                                         gint             *x,
+                                                         gint             *y,
+                                                         GdkModifierType  *mask);
+static GdkWindow * multihead_window_at_device_position  (GdkDisplay       *display,
+                                                         GdkDevice        *device,
+                                                         gint             *win_x,
+                                                         gint             *win_y);
+
+static void        multihead_default_get_pointer        (GdkDisplay       *display,
+                                                         GdkScreen       **screen,
+                                                         gint             *x,
+                                                         gint             *y,
+                                                         GdkModifierType  *mask);
+static GdkWindow * multihead_default_window_get_pointer (GdkDisplay      *display,
+                                                         GdkWindow       *window,
+                                                         gint            *x,
+                                                         gint            *y,
+                                                         GdkModifierType *mask);
+static GdkWindow * multihead_default_window_at_pointer  (GdkDisplay      *display,
+                                                         gint            *win_x,
+                                                         gint            *win_y);
 
 
 static void       singlehead_get_pointer (GdkDisplay       *display,
@@ -62,23 +94,37 @@ static GdkWindow* singlehead_default_window_get_pointer (GdkWindow       *window
 static GdkWindow* singlehead_default_window_at_pointer  (GdkScreen       *screen,
 							 gint            *win_x,
 							 gint            *win_y);
-static GdkWindow *gdk_window_real_window_get_pointer     (GdkDisplay       *display,
-                                                          GdkWindow        *window,
-                                                          gint             *x,
-                                                          gint             *y,
-                                                          GdkModifierType  *mask);
-static GdkWindow *gdk_display_real_get_window_at_pointer (GdkDisplay       *display,
-                                                          gint             *win_x,
-                                                          gint             *win_y);
+static GdkWindow *gdk_window_real_window_get_device_position     (GdkDisplay       *display,
+                                                                  GdkDevice        *device,
+                                                                  GdkWindow        *window,
+                                                                  gint             *x,
+                                                                  gint             *y,
+                                                                  GdkModifierType  *mask);
+static GdkWindow *gdk_display_real_get_window_at_device_position (GdkDisplay       *display,
+                                                                  GdkDevice        *device,
+                                                                  gint             *win_x,
+                                                                  gint             *win_y);
 
 static guint signals[LAST_SIGNAL] = { 0 };
 
 static char *gdk_sm_client_id;
 
-static const GdkDisplayPointerHooks default_pointer_hooks = {
-  _gdk_windowing_get_pointer,
-  gdk_window_real_window_get_pointer,
-  gdk_display_real_get_window_at_pointer
+static const GdkDisplayDeviceHooks default_device_hooks = {
+  _gdk_windowing_get_device_state,
+  gdk_window_real_window_get_device_position,
+  gdk_display_real_get_window_at_device_position
+};
+
+static const GdkDisplayDeviceHooks multihead_pointer_hooks = {
+  multihead_get_device_state,
+  multihead_window_get_device_position,
+  multihead_window_at_device_position
+};
+
+static const GdkDisplayPointerHooks multihead_default_pointer_hooks = {
+  multihead_default_get_pointer,
+  multihead_default_window_get_pointer,
+  multihead_default_window_at_pointer
 };
 
 static const GdkDisplayPointerHooks singlehead_pointer_hooks = {
@@ -93,6 +139,7 @@ static const GdkPointerHooks singlehead_default_pointer_hooks = {
 };
 
 static const GdkPointerHooks *singlehead_current_pointer_hooks = &singlehead_default_pointer_hooks;
+static const GdkDisplayPointerHooks *multihead_current_pointer_hooks = &multihead_default_pointer_hooks;
 
 G_DEFINE_TYPE (GdkDisplay, gdk_display, G_TYPE_OBJECT)
 
@@ -100,11 +147,24 @@ static void
 gdk_display_class_init (GdkDisplayClass *class)
 {
   GObjectClass *object_class = G_OBJECT_CLASS (class);
-  
+
   object_class->finalize = gdk_display_finalize;
   object_class->dispose = gdk_display_dispose;
 
   /**
+   * GdkDisplay::opened.
+   * @display: the object on which the signal is emitted
+   *
+   */
+  signals[OPENED] =
+    g_signal_new (g_intern_static_string ("opened"),
+		  G_OBJECT_CLASS_TYPE (object_class),
+                  G_SIGNAL_RUN_LAST,
+                  0, NULL, NULL,
+                  g_cclosure_marshal_VOID__VOID,
+                  G_TYPE_NONE, 0);
+
+  /**
    * GdkDisplay::closed:
    * @display: the object on which the signal is emitted
    * @is_error: %TRUE if the display was closed due to an error
@@ -127,26 +187,85 @@ gdk_display_class_init (GdkDisplayClass *class)
 }
 
 static void
+free_pointer_info (GdkPointerWindowInfo *info)
+{
+  g_object_unref (info->toplevel_under_pointer);
+  g_slice_free (GdkPointerWindowInfo, info);
+}
+
+static void
+free_device_grab (GdkDeviceGrabInfo *info)
+{
+  g_object_unref (info->window);
+  g_object_unref (info->native_window);
+  g_free (info);
+}
+
+static gboolean
+free_device_grabs_foreach (gpointer key,
+                           gpointer value,
+                           gpointer user_data)
+{
+  GList *list = value;
+
+  g_list_foreach (list, (GFunc) free_device_grab, NULL);
+  g_list_free (list);
+
+  return TRUE;
+}
+
+static void
+device_removed_cb (GdkDeviceManager *device_manager,
+                   GdkDevice        *device,
+                   GdkDisplay       *display)
+{
+  g_hash_table_remove (display->multiple_click_info, device);
+  g_hash_table_remove (display->device_grabs, device);
+  g_hash_table_remove (display->pointers_info, device);
+
+  /* FIXME: change core pointer and remove from device list */
+}
+
+static void
+gdk_display_opened (GdkDisplay *display)
+{
+  GdkDeviceManager *device_manager;
+
+  device_manager = gdk_display_get_device_manager (display);
+
+  g_signal_connect (device_manager, "device-removed",
+                    G_CALLBACK (device_removed_cb), display);
+}
+
+static void
 gdk_display_init (GdkDisplay *display)
 {
   _gdk_displays = g_slist_prepend (_gdk_displays, display);
 
-  display->button_click_time[0] = display->button_click_time[1] = 0;
-  display->button_window[0] = display->button_window[1] = NULL;
-  display->button_number[0] = display->button_number[1] = -1;
-  display->button_x[0] = display->button_x[1] = 0;
-  display->button_y[0] = display->button_y[1] = 0;
-
   display->double_click_time = 250;
   display->double_click_distance = 5;
 
-  display->pointer_hooks = &default_pointer_hooks;
+  display->device_hooks = &default_device_hooks;
+
+  display->device_grabs = g_hash_table_new (NULL, NULL);
+
+  display->pointers_info = g_hash_table_new_full (NULL, NULL, NULL,
+                                                  (GDestroyNotify) free_pointer_info);
+
+  display->multiple_click_info = g_hash_table_new_full (NULL, NULL, NULL,
+                                                        (GDestroyNotify) g_free);
+
+  g_signal_connect (display, "opened",
+                    G_CALLBACK (gdk_display_opened), NULL);
 }
 
 static void
 gdk_display_dispose (GObject *object)
 {
   GdkDisplay *display = GDK_DISPLAY_OBJECT (object);
+  GdkDeviceManager *device_manager;
+
+  device_manager = gdk_display_get_device_manager (GDK_DISPLAY_OBJECT (object));
 
   g_list_foreach (display->queued_events, (GFunc)gdk_event_free, NULL);
   g_list_free (display->queued_events);
@@ -165,12 +284,27 @@ gdk_display_dispose (GObject *object)
                                                  NULL);
     }
 
+  g_signal_handlers_disconnect_by_func (device_manager, device_removed_cb, object);
+
   G_OBJECT_CLASS (gdk_display_parent_class)->dispose (object);
 }
 
 static void
 gdk_display_finalize (GObject *object)
 {
+  GdkDisplay *display = GDK_DISPLAY_OBJECT (object);
+
+  g_hash_table_foreach_remove (display->device_grabs,
+                               free_device_grabs_foreach,
+                               NULL);
+  g_hash_table_destroy (display->device_grabs);
+
+  g_hash_table_destroy (display->pointers_info);
+  g_hash_table_destroy (display->multiple_click_info);
+
+  if (display->device_manager)
+    g_object_unref (display->device_manager);
+
   G_OBJECT_CLASS (gdk_display_parent_class)->finalize (object);
 }
 
@@ -273,6 +407,43 @@ gdk_display_put_event (GdkDisplay     *display,
 }
 
 /**
+ * gdk_display_pointer_ungrab:
+ * @display: a #GdkDisplay.
+ * @time_: a timestap (e.g. %GDK_CURRENT_TIME).
+ *
+ * Release any pointer grab.
+ *
+ * Since: 2.2
+ */
+void
+gdk_display_pointer_ungrab (GdkDisplay *display,
+			    guint32     time_)
+{
+  GdkDeviceManager *device_manager;
+  GList *devices, *dev;
+  GdkDevice *device;
+
+  g_return_if_fail (GDK_IS_DISPLAY (display));
+
+  device_manager = gdk_display_get_device_manager (display);
+  devices = gdk_device_manager_get_devices (device_manager, GDK_DEVICE_TYPE_MASTER);
+
+  /* FIXME: Should this be generic to all backends? */
+  /* FIXME: What happens with extended devices? */
+  for (dev = devices; dev; dev = dev->next)
+    {
+      device = dev->data;
+
+      if (device->source != GDK_SOURCE_MOUSE)
+        continue;
+
+      gdk_device_ungrab (device, time_);
+    }
+
+  g_list_free (devices);
+}
+
+/**
  * gdk_pointer_ungrab:
  * @time_: a timestamp from a #GdkEvent, or %GDK_CURRENT_TIME if no 
  *  timestamp is available.
@@ -304,6 +475,43 @@ gdk_pointer_is_grabbed (void)
 }
 
 /**
+ * gdk_display_keyboard_ungrab:
+ * @display: a #GdkDisplay.
+ * @time_: a timestap (e.g #GDK_CURRENT_TIME).
+ *
+ * Release any keyboard grab
+ *
+ * Since: 2.2
+ */
+void
+gdk_display_keyboard_ungrab (GdkDisplay *display,
+			     guint32     time)
+{
+  GdkDeviceManager *device_manager;
+  GList *devices, *dev;
+  GdkDevice *device;
+
+  g_return_if_fail (GDK_IS_DISPLAY (display));
+
+  device_manager = gdk_display_get_device_manager (display);
+  devices = gdk_device_manager_get_devices (device_manager, GDK_DEVICE_TYPE_MASTER);
+
+  /* FIXME: Should this be generic to all backends? */
+  /* FIXME: What happens with extended devices? */
+  for (dev = devices; dev; dev = dev->next)
+    {
+      device = dev->data;
+
+      if (device->source != GDK_SOURCE_KEYBOARD)
+        continue;
+
+      gdk_device_ungrab (device, time);
+    }
+
+  g_list_free (devices);
+}
+
+/**
  * gdk_keyboard_ungrab:
  * @time_: a timestamp from a #GdkEvent, or %GDK_CURRENT_TIME if no
  *        timestamp is available.
@@ -449,7 +657,7 @@ _gdk_display_enable_motion_hints (GdkDisplay *display)
 {
   gulong serial;
   
-  if (display->pointer_info.motion_hint_serial != 0)
+  if (display->motion_hint_serial != 0)
     {
       serial = _gdk_windowing_window_get_next_serial (display);
       /* We might not actually generate the next request, so
@@ -458,12 +666,113 @@ _gdk_display_enable_motion_hints (GdkDisplay *display)
 	 anyway. */
       if (serial > 0)
 	serial--;
-      if (serial < display->pointer_info.motion_hint_serial)
-	display->pointer_info.motion_hint_serial = serial;
+      if (serial < display->motion_hint_serial)
+	display->motion_hint_serial = serial;
     }
 }
 
 /**
+ * gdk_display_get_device_state:
+ * @display: a #GdkDisplay.
+ * @device: device to query status to.
+ * @screen: location to store the #GdkScreen the @device is on, or %NULL.
+ * @x: location to store root window X coordinate of @device, or %NULL.
+ * @y: location to store root window Y coordinate of @device, or %NULL.
+ * @mask: location to store current modifier mask for @device, or %NULL.
+ *
+ * Gets the current location and state of @device for a given display.
+ **/
+void
+gdk_display_get_device_state (GdkDisplay       *display,
+                              GdkDevice        *device,
+                              GdkScreen       **screen,
+                              gint             *x,
+                              gint             *y,
+                              GdkModifierType  *mask)
+{
+  GdkScreen *tmp_screen;
+  gint tmp_x, tmp_y;
+  GdkModifierType tmp_mask;
+
+  g_return_if_fail (GDK_IS_DISPLAY (display));
+  g_return_if_fail (GDK_IS_DEVICE (device));
+
+  display->device_hooks->get_device_state (display, device, &tmp_screen, &tmp_x, &tmp_y, &tmp_mask);
+
+  if (screen)
+    *screen = tmp_screen;
+  if (x)
+    *x = tmp_x;
+  if (y)
+    *y = tmp_y;
+  if (mask)
+    *mask = tmp_mask;
+}
+
+/**
+ * gdk_display_get_window_at_device_position:
+ * @display: a #GdkDisplay.
+ * @device: #GdkDevice to query info to.
+ * @win_x: return location for the X coordinate of the device location, relative to the window origin, or %NULL.
+ * @win_y: return location for the Y coordinate of the device location, relative to the window origin, or %NULL.
+ *
+ * Obtains the window underneath @device, returning the location of the device in @win_x and @win_y. Returns
+ * %NULL if the window tree under @device is not known to GDK (for example, belongs to another application).
+ *
+ * Returns: the #GdkWindow under the device position, or %NULL.
+ **/
+GdkWindow *
+gdk_display_get_window_at_device_position (GdkDisplay *display,
+                                           GdkDevice  *device,
+                                           gint       *win_x,
+                                           gint       *win_y)
+{
+  gint tmp_x, tmp_y;
+  GdkWindow *window;
+
+  g_return_val_if_fail (GDK_IS_DISPLAY (display), NULL);
+  g_return_val_if_fail (GDK_IS_DEVICE (device), NULL);
+
+  window = display->device_hooks->window_at_device_position (display, device, &tmp_x, &tmp_y);
+
+  if (win_x)
+    *win_x = tmp_x;
+  if (win_y)
+    *win_y = tmp_y;
+
+  return window;
+}
+
+/**
+ * gdk_display_set_device_hooks:
+ * @display: a #GdkDisplay.
+ * @new_hooks: a table of pointers to functions for getting quantities related to all
+ *             devices position, or %NULL to restore the default table.
+ *
+ * This function allows for hooking into the operation of getting the current location of any
+ * #GdkDevice on a particular #GdkDisplay. This is only useful for such low-level tools as
+ * an event recorder. Applications should never have any reason to use this facility.
+ *
+ * Returns: The previous device hook table.
+ **/
+GdkDisplayDeviceHooks *
+gdk_display_set_device_hooks (GdkDisplay                  *display,
+                              const GdkDisplayDeviceHooks *new_hooks)
+{
+  const GdkDisplayDeviceHooks *result;
+
+  g_return_val_if_fail (GDK_IS_DISPLAY (display), NULL);
+  result = display->device_hooks;
+
+  if (new_hooks)
+    display->device_hooks = new_hooks;
+  else
+    display->device_hooks = &default_device_hooks;
+
+  return (GdkDisplayDeviceHooks *) result;
+}
+
+/**
  * gdk_display_get_pointer:
  * @display: a #GdkDisplay
  * @screen: location to store the screen that the
@@ -484,33 +793,21 @@ gdk_display_get_pointer (GdkDisplay      *display,
 			 gint            *y,
 			 GdkModifierType *mask)
 {
-  GdkScreen *tmp_screen;
-  gint tmp_x, tmp_y;
-  GdkModifierType tmp_mask;
-  
   g_return_if_fail (GDK_IS_DISPLAY (display));
 
-  display->pointer_hooks->get_pointer (display, &tmp_screen, &tmp_x, &tmp_y, &tmp_mask);
-
-  if (screen)
-    *screen = tmp_screen;
-  if (x)
-    *x = tmp_x;
-  if (y)
-    *y = tmp_y;
-  if (mask)
-    *mask = tmp_mask;
+  gdk_display_get_device_state (display, display->core_pointer, screen, x, y, mask);
 }
 
 static GdkWindow *
-gdk_display_real_get_window_at_pointer (GdkDisplay *display,
-                                        gint       *win_x,
-                                        gint       *win_y)
+gdk_display_real_get_window_at_device_position (GdkDisplay *display,
+                                                GdkDevice  *device,
+                                                gint       *win_x,
+                                                gint       *win_y)
 {
   GdkWindow *window;
   gint x, y;
 
-  window = _gdk_windowing_window_at_pointer (display, &x, &y, NULL, FALSE);
+  window = _gdk_windowing_window_at_device_position (display, device, &x, &y, NULL, FALSE);
 
   /* This might need corrections, as the native window returned
      may contain client side children */
@@ -532,11 +829,12 @@ gdk_display_real_get_window_at_pointer (GdkDisplay *display,
 }
 
 static GdkWindow *
-gdk_window_real_window_get_pointer (GdkDisplay       *display,
-                                    GdkWindow        *window,
-                                    gint             *x,
-                                    gint             *y,
-                                    GdkModifierType  *mask)
+gdk_window_real_window_get_device_position (GdkDisplay       *display,
+                                            GdkDevice        *device,
+                                            GdkWindow        *window,
+                                            gint             *x,
+                                            gint             *y,
+                                            GdkModifierType  *mask)
 {
   GdkWindowObject *private;
   gint tmpx, tmpy;
@@ -545,9 +843,10 @@ gdk_window_real_window_get_pointer (GdkDisplay       *display,
 
   private = (GdkWindowObject *) window;
 
-  normal_child = GDK_WINDOW_IMPL_GET_IFACE (private->impl)->get_pointer (window,
-									 &tmpx, &tmpy,
-									 &tmp_mask);
+  normal_child = GDK_WINDOW_IMPL_GET_IFACE (private->impl)->get_device_state (window,
+                                                                              device,
+                                                                              &tmpx, &tmpy,
+                                                                              &tmp_mask);
   /* We got the coords on the impl, convert to the window */
   tmpx -= private->abs_x;
   tmpy -= private->abs_y;
@@ -586,19 +885,74 @@ gdk_display_get_window_at_pointer (GdkDisplay *display,
 				   gint       *win_x,
 				   gint       *win_y)
 {
-  gint tmp_x, tmp_y;
-  GdkWindow *window;
-
   g_return_val_if_fail (GDK_IS_DISPLAY (display), NULL);
 
-  window = display->pointer_hooks->window_at_pointer (display, &tmp_x, &tmp_y);
+  return gdk_display_get_window_at_device_position (display, display->core_pointer, win_x, win_y);
+}
 
-  if (win_x)
-    *win_x = tmp_x;
-  if (win_y)
-    *win_y = tmp_y;
+static void
+multihead_get_device_state (GdkDisplay       *display,
+                            GdkDevice        *device,
+                            GdkScreen       **screen,
+                            gint             *x,
+                            gint             *y,
+                            GdkModifierType  *mask)
+{
+  multihead_current_pointer_hooks->get_pointer (display, screen, x, y, mask);
+}
 
-  return window;
+static GdkWindow *
+multihead_window_get_device_position (GdkDisplay      *display,
+                                      GdkDevice       *device,
+                                      GdkWindow       *window,
+                                      gint            *x,
+                                      gint            *y,
+                                      GdkModifierType *mask)
+{
+  return multihead_current_pointer_hooks->window_get_pointer (display, window, x, y, mask);
+}
+
+static GdkWindow *
+multihead_window_at_device_position (GdkDisplay *display,
+                                     GdkDevice  *device,
+                                     gint       *win_x,
+                                     gint       *win_y)
+{
+  return multihead_current_pointer_hooks->window_at_pointer (display, win_x, win_y);
+}
+
+static void
+multihead_default_get_pointer (GdkDisplay       *display,
+                               GdkScreen       **screen,
+                               gint             *x,
+                               gint             *y,
+                               GdkModifierType  *mask)
+{
+  return _gdk_windowing_get_device_state (display,
+                                          display->core_pointer,
+                                          screen, x, y, mask);
+}
+
+static GdkWindow *
+multihead_default_window_get_pointer (GdkDisplay      *display,
+                                      GdkWindow       *window,
+                                      gint            *x,
+                                      gint            *y,
+                                      GdkModifierType *mask)
+{
+  return gdk_window_real_window_get_device_position (display,
+                                                     display->core_pointer,
+                                                     window, x, y, mask);
+}
+
+static GdkWindow *
+multihead_default_window_at_pointer (GdkDisplay *display,
+                                     gint       *win_x,
+                                     gint       *win_y)
+{
+  return gdk_display_real_get_window_at_device_position (display,
+                                                         display->core_pointer,
+                                                         win_x, win_y);
 }
 
 /**
@@ -622,15 +976,16 @@ GdkDisplayPointerHooks *
 gdk_display_set_pointer_hooks (GdkDisplay                   *display,
 			       const GdkDisplayPointerHooks *new_hooks)
 {
-  const GdkDisplayPointerHooks *result;
+  const GdkDisplayPointerHooks *result = multihead_current_pointer_hooks;
 
   g_return_val_if_fail (GDK_IS_DISPLAY (display), NULL);
-  result = display->pointer_hooks;
 
   if (new_hooks)
-    display->pointer_hooks = new_hooks;
+    multihead_current_pointer_hooks = new_hooks;
   else
-    display->pointer_hooks = &default_pointer_hooks;
+    multihead_current_pointer_hooks = &multihead_default_pointer_hooks;
+
+  gdk_display_set_device_hooks (display, &multihead_pointer_hooks);
 
   return (GdkDisplayPointerHooks *)result;
 }
@@ -677,8 +1032,13 @@ singlehead_default_window_get_pointer (GdkWindow       *window,
 				       gint            *y,
 				       GdkModifierType *mask)
 {
-  return gdk_window_real_window_get_pointer (gdk_drawable_get_display (window),
-                                             window, x, y, mask);
+  GdkDisplay *display;
+
+  display = gdk_drawable_get_display (window);
+
+  return gdk_window_real_window_get_device_position (display,
+                                                     display->core_pointer,
+                                                     window, x, y, mask);
 }
 
 static GdkWindow*
@@ -686,8 +1046,13 @@ singlehead_default_window_at_pointer  (GdkScreen       *screen,
 				       gint            *win_x,
 				       gint            *win_y)
 {
-  return gdk_display_real_get_window_at_pointer (gdk_screen_get_display (screen),
-                                                 win_x, win_y);
+  GdkDisplay *display;
+
+  display = gdk_screen_get_display (screen);
+
+  return gdk_display_real_get_window_at_device_position (display,
+                                                         display->core_pointer,
+                                                         win_x, win_y);
 }
 
 /**
@@ -725,7 +1090,7 @@ gdk_set_pointer_hooks (const GdkPointerHooks *new_hooks)
 
 static void
 generate_grab_broken_event (GdkWindow *window,
-			    gboolean   keyboard,
+                            GdkDevice *device,
 			    gboolean   implicit,
 			    GdkWindow *grab_window)
 {
@@ -737,41 +1102,47 @@ generate_grab_broken_event (GdkWindow *window,
       event.type = GDK_GRAB_BROKEN;
       event.grab_broken.window = window;
       event.grab_broken.send_event = 0;
-      event.grab_broken.keyboard = keyboard;
+      event.grab_broken.keyboard = (device->source == GDK_SOURCE_KEYBOARD) ? TRUE : FALSE;
       event.grab_broken.implicit = implicit;
       event.grab_broken.grab_window = grab_window;
+      event.grab_broken.device = device;
       gdk_event_put (&event);
     }
 }
 
-GdkPointerGrabInfo *
-_gdk_display_get_last_pointer_grab (GdkDisplay *display)
+GdkDeviceGrabInfo *
+_gdk_display_get_last_device_grab (GdkDisplay *display,
+                                   GdkDevice  *device)
 {
   GList *l;
 
-  l = g_list_last (display->pointer_grabs);
+  l = g_hash_table_lookup (display->device_grabs, device);
 
-  if (l == NULL)
-    return NULL;
-  else
-    return (GdkPointerGrabInfo *)l->data;
-}
+  if (l)
+    {
+      l = g_list_last (l);
+      return l->data;
+    }
 
+  return NULL;
+}
 
-GdkPointerGrabInfo *
-_gdk_display_add_pointer_grab (GdkDisplay *display,
-			       GdkWindow *window,
-			       GdkWindow *native_window,
-			       gboolean owner_events,
-			       GdkEventMask event_mask,
-			       unsigned long serial_start,
-			       guint32 time,
-			       gboolean implicit)
+GdkDeviceGrabInfo *
+_gdk_display_add_device_grab (GdkDisplay       *display,
+                              GdkDevice        *device,
+                              GdkWindow        *window,
+                              GdkWindow        *native_window,
+                              GdkGrabOwnership  grab_ownership,
+                              gboolean          owner_events,
+                              GdkEventMask      event_mask,
+                              unsigned long     serial_start,
+                              guint32           time,
+                              gboolean          implicit)
 {
-  GdkPointerGrabInfo *info, *other_info;
-  GList *l;
+  GdkDeviceGrabInfo *info, *other_info;
+  GList *grabs, *l;
 
-  info = g_new0 (GdkPointerGrabInfo, 1);
+  info = g_new0 (GdkDeviceGrabInfo, 1);
 
   info->window = g_object_ref (window);
   info->native_window = g_object_ref (native_window);
@@ -781,19 +1152,22 @@ _gdk_display_add_pointer_grab (GdkDisplay *display,
   info->event_mask = event_mask;
   info->time = time;
   info->implicit = implicit;
+  info->ownership = grab_ownership;
+
+  grabs = g_hash_table_lookup (display->device_grabs, device);
 
   /* Find the first grab that has a larger start time (if any) and insert
    * before that. I.E we insert after already existing grabs with same
    * start time */
-  for (l = display->pointer_grabs; l != NULL; l = l->next)
+  for (l = grabs; l != NULL; l = l->next)
     {
       other_info = l->data;
-      
+
       if (info->serial_start < other_info->serial_start)
 	break;
     }
-  display->pointer_grabs =
-    g_list_insert_before (display->pointer_grabs, l, info);
+
+  grabs = g_list_insert_before (grabs, l, info);
 
   /* Make sure the new grab end before next grab */
   if (l)
@@ -801,9 +1175,9 @@ _gdk_display_add_pointer_grab (GdkDisplay *display,
       other_info = l->data;
       info->serial_end = other_info->serial_start;
     }
-  
+
   /* Find any previous grab and update its end time */
-  l = g_list_find  (display->pointer_grabs, info);
+  l = g_list_find (grabs, info);
   l = l->prev;
   if (l)
     {
@@ -811,27 +1185,22 @@ _gdk_display_add_pointer_grab (GdkDisplay *display,
       other_info->serial_end = serial_start;
     }
 
-  return info;
-}
+  g_hash_table_insert (display->device_grabs, device, grabs);
 
-static void
-free_pointer_grab (GdkPointerGrabInfo *info)
-{
-  g_object_unref (info->window);
-  g_object_unref (info->native_window);
-  g_free (info);
+  return info;
 }
 
 /* _gdk_synthesize_crossing_events only works inside one toplevel.
    This function splits things into two calls if needed, converting the
    coordinates to the right toplevel */
 static void
-synthesize_crossing_events (GdkDisplay *display,
-			    GdkWindow *src_window,
-			    GdkWindow *dest_window,
-			    GdkCrossingMode crossing_mode,
-			    guint32 time,
-			    gulong serial)
+synthesize_crossing_events (GdkDisplay      *display,
+                            GdkDevice       *device,
+			    GdkWindow       *src_window,
+			    GdkWindow       *dest_window,
+			    GdkCrossingMode  crossing_mode,
+			    guint32          time,
+			    gulong           serial)
 {
   GdkWindow *src_toplevel, *dest_toplevel;
   GdkModifierType state;
@@ -862,6 +1231,7 @@ synthesize_crossing_events (GdkDisplay *display,
       _gdk_synthesize_crossing_events (display,
 				       src_window,
 				       dest_window,
+                                       device,
 				       crossing_mode,
 				       x, y, state,
 				       time,
@@ -873,13 +1243,14 @@ synthesize_crossing_events (GdkDisplay *display,
       gdk_window_get_pointer (src_toplevel,
 			      &x, &y, &state);
       _gdk_synthesize_crossing_events (display,
-				       src_window,
-				       NULL,
-				       crossing_mode,
-				       x, y, state,
-				       time,
-				       NULL,
-				       serial, FALSE);
+                                       src_window,
+                                       NULL,
+                                       device,
+                                       crossing_mode,
+                                       x, y, state,
+                                       time,
+                                       NULL,
+                                       serial, FALSE);
     }
   else
     {
@@ -889,6 +1260,7 @@ synthesize_crossing_events (GdkDisplay *display,
       _gdk_synthesize_crossing_events (display,
 				       src_window,
 				       NULL,
+                                       device,
 				       crossing_mode,
 				       x, y, state,
 				       time,
@@ -899,6 +1271,7 @@ synthesize_crossing_events (GdkDisplay *display,
       _gdk_synthesize_crossing_events (display,
 				       NULL,
 				       dest_window,
+                                       device,
 				       crossing_mode,
 				       x, y, state,
 				       time,
@@ -908,15 +1281,18 @@ synthesize_crossing_events (GdkDisplay *display,
 }
 
 static GdkWindow *
-get_current_toplevel (GdkDisplay *display,
-		      int *x_out, int *y_out,
+get_current_toplevel (GdkDisplay      *display,
+                      GdkDevice       *device,
+                      int             *x_out,
+                      int             *y_out,
 		      GdkModifierType *state_out)
 {
   GdkWindow *pointer_window;
   int x, y;
   GdkModifierType state;
 
-  pointer_window = _gdk_windowing_window_at_pointer (display,  &x, &y, &state, TRUE);
+  pointer_window = _gdk_windowing_window_at_device_position (display, device, &x, &y, &state, TRUE);
+
   if (pointer_window != NULL &&
       (GDK_WINDOW_DESTROYED (pointer_window) ||
        GDK_WINDOW_TYPE (pointer_window) == GDK_WINDOW_ROOT ||
@@ -930,29 +1306,32 @@ get_current_toplevel (GdkDisplay *display,
 }
 
 static void
-switch_to_pointer_grab (GdkDisplay *display,
-			GdkPointerGrabInfo *grab,
-			GdkPointerGrabInfo *last_grab,
-			guint32 time,
-			gulong serial)
+switch_to_pointer_grab (GdkDisplay        *display,
+                        GdkDevice         *device,
+			GdkDeviceGrabInfo *grab,
+			GdkDeviceGrabInfo *last_grab,
+			guint32            time,
+			gulong             serial)
 {
   GdkWindow *src_window, *pointer_window, *new_toplevel;
+  GdkPointerWindowInfo *info;
   GList *old_grabs;
   GdkModifierType state;
   int x, y;
 
   /* Temporarily unset pointer to make sure we send the crossing events below */
-  old_grabs = display->pointer_grabs;
-  display->pointer_grabs = NULL;
-  
+  old_grabs = g_hash_table_lookup (display->device_grabs, device);
+  g_hash_table_steal (display->device_grabs, device);
+  info = _gdk_display_get_pointer_info (display, device);
+
   if (grab)
     {
       /* New grab is in effect */
-      
+
       /* We need to generate crossing events for the grab.
        * However, there are never any crossing events for implicit grabs
        * TODO: ... Actually, this could happen if the pointer window
-       *           doesn't have button mask so a parent gets the event... 
+       *           doesn't have button mask so a parent gets the event...
        */
       if (!grab->implicit)
 	{
@@ -961,19 +1340,17 @@ switch_to_pointer_grab (GdkDisplay *display,
 	  if (last_grab)
 	    src_window = last_grab->window;
 	  else
-	    src_window = display->pointer_info.window_under_pointer;
-	  
+	    src_window = info->window_under_pointer;
+
 	  if (src_window != grab->window)
-	    {
-	      synthesize_crossing_events (display,
-					  src_window, grab->window,
-					  GDK_CROSSING_GRAB, time, serial);
-	    }
+            synthesize_crossing_events (display, device,
+                                        src_window, grab->window,
+                                        GDK_CROSSING_GRAB, time, serial);
 
 	  /* !owner_event Grabbing a window that we're not inside, current status is
 	     now NULL (i.e. outside grabbed window) */
-	  if (!grab->owner_events && display->pointer_info.window_under_pointer != grab->window)
-	    _gdk_display_set_window_under_pointer (display, NULL);
+	  if (!grab->owner_events && info->window_under_pointer != grab->window)
+	    _gdk_display_set_window_under_pointer (display, device, NULL);
 	}
 
       grab->activated = TRUE;
@@ -989,18 +1366,18 @@ switch_to_pointer_grab (GdkDisplay *display,
 	  /* We force check what window we're in, and update the toplevel_under_pointer info,
 	   * as that won't get told of this change with toplevel enter events.
 	   */
-	  if (display->pointer_info.toplevel_under_pointer)
-	    g_object_unref (display->pointer_info.toplevel_under_pointer);
-	  display->pointer_info.toplevel_under_pointer = NULL;
+	  if (info->toplevel_under_pointer)
+	    g_object_unref (info->toplevel_under_pointer);
+	  info->toplevel_under_pointer = NULL;
 
-	  new_toplevel = get_current_toplevel (display, &x, &y, &state);
+	  new_toplevel = get_current_toplevel (display, device, &x, &y, &state);
 	  if (new_toplevel)
 	    {
 	      /* w is now toplevel and x,y in toplevel coords */
-	      display->pointer_info.toplevel_under_pointer = g_object_ref (new_toplevel);
-	      display->pointer_info.toplevel_x = x;
-	      display->pointer_info.toplevel_y = y;
-	      display->pointer_info.state = state;
+	      info->toplevel_under_pointer = g_object_ref (new_toplevel);
+	      info->toplevel_x = x;
+	      info->toplevel_y = y;
+	      info->state = state;
 	    }
 	}
 
@@ -1015,55 +1392,59 @@ switch_to_pointer_grab (GdkDisplay *display,
 						x, y,
 						NULL, NULL);
 	    }
-	  
+
 	  if (pointer_window != last_grab->window)
-	    synthesize_crossing_events (display,
-					last_grab->window, pointer_window,
-					GDK_CROSSING_UNGRAB, time, serial);
-	  
+            synthesize_crossing_events (display, device,
+                                        last_grab->window, pointer_window,
+                                        GDK_CROSSING_UNGRAB, time, serial);
+
 	  /* We're now ungrabbed, update the window_under_pointer */
-	  _gdk_display_set_window_under_pointer (display, pointer_window);
+	  _gdk_display_set_window_under_pointer (display, device, pointer_window);
 	}
     }
-  
-  display->pointer_grabs = old_grabs;
 
+  g_hash_table_insert (display->device_grabs, device, old_grabs);
 }
 
 void
-_gdk_display_pointer_grab_update (GdkDisplay *display,
-				  gulong current_serial)
+_gdk_display_device_grab_update (GdkDisplay *display,
+                                 GdkDevice  *device,
+                                 gulong      current_serial)
 {
-  GdkPointerGrabInfo *current_grab, *next_grab;
+  GdkDeviceGrabInfo *current_grab, *next_grab;
+  GList *grabs;
   guint32 time;
-  
+
   time = display->last_event_time;
+  grabs = g_hash_table_lookup (display->device_grabs, device);
 
-  while (display->pointer_grabs != NULL)
+  while (grabs != NULL)
     {
-      current_grab = display->pointer_grabs->data;
+      current_grab = grabs->data;
 
       if (current_grab->serial_start > current_serial)
 	return; /* Hasn't started yet */
-      
+
       if (current_grab->serial_end > current_serial)
 	{
 	  /* This one hasn't ended yet.
 	     its the currently active one or scheduled to be active */
 
 	  if (!current_grab->activated)
-	    switch_to_pointer_grab (display, current_grab, NULL, time, current_serial);
-	  
+            {
+              if (device->source != GDK_SOURCE_KEYBOARD)
+                switch_to_pointer_grab (display, device, current_grab, NULL, time, current_serial);
+            }
+
 	  break;
 	}
 
-
       next_grab = NULL;
-      if (display->pointer_grabs->next)
+      if (grabs->next)
 	{
 	  /* This is the next active grab */
-	  next_grab = display->pointer_grabs->next->data;
-	  
+	  next_grab = grabs->next->data;
+
 	  if (next_grab->serial_start > current_serial)
 	    next_grab = NULL; /* Actually its not yet active */
 	}
@@ -1071,52 +1452,64 @@ _gdk_display_pointer_grab_update (GdkDisplay *display,
       if ((next_grab == NULL && current_grab->implicit_ungrab) ||
 	  (next_grab != NULL && current_grab->window != next_grab->window))
 	generate_grab_broken_event (GDK_WINDOW (current_grab->window),
-				    FALSE, current_grab->implicit,
+                                    device,
+				    current_grab->implicit,
 				    next_grab? next_grab->window : NULL);
 
       /* Remove old grab */
-      display->pointer_grabs =
-	g_list_delete_link (display->pointer_grabs,
-			    display->pointer_grabs);
-      
-      switch_to_pointer_grab (display,
-			      next_grab, current_grab,
-			      time, current_serial);
-      
-      free_pointer_grab (current_grab);
+      grabs = g_list_delete_link (grabs, grabs);
+      g_hash_table_insert (display->device_grabs, device, grabs);
+
+      if (device->source != GDK_SOURCE_KEYBOARD)
+        switch_to_pointer_grab (display, device,
+                                next_grab, current_grab,
+                                time, current_serial);
+
+      free_device_grab (current_grab);
     }
 }
 
 static GList *
-find_pointer_grab (GdkDisplay *display,
-		   gulong serial)
+grab_list_find (GList  *grabs,
+                gulong  serial)
 {
-  GdkPointerGrabInfo *grab;
-  GList *l;
+  GdkDeviceGrabInfo *grab;
 
-  for (l = display->pointer_grabs; l != NULL; l = l->next)
+  while (grabs)
     {
-      grab = l->data;
+      grab = grabs->data;
 
       if (serial >= grab->serial_start && serial < grab->serial_end)
-	return l;
+	return grabs;
+
+      grabs = grabs->next;
     }
-  
+
   return NULL;
 }
 
+static GList *
+find_device_grab (GdkDisplay *display,
+                   GdkDevice  *device,
+                   gulong      serial)
+{
+  GList *l;
 
+  l = g_hash_table_lookup (display->device_grabs, device);
+  return grab_list_find (l, serial);
+}
 
-GdkPointerGrabInfo *
-_gdk_display_has_pointer_grab (GdkDisplay *display,
-			       gulong serial)
+GdkDeviceGrabInfo *
+_gdk_display_has_device_grab (GdkDisplay *display,
+                              GdkDevice  *device,
+                              gulong      serial)
 {
   GList *l;
 
-  l = find_pointer_grab (display, serial);
+  l = find_device_grab (display, device, serial);
   if (l)
     return l->data;
-  
+
   return NULL;
 }
 
@@ -1124,16 +1517,17 @@ _gdk_display_has_pointer_grab (GdkDisplay *display,
  * If if_child is non-NULL, end the grab only if the grabbed
  * window is the same as if_child or a descendant of it */
 gboolean
-_gdk_display_end_pointer_grab (GdkDisplay *display,
-			       gulong serial,
-			       GdkWindow *if_child,
-			       gboolean implicit)
+_gdk_display_end_device_grab (GdkDisplay *display,
+                              GdkDevice  *device,
+                              gulong      serial,
+                              GdkWindow  *if_child,
+                              gboolean    implicit)
 {
-  GdkPointerGrabInfo *grab;
+  GdkDeviceGrabInfo *grab;
   GList *l;
 
-  l = find_pointer_grab (display, serial);
-  
+  l = find_device_grab (display, device, serial);
+
   if (l == NULL)
     return FALSE;
 
@@ -1150,96 +1544,128 @@ _gdk_display_end_pointer_grab (GdkDisplay *display,
   return FALSE;
 }
 
-void
-_gdk_display_set_has_keyboard_grab (GdkDisplay *display,
-				    GdkWindow *window,
-				    GdkWindow *native_window,
-				    gboolean owner_events,
-				    unsigned long serial,
-				    guint32 time)
-{
-  if (display->keyboard_grab.window != NULL &&
-      display->keyboard_grab.window != window)
-    generate_grab_broken_event (display->keyboard_grab.window,
-				TRUE, FALSE, window);
-  
-  display->keyboard_grab.window = window;
-  display->keyboard_grab.native_window = native_window;
-  display->keyboard_grab.owner_events = owner_events;
-  display->keyboard_grab.serial = serial;
-  display->keyboard_grab.time = time;      
+/* Returns TRUE if device events are not blocked by any grab */
+gboolean
+_gdk_display_check_grab_ownership (GdkDisplay *display,
+                                   GdkDevice  *device,
+                                   gulong      serial)
+{
+  GHashTableIter iter;
+  gpointer key, value;
+  GdkGrabOwnership higher_ownership, device_ownership;
+  gboolean device_is_keyboard;
+
+  g_hash_table_iter_init (&iter, display->device_grabs);
+  higher_ownership = device_ownership = GDK_OWNERSHIP_NONE;
+  device_is_keyboard = (device->source == GDK_SOURCE_KEYBOARD);
+
+  while (g_hash_table_iter_next (&iter, &key, &value))
+    {
+      GdkDeviceGrabInfo *grab;
+      GdkDevice *dev;
+      GList *grabs;
+
+      dev = key;
+      grabs = value;
+      grabs = grab_list_find (grabs, serial);
+
+      if (!grabs)
+        continue;
+
+      /* Discard device if it's not of the same type */
+      if ((device_is_keyboard && dev->source != GDK_SOURCE_KEYBOARD) ||
+          (!device_is_keyboard && dev->source == GDK_SOURCE_KEYBOARD))
+        continue;
+
+      grab = grabs->data;
+
+      if (dev == device)
+        device_ownership = grab->ownership;
+      else
+        {
+          if (grab->ownership > higher_ownership)
+            higher_ownership = grab->ownership;
+        }
+    }
+
+  if (higher_ownership > device_ownership)
+    {
+      /* There's a higher priority ownership
+       * going on for other device(s)
+       */
+      return FALSE;
+    }
+
+  return TRUE;
 }
 
-void
-_gdk_display_unset_has_keyboard_grab (GdkDisplay *display,
-				      gboolean implicit)
+GdkPointerWindowInfo *
+_gdk_display_get_pointer_info (GdkDisplay *display,
+                               GdkDevice  *device)
 {
-  if (implicit)
-    generate_grab_broken_event (display->keyboard_grab.window,
-				TRUE, FALSE, NULL);
-  display->keyboard_grab.window = NULL;  
+  GdkPointerWindowInfo *info;
+
+  if (G_UNLIKELY (!device))
+    return NULL;
+
+  info = g_hash_table_lookup (display->pointers_info, device);
+
+  if (G_UNLIKELY (!info))
+    {
+      info = g_slice_new0 (GdkPointerWindowInfo);
+      g_hash_table_insert (display->pointers_info, device, info);
+    }
+
+  return info;
 }
 
-/**
- * gdk_keyboard_grab_info_libgtk_only:
- * @display: the display for which to get the grab information
- * @grab_window: location to store current grab window
- * @owner_events: location to store boolean indicating whether
- *   the @owner_events flag to gdk_keyboard_grab() was %TRUE.
- * 
- * Determines information about the current keyboard grab.
- * This is not public API and must not be used by applications.
- * 
- * Return value: %TRUE if this application currently has the
- *  keyboard grabbed.
- **/
-gboolean
-gdk_keyboard_grab_info_libgtk_only (GdkDisplay *display,
-				    GdkWindow **grab_window,
-				    gboolean   *owner_events)
+void
+_gdk_display_pointer_info_foreach (GdkDisplay                   *display,
+                                   GdkDisplayPointerInfoForeach  func,
+                                   gpointer                      user_data)
 {
-  g_return_val_if_fail (GDK_IS_DISPLAY (display), FALSE);
+  GHashTableIter iter;
+  gpointer key, value;
 
-  if (display->keyboard_grab.window)
+  g_hash_table_iter_init (&iter, display->pointers_info);
+
+  while (g_hash_table_iter_next (&iter, &key, &value))
     {
-      if (grab_window)
-        *grab_window = display->keyboard_grab.window;
-      if (owner_events)
-        *owner_events = display->keyboard_grab.owner_events;
+      GdkPointerWindowInfo *info = value;
+      GdkDevice *device = key;
 
-      return TRUE;
+      (func) (display, device, info, user_data);
     }
-  else
-    return FALSE;
 }
 
 /**
- * gdk_pointer_grab_info_libgtk_only:
- * @display: the #GdkDisplay for which to get the grab information
+ * gdk_device_grab_info_libgtk_only:
+ * @display: the display for which to get the grab information
+ * @device: device to get the grab information from
  * @grab_window: location to store current grab window
  * @owner_events: location to store boolean indicating whether
- *   the @owner_events flag to gdk_pointer_grab() was %TRUE.
- * 
- * Determines information about the current pointer grab.
+ *   the @owner_events flag to gdk_keyboard_grab() or
+ *   gdk_pointer_grab() was %TRUE.
+ *
+ * Determines information about the current keyboard grab.
  * This is not public API and must not be used by applications.
- * 
+ *
  * Return value: %TRUE if this application currently has the
- *  pointer grabbed.
+ *  keyboard grabbed.
  **/
 gboolean
-gdk_pointer_grab_info_libgtk_only (GdkDisplay *display,
-				   GdkWindow **grab_window,
-				   gboolean   *owner_events)
+gdk_device_grab_info_libgtk_only (GdkDisplay  *display,
+                                  GdkDevice   *device,
+                                  GdkWindow  **grab_window,
+                                  gboolean    *owner_events)
 {
-  GdkPointerGrabInfo *info;
-  
+  GdkDeviceGrabInfo *info;
+
   g_return_val_if_fail (GDK_IS_DISPLAY (display), FALSE);
+  g_return_val_if_fail (GDK_IS_DEVICE (device), FALSE);
+
+  info = _gdk_display_get_last_device_grab (display, device);
 
-  /* What we're interested in is the steady state (ie last grab),
-     because we're interested e.g. if we grabbed so that we
-     can ungrab, even if our grab is not active just yet. */
-  info = _gdk_display_get_last_pointer_grab (display);
-  
   if (info)
     {
       if (grab_window)
@@ -1253,7 +1679,6 @@ gdk_pointer_grab_info_libgtk_only (GdkDisplay *display,
     return FALSE;
 }
 
-
 /**
  * gdk_display_pointer_is_grabbed:
  * @display: a #GdkDisplay
@@ -1267,17 +1692,60 @@ gdk_pointer_grab_info_libgtk_only (GdkDisplay *display,
 gboolean
 gdk_display_pointer_is_grabbed (GdkDisplay *display)
 {
-  GdkPointerGrabInfo *info;
-  
+  GdkDeviceManager *device_manager;
+  GList *devices, *dev;
+  GdkDevice *device;
+
+  g_return_val_if_fail (GDK_IS_DISPLAY (display), TRUE);
+
+  device_manager = gdk_display_get_device_manager (display);
+  devices = gdk_device_manager_get_devices (device_manager, GDK_DEVICE_TYPE_MASTER);
+
+  for (dev = devices; dev; dev = dev->next)
+    {
+      device = dev->data;
+
+      if (device->source == GDK_SOURCE_MOUSE &&
+          gdk_display_device_is_grabbed (display, device))
+        return TRUE;
+    }
+
+  return FALSE;
+}
+
+gboolean
+gdk_display_device_is_grabbed (GdkDisplay *display,
+                               GdkDevice  *device)
+{
+  GdkDeviceGrabInfo *info;
+
   g_return_val_if_fail (GDK_IS_DISPLAY (display), TRUE);
+  g_return_val_if_fail (GDK_IS_DEVICE (device), TRUE);
 
   /* What we're interested in is the steady state (ie last grab),
      because we're interested e.g. if we grabbed so that we
      can ungrab, even if our grab is not active just yet. */
-  info = _gdk_display_get_last_pointer_grab (display);
-  
+  info = _gdk_display_get_last_device_grab (display, device);
+
   return (info && !info->implicit);
 }
 
+/**
+ * gdk_display_get_device_manager:
+ * @display: a #GdkDisplay.
+ *
+ * Returns the #GdkDeviceManager associated to @display.
+ *
+ * Returns: A #GdkDeviceManager, or %NULL. This memory is
+ *          owned by GDK and must not be freed or unreferenced.
+ **/
+GdkDeviceManager *
+gdk_display_get_device_manager (GdkDisplay *display)
+{
+  g_return_val_if_fail (GDK_IS_DISPLAY (display), NULL);
+
+  return display->device_manager;
+}
+
 #define __GDK_DISPLAY_C__
 #include "gdkaliasdef.c"
diff --git a/gdk/gdkdisplay.h b/gdk/gdkdisplay.h
index 2838d44..7b06f66 100644
--- a/gdk/gdkdisplay.h
+++ b/gdk/gdkdisplay.h
@@ -30,11 +30,13 @@
 
 #include <gdk/gdktypes.h>
 #include <gdk/gdkevents.h>
+#include <gdk/gdkdevicemanager.h>
 
 G_BEGIN_DECLS
 
 typedef struct _GdkDisplayClass GdkDisplayClass;
 typedef struct _GdkDisplayPointerHooks GdkDisplayPointerHooks;
+typedef struct _GdkDisplayDeviceHooks GdkDisplayDeviceHooks;
 
 #define GDK_TYPE_DISPLAY              (gdk_display_get_type ())
 #define GDK_DISPLAY_OBJECT(object)    (G_TYPE_CHECK_INSTANCE_CAST ((object), GDK_TYPE_DISPLAY, GdkDisplay))
@@ -67,9 +69,17 @@ typedef struct
   gdouble toplevel_x, toplevel_y; 
   guint32 state;
   guint32 button;
-  gulong motion_hint_serial; /* 0 == didn't deliver hinted motion event */
 } GdkPointerWindowInfo;
 
+typedef struct
+{
+  guint32 button_click_time[2];	/* The last 2 button click times. */
+  GdkWindow *button_window[2];  /* The last 2 windows to receive button presses. */
+  gint button_number[2];        /* The last 2 buttons to be pressed. */
+  gint button_x[2];             /* The last 2 button click positions. */
+  gint button_y[2];
+} GdkMultipleClickInfo;
+
 struct _GdkDisplay
 {
   GObject parent_instance;
@@ -81,28 +91,30 @@ struct _GdkDisplay
   /* Information for determining if the latest button click
    * is part of a double-click or triple-click
    */
-  guint32 button_click_time[2];	/* The last 2 button click times. */
-  GdkWindow *button_window[2];  /* The last 2 windows to receive button presses. */
-  gint button_number[2];        /* The last 2 buttons to be pressed. */
+  GHashTable *multiple_click_info;
 
   guint double_click_time;	/* Maximum time between clicks in msecs */
   GdkDevice *core_pointer;	/* Core pointer device */
 
-  const GdkDisplayPointerHooks *pointer_hooks; /* Current hooks for querying pointer */
+  const GdkDisplayDeviceHooks *device_hooks; /* Current hooks for querying pointer */
   
   guint closed : 1;		/* Whether this display has been closed */
   guint ignore_core_events : 1; /* Don't send core motion and button event */
 
   guint double_click_distance;	/* Maximum distance between clicks in pixels */
-  gint button_x[2];             /* The last 2 button click positions. */
-  gint button_y[2];
 
-  GList *pointer_grabs;
-  GdkKeyboardGrabInfo keyboard_grab;
-  GdkPointerWindowInfo pointer_info;
+  GHashTable *device_grabs;
+
+  gulong motion_hint_serial; /* 0 == didn't deliver hinted motion event */
+
+  /* Hashtable containing a GdkPointerWindowInfo for each device */
+  GHashTable *pointers_info;
 
   /* Last reported event time from server */
   guint32 last_event_time;
+
+  /* Device manager associated to the display */
+  GdkDeviceManager *device_manager;
 };
 
 struct _GdkDisplayClass
@@ -138,6 +150,26 @@ struct _GdkDisplayPointerHooks
 				    gint            *win_y);
 };
 
+struct _GdkDisplayDeviceHooks
+{
+  void (* get_device_state)                  (GdkDisplay       *display,
+                                              GdkDevice        *device,
+                                              GdkScreen       **screen,
+                                              gint             *x,
+                                              gint             *y,
+                                              GdkModifierType  *mask);
+  GdkWindow * (* window_get_device_position) (GdkDisplay      *display,
+                                              GdkDevice       *device,
+                                              GdkWindow       *window,
+                                              gint            *x,
+                                              gint            *y,
+                                              GdkModifierType *mask);
+  GdkWindow * (* window_at_device_position)  (GdkDisplay *display,
+                                              GdkDevice  *device,
+                                              gint       *win_x,
+                                              gint       *win_y);
+};
+
 GType       gdk_display_get_type (void) G_GNUC_CONST;
 GdkDisplay *gdk_display_open                (const gchar *display_name);
 
@@ -147,11 +179,17 @@ gint        gdk_display_get_n_screens      (GdkDisplay  *display);
 GdkScreen * gdk_display_get_screen         (GdkDisplay  *display,
 					    gint         screen_num);
 GdkScreen * gdk_display_get_default_screen (GdkDisplay  *display);
+
+#ifndef GDK_MULTIDEVICE_SAFE
 void        gdk_display_pointer_ungrab     (GdkDisplay  *display,
 					    guint32      time_);
 void        gdk_display_keyboard_ungrab    (GdkDisplay  *display,
 					    guint32      time_);
 gboolean    gdk_display_pointer_is_grabbed (GdkDisplay  *display);
+#endif /* GDK_MULTIDEVICE_SAFE */
+
+gboolean    gdk_display_device_is_grabbed  (GdkDisplay  *display,
+                                            GdkDevice   *device);
 void        gdk_display_beep               (GdkDisplay  *display);
 void        gdk_display_sync               (GdkDisplay  *display);
 void        gdk_display_flush              (GdkDisplay  *display);
@@ -177,7 +215,11 @@ void gdk_display_set_double_click_distance (GdkDisplay   *display,
 
 GdkDisplay *gdk_display_get_default (void);
 
+#ifndef GDK_MULTIDEVICE_SAFE
+
+#ifndef GDK_DISABLE_DEPRECATED
 GdkDevice  *gdk_display_get_core_pointer (GdkDisplay *display);
+#endif /* GDK_DISABLE_DEPRECATED */
 
 void             gdk_display_get_pointer           (GdkDisplay             *display,
 						    GdkScreen             **screen,
@@ -191,9 +233,31 @@ void             gdk_display_warp_pointer          (GdkDisplay             *disp
 						    GdkScreen              *screen,
 						    gint                   x,
 						    gint                   y);
-
+#endif /* GDK_MULTIDEVICE_SAFE */
+
+void             gdk_display_get_device_state              (GdkDisplay            *display,
+                                                            GdkDevice             *device,
+                                                            GdkScreen            **screen,
+                                                            gint                  *x,
+                                                            gint                  *y,
+                                                            GdkModifierType       *mask);
+GdkWindow *      gdk_display_get_window_at_device_position (GdkDisplay            *display,
+                                                            GdkDevice             *device,
+                                                            gint                  *win_x,
+                                                            gint                  *win_y);
+void             gdk_display_warp_device                   (GdkDisplay            *display,
+                                                            GdkDevice             *device,
+                                                            GdkScreen             *screen,
+                                                            gint                   x,
+                                                            gint                   y);
+
+#ifndef GDK_MULTIDEVICE_SAFE
 GdkDisplayPointerHooks *gdk_display_set_pointer_hooks (GdkDisplay                   *display,
 						       const GdkDisplayPointerHooks *new_hooks);
+#endif /* GDK_MULTIDEVICE_SAFE */
+
+GdkDisplayDeviceHooks *gdk_display_set_device_hooks (GdkDisplay                  *display,
+                                                     const GdkDisplayDeviceHooks *new_hooks);
 
 GdkDisplay *gdk_display_open_default_libgtk_only (void);
 
@@ -221,6 +285,9 @@ gboolean gdk_display_supports_shapes           (GdkDisplay    *display);
 gboolean gdk_display_supports_input_shapes     (GdkDisplay    *display);
 gboolean gdk_display_supports_composite        (GdkDisplay    *display);
 
+GdkDeviceManager * gdk_display_get_device_manager (GdkDisplay *display);
+
+
 G_END_DECLS
 
 #endif	/* __GDK_DISPLAY_H__ */
diff --git a/gdk/gdkevents.c b/gdk/gdkevents.c
index 90e16d7..b57874b 100644
--- a/gdk/gdkevents.c
+++ b/gdk/gdkevents.c
@@ -26,6 +26,7 @@
 
 #include "config.h"
 #include <string.h>		/* For memset() */
+#include <math.h>
 
 #include "gdk.h"
 #include "gdkinternals.h"
@@ -918,6 +919,47 @@ gdk_event_get_axis (const GdkEvent *event,
 }
 
 /**
+ * gdk_event_get_device:
+ * @event: a #GdkEvent.
+ *
+ * If the event contains a "device" field, this function will return
+ * it, else it will return %NULL.
+ *
+ * Returns: a #GdkDevice, or %NULL.
+ **/
+GdkDevice *
+gdk_event_get_device (const GdkEvent *event)
+{
+  switch (event->type)
+    {
+    case GDK_MOTION_NOTIFY:
+      return event->motion.device;
+    case GDK_BUTTON_PRESS:
+    case GDK_2BUTTON_PRESS:
+    case GDK_3BUTTON_PRESS:
+    case GDK_BUTTON_RELEASE:
+      return event->button.device;
+    case GDK_KEY_PRESS:
+    case GDK_KEY_RELEASE:
+      return event->key.device;
+    case GDK_FOCUS_CHANGE:
+      return event->focus_change.device;
+    case GDK_ENTER_NOTIFY:
+    case GDK_LEAVE_NOTIFY:
+      return event->crossing.device;
+    case GDK_SCROLL:
+      return event->scroll.device;
+    case GDK_GRAB_BROKEN:
+      return event->grab_broken.device;
+    case GDK_PROXIMITY_IN:
+    case GDK_PROXIMITY_OUT:
+      return event->proximity.device;
+    default:
+      return NULL;
+    }
+}
+
+/**
  * gdk_event_request_motions:
  * @event: a valid #GdkEvent
  *
@@ -956,6 +998,95 @@ gdk_event_request_motions (const GdkEventMotion *event)
     }
 }
 
+static gboolean
+gdk_events_get_axis_distances (GdkEvent *event1,
+                               GdkEvent *event2,
+                               gdouble  *x_distance,
+                               gdouble  *y_distance,
+                               gdouble  *distance)
+{
+  gdouble x1, x2, y1, y2;
+  gdouble xd, yd;
+
+  if (!gdk_event_get_coords (event1, &x1, &y1) ||
+      !gdk_event_get_coords (event2, &x2, &y2))
+    return FALSE;
+
+  xd = x2 - x1;
+  yd = y2 - y1;
+
+  if (x_distance)
+    *x_distance = xd;
+
+  if (y_distance)
+    *y_distance = yd;
+
+  if (distance)
+    *distance = sqrt ((xd * xd) + (yd * yd));
+
+  return TRUE;
+}
+
+gboolean
+gdk_events_get_distance (GdkEvent *event1,
+                         GdkEvent *event2,
+                         gdouble  *distance)
+{
+  return gdk_events_get_axis_distances (event1, event2,
+                                        NULL, NULL,
+                                        distance);
+}
+
+gboolean
+gdk_events_get_angle (GdkEvent *event1,
+                      GdkEvent *event2,
+                      gdouble  *angle)
+{
+  gdouble x_distance, y_distance, distance;
+
+  if (!gdk_events_get_axis_distances (event1, event2,
+                                      &x_distance, &y_distance,
+                                      &distance))
+    return FALSE;
+
+  if (angle)
+    {
+      *angle = atan2 (x_distance, y_distance);
+
+      /* Invert angle */
+      *angle = (2 * G_PI) - *angle;
+
+      /* Shift it 90° */
+      *angle += G_PI / 2;
+
+      /* And constraint it to 0°-360° */
+      *angle = fmod (*angle, 2 * G_PI);
+    }
+
+  return TRUE;
+}
+
+gboolean
+gdk_events_get_center (GdkEvent *event1,
+                       GdkEvent *event2,
+                       gdouble  *x,
+                       gdouble  *y)
+{
+  gdouble x1, x2, y1, y2;
+
+  if (!gdk_event_get_coords (event1, &x1, &y1) ||
+      !gdk_event_get_coords (event2, &x2, &y2))
+    return FALSE;
+
+  if (x)
+    *x = (x2 + x1) / 2;
+
+  if (y)
+    *y = (y2 + y1) / 2;
+
+  return TRUE;
+}
+
 /**
  * gdk_event_set_screen:
  * @event: a #GdkEvent
@@ -1184,54 +1315,67 @@ void
 _gdk_event_button_generate (GdkDisplay *display,
 			    GdkEvent   *event)
 {
-  if ((event->button.time < (display->button_click_time[1] + 2*display->double_click_time)) &&
-      (event->button.window == display->button_window[1]) &&
-      (event->button.button == display->button_number[1]) &&
-      (ABS (event->button.x - display->button_x[1]) <= display->double_click_distance) &&
-      (ABS (event->button.y - display->button_y[1]) <= display->double_click_distance))
-{
+  GdkMultipleClickInfo *info;
+
+  info = g_hash_table_lookup (display->multiple_click_info, event->button.device);
+
+  if (G_UNLIKELY (!info))
+    {
+      info = g_new0 (GdkMultipleClickInfo, 1);
+      info->button_number[0] = info->button_number[1] = -1;
+
+      g_hash_table_insert (display->multiple_click_info,
+                           event->button.device, info);
+    }
+
+  if ((event->button.time < (info->button_click_time[1] + 2 * display->double_click_time)) &&
+      (event->button.window == info->button_window[1]) &&
+      (event->button.button == info->button_number[1]) &&
+      (ABS (event->button.x - info->button_x[1]) <= display->double_click_distance) &&
+      (ABS (event->button.y - info->button_y[1]) <= display->double_click_distance))
+    {
       gdk_synthesize_click (display, event, 3);
-            
-      display->button_click_time[1] = 0;
-      display->button_click_time[0] = 0;
-      display->button_window[1] = NULL;
-      display->button_window[0] = NULL;
-      display->button_number[1] = -1;
-      display->button_number[0] = -1;
-      display->button_x[0] = display->button_x[1] = 0;
-      display->button_y[0] = display->button_y[1] = 0;
+
+      info->button_click_time[1] = 0;
+      info->button_click_time[0] = 0;
+      info->button_window[1] = NULL;
+      info->button_window[0] = NULL;
+      info->button_number[1] = -1;
+      info->button_number[0] = -1;
+      info->button_x[0] = info->button_x[1] = 0;
+      info->button_y[0] = info->button_y[1] = 0;
     }
-  else if ((event->button.time < (display->button_click_time[0] + display->double_click_time)) &&
-	   (event->button.window == display->button_window[0]) &&
-	   (event->button.button == display->button_number[0]) &&
-	   (ABS (event->button.x - display->button_x[0]) <= display->double_click_distance) &&
-	   (ABS (event->button.y - display->button_y[0]) <= display->double_click_distance))
+  else if ((event->button.time < (info->button_click_time[0] + display->double_click_time)) &&
+	   (event->button.window == info->button_window[0]) &&
+	   (event->button.button == info->button_number[0]) &&
+	   (ABS (event->button.x - info->button_x[0]) <= display->double_click_distance) &&
+	   (ABS (event->button.y - info->button_y[0]) <= display->double_click_distance))
     {
       gdk_synthesize_click (display, event, 2);
       
-      display->button_click_time[1] = display->button_click_time[0];
-      display->button_click_time[0] = event->button.time;
-      display->button_window[1] = display->button_window[0];
-      display->button_window[0] = event->button.window;
-      display->button_number[1] = display->button_number[0];
-      display->button_number[0] = event->button.button;
-      display->button_x[1] = display->button_x[0];
-      display->button_x[0] = event->button.x;
-      display->button_y[1] = display->button_y[0];
-      display->button_y[0] = event->button.y;
+      info->button_click_time[1] = info->button_click_time[0];
+      info->button_click_time[0] = event->button.time;
+      info->button_window[1] = info->button_window[0];
+      info->button_window[0] = event->button.window;
+      info->button_number[1] = info->button_number[0];
+      info->button_number[0] = event->button.button;
+      info->button_x[1] = info->button_x[0];
+      info->button_x[0] = event->button.x;
+      info->button_y[1] = info->button_y[0];
+      info->button_y[0] = event->button.y;
     }
   else
     {
-      display->button_click_time[1] = 0;
-      display->button_click_time[0] = event->button.time;
-      display->button_window[1] = NULL;
-      display->button_window[0] = event->button.window;
-      display->button_number[1] = -1;
-      display->button_number[0] = event->button.button;
-      display->button_x[1] = 0;
-      display->button_x[0] = event->button.x;
-      display->button_y[1] = 0;
-      display->button_y[0] = event->button.y;
+      info->button_click_time[1] = 0;
+      info->button_click_time[0] = event->button.time;
+      info->button_window[1] = NULL;
+      info->button_window[0] = event->button.window;
+      info->button_number[1] = -1;
+      info->button_number[0] = event->button.button;
+      info->button_x[1] = 0;
+      info->button_x[0] = event->button.x;
+      info->button_y[1] = 0;
+      info->button_y[0] = event->button.y;
     }
 }
 
diff --git a/gdk/gdkevents.h b/gdk/gdkevents.h
index e6c516c..7ad0329 100644
--- a/gdk/gdkevents.h
+++ b/gdk/gdkevents.h
@@ -34,7 +34,7 @@
 #include <gdk/gdkcolor.h>
 #include <gdk/gdktypes.h>
 #include <gdk/gdkdnd.h>
-#include <gdk/gdkinput.h>
+#include <gdk/gdkdevice.h>
 
 G_BEGIN_DECLS
 
@@ -352,6 +352,7 @@ struct _GdkEventKey
   guint16 hardware_keycode;
   guint8 group;
   guint is_modifier : 1;
+  GdkDevice *device;
 };
 
 struct _GdkEventCrossing
@@ -369,6 +370,7 @@ struct _GdkEventCrossing
   GdkNotifyType detail;
   gboolean focus;
   guint state;
+  GdkDevice *device;
 };
 
 struct _GdkEventFocus
@@ -377,6 +379,7 @@ struct _GdkEventFocus
   GdkWindow *window;
   gint8 send_event;
   gint16 in;
+  GdkDevice *device;
 };
 
 struct _GdkEventConfigure
@@ -474,6 +477,7 @@ struct _GdkEventGrabBroken {
   gboolean keyboard;
   gboolean implicit;
   GdkWindow *grab_window;
+  GdkDevice *device;
 };
 
 /* Event types for DND */
@@ -540,7 +544,20 @@ gboolean  gdk_event_get_root_coords	(const GdkEvent  *event,
 gboolean  gdk_event_get_axis            (const GdkEvent  *event,
                                          GdkAxisUse       axis_use,
                                          gdouble         *value);
+GdkDevice* gdk_event_get_device         (const GdkEvent  *event);
 void      gdk_event_request_motions     (const GdkEventMotion *event);
+
+gboolean  gdk_events_get_distance       (GdkEvent        *event1,
+                                         GdkEvent        *event2,
+                                         gdouble         *distance);
+gboolean  gdk_events_get_angle          (GdkEvent        *event1,
+                                         GdkEvent        *event2,
+                                         gdouble         *angle);
+gboolean  gdk_events_get_center         (GdkEvent        *event1,
+                                         GdkEvent        *event2,
+                                         gdouble         *x,
+                                         gdouble         *y);
+
 void	  gdk_event_handler_set 	(GdkEventFunc    func,
 					 gpointer        data,
 					 GDestroyNotify  notify);
diff --git a/gdk/gdkinput.h b/gdk/gdkinput.h
index 454def8..739ed02 100644
--- a/gdk/gdkinput.h
+++ b/gdk/gdkinput.h
@@ -32,144 +32,27 @@
 #define __GDK_INPUT_H__
 
 #include <gdk/gdktypes.h>
+#include <gdk/gdkdevice.h>
 
 G_BEGIN_DECLS
 
-#define GDK_TYPE_DEVICE              (gdk_device_get_type ())
-#define GDK_DEVICE(object)           (G_TYPE_CHECK_INSTANCE_CAST ((object), GDK_TYPE_DEVICE, GdkDevice))
-#define GDK_DEVICE_CLASS(klass)      (G_TYPE_CHECK_CLASS_CAST ((klass), GDK_TYPE_DEVICE, GdkDeviceClass))
-#define GDK_IS_DEVICE(object)        (G_TYPE_CHECK_INSTANCE_TYPE ((object), GDK_TYPE_DEVICE))
-#define GDK_IS_DEVICE_CLASS(klass)   (G_TYPE_CHECK_CLASS_TYPE ((klass), GDK_TYPE_DEVICE))
-#define GDK_DEVICE_GET_CLASS(obj)    (G_TYPE_INSTANCE_GET_CLASS ((obj), GDK_TYPE_DEVICE, GdkDeviceClass))
-
-typedef struct _GdkDeviceKey	    GdkDeviceKey;
-typedef struct _GdkDeviceAxis	    GdkDeviceAxis;
-typedef struct _GdkDevice	    GdkDevice;
-typedef struct _GdkDeviceClass	    GdkDeviceClass;
-typedef struct _GdkTimeCoord	    GdkTimeCoord;
-
-typedef enum
-{
-  GDK_EXTENSION_EVENTS_NONE,
-  GDK_EXTENSION_EVENTS_ALL,
-  GDK_EXTENSION_EVENTS_CURSOR
-} GdkExtensionMode;
-
-typedef enum
-{
-  GDK_SOURCE_MOUSE,
-  GDK_SOURCE_PEN,
-  GDK_SOURCE_ERASER,
-  GDK_SOURCE_CURSOR
-} GdkInputSource;
-
-typedef enum
-{
-  GDK_MODE_DISABLED,
-  GDK_MODE_SCREEN,
-  GDK_MODE_WINDOW
-} GdkInputMode;
-
-typedef enum
-{
-  GDK_AXIS_IGNORE,
-  GDK_AXIS_X,
-  GDK_AXIS_Y,
-  GDK_AXIS_PRESSURE,
-  GDK_AXIS_XTILT,
-  GDK_AXIS_YTILT,
-  GDK_AXIS_WHEEL,
-  GDK_AXIS_LAST
-} GdkAxisUse;
-
-struct _GdkDeviceKey
-{
-  guint keyval;
-  GdkModifierType modifiers;
-};
-
-struct _GdkDeviceAxis
-{
-  GdkAxisUse use;
-  gdouble    min;
-  gdouble    max;
-};
-
-struct _GdkDevice
-{
-  GObject parent_instance;
-  /* All fields are read-only */
-	  
-  gchar *name;
-  GdkInputSource source;
-  GdkInputMode mode;
-  gboolean has_cursor;	     /* TRUE if the X pointer follows device motion */
-	  
-  gint num_axes;
-  GdkDeviceAxis *axes;
-	  
-  gint num_keys;
-  GdkDeviceKey *keys;
-};
-
-/* We don't allocate each coordinate this big, but we use it to
- * be ANSI compliant and avoid accessing past the defined limits.
- */
-#define GDK_MAX_TIMECOORD_AXES 128
-
-struct _GdkTimeCoord
-{
-  guint32 time;
-  gdouble axes[GDK_MAX_TIMECOORD_AXES];
-};
-
-GType          gdk_device_get_type      (void) G_GNUC_CONST;
+#if !defined (GDK_MULTIDEVICE_SAFE) && !defined (GDK_DISABLE_DEPRECATED)
 
 #ifndef GDK_MULTIHEAD_SAFE
-/* Returns a list of GdkDevice * */	  
-GList *        gdk_devices_list              (void);
-#endif /* GDK_MULTIHEAD_SAFE */
 
-/* Functions to configure a device */
-void           gdk_device_set_source    (GdkDevice      *device,
-					 GdkInputSource  source);
-	  
-gboolean       gdk_device_set_mode      (GdkDevice      *device,
-					 GdkInputMode    mode);
+/* Returns a list of GdkDevice * */
+GList *    gdk_devices_list            (void);
 
-void           gdk_device_set_key       (GdkDevice      *device,
-					 guint           index_,
-					 guint           keyval,
-					 GdkModifierType modifiers);
+GdkDevice *gdk_device_get_core_pointer (void);
 
-void     gdk_device_set_axis_use (GdkDevice         *device,
-				  guint              index_,
-				  GdkAxisUse         use);
-void     gdk_device_get_state    (GdkDevice         *device,
-				  GdkWindow         *window,
-				  gdouble           *axes,
-				  GdkModifierType   *mask);
-gboolean gdk_device_get_history  (GdkDevice         *device,
-				  GdkWindow         *window,
-				  guint32            start,
-				  guint32            stop,
-				  GdkTimeCoord    ***events,
-				  gint              *n_events);
-void     gdk_device_free_history (GdkTimeCoord     **events,
-				  gint               n_events);
-gboolean gdk_device_get_axis     (GdkDevice         *device,
-				  gdouble           *axes,
-				  GdkAxisUse         use,
-				  gdouble           *value);
+#endif /* GDK_MULTIHEAD_SAFE */
 
 void gdk_input_set_extension_events (GdkWindow        *window,
 				     gint              mask,
 				     GdkExtensionMode  mode);
 
-#ifndef GDK_MULTIHEAD_SAFE
-GdkDevice *gdk_device_get_core_pointer (void);
-#endif
- 
+#endif /* !GDK_MULTIDEVICE_SAFE && GDK_DISABLE_DEPRECATED */
+
 G_END_DECLS
 
 #endif /* __GDK_INPUT_H__ */
diff --git a/gdk/gdkinternals.h b/gdk/gdkinternals.h
index 0681bdc..69e4c26 100644
--- a/gdk/gdkinternals.h
+++ b/gdk/gdkinternals.h
@@ -182,13 +182,19 @@ typedef struct
   guint event_mask;
   gboolean implicit;
   guint32 time;
+  GdkGrabOwnership ownership;
 
-  gboolean activated;
-  gboolean implicit_ungrab;
-} GdkPointerGrabInfo;
+  guint activated : 1;
+  guint implicit_ungrab : 1;
+} GdkDeviceGrabInfo;
 
 typedef struct _GdkInputWindow GdkInputWindow;
 
+typedef void (* GdkDisplayPointerInfoForeach) (GdkDisplay           *display,
+                                               GdkDevice            *device,
+                                               GdkPointerWindowInfo *device_info,
+                                               gpointer              user_data);
+
 /* Private version of GdkWindowObject. The initial part of this strucuture
    is public for historical reasons. Don't change that part */
 typedef struct _GdkWindowPaint             GdkWindowPaint;
@@ -236,6 +242,7 @@ struct _GdkWindowObject
   guint accept_focus : 1;
   guint focus_on_map : 1;
   guint shaped : 1;
+  guint support_multidevice : 1;
   
   GdkEventMask event_mask;
 
@@ -255,6 +262,7 @@ struct _GdkWindowObject
   GdkRegion *clip_region; /* Clip region (wrt toplevel) in window coords */
   GdkRegion *clip_region_with_children; /* Clip region in window coords */
   GdkCursor *cursor;
+  GHashTable *device_cursor;
   gint8 toplevel_window_type;
   guint synthesize_crossing_event_queued : 1;
   guint effective_visibility : 2;
@@ -273,6 +281,9 @@ struct _GdkWindowObject
   GdkRegion *input_shape;
   
   cairo_surface_t *cairo_surface;
+
+  GList *devices_inside;
+  GHashTable *device_events;
 };
 
 #define GDK_WINDOW_TYPE(d) (((GdkWindowObject*)(GDK_WINDOW (d)))->window_type)
@@ -314,6 +325,9 @@ void gdk_synthesize_window_state (GdkWindow     *window,
                                   GdkWindowState unset_flags,
                                   GdkWindowState set_flags);
 
+GdkDeviceManager * _gdk_device_manager_new (GdkDisplay *display);
+
+
 #define GDK_SCRATCH_IMAGE_WIDTH 256
 #define GDK_SCRATCH_IMAGE_HEIGHT 64
 
@@ -452,17 +466,20 @@ GdkRegion *_gdk_windowing_get_shape_for_mask    (GdkBitmap *mask);
 void     _gdk_windowing_window_beep             (GdkWindow *window);
 
 
-void       _gdk_windowing_get_pointer        (GdkDisplay       *display,
+void       _gdk_windowing_get_device_state   (GdkDisplay       *display,
+                                              GdkDevice        *device,
 					      GdkScreen       **screen,
 					      gint             *x,
 					      gint             *y,
 					      GdkModifierType  *mask);
-GdkWindow* _gdk_windowing_window_at_pointer  (GdkDisplay       *display,
-					      gint             *win_x,
-					      gint             *win_y,
-					      GdkModifierType  *mask,
-					      gboolean          get_toplevel);
-GdkGrabStatus _gdk_windowing_pointer_grab    (GdkWindow        *window,
+GdkWindow* _gdk_windowing_window_at_device_position  (GdkDisplay       *display,
+                                                      GdkDevice        *device,
+                                                      gint             *win_x,
+                                                      gint             *win_y,
+                                                      GdkModifierType  *mask,
+                                                      gboolean          get_toplevel);
+GdkGrabStatus _gdk_windowing_device_grab     (GdkDevice        *device,
+                                              GdkWindow        *window,
 					      GdkWindow        *native,
 					      gboolean          owner_events,
 					      GdkEventMask      event_mask,
@@ -579,34 +596,40 @@ char *_gdk_windowing_get_startup_notify_id (GAppLaunchContext *context,
 void  _gdk_windowing_launch_failed         (GAppLaunchContext *context, 
 				            const char        *startup_notify_id);
 
-GdkPointerGrabInfo *_gdk_display_get_active_pointer_grab (GdkDisplay *display);
-void _gdk_display_pointer_grab_update                    (GdkDisplay *display,
-							  gulong current_serial);
-GdkPointerGrabInfo *_gdk_display_get_last_pointer_grab (GdkDisplay *display);
-GdkPointerGrabInfo *_gdk_display_add_pointer_grab  (GdkDisplay *display,
-						    GdkWindow *window,
-						    GdkWindow *native_window,
-						    gboolean owner_events,
-						    GdkEventMask event_mask,
-						    unsigned long serial_start,
-						    guint32 time,
-						    gboolean implicit);
-GdkPointerGrabInfo * _gdk_display_has_pointer_grab (GdkDisplay *display,
-						    gulong serial);
-gboolean _gdk_display_end_pointer_grab (GdkDisplay *display,
-					gulong serial,
-					GdkWindow *if_child,
-					gboolean implicit);
-void _gdk_display_set_has_keyboard_grab (GdkDisplay *display,
-					 GdkWindow *window,
-					 GdkWindow *native_window,
-					 gboolean owner_events,
-					 unsigned long serial,
-					 guint32 time);
-void _gdk_display_unset_has_keyboard_grab (GdkDisplay *display,
-					   gboolean implicit);
+void _gdk_display_device_grab_update                     (GdkDisplay *display,
+                                                          GdkDevice  *device,
+                                                          gulong      current_serial);
+GdkDeviceGrabInfo  *_gdk_display_get_last_device_grab  (GdkDisplay *display,
+                                                        GdkDevice  *device);
+GdkDeviceGrabInfo  *_gdk_display_add_device_grab   (GdkDisplay       *display,
+                                                    GdkDevice        *device,
+						    GdkWindow        *window,
+						    GdkWindow        *native_window,
+                                                    GdkGrabOwnership  grab_ownership,
+						    gboolean          owner_events,
+						    GdkEventMask      event_mask,
+						    unsigned long     serial_start,
+						    guint32           time,
+						    gboolean          implicit);
+GdkDeviceGrabInfo  * _gdk_display_has_device_grab  (GdkDisplay *display,
+                                                    GdkDevice  *device,
+						    gulong      serial);
+gboolean _gdk_display_end_device_grab  (GdkDisplay *display,
+                                        GdkDevice  *device,
+					gulong      serial,
+					GdkWindow  *if_child,
+					gboolean    implicit);
+gboolean _gdk_display_check_grab_ownership (GdkDisplay *display,
+                                            GdkDevice  *device,
+                                            gulong      serial);
 void _gdk_display_enable_motion_hints     (GdkDisplay *display);
 
+GdkPointerWindowInfo * _gdk_display_get_pointer_info (GdkDisplay *display,
+                                                      GdkDevice  *device);
+
+void _gdk_display_pointer_info_foreach (GdkDisplay                   *display,
+                                        GdkDisplayPointerInfoForeach  func,
+                                        gpointer                      user_data);
 
 void _gdk_window_invalidate_for_expose (GdkWindow       *window,
 					GdkRegion       *region);
@@ -638,6 +661,7 @@ gboolean _gdk_window_event_parent_of (GdkWindow *parent,
 void _gdk_synthesize_crossing_events (GdkDisplay                 *display,
 				      GdkWindow                  *src,
 				      GdkWindow                  *dest,
+                                      GdkDevice                  *device,
 				      GdkCrossingMode             mode,
 				      gint                        toplevel_x,
 				      gint                        toplevel_y,
@@ -647,7 +671,8 @@ void _gdk_synthesize_crossing_events (GdkDisplay                 *display,
 				      gulong                      serial,
 				      gboolean                    non_linear);
 void _gdk_display_set_window_under_pointer (GdkDisplay *display,
-					    GdkWindow *window);
+                                            GdkDevice  *device,
+					    GdkWindow  *window);
 
 
 void _gdk_synthesize_crossing_events_for_geometry_change (GdkWindow *changed_window);
diff --git a/gdk/gdkoffscreenwindow.c b/gdk/gdkoffscreenwindow.c
index 2622235..4b1bfc3 100644
--- a/gdk/gdkoffscreenwindow.c
+++ b/gdk/gdkoffscreenwindow.c
@@ -788,10 +788,11 @@ gdk_offscreen_window_get_deskrelative_origin (GdkWindow *window,
 }
 
 static gboolean
-gdk_offscreen_window_get_pointer (GdkWindow       *window,
-				  gint            *x,
-				  gint            *y,
-				  GdkModifierType *mask)
+gdk_offscreen_window_get_device_state (GdkWindow       *window,
+                                       GdkDevice       *device,
+                                       gint            *x,
+                                       gint            *y,
+                                       GdkModifierType *mask)
 {
   GdkWindowObject *private = GDK_WINDOW_OBJECT (window);
   GdkOffscreenWindow *offscreen;
@@ -806,7 +807,7 @@ gdk_offscreen_window_get_pointer (GdkWindow       *window,
   offscreen = GDK_OFFSCREEN_WINDOW (private->impl);
   if (offscreen->embedder != NULL)
     {
-      gdk_window_get_pointer (offscreen->embedder, &tmpx, &tmpy, &tmpmask);
+      gdk_window_get_device_position (offscreen->embedder, device, &tmpx, &tmpy, &tmpmask);
       from_embedder (window,
 		     tmpx, tmpy,
 		     &dtmpx, &dtmpy);
@@ -1087,27 +1088,6 @@ gdk_offscreen_window_set_static_gravities (GdkWindow *window,
 }
 
 static void
-gdk_offscreen_window_set_cursor (GdkWindow *window,
-				 GdkCursor *cursor)
-{
-  GdkWindowObject *private = (GdkWindowObject *)window;
-  GdkOffscreenWindow *offscreen;
-
-  offscreen = GDK_OFFSCREEN_WINDOW (private->impl);
-
-  if (offscreen->cursor)
-    {
-      gdk_cursor_unref (offscreen->cursor);
-      offscreen->cursor = NULL;
-    }
-
-  if (cursor)
-    offscreen->cursor = gdk_cursor_ref (cursor);
-
-  /* TODO: The cursor is never actually used... */
-}
-
-static void
 gdk_offscreen_window_get_geometry (GdkWindow *window,
 				   gint      *x,
 				   gint      *y,
@@ -1266,7 +1246,6 @@ gdk_offscreen_window_impl_iface_init (GdkWindowImplIface *iface)
   iface->get_events = gdk_offscreen_window_get_events;
   iface->set_events = gdk_offscreen_window_set_events;
   iface->reparent = gdk_offscreen_window_reparent;
-  iface->set_cursor = gdk_offscreen_window_set_cursor;
   iface->get_geometry = gdk_offscreen_window_get_geometry;
   iface->shape_combine_region = gdk_offscreen_window_shape_combine_region;
   iface->input_shape_combine_region = gdk_offscreen_window_input_shape_combine_region;
@@ -1275,7 +1254,7 @@ gdk_offscreen_window_impl_iface_init (GdkWindowImplIface *iface)
   iface->queue_translation = gdk_offscreen_window_queue_translation;
   iface->get_root_coords = gdk_offscreen_window_get_root_coords;
   iface->get_deskrelative_origin = gdk_offscreen_window_get_deskrelative_origin;
-  iface->get_pointer = gdk_offscreen_window_get_pointer;
+  iface->get_device_state = gdk_offscreen_window_get_device_state;
   iface->destroy = gdk_offscreen_window_destroy;
 }
 
diff --git a/gdk/gdktypes.h b/gdk/gdktypes.h
index ac8e443..54113ab 100644
--- a/gdk/gdktypes.h
+++ b/gdk/gdktypes.h
@@ -175,6 +175,13 @@ typedef enum
   GDK_GRAB_FROZEN          = 4
 } GdkGrabStatus;
 
+typedef enum
+{
+  GDK_OWNERSHIP_NONE,
+  GDK_OWNERSHIP_WINDOW,
+  GDK_OWNERSHIP_APPLICATION
+} GdkGrabOwnership;
+
 typedef void (*GdkInputFunction) (gpointer	    data,
 				  gint		    source,
 				  GdkInputCondition condition);
diff --git a/gdk/gdkwindow.c b/gdk/gdkwindow.c
index 2bb5551..fd1bfaa 100644
--- a/gdk/gdkwindow.c
+++ b/gdk/gdkwindow.c
@@ -34,6 +34,7 @@
 #include "gdkdrawable.h"
 #include "gdkintl.h"
 #include "gdkscreen.h"
+#include "gdkdeviceprivate.h"
 #include "gdkmarshalers.h"
 #include "gdkalias.h"
 
@@ -151,7 +152,6 @@ typedef struct {
   int dx, dy; /* The amount that the source was moved to reach dest_region */
 } GdkWindowRegionMove;
 
-
 /* Global info */
 
 static GdkGC *gdk_window_create_gc      (GdkDrawable     *drawable,
@@ -328,7 +328,8 @@ static void do_move_region_bits_on_impl (GdkWindowObject *private,
 					 int dx, int dy);
 static void gdk_window_invalidate_in_parent (GdkWindowObject *private);
 static void move_native_children        (GdkWindowObject *private);
-static void update_cursor               (GdkDisplay *display);
+static void update_cursor               (GdkDisplay *display,
+                                         GdkDevice  *device);
 static void impl_window_add_update_area (GdkWindowObject *impl_window,
 					 GdkRegion *region);
 static void gdk_window_region_move_free (GdkWindowRegionMove *move);
@@ -559,10 +560,30 @@ gdk_window_class_init (GdkWindowObjectClass *klass)
 }
 
 static void
+device_removed_cb (GdkDeviceManager *device_manager,
+                   GdkDevice        *device,
+                   GdkWindow        *window)
+{
+  GdkWindowObject *private;
+
+  private = (GdkWindowObject *) window;
+
+  private->devices_inside = g_list_remove (private->devices_inside, device);
+  g_hash_table_remove (private->device_cursor, device);
+
+  if (private->device_events)
+    g_hash_table_remove (private->device_events, device);
+}
+
+static void
 gdk_window_finalize (GObject *object)
 {
   GdkWindow *window = GDK_WINDOW (object);
   GdkWindowObject *obj = (GdkWindowObject *) object;
+  GdkDeviceManager *device_manager;
+
+  device_manager = gdk_display_get_device_manager (GDK_WINDOW_DISPLAY (window));
+  g_signal_handlers_disconnect_by_func (device_manager, device_removed_cb, window);
 
   if (!GDK_WINDOW_DESTROYED (window))
     {
@@ -599,6 +620,15 @@ gdk_window_finalize (GObject *object)
   if (obj->cursor)
     gdk_cursor_unref (obj->cursor);
 
+  if (obj->device_cursor)
+    g_hash_table_destroy (obj->device_cursor);
+
+  if (obj->device_events)
+    g_hash_table_destroy (obj->device_events);
+
+  if (obj->devices_inside)
+    g_list_free (obj->devices_inside);
+
   G_OBJECT_CLASS (parent_class)->finalize (object);
 }
 
@@ -1155,12 +1185,20 @@ find_native_sibling_above (GdkWindowObject *parent,
 }
 
 static GdkEventMask
-get_native_event_mask (GdkWindowObject *private)
+get_native_device_event_mask (GdkWindowObject *private,
+                              GdkDevice       *device)
 {
+  GdkEventMask event_mask;
+
+  if (device)
+    event_mask = GPOINTER_TO_INT (g_hash_table_lookup (private->device_events, device));
+  else
+    event_mask = private->event_mask;
+
   if (_gdk_native_windows ||
       private->window_type == GDK_WINDOW_ROOT ||
       private->window_type == GDK_WINDOW_FOREIGN)
-    return private->event_mask;
+    return event_mask;
   else
     {
       GdkEventMask mask;
@@ -1221,6 +1259,12 @@ get_native_grab_event_mask (GdkEventMask grab_mask)
        GDK_BUTTON3_MOTION_MASK));
 }
 
+static GdkEventMask
+get_native_event_mask (GdkWindowObject *private)
+{
+  return get_native_device_event_mask (private, NULL);
+}
+
 /* Puts the native window in the right order wrt the other native windows
  * in the hierarchy, given the position it has in the client side data.
  * This is useful if some operation changed the stacking order.
@@ -1273,6 +1317,7 @@ gdk_window_new (GdkWindow     *parent,
   gboolean native;
   GdkEventMask event_mask;
   GdkWindow *real_parent;
+  GdkDeviceManager *device_manager;
 
   g_return_val_if_fail (attributes != NULL, NULL);
 
@@ -1449,6 +1494,13 @@ gdk_window_new (GdkWindow     *parent,
 				  (attributes->cursor) :
 				  NULL));
 
+  private->device_cursor = g_hash_table_new_full (NULL, NULL, NULL,
+                                                  (GDestroyNotify) gdk_cursor_unref);
+
+  device_manager = gdk_display_get_device_manager (GDK_WINDOW_DISPLAY (parent));
+  g_signal_connect (device_manager, "device-removed",
+                    G_CALLBACK (device_removed_cb), window);
+
   return window;
 }
 
@@ -1843,6 +1895,30 @@ window_remove_filters (GdkWindow *window)
     }
 }
 
+static void
+update_pointer_info_foreach (GdkDisplay           *display,
+                             GdkDevice            *device,
+                             GdkPointerWindowInfo *pointer_info,
+                             gpointer              user_data)
+{
+  GdkWindow *window = user_data;
+
+  if (pointer_info->toplevel_under_pointer == window)
+    {
+      g_object_unref (pointer_info->toplevel_under_pointer);
+      pointer_info->toplevel_under_pointer = NULL;
+    }
+}
+
+static void
+window_remove_from_pointer_info (GdkWindow  *window,
+                                 GdkDisplay *display)
+{
+  _gdk_display_pointer_info_foreach (display,
+                                     update_pointer_info_foreach,
+                                     window);
+}
+
 /**
  * _gdk_window_destroy_hierarchy:
  * @window: a #GdkWindow
@@ -1982,12 +2058,8 @@ _gdk_window_destroy_hierarchy (GdkWindow *window,
 					   NULL, NULL);
 	    }
 
-
 	  impl_iface = GDK_WINDOW_IMPL_GET_IFACE (private->impl);
 
-	  if (private->extension_events)
-	    impl_iface->input_window_destroy (window);
-
 	  if (gdk_window_has_impl (private))
 	    impl_iface->destroy (window, recursing_native,
 				 foreign_destroy);
@@ -2011,11 +2083,7 @@ _gdk_window_destroy_hierarchy (GdkWindow *window,
 
 	  private->redirect = NULL;
 
-	  if (display->pointer_info.toplevel_under_pointer == window)
-	    {
-	      g_object_unref (display->pointer_info.toplevel_under_pointer);
-	      display->pointer_info.toplevel_under_pointer = NULL;
-	    }
+	  window_remove_from_pointer_info (window, display);
 
 	  if (private->clip_region)
 	    {
@@ -3309,8 +3377,6 @@ gdk_window_flush_if_exposing (GdkWindow *window)
 {
   GdkWindowObject *private;
   GdkWindowObject *impl_window;
-  GList *l;
-  GdkWindowRegionMove *move;
 
   private = (GdkWindowObject *) window;
   impl_window = gdk_window_get_impl_window (private);
@@ -6074,29 +6140,48 @@ gdk_window_get_pointer (GdkWindow	  *window,
 			GdkModifierType   *mask)
 {
   GdkDisplay *display;
-  gint tmp_x, tmp_y;
-  GdkModifierType tmp_mask;
-  GdkWindow *child;
 
-  g_return_val_if_fail (window == NULL || GDK_IS_WINDOW (window), NULL);
+  g_return_val_if_fail (GDK_IS_WINDOW (window), NULL);
 
-  if (window)
-    {
-      display = gdk_drawable_get_display (window);
-    }
-  else
-    {
-      GdkScreen *screen = gdk_screen_get_default ();
+  display = gdk_drawable_get_display (window);
 
-      display = gdk_screen_get_display (screen);
-      window = gdk_screen_get_root_window (screen);
+  return gdk_window_get_device_position (window, display->core_pointer, x, y, mask);
+}
 
-      GDK_NOTE (MULTIHEAD,
-		g_message ("Passing NULL for window to gdk_window_get_pointer()\n"
-			   "is not multihead safe"));
-    }
+/**
+ * gdk_window_get_device_position:
+ * @window: a #GdkWindow.
+ * @device: #GdkDevice to query to.
+ * @x: return location for the X coordinate of @device, or %NULL.
+ * @y: return location for the Y coordinate of @device, or %NULL.
+ * @mask: return location for the modifier mask, or %NULL.
+ *
+ * Obtains the current device position and modifier state.
+ * The position is given in coordinates relative to the upper left
+ * corner of @window.
+ *
+ * Returns: The window underneath @device (as with
+ * gdk_display_get_window_at_device_position()), or %NULL if the
+ * window is not known to GDK.
+ **/
+GdkWindow *
+gdk_window_get_device_position (GdkWindow       *window,
+                                GdkDevice       *device,
+                                gint            *x,
+                                gint            *y,
+                                GdkModifierType *mask)
+{
+  GdkDisplay *display;
+  gint tmp_x, tmp_y;
+  GdkModifierType tmp_mask;
+  GdkWindow *child;
 
-  child = display->pointer_hooks->window_get_pointer (display, window, &tmp_x, &tmp_y, &tmp_mask);
+  g_return_val_if_fail (GDK_IS_WINDOW (window), NULL);
+  g_return_val_if_fail (GDK_IS_DEVICE (device), NULL);
+
+  display = gdk_drawable_get_display (window);
+  child = display->device_hooks->window_get_device_position (display, device, window,
+                                                             &tmp_x, &tmp_y, &tmp_mask);
 
   if (x)
     *x = tmp_x;
@@ -6775,30 +6860,31 @@ gdk_window_hide (GdkWindow *window)
   else if (was_mapped)
     {
       GdkDisplay *display;
+      GdkDeviceManager *device_manager;
+      GList *devices, *d;
 
       /* May need to break grabs on children */
       display = gdk_drawable_get_display (window);
+      device_manager = gdk_display_get_device_manager (display);
 
-      if (_gdk_display_end_pointer_grab (display,
-					 _gdk_windowing_window_get_next_serial (display),
-					 window,
-					 TRUE))
-	gdk_display_pointer_ungrab (display, GDK_CURRENT_TIME);
+      /* Get all devices */
+      devices = gdk_device_manager_get_devices (device_manager, GDK_DEVICE_TYPE_MASTER);
+      devices = g_list_concat (devices, gdk_device_manager_get_devices (device_manager, GDK_DEVICE_TYPE_SLAVE));
+      devices = g_list_concat (devices, gdk_device_manager_get_devices (device_manager, GDK_DEVICE_TYPE_FLOATING));
 
-      if (display->keyboard_grab.window != NULL)
-	{
-	  if (is_parent_of (window, display->keyboard_grab.window))
-	    {
-	      /* Call this ourselves, even though gdk_display_keyboard_ungrab
-		 does so too, since we want to pass implicit == TRUE so the
-		 broken grab event is generated */
-	      _gdk_display_unset_has_keyboard_grab (display,
-						    TRUE);
-	      gdk_display_keyboard_ungrab (display, GDK_CURRENT_TIME);
-	    }
-	}
+      for (d = devices; d; d = d->next)
+        {
+          GdkDevice *device = d->data;
+
+          if (_gdk_display_end_device_grab (display, device,
+                                            _gdk_windowing_window_get_next_serial (display),
+                                            window,
+                                            TRUE))
+            gdk_device_ungrab (device, GDK_CURRENT_TIME);
+        }
 
       private->state = GDK_WINDOW_STATE_WITHDRAWN;
+      g_list_free (devices);
     }
 
   did_hide = _gdk_window_update_viewable (window);
@@ -6879,9 +6965,10 @@ gdk_window_withdraw (GdkWindow *window)
  * @event_mask: event mask for @window
  *
  * The event mask for a window determines which events will be reported
- * for that window. For example, an event mask including #GDK_BUTTON_PRESS_MASK
- * means the window should report button press events. The event mask
- * is the bitwise OR of values from the #GdkEventMask enumeration.
+ * for that window from all master input devices. For example, an event mask
+ * including #GDK_BUTTON_PRESS_MASK means the window should report button
+ * press events. The event mask is the bitwise OR of values from the
+ * #GdkEventMask enumeration.
  **/
 void
 gdk_window_set_events (GdkWindow       *window,
@@ -6918,7 +7005,8 @@ gdk_window_set_events (GdkWindow       *window,
  * gdk_window_get_events:
  * @window: a #GdkWindow
  *
- * Gets the event mask for @window. See gdk_window_set_events().
+ * Gets the event mask for @window for all master input devices. See
+ * gdk_window_set_events().
  *
  * Return value: event mask for @window
  **/
@@ -6936,6 +7024,111 @@ gdk_window_get_events (GdkWindow *window)
   return private->event_mask;
 }
 
+/**
+ * gdk_window_set_device_events:
+ * @window: a #GdkWindow
+ * @device: #GdkDevice to enable events for.
+ * @event_mask: event mask for @window
+ *
+ * Sets the event mask for a given device (Normally a floating device, not
+ * attached to any visible pointer) to @window. For example, an event mask
+ * including #GDK_BUTTON_PRESS_MASK means the window should report button
+ * press events. The event mask is the bitwise OR of values from the
+ * #GdkEventMask enumeration.
+ **/
+void
+gdk_window_set_device_events (GdkWindow    *window,
+                              GdkDevice    *device,
+                              GdkEventMask  event_mask)
+{
+  GdkEventMask device_mask;
+  GdkWindowObject *private;
+  GdkDisplay *display;
+  GdkWindow *native;
+
+  g_return_if_fail (GDK_IS_WINDOW (window));
+  g_return_if_fail (GDK_IS_DEVICE (device));
+
+  if (GDK_WINDOW_DESTROYED (window))
+    return;
+
+  private = (GdkWindowObject *) window;
+
+  /* If motion hint is disabled, enable motion events again */
+  display = gdk_drawable_get_display (window);
+  if ((private->event_mask & GDK_POINTER_MOTION_HINT_MASK) &&
+      !(event_mask & GDK_POINTER_MOTION_HINT_MASK))
+    _gdk_display_enable_motion_hints (display);
+
+  if (G_UNLIKELY (!private->device_events))
+    private->device_events = g_hash_table_new (NULL, NULL);
+
+  if (event_mask == 0)
+    {
+      /* FIXME: unsetting events on a master device
+       * would restore private->event_mask
+       */
+      g_hash_table_remove (private->device_events, device);
+    }
+  else
+    g_hash_table_insert (private->device_events, device,
+                         GINT_TO_POINTER (event_mask));
+
+  if (_gdk_native_windows)
+    native = window;
+  else
+    native = gdk_window_get_toplevel (window);
+
+  while (gdk_window_is_offscreen ((GdkWindowObject *)native))
+    {
+      native = gdk_offscreen_window_get_embedder (native);
+
+      if (native == NULL ||
+	  (!_gdk_window_has_impl (native) &&
+	   !gdk_window_is_viewable (native)))
+	return;
+
+      native = gdk_window_get_toplevel (native);
+    }
+
+  device_mask = get_native_device_event_mask (private, device);
+  GDK_DEVICE_GET_CLASS (device)->select_window_events (device, native, device_mask);
+}
+
+/**
+ * gdk_window_get_device_events:
+ * @window: a #GdkWindow.
+ * @device: a #GdkDevice.
+ *
+ * Returns the event mask for @window corresponding to an specific device.
+ *
+ * Returns: device event mask for @window
+ **/
+GdkEventMask
+gdk_window_get_device_events (GdkWindow *window,
+                              GdkDevice *device)
+{
+  GdkWindowObject *private;
+  GdkEventMask mask;
+
+  g_return_val_if_fail (GDK_IS_WINDOW (window), 0);
+  g_return_val_if_fail (GDK_IS_DEVICE (device), 0);
+
+  if (GDK_WINDOW_DESTROYED (window))
+    return 0;
+
+  private = (GdkWindowObject *) window;
+
+  if (!private->device_events)
+    return 0;
+
+  mask = GPOINTER_TO_INT (g_hash_table_lookup (private->device_events, device));
+
+  /* FIXME: device could be controlled by private->event_mask */
+
+  return mask;
+}
+
 static void
 gdk_window_move_resize_toplevel (GdkWindow *window,
 				 gboolean   with_move,
@@ -7636,6 +7829,23 @@ gdk_window_set_back_pixmap (GdkWindow *window,
     }
 }
 
+static void
+update_cursor_foreach (GdkDisplay           *display,
+                       GdkDevice            *device,
+                       GdkPointerWindowInfo *pointer_info,
+                       gpointer              user_data)
+{
+  GdkWindow *window = user_data;
+  GdkWindowObject *private = (GdkWindowObject *) window;
+
+  if (_gdk_native_windows ||
+      private->window_type == GDK_WINDOW_ROOT ||
+      private->window_type == GDK_WINDOW_FOREIGN)
+    GDK_WINDOW_IMPL_GET_IFACE (private->impl)->set_device_cursor (window, device, private->cursor);
+  else if (_gdk_window_event_parent_of (window, pointer_info->window_under_pointer))
+    update_cursor (display, device);
+}
+
 /**
  * gdk_window_get_cursor:
  * @window: a #GdkWindow
@@ -7668,7 +7878,7 @@ gdk_window_get_cursor (GdkWindow *window)
  * @window: a #GdkWindow
  * @cursor: a cursor
  *
- * Sets the mouse pointer for a #GdkWindow. Use gdk_cursor_new_for_display()
+ * Sets the default mouse pointer for a #GdkWindow. Use gdk_cursor_new_for_display()
  * or gdk_cursor_new_from_pixmap() to create the cursor. To make the cursor
  * invisible, use %GDK_BLANK_CURSOR. Passing %NULL for the @cursor argument
  * to gdk_window_set_cursor() means that @window will use the cursor of its
@@ -7679,7 +7889,6 @@ gdk_window_set_cursor (GdkWindow *window,
 		       GdkCursor *cursor)
 {
   GdkWindowObject *private;
-  GdkWindowImplIface *impl_iface;
   GdkDisplay *display;
 
   g_return_if_fail (GDK_IS_WINDOW (window));
@@ -7698,21 +7907,86 @@ gdk_window_set_cursor (GdkWindow *window,
       if (cursor)
 	private->cursor = gdk_cursor_ref (cursor);
 
-      if (_gdk_native_windows ||
-	  private->window_type == GDK_WINDOW_ROOT ||
-          private->window_type == GDK_WINDOW_FOREIGN)
-	{
-	  impl_iface = GDK_WINDOW_IMPL_GET_IFACE (private->impl);
-	  impl_iface->set_cursor (window, cursor);
-	}
-      else if (_gdk_window_event_parent_of (window, display->pointer_info.window_under_pointer))
-	update_cursor (display);
+      _gdk_display_pointer_info_foreach (display,
+                                         update_cursor_foreach,
+                                         window);
 
       g_object_notify (G_OBJECT (window), "cursor");
     }
 }
 
 /**
+ * gdk_window_get_device_cursor:
+ * @window: a #GdkWindow.
+ * @device: a #GdkDevice.
+ *
+ * Retrieves a #GdkCursor pointer for the @device currently set on the
+ * specified #GdkWindow, or %NULL.  If the return value is %NULL then
+ * there is no custom cursor set on the specified window, and it is
+ * using the cursor for its parent window.
+ *
+ * Returns: a #GdkCursor, or %NULL. The returned object is owned
+ *   by the #GdkWindow and should not be unreferenced directly. Use
+ *   gdk_window_set_cursor() to unset the cursor of the window
+ **/
+GdkCursor *
+gdk_window_get_device_cursor (GdkWindow *window,
+                              GdkDevice *device)
+{
+  GdkWindowObject *private;
+
+  g_return_val_if_fail (GDK_IS_WINDOW (window), NULL);
+  g_return_val_if_fail (GDK_IS_DEVICE (device), NULL);
+
+  private = (GdkWindowObject *) window;
+
+  return g_hash_table_lookup (private->device_cursor, device);
+}
+
+/**
+ * gdk_window_set_device_cursor:
+ * @window: a #Gdkwindow
+ * @device: a #GdkDevice
+ * @cursor: a #GdkCursor
+ *
+ * Sets a specific #GdkCursor for a given device when it gets inside @window.
+ * Use gdk_cursor_new_for_display() or gdk_cursor_new_from_pixmap() to create
+ * the cursor. To make the cursor invisible, use %GDK_BLANK_CURSOR. Passing
+ * %NULL for the @cursor argument to gdk_window_set_cursor() means that
+ * @window will use the cursor of its parent window. Most windows should
+ * use this default.
+ **/
+void
+gdk_window_set_device_cursor (GdkWindow *window,
+                              GdkDevice *device,
+                              GdkCursor *cursor)
+{
+  GdkWindowObject *private;
+  GdkDisplay *display;
+
+  g_return_if_fail (GDK_IS_WINDOW (window));
+  g_return_if_fail (GDK_IS_DEVICE (window));
+
+  private = (GdkWindowObject *) window;
+  display = gdk_drawable_get_display (window);
+
+  if (!cursor)
+    g_hash_table_remove (private->device_cursor, device);
+  else
+    g_hash_table_replace (private->device_cursor, device, cursor);
+
+  if (!GDK_WINDOW_DESTROYED (window))
+    {
+      GdkPointerWindowInfo *pointer_info;
+
+      pointer_info = _gdk_display_get_pointer_info (display, device);
+
+      if (_gdk_window_event_parent_of (window, pointer_info->window_under_pointer))
+        update_cursor (display, device);
+    }
+}
+
+/**
  * gdk_window_get_geometry:
  * @window: a #GdkWindow
  * @x: return location for X coordinate of window (relative to its parent)
@@ -8742,27 +9016,34 @@ _gdk_window_event_parent_of (GdkWindow *parent,
 }
 
 static void
-update_cursor (GdkDisplay *display)
+update_cursor (GdkDisplay *display,
+               GdkDevice  *device)
 {
   GdkWindowObject *cursor_window, *parent, *toplevel;
   GdkWindow *pointer_window;
   GdkWindowImplIface *impl_iface;
-  GdkPointerGrabInfo *grab;
+  GdkPointerWindowInfo *pointer_info;
+  GdkDeviceGrabInfo *grab;
 
-  pointer_window = display->pointer_info.window_under_pointer;
+  pointer_info = _gdk_display_get_pointer_info (display, device);
+  pointer_window = pointer_info->window_under_pointer;
 
   /* We ignore the serials here and just pick the last grab
      we've sent, as that would shortly be used anyway. */
-  grab = _gdk_display_get_last_pointer_grab (display);
+  grab = _gdk_display_get_last_device_grab (display, device);
   if (/* have grab */
       grab != NULL &&
       /* the pointer is not in a descendant of the grab window */
       !_gdk_window_event_parent_of (grab->window, pointer_window))
-    /* use the cursor from the grab window */
-    cursor_window = (GdkWindowObject *)grab->window;
+    {
+      /* use the cursor from the grab window */
+      cursor_window = (GdkWindowObject *) grab->window;
+    }
   else
-    /* otherwise use the cursor from the pointer window */
-    cursor_window = (GdkWindowObject *)pointer_window;
+    {
+      /* otherwise use the cursor from the pointer window */
+      cursor_window = (GdkWindowObject *) pointer_window;
+    }
 
   /* Find the first window with the cursor actually set, as
      the cursor is inherited from the parent */
@@ -8773,9 +9054,10 @@ update_cursor (GdkDisplay *display)
 
   /* Set all cursors on toplevel, otherwise its tricky to keep track of
    * which native window has what cursor set. */
-  toplevel = (GdkWindowObject *)get_event_toplevel (pointer_window);
+  toplevel = (GdkWindowObject *) get_event_toplevel (pointer_window);
   impl_iface = GDK_WINDOW_IMPL_GET_IFACE (toplevel->impl);
-  impl_iface->set_cursor ((GdkWindow *)toplevel, cursor_window->cursor);
+  impl_iface->set_device_cursor ((GdkWindow *) toplevel, device,
+                                 cursor_window->cursor);
 }
 
 static void
@@ -9040,6 +9322,57 @@ gdk_window_beep (GdkWindow *window)
     gdk_display_beep (display);
 }
 
+/**
+ * gdk_window_set_support_multidevice:
+ * @window: a #GdkWindow.
+ * @support_multidevice: %TRUE to enable multidevice support in @window.
+ *
+ * This function will enable multidevice features in @window.
+ *
+ * Multidevice aware windows will need to handle properly some things such
+ * as multiple per enter/leave events (one per device) and grab ownerships.
+ **/
+void
+gdk_window_set_support_multidevice (GdkWindow *window,
+                                    gboolean   support_multidevice)
+{
+  GdkWindowObject *private = (GdkWindowObject *) window;
+
+  g_return_if_fail (GDK_IS_WINDOW (window));
+
+  if (GDK_WINDOW_DESTROYED (window))
+    return;
+
+  if (private->support_multidevice == support_multidevice)
+    return;
+
+  private->support_multidevice = support_multidevice;
+
+  /* FIXME: What to do if called when some pointers are inside the window ? */
+}
+
+/**
+ * gdk_window_get_support_multidevice:
+ * @window: a #GdkWindow.
+ *
+ * Returns %TRUE if the window is aware of the existence of multiple
+ * devices.
+ *
+ * Returns: %TRUE if the window handles multidevice features.
+ **/
+gboolean
+gdk_window_get_support_multidevice (GdkWindow *window)
+{
+  GdkWindowObject *private = (GdkWindowObject *) window;
+
+  g_return_val_if_fail (GDK_IS_WINDOW (window), FALSE);
+
+  if (GDK_WINDOW_DESTROYED (window))
+    return FALSE;
+
+  return private->support_multidevice;
+}
+
 static const guint type_masks[] = {
   GDK_SUBSTRUCTURE_MASK, /* GDK_DELETE                 = 0  */
   GDK_STRUCTURE_MASK, /* GDK_DESTROY                   = 1  */
@@ -9269,6 +9602,7 @@ send_crossing_event (GdkDisplay                 *display,
 		     GdkCrossingMode             mode,
 		     GdkNotifyType               notify_type,
 		     GdkWindow                  *subwindow,
+                     GdkDevice                  *device,
 		     gint                        toplevel_x,
 		     gint                        toplevel_y,
 		     GdkModifierType             mask,
@@ -9278,10 +9612,10 @@ send_crossing_event (GdkDisplay                 *display,
 {
   GdkEvent *event;
   guint32 window_event_mask, type_event_mask;
-  GdkPointerGrabInfo *grab;
-  GdkWindowImplIface *impl_iface;
+  GdkDeviceGrabInfo *grab;
+  gboolean block_event = FALSE;
 
-  grab = _gdk_display_has_pointer_grab (display, serial);
+  grab = _gdk_display_has_device_grab (display, device, serial);
 
   if (grab != NULL &&
       !grab->owner_events)
@@ -9295,21 +9629,40 @@ send_crossing_event (GdkDisplay                 *display,
     window_event_mask = window->event_mask;
 
   if (type == GDK_LEAVE_NOTIFY)
-    type_event_mask = GDK_LEAVE_NOTIFY_MASK;
-  else
-    type_event_mask = GDK_ENTER_NOTIFY_MASK;
+    {
+      type_event_mask = GDK_LEAVE_NOTIFY_MASK;
+      window->devices_inside = g_list_remove (window->devices_inside, device);
 
-  if (window->extension_events != 0)
+      if (!window->support_multidevice && window->devices_inside)
+        {
+          /* Block leave events unless it's the last pointer */
+          block_event = TRUE;
+        }
+    }
+  else
     {
-      impl_iface = GDK_WINDOW_IMPL_GET_IFACE (window->impl);
-      impl_iface->input_window_crossing ((GdkWindow *)window,
-					 type == GDK_ENTER_NOTIFY);
+      type_event_mask = GDK_ENTER_NOTIFY_MASK;
+
+      if (!window->support_multidevice && window->devices_inside)
+        {
+          /* Only emit enter events for the first device */
+          block_event = TRUE;
+        }
+
+      if (gdk_device_get_device_type (device) == GDK_DEVICE_TYPE_MASTER &&
+          device->mode != GDK_MODE_DISABLED &&
+          !g_list_find (window->devices_inside, device))
+        window->devices_inside = g_list_prepend (window->devices_inside, device);
     }
 
+  if (block_event)
+    return;
+
   if (window_event_mask & type_event_mask)
     {
       event = _gdk_make_event ((GdkWindow *)window, type, event_in_queue, TRUE);
       event->crossing.time = time_;
+      event->crossing.device = device;
       event->crossing.subwindow = subwindow;
       if (subwindow)
 	g_object_ref (subwindow);
@@ -9335,6 +9688,7 @@ void
 _gdk_synthesize_crossing_events (GdkDisplay                 *display,
 				 GdkWindow                  *src,
 				 GdkWindow                  *dest,
+                                 GdkDevice                  *device,
 				 GdkCrossingMode             mode,
 				 gint                        toplevel_x,
 				 gint                        toplevel_y,
@@ -9359,6 +9713,18 @@ _gdk_synthesize_crossing_events (GdkDisplay                 *display,
   if (a == b)
     return; /* No crossings generated between src and dest */
 
+  if (gdk_device_get_device_type (device) != GDK_DEVICE_TYPE_MASTER)
+    {
+      if (a && gdk_window_get_device_events (src, device) == 0)
+        a = NULL;
+
+      if (b && gdk_window_get_device_events (dest, device) == 0)
+        b = NULL;
+    }
+
+  if (!a && !b)
+    return;
+
   c = find_common_ancestor (a, b);
 
   non_linear |= (c != a) && (c != b);
@@ -9378,7 +9744,7 @@ _gdk_synthesize_crossing_events (GdkDisplay                 *display,
 			   a, GDK_LEAVE_NOTIFY,
 			   mode,
 			   notify_type,
-			   NULL,
+			   NULL, device,
 			   toplevel_x, toplevel_y,
 			   mask, time_,
 			   event_in_queue,
@@ -9400,6 +9766,7 @@ _gdk_synthesize_crossing_events (GdkDisplay                 *display,
 				   mode,
 				   notify_type,
 				   (GdkWindow *)last,
+                                   device,
 				   toplevel_x, toplevel_y,
 				   mask, time_,
 				   event_in_queue,
@@ -9446,6 +9813,7 @@ _gdk_synthesize_crossing_events (GdkDisplay                 *display,
 				   mode,
 				   notify_type,
 				   (GdkWindow *)next,
+                                   device,
 				   toplevel_x, toplevel_y,
 				   mask, time_,
 				   event_in_queue,
@@ -9467,6 +9835,7 @@ _gdk_synthesize_crossing_events (GdkDisplay                 *display,
 			   mode,
 			   notify_type,
 			   NULL,
+                           device,
 			   toplevel_x, toplevel_y,
 			   mask, time_,
 			   event_in_queue,
@@ -9481,14 +9850,18 @@ _gdk_synthesize_crossing_events (GdkDisplay                 *display,
 static GdkWindow *
 get_pointer_window (GdkDisplay *display,
 		    GdkWindow *event_window,
+                    GdkDevice *device,
 		    gdouble toplevel_x,
 		    gdouble toplevel_y,
 		    gulong serial)
 {
   GdkWindow *pointer_window;
-  GdkPointerGrabInfo *grab;
+  GdkDeviceGrabInfo *grab;
+  GdkPointerWindowInfo *pointer_info;
 
-  if (event_window == display->pointer_info.toplevel_under_pointer)
+  pointer_info = _gdk_display_get_pointer_info (display, device);
+
+  if (event_window == pointer_info->toplevel_under_pointer)
     pointer_window =
       _gdk_window_find_descendant_at (event_window,
 				      toplevel_x, toplevel_y,
@@ -9496,7 +9869,7 @@ get_pointer_window (GdkDisplay *display,
   else
     pointer_window = NULL;
 
-  grab = _gdk_display_has_pointer_grab (display, serial);
+  grab = _gdk_display_has_device_grab (display, device, serial);
   if (grab != NULL &&
       !grab->owner_events &&
       pointer_window != grab->window)
@@ -9507,21 +9880,27 @@ get_pointer_window (GdkDisplay *display,
 
 void
 _gdk_display_set_window_under_pointer (GdkDisplay *display,
-				       GdkWindow *window)
+                                       GdkDevice  *device,
+				       GdkWindow  *window)
 {
+  GdkPointerWindowInfo *device_info;
+
   /* We don't track this if all native, and it can cause issues
      with the update_cursor call below */
   if (_gdk_native_windows)
     return;
 
-  if (display->pointer_info.window_under_pointer)
-    g_object_unref (display->pointer_info.window_under_pointer);
-  display->pointer_info.window_under_pointer = window;
-  if (window)
-    g_object_ref (window);
+  device_info = _gdk_display_get_pointer_info (display, device);
+
+  if (device_info->window_under_pointer)
+    g_object_unref (device_info->window_under_pointer);
+  device_info->window_under_pointer = window;
 
   if (window)
-    update_cursor (display);
+    {
+      g_object_ref (window);
+      update_cursor (display, device);
+    }
 
   _gdk_display_enable_motion_hints (display);
 }
@@ -9558,8 +9937,11 @@ gdk_pointer_grab (GdkWindow *	  window,
 {
   GdkWindow *native;
   GdkDisplay *display;
-  GdkGrabStatus res;
+  GdkDeviceManager *device_manager;
+  GdkDevice *device;
+  GdkGrabStatus res = 0;
   gulong serial;
+  GList *devices, *dev;
 
   g_return_val_if_fail (window != NULL, 0);
   g_return_val_if_fail (GDK_IS_WINDOW (window), 0);
@@ -9599,24 +9981,123 @@ gdk_pointer_grab (GdkWindow *	  window,
   display = gdk_drawable_get_display (window);
 
   serial = _gdk_windowing_window_get_next_serial (display);
+  device_manager = gdk_display_get_device_manager (display);
+  devices = gdk_device_manager_get_devices (device_manager, GDK_DEVICE_TYPE_MASTER);
+
+  /* FIXME: Should this be generic to all backends? */
+  /* FIXME: What happens with extended devices? */
+  for (dev = devices; dev; dev = dev->next)
+    {
+      device = dev->data;
+
+      if (device->source != GDK_SOURCE_MOUSE)
+        continue;
+
+      res = _gdk_windowing_device_grab (device,
+                                        window,
+                                        native,
+                                        owner_events,
+                                        get_native_grab_event_mask (event_mask),
+                                        confine_to,
+                                        cursor,
+                                        time);
+
+      if (res == GDK_GRAB_SUCCESS)
+        _gdk_display_add_device_grab (display,
+                                      device,
+                                      window,
+                                      native,
+                                      GDK_OWNERSHIP_NONE,
+                                      owner_events,
+                                      event_mask,
+                                      serial,
+                                      time,
+                                      FALSE);
+    }
+
+  /* FIXME: handle errors when grabbing */
+
+  g_list_free (devices);
+
+  return res;
+}
+
+GdkGrabStatus
+gdk_keyboard_grab (GdkWindow *window,
+		   gboolean   owner_events,
+		   guint32    time)
+{
+  GdkWindow *native;
+  GdkDisplay *display;
+  GdkDeviceManager *device_manager;
+  GdkDevice *device;
+  GdkGrabStatus res = 0;
+  gulong serial;
+  GList *devices, *dev;
+
+  g_return_val_if_fail (GDK_IS_WINDOW (window), 0);
+
+  /* Non-viewable client side window => fail */
+  if (!_gdk_window_has_impl (window) &&
+      !gdk_window_is_viewable (window))
+    return GDK_GRAB_NOT_VIEWABLE;
+
+  if (_gdk_native_windows)
+    native = window;
+  else
+    native = gdk_window_get_toplevel (window);
+
+  while (gdk_window_is_offscreen ((GdkWindowObject *)native))
+    {
+      native = gdk_offscreen_window_get_embedder (native);
+
+      if (native == NULL ||
+	  (!_gdk_window_has_impl (native) &&
+	   !gdk_window_is_viewable (native)))
+	return GDK_GRAB_NOT_VIEWABLE;
+
+      native = gdk_window_get_toplevel (native);
+    }
+
+  display = gdk_drawable_get_display (window);
+
+  serial = _gdk_windowing_window_get_next_serial (display);
+  device_manager = gdk_display_get_device_manager (display);
+  devices = gdk_device_manager_get_devices (device_manager, GDK_DEVICE_TYPE_MASTER);
+
+  /* FIXME: Should this be generic to all backends? */
+  /* FIXME: What happens with extended devices? */
+  for (dev = devices; dev; dev = dev->next)
+    {
+      device = dev->data;
+
+      if (device->source != GDK_SOURCE_KEYBOARD)
+        continue;
 
-  res = _gdk_windowing_pointer_grab (window,
-				     native,
-				     owner_events,
-				     get_native_grab_event_mask (event_mask),
-				     confine_to,
-				     cursor,
-				     time);
-
-  if (res == GDK_GRAB_SUCCESS)
-    _gdk_display_add_pointer_grab (display,
-				   window,
-				   native,
-				   owner_events,
-				   event_mask,
-				   serial,
-				   time,
-				   FALSE);
+      res = _gdk_windowing_device_grab (device,
+                                        window,
+                                        native,
+                                        owner_events,
+                                        GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK,
+                                        NULL,
+                                        NULL,
+                                        time);
+
+      if (res == GDK_GRAB_SUCCESS)
+        _gdk_display_add_device_grab (display,
+                                      device,
+                                      window,
+                                      native,
+                                      GDK_OWNERSHIP_NONE,
+                                      owner_events, 0,
+                                      serial,
+                                      time,
+                                      FALSE);
+    }
+
+  /* FIXME: handle errors when grabbing */
+
+  g_list_free (devices);
 
   return res;
 }
@@ -9643,7 +10124,8 @@ do_synthesize_crossing_event (gpointer data)
   GdkDisplay *display;
   GdkWindow *changed_toplevel;
   GdkWindowObject *changed_toplevel_priv;
-  GdkWindow *new_window_under_pointer;
+  GHashTableIter iter;
+  gpointer key, value;
   gulong serial;
 
   changed_toplevel = data;
@@ -9656,30 +10138,39 @@ do_synthesize_crossing_event (gpointer data)
 
   display = gdk_drawable_get_display (changed_toplevel);
   serial = _gdk_windowing_window_get_next_serial (display);
+  g_hash_table_iter_init (&iter, display->pointers_info);
 
-  if (changed_toplevel == display->pointer_info.toplevel_under_pointer)
+  while (g_hash_table_iter_next (&iter, &key, &value))
     {
-      new_window_under_pointer =
-	get_pointer_window (display, changed_toplevel,
-			    display->pointer_info.toplevel_x,
-			    display->pointer_info.toplevel_y,
-			    serial);
-      if (new_window_under_pointer !=
-	  display->pointer_info.window_under_pointer)
-	{
-	  _gdk_synthesize_crossing_events (display,
-					   display->pointer_info.window_under_pointer,
-					   new_window_under_pointer,
-					   GDK_CROSSING_NORMAL,
-					   display->pointer_info.toplevel_x,
-					   display->pointer_info.toplevel_y,
-					   display->pointer_info.state,
-					   GDK_CURRENT_TIME,
-					   NULL,
-					   serial,
-					   FALSE);
-	  _gdk_display_set_window_under_pointer (display, new_window_under_pointer);
-	}
+      GdkWindow *new_window_under_pointer;
+      GdkPointerWindowInfo *pointer_info = value;
+      GdkDevice *device = key;
+
+      if (changed_toplevel == pointer_info->toplevel_under_pointer)
+        {
+          new_window_under_pointer =
+            get_pointer_window (display, changed_toplevel,
+                                device,
+                                pointer_info->toplevel_x,
+                                pointer_info->toplevel_y,
+                                serial);
+          if (new_window_under_pointer != pointer_info->window_under_pointer)
+            {
+              _gdk_synthesize_crossing_events (display,
+                                               pointer_info->window_under_pointer,
+                                               new_window_under_pointer,
+                                               device,
+                                               GDK_CROSSING_NORMAL,
+                                               pointer_info->toplevel_x,
+                                               pointer_info->toplevel_y,
+                                               pointer_info->state,
+                                               GDK_CURRENT_TIME,
+                                               NULL,
+                                               serial,
+                                               FALSE);
+              _gdk_display_set_window_under_pointer (display, device, new_window_under_pointer);
+            }
+        }
     }
 
   return FALSE;
@@ -9698,12 +10189,12 @@ _gdk_synthesize_crossing_events_for_geometry_change (GdkWindow *changed_window)
   display = gdk_drawable_get_display (changed_window);
 
   toplevel = get_event_toplevel (changed_window);
-  toplevel_priv = (GdkWindowObject *)toplevel;
+  toplevel_priv = (GdkWindowObject *) toplevel;
 
-  if (toplevel == display->pointer_info.toplevel_under_pointer &&
-      !toplevel_priv->synthesize_crossing_event_queued)
+  if (!toplevel_priv->synthesize_crossing_event_queued)
     {
       toplevel_priv->synthesize_crossing_event_queued = TRUE;
+
       g_idle_add_full (GDK_PRIORITY_EVENTS - 1,
 		       do_synthesize_crossing_event,
 		       g_object_ref (toplevel),
@@ -9714,6 +10205,7 @@ _gdk_synthesize_crossing_events_for_geometry_change (GdkWindow *changed_window)
 /* Don't use for crossing events */
 static GdkWindow *
 get_event_window (GdkDisplay                 *display,
+                  GdkDevice                  *device,
 		  GdkWindow                  *pointer_window,
 		  GdkEventType                type,
 		  GdkModifierType             mask,
@@ -9723,9 +10215,9 @@ get_event_window (GdkDisplay                 *display,
   guint evmask;
   GdkWindow *grab_window;
   GdkWindowObject *w;
-  GdkPointerGrabInfo *grab;
+  GdkDeviceGrabInfo *grab;
 
-  grab = _gdk_display_has_pointer_grab (display, serial);
+  grab = _gdk_display_has_device_grab (display, device, serial);
 
   if (grab != NULL && !grab->owner_events)
     {
@@ -9786,6 +10278,8 @@ proxy_pointer_event (GdkDisplay                 *display,
 {
   GdkWindow *toplevel_window, *event_window;
   GdkWindow *pointer_window;
+  GdkPointerWindowInfo *pointer_info;
+  GdkDevice *device;
   GdkEvent *event;
   guint state;
   gdouble toplevel_x, toplevel_y;
@@ -9796,6 +10290,8 @@ proxy_pointer_event (GdkDisplay                 *display,
   gdk_event_get_coords (source_event, &toplevel_x, &toplevel_y);
   gdk_event_get_state (source_event, &state);
   time_ = gdk_event_get_time (source_event);
+  device = gdk_event_get_device (source_event);
+  pointer_info = _gdk_display_get_pointer_info (display, device);
   toplevel_window = convert_native_coords_to_toplevel (event_window,
 						       toplevel_x, toplevel_y,
 						       &toplevel_x, &toplevel_y);
@@ -9826,8 +10322,9 @@ proxy_pointer_event (GdkDisplay                 *display,
       /* Send leave events from window under pointer to event window
 	 that will get the subwindow == NULL window */
       _gdk_synthesize_crossing_events (display,
-				       display->pointer_info.window_under_pointer,
+				       pointer_info->window_under_pointer,
 				       event_window,
+                                       device,
 				       source_event->crossing.mode,
 				       toplevel_x, toplevel_y,
 				       state, time_,
@@ -9843,16 +10340,17 @@ proxy_pointer_event (GdkDisplay                 *display,
 			   source_event->crossing.mode,
 			   source_event->crossing.detail,
 			   NULL,
-			   toplevel_x,   toplevel_y,
+                           device,
+			   toplevel_x, toplevel_y,
 			   state, time_,
 			   source_event,
 			   serial);
 
-      _gdk_display_set_window_under_pointer (display, NULL);
+      _gdk_display_set_window_under_pointer (display, device, NULL);
       return TRUE;
     }
 
-  pointer_window = get_pointer_window (display, toplevel_window,
+  pointer_window = get_pointer_window (display, toplevel_window, device,
 				       toplevel_x, toplevel_y, serial);
 
   if (((source_event->type == GDK_ENTER_NOTIFY &&
@@ -9872,7 +10370,8 @@ proxy_pointer_event (GdkDisplay                 *display,
 			   source_event->crossing.mode,
 			   source_event->crossing.detail,
 			   NULL,
-			   toplevel_x,   toplevel_y,
+                           device,
+			   toplevel_x, toplevel_y,
 			   state, time_,
 			   source_event,
 			   serial);
@@ -9881,30 +10380,32 @@ proxy_pointer_event (GdkDisplay                 *display,
       _gdk_synthesize_crossing_events (display,
 				       event_window,
 				       pointer_window,
+                                       device,
 				       source_event->crossing.mode,
 				       toplevel_x, toplevel_y,
 				       state, time_,
 				       source_event,
 				       serial, non_linear);
-      _gdk_display_set_window_under_pointer (display, pointer_window);
+      _gdk_display_set_window_under_pointer (display, device, pointer_window);
       return TRUE;
     }
 
-  if (display->pointer_info.window_under_pointer != pointer_window)
+  if (pointer_info->window_under_pointer != pointer_window)
     {
       /* Either a toplevel crossing notify that ended up inside a child window,
 	 or a motion notify that got into another child window  */
 
       /* Different than last time, send crossing events */
       _gdk_synthesize_crossing_events (display,
-				       display->pointer_info.window_under_pointer,
+				       pointer_info->window_under_pointer,
 				       pointer_window,
+                                       device,
 				       GDK_CROSSING_NORMAL,
 				       toplevel_x, toplevel_y,
 				       state, time_,
 				       source_event,
 				       serial, non_linear);
-      _gdk_display_set_window_under_pointer (display, pointer_window);
+      _gdk_display_set_window_under_pointer (display, device, pointer_window);
     }
   else if (source_event->type == GDK_MOTION_NOTIFY)
     {
@@ -9913,24 +10414,30 @@ proxy_pointer_event (GdkDisplay                 *display,
       gboolean is_hint;
 
       event_win = get_event_window (display,
-				    pointer_window,
-				    source_event->type,
-				    state,
-				    &evmask,
-				    serial);
+                                    device,
+                                    pointer_window,
+                                    source_event->type,
+                                    state,
+                                    &evmask,
+                                    serial);
+
+      if (event_win &&
+          gdk_device_get_device_type (device) != GDK_DEVICE_TYPE_MASTER &&
+          gdk_window_get_device_events (event_win, device) == 0)
+        return TRUE;
 
       is_hint = FALSE;
 
       if (event_win &&
 	  (evmask & GDK_POINTER_MOTION_HINT_MASK))
 	{
-	  if (display->pointer_info.motion_hint_serial != 0 &&
-	      serial < display->pointer_info.motion_hint_serial)
+	  if (display->motion_hint_serial != 0 &&
+	      serial < display->motion_hint_serial)
 	    event_win = NULL; /* Ignore event */
 	  else
 	    {
 	      is_hint = TRUE;
-	      display->pointer_info.motion_hint_serial = G_MAXULONG;
+	      display->motion_hint_serial = G_MAXULONG;
 	    }
 	}
 
@@ -9942,11 +10449,12 @@ proxy_pointer_event (GdkDisplay                 *display,
 					     toplevel_x, toplevel_y,
 					     &event->motion.x, &event->motion.y);
 	  event->motion.x_root = source_event->motion.x_root;
-	  event->motion.y_root = source_event->motion.y_root;;
+	  event->motion.y_root = source_event->motion.y_root;
 	  event->motion.state = state;
 	  event->motion.is_hint = is_hint;
-	  event->motion.device = NULL;
 	  event->motion.device = source_event->motion.device;
+          event->motion.axes = g_memdup (source_event->motion.axes,
+                                         sizeof (gdouble) * source_event->motion.device->num_axes);
 	}
     }
 
@@ -9976,12 +10484,14 @@ proxy_button_event (GdkEvent *source_event,
   gdouble toplevel_x, toplevel_y;
   GdkDisplay *display;
   GdkWindowObject *w;
+  GdkDevice *device;
 
   type = source_event->any.type;
   event_window = source_event->any.window;
   gdk_event_get_coords (source_event, &toplevel_x, &toplevel_y);
   gdk_event_get_state (source_event, &state);
   time_ = gdk_event_get_time (source_event);
+  device = gdk_event_get_device (source_event);
   display = gdk_drawable_get_display (source_event->any.window);
   toplevel_window = convert_native_coords_to_toplevel (event_window,
 						       toplevel_x, toplevel_y,
@@ -9989,7 +10499,7 @@ proxy_button_event (GdkEvent *source_event,
 
   if (type == GDK_BUTTON_PRESS &&
       !source_event->any.send_event &&
-      _gdk_display_has_pointer_grab (display, serial) == NULL)
+      _gdk_display_has_device_grab (display, device, serial) == NULL)
     {
       pointer_window =
 	_gdk_window_find_descendant_at (toplevel_window,
@@ -10008,22 +10518,25 @@ proxy_button_event (GdkEvent *source_event,
 	}
       pointer_window = (GdkWindow *)w;
 
-      _gdk_display_add_pointer_grab  (display,
-				      pointer_window,
-				      toplevel_window,
-				      FALSE,
-				      gdk_window_get_events (pointer_window),
-				      serial,
+      _gdk_display_add_device_grab  (display,
+                                     device,
+                                     pointer_window,
+                                     toplevel_window,
+                                     GDK_OWNERSHIP_NONE,
+                                     FALSE,
+                                     gdk_window_get_events (pointer_window),
+                                     serial,
 				      time_,
-				      TRUE);
-      _gdk_display_pointer_grab_update (display, serial);
+                                     TRUE);
+      _gdk_display_device_grab_update (display, device, serial);
     }
 
-  pointer_window = get_pointer_window (display, toplevel_window,
+  pointer_window = get_pointer_window (display, toplevel_window, device,
 				       toplevel_x, toplevel_y,
 				       serial);
 
   event_win = get_event_window (display,
+                                device,
 				pointer_window,
 				type, state,
 				NULL, serial);
@@ -10031,6 +10544,10 @@ proxy_button_event (GdkEvent *source_event,
   if (event_win == NULL || display->ignore_core_events)
     return TRUE;
 
+  if (gdk_device_get_device_type (device) != GDK_DEVICE_TYPE_MASTER &&
+      gdk_window_get_device_events (event_win, device) == 0)
+    return TRUE;
+
   event = _gdk_make_event (event_win, type, source_event, FALSE);
 
   switch (type)
@@ -10045,6 +10562,8 @@ proxy_button_event (GdkEvent *source_event,
       event->button.y_root = source_event->button.y_root;
       event->button.state = state;
       event->button.device = source_event->button.device;
+      event->button.axes = g_memdup (source_event->button.axes,
+                                     sizeof (gdouble) * source_event->button.device->num_axes);
 
       if (type == GDK_BUTTON_PRESS)
 	_gdk_event_button_generate (display, event);
@@ -10143,22 +10662,6 @@ gdk_window_print_tree (GdkWindow *window,
 
 #endif /* DEBUG_WINDOW_PRINTING */
 
-static gboolean
-is_input_event (GdkDisplay *display,
-		GdkEvent *event)
-{
-  GdkDevice *core_pointer;
-
-  core_pointer = gdk_display_get_core_pointer (display);
-  if ((event->type == GDK_MOTION_NOTIFY &&
-       event->motion.device != core_pointer) ||
-      ((event->type == GDK_BUTTON_PRESS ||
-	event->type == GDK_BUTTON_RELEASE) &&
-       event->button.device != core_pointer))
-    return TRUE;
-  return FALSE;
-}
-
 void
 _gdk_windowing_got_event (GdkDisplay *display,
 			  GList      *event_link,
@@ -10170,19 +10673,39 @@ _gdk_windowing_got_event (GdkDisplay *display,
   gdouble x, y;
   gboolean unlink_event;
   guint old_state, old_button;
-  GdkPointerGrabInfo *button_release_grab;
+  GdkDeviceGrabInfo *button_release_grab;
+  GdkPointerWindowInfo *pointer_info;
+  GdkDevice *device;
   gboolean is_toplevel;
 
   if (gdk_event_get_time (event) != GDK_CURRENT_TIME)
     display->last_event_time = gdk_event_get_time (event);
 
-  _gdk_display_pointer_grab_update (display,
-				    serial);
+  device = gdk_event_get_device (event);
+
+  if (device)
+    {
+      GdkInputMode mode;
+
+      g_object_get (device, "input-mode", &mode, NULL);
+      _gdk_display_device_grab_update (display, device, serial);
+
+      if (mode == GDK_MODE_DISABLED ||
+          !_gdk_display_check_grab_ownership (display, device, serial))
+        {
+          /* Device events are blocked by another
+           * device grab, or the device is disabled
+           */
+          unlink_event = TRUE;
+          goto out;
+        }
+    }
 
   event_window = event->any.window;
   if (!event_window)
     return;
 
+  pointer_info = _gdk_display_get_pointer_info (display, device);
   event_private = GDK_WINDOW_OBJECT (event_window);
 
 #ifdef DEBUG_WINDOW_PRINTING
@@ -10199,31 +10722,32 @@ _gdk_windowing_got_event (GdkDisplay *display,
     {
       if (event->type == GDK_BUTTON_PRESS &&
 	  !event->any.send_event &&
-	  _gdk_display_has_pointer_grab (display, serial) == NULL)
+	  _gdk_display_has_device_grab (display, device, serial) == NULL)
 	{
-	  _gdk_display_add_pointer_grab  (display,
-					  event_window,
-					  event_window,
-					  FALSE,
-					  gdk_window_get_events (event_window),
-					  serial,
-					  gdk_event_get_time (event),
-					  TRUE);
-	  _gdk_display_pointer_grab_update (display,
-					    serial);
+	  _gdk_display_add_device_grab  (display,
+                                         device,
+                                         event_window,
+                                         event_window,
+                                         GDK_OWNERSHIP_NONE,
+                                         FALSE,
+                                         gdk_window_get_events (event_window),
+                                         serial,
+                                         gdk_event_get_time (event),
+                                         TRUE);
+	  _gdk_display_device_grab_update (display, device, serial);
 	}
       if (event->type == GDK_BUTTON_RELEASE &&
 	  !event->any.send_event)
 	{
 	  button_release_grab =
-	    _gdk_display_has_pointer_grab (display, serial);
+            _gdk_display_has_device_grab (display, device, serial);
 	  if (button_release_grab &&
 	      button_release_grab->implicit &&
 	      (event->button.state & GDK_ANY_BUTTON_MASK & ~(GDK_BUTTON1_MASK << (event->button.button - 1))) == 0)
 	    {
 	      button_release_grab->serial_end = serial;
 	      button_release_grab->implicit_ungrab = FALSE;
-	      _gdk_display_pointer_grab_update (display, serial);
+	      _gdk_display_device_grab_update (display, device, serial);
 	    }
 	}
 
@@ -10241,9 +10765,6 @@ _gdk_windowing_got_event (GdkDisplay *display,
       return;
     }
 
-  if (is_input_event (display, event))
-    return;
-
   if (!(is_button_type (event->type) ||
 	is_motion_type (event->type)) ||
       event_private->window_type == GDK_WINDOW_ROOT)
@@ -10255,7 +10776,7 @@ _gdk_windowing_got_event (GdkDisplay *display,
        event->type == GDK_LEAVE_NOTIFY) &&
       (event->crossing.mode == GDK_CROSSING_GRAB ||
        event->crossing.mode == GDK_CROSSING_UNGRAB) &&
-      (_gdk_display_has_pointer_grab (display, serial) ||
+      (_gdk_display_has_device_grab (display, device, serial) ||
        event->crossing.detail == GDK_NOTIFY_INFERIOR))
     {
       /* We synthesize all crossing events due to grabs ourselves,
@@ -10281,9 +10802,9 @@ _gdk_windowing_got_event (GdkDisplay *display,
 	  event->type == GDK_ENTER_NOTIFY &&
 	  event->crossing.mode == GDK_CROSSING_UNGRAB)
 	{
-	  if (display->pointer_info.toplevel_under_pointer)
-	    g_object_unref (display->pointer_info.toplevel_under_pointer);
-	  display->pointer_info.toplevel_under_pointer = g_object_ref (event_window);
+	  if (pointer_info->toplevel_under_pointer)
+	    g_object_unref (pointer_info->toplevel_under_pointer);
+	  pointer_info->toplevel_under_pointer = g_object_ref (event_window);
 	}
 
       unlink_event = TRUE;
@@ -10296,35 +10817,35 @@ _gdk_windowing_got_event (GdkDisplay *display,
       if (event->type == GDK_ENTER_NOTIFY &&
 	  event->crossing.detail != GDK_NOTIFY_INFERIOR)
 	{
-	  if (display->pointer_info.toplevel_under_pointer)
-	    g_object_unref (display->pointer_info.toplevel_under_pointer);
-	  display->pointer_info.toplevel_under_pointer = g_object_ref (event_window);
+	  if (pointer_info->toplevel_under_pointer)
+	    g_object_unref (pointer_info->toplevel_under_pointer);
+	  pointer_info->toplevel_under_pointer = g_object_ref (event_window);
 	}
       else if (event->type == GDK_LEAVE_NOTIFY &&
 	       event->crossing.detail != GDK_NOTIFY_INFERIOR &&
-	       display->pointer_info.toplevel_under_pointer == event_window)
+	       pointer_info->toplevel_under_pointer == event_window)
 	{
-	  if (display->pointer_info.toplevel_under_pointer)
-	    g_object_unref (display->pointer_info.toplevel_under_pointer);
-	  display->pointer_info.toplevel_under_pointer = NULL;
+	  if (pointer_info->toplevel_under_pointer)
+	    g_object_unref (pointer_info->toplevel_under_pointer);
+	  pointer_info->toplevel_under_pointer = NULL;
 	}
     }
 
   /* Store last pointer window and position/state */
-  old_state = display->pointer_info.state;
-  old_button = display->pointer_info.button;
+  old_state = pointer_info->state;
+  old_button = pointer_info->button;
 
   gdk_event_get_coords (event, &x, &y);
   convert_native_coords_to_toplevel (event_window, x, y,  &x, &y);
-  display->pointer_info.toplevel_x = x;
-  display->pointer_info.toplevel_y = y;
-  gdk_event_get_state (event, &display->pointer_info.state);
+  pointer_info->toplevel_x = x;
+  pointer_info->toplevel_y = y;
+  gdk_event_get_state (event, &pointer_info->state);
   if (event->type == GDK_BUTTON_PRESS ||
       event->type == GDK_BUTTON_RELEASE)
-    display->pointer_info.button = event->button.button;
+    pointer_info->button = event->button.button;
 
-  if (display->pointer_info.state != old_state ||
-      display->pointer_info.button != old_button)
+  if (pointer_info->state != old_state ||
+      pointer_info->button != old_button)
     _gdk_display_enable_motion_hints (display);
 
   unlink_event = FALSE;
@@ -10340,14 +10861,14 @@ _gdk_windowing_got_event (GdkDisplay *display,
       !event->any.send_event)
     {
       button_release_grab =
-	_gdk_display_has_pointer_grab (display, serial);
+	_gdk_display_has_device_grab (display, device, serial);
       if (button_release_grab &&
 	  button_release_grab->implicit &&
 	  (event->button.state & GDK_ANY_BUTTON_MASK & ~(GDK_BUTTON1_MASK << (event->button.button - 1))) == 0)
 	{
 	  button_release_grab->serial_end = serial;
 	  button_release_grab->implicit_ungrab = FALSE;
-	  _gdk_display_pointer_grab_update (display, serial);
+	  _gdk_display_device_grab_update (display, device, serial);
 	}
     }
 
@@ -10370,9 +10891,10 @@ get_extension_event_window (GdkDisplay                 *display,
   guint evmask;
   GdkWindow *grab_window;
   GdkWindowObject *w;
-  GdkPointerGrabInfo *grab;
+  GdkDeviceGrabInfo *grab;
 
-  grab = _gdk_display_has_pointer_grab (display, serial);
+  /* FIXME: which device? */
+  grab = _gdk_display_has_device_grab (display, display->core_pointer, serial);
 
   if (grab != NULL && !grab->owner_events)
     {
@@ -10431,7 +10953,8 @@ _gdk_window_get_input_window_for_event (GdkWindow *native_window,
   toplevel_window = convert_native_coords_to_toplevel (native_window,
 						       toplevel_x, toplevel_y,
 						       &toplevel_x, &toplevel_y);
-  pointer_window = get_pointer_window (display, toplevel_window,
+  /* FIXME: which device? */
+  pointer_window = get_pointer_window (display, toplevel_window, NULL,
 				       toplevel_x, toplevel_y, serial);
   event_win = get_extension_event_window (display,
 					  pointer_window,
diff --git a/gdk/gdkwindow.h b/gdk/gdkwindow.h
index 1a42ed5..0a84ab5 100644
--- a/gdk/gdkwindow.h
+++ b/gdk/gdkwindow.h
@@ -311,6 +311,7 @@ struct _GdkWindowObject
   guint accept_focus : 1;
   guint focus_on_map : 1;
   guint shaped : 1;
+  guint support_multidevice : 1;
   
   GdkEventMask event_mask;
 
@@ -335,8 +336,11 @@ GdkWindow*    gdk_window_new                   (GdkWindow     *parent,
 void          gdk_window_destroy               (GdkWindow     *window);
 GdkWindowType gdk_window_get_window_type       (GdkWindow     *window);
 gboolean      gdk_window_is_destroyed          (GdkWindow     *window);
+
+#ifndef GDK_MULTIDEVICE_SAFE
 GdkWindow*    gdk_window_at_pointer            (gint          *win_x,
                                                 gint          *win_y);
+#endif /* GDK_MULTIDEVICE_SAFE */
 void          gdk_window_show                  (GdkWindow     *window);
 void          gdk_window_hide                  (GdkWindow     *window);
 void          gdk_window_withdraw              (GdkWindow     *window);
@@ -527,6 +531,11 @@ void	      gdk_window_set_back_pixmap (GdkWindow	  *window,
 void	      gdk_window_set_cursor	 (GdkWindow	  *window,
 					  GdkCursor	  *cursor);
 GdkCursor    *gdk_window_get_cursor      (GdkWindow       *window);
+void	      gdk_window_set_device_cursor (GdkWindow	  *window,
+                                            GdkDevice     *device,
+                                            GdkCursor	  *cursor);
+GdkCursor    *gdk_window_get_device_cursor (GdkWindow     *window,
+                                            GdkDevice     *device);
 void	      gdk_window_get_user_data	 (GdkWindow	  *window,
 					  gpointer	  *data);
 void	      gdk_window_get_geometry	 (GdkWindow	  *window,
@@ -559,10 +568,18 @@ void	      gdk_window_get_root_origin (GdkWindow	  *window,
 					  gint		  *y);
 void          gdk_window_get_frame_extents (GdkWindow     *window,
                                             GdkRectangle  *rect);
+
+#ifndef GDK_MULTIDEVICE_SAFE
 GdkWindow*    gdk_window_get_pointer	 (GdkWindow	  *window,
 					  gint		  *x,
 					  gint		  *y,
 					  GdkModifierType *mask);
+#endif /* GDK_MULTIDEVICE_SAFE */
+GdkWindow *   gdk_window_get_device_position (GdkWindow       *window,
+                                              GdkDevice       *device,
+                                              gint            *x,
+                                              gint            *y,
+                                              GdkModifierType *mask);
 GdkWindow *   gdk_window_get_parent      (GdkWindow       *window);
 GdkWindow *   gdk_window_get_toplevel    (GdkWindow       *window);
 
@@ -571,6 +588,9 @@ GList *       gdk_window_peek_children   (GdkWindow       *window);
 GdkEventMask  gdk_window_get_events	 (GdkWindow	  *window);
 void	      gdk_window_set_events	 (GdkWindow	  *window,
 					  GdkEventMask	   event_mask);
+void          gdk_window_set_device_events (GdkWindow    *window,
+                                            GdkDevice    *device,
+                                            GdkEventMask  event_mask);
 
 void          gdk_window_set_icon_list   (GdkWindow       *window,
 					  GList           *pixbufs);
@@ -663,9 +683,9 @@ void gdk_window_get_internal_paint_info (GdkWindow    *window,
 void gdk_window_enable_synchronized_configure (GdkWindow *window);
 void gdk_window_configure_finished            (GdkWindow *window);
 
-#ifndef GDK_MULTIHEAD_SAFE
-GdkPointerHooks *gdk_set_pointer_hooks (const GdkPointerHooks *new_hooks);   
-#endif /* GDK_MULTIHEAD_SAFE */
+#if !defined (GDK_MULTIHEAD_SAFE) && !defined (GDK_MULTIDEVICE_SAFE)
+GdkPointerHooks *gdk_set_pointer_hooks (const GdkPointerHooks *new_hooks);
+#endif /* !GDK_MULTIHEAD_SAFE && !GDK_MULTIDEVICE_SAFE */
 
 GdkWindow *gdk_get_default_root_window (void);
 
@@ -686,6 +706,11 @@ void       gdk_window_redirect_to_drawable   (GdkWindow     *window,
                                               gint           height);
 void       gdk_window_remove_redirection     (GdkWindow     *window);
 
+/* Multidevice support */
+void       gdk_window_set_support_multidevice (GdkWindow *window,
+                                               gboolean   support_multidevice);
+gboolean   gdk_window_get_support_multidevice (GdkWindow *window);
+
 #ifndef GDK_DISABLE_DEPRECATED
 #define GDK_ROOT_PARENT()             (gdk_get_default_root_window ())
 #define gdk_window_get_size            gdk_drawable_get_size
diff --git a/gdk/gdkwindowimpl.h b/gdk/gdkwindowimpl.h
index 00f2f1b..c418a12 100644
--- a/gdk/gdkwindowimpl.h
+++ b/gdk/gdkwindowimpl.h
@@ -77,8 +77,9 @@ struct _GdkWindowImplIface
   void         (* clear_region)         (GdkWindow       *window,
 					 GdkRegion       *region,
 					 gboolean         send_expose);
-  
-  void         (* set_cursor)           (GdkWindow       *window,
+
+  void         (* set_device_cursor)    (GdkWindow       *window,
+                                         GdkDevice       *device,
                                          GdkCursor       *cursor);
 
   void         (* get_geometry)         (GdkWindow       *window,
@@ -95,10 +96,11 @@ struct _GdkWindowImplIface
   gint         (* get_deskrelative_origin) (GdkWindow       *window,
                                          gint            *x,
                                          gint            *y);
-  gboolean     (* get_pointer)          (GdkWindow       *window,
+  gboolean     (* get_device_state)     (GdkWindow       *window,
+                                         GdkDevice       *device,
                                          gint            *x,
                                          gint            *y,
-					 GdkModifierType  *mask);
+                                         GdkModifierType *mask);
 
   void         (* shape_combine_region) (GdkWindow       *window,
                                          const GdkRegion *shape_region,
@@ -142,10 +144,6 @@ struct _GdkWindowImplIface
   void         (* destroy)              (GdkWindow       *window,
 					 gboolean         recursing,
 					 gboolean         foreign_destroy);
-
-  void         (* input_window_destroy) (GdkWindow       *window);
-  void         (* input_window_crossing)(GdkWindow       *window,
-					 gboolean         enter);
 };
 
 /* Interface Functions */
diff --git a/gdk/x11/Makefile.am b/gdk/x11/Makefile.am
index 5d41ec8..0bfc0a5 100644
--- a/gdk/x11/Makefile.am
+++ b/gdk/x11/Makefile.am
@@ -23,19 +23,25 @@ libgdk_x11_la_SOURCES =    	\
 	gdkasync.h		\
 	gdkcolor-x11.c	   	\
 	gdkcursor-x11.c	   	\
+	gdkdevice-core.c	\
+	gdkdevicemanager-core.c \
+	gdkdevicemanager-x11.c	\
 	gdkdisplay-x11.c	\
 	gdkdisplay-x11.h	\
 	gdkdnd-x11.c	   	\
 	gdkdrawable-x11.c  	\
 	gdkdrawable-x11.h	\
-	gdkevents-x11.c	   	\
+	gdkeventsource.c	\
+	gdkeventsource.h	\
+	gdkeventtranslator.c	\
+	gdkeventtranslator.h	\
 	gdkfont-x11.c	   	\
 	gdkgc-x11.c 	   	\
 	gdkgeometry-x11.c  	\
 	gdkglobals-x11.c   	\
 	gdkim-x11.c	   	\
 	gdkimage-x11.c	   	\
-	gdkinput.c	   	\
+	gdkinput.c		\
 	gdkkeys-x11.c		\
 	gdkmain-x11.c	   	\
 	gdkpixmap-x11.c	   	\
@@ -53,16 +59,17 @@ libgdk_x11_la_SOURCES =    	\
 	gdkxid.c	   	\
 	gdkx.h		   	\
 	gdkprivate-x11.h   	\
-	gdkinputprivate.h  	\
 	xsettings-client.h	\
 	xsettings-client.c	\
 	xsettings-common.h	\
 	xsettings-common.c
 
-if XINPUT_XFREE
-libgdk_x11_la_SOURCES += gdkinput-x11.c gdkinput-xfree.c
+if XINPUT_2
+libgdk_x11_la_SOURCES += gdkdevicemanager-xi2.c gdkdevice-xi2.c
 else
-libgdk_x11_la_SOURCES += gdkinput-none.c
+if XINPUT_XFREE
+libgdk_x11_la_SOURCES += gdkdevicemanager-xi.c gdkdevice-xi.c
+endif
 endif
 
 
diff --git a/gdk/x11/gdkdevice-core.c b/gdk/x11/gdkdevice-core.c
new file mode 100644
index 0000000..56dc148
--- /dev/null
+++ b/gdk/x11/gdkdevice-core.c
@@ -0,0 +1,496 @@
+/* GDK - The GIMP Drawing Kit
+ * Copyright (C) 2009 Carlos Garnacho <carlosg@gnome.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * 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.
+ */
+
+#include <gdk/gdkwindow.h>
+#include "gdkdevice-core.h"
+#include "gdkprivate-x11.h"
+#include "gdkx.h"
+
+static gboolean gdk_device_core_get_history (GdkDevice      *device,
+                                             GdkWindow      *window,
+                                             guint32         start,
+                                             guint32         stop,
+                                             GdkTimeCoord ***events,
+                                             guint          *n_events);
+static void gdk_device_core_get_state (GdkDevice       *device,
+                                       GdkWindow       *window,
+                                       gdouble         *axes,
+                                       GdkModifierType *mask);
+static void gdk_device_core_set_window_cursor (GdkDevice *device,
+                                               GdkWindow *window,
+                                               GdkCursor *cursor);
+static void gdk_device_core_warp (GdkDevice *device,
+                                  GdkScreen *screen,
+                                  gint       x,
+                                  gint       y);
+static gboolean gdk_device_core_query_state (GdkDevice        *device,
+                                             GdkWindow        *window,
+                                             GdkWindow       **root_window,
+                                             GdkWindow       **child_window,
+                                             gint             *root_x,
+                                             gint             *root_y,
+                                             gint             *win_x,
+                                             gint             *win_y,
+                                             GdkModifierType  *mask);
+static GdkGrabStatus gdk_device_core_grab   (GdkDevice     *device,
+                                             GdkWindow     *window,
+                                             gboolean       owner_events,
+                                             GdkEventMask   event_mask,
+                                             GdkWindow     *confine_to,
+                                             GdkCursor     *cursor,
+                                             guint32        time_);
+static void          gdk_device_core_ungrab (GdkDevice     *device,
+                                             guint32        time_);
+static GdkWindow * gdk_device_core_window_at_position (GdkDevice       *device,
+                                                       gint            *win_x,
+                                                       gint            *win_y,
+                                                       GdkModifierType *mask,
+                                                       gboolean         get_toplevel);
+static void      gdk_device_core_select_window_events (GdkDevice       *device,
+                                                       GdkWindow       *window,
+                                                       GdkEventMask     event_mask);
+
+
+G_DEFINE_TYPE (GdkDeviceCore, gdk_device_core, GDK_TYPE_DEVICE)
+
+static void
+gdk_device_core_class_init (GdkDeviceCoreClass *klass)
+{
+  GdkDeviceClass *device_class = GDK_DEVICE_CLASS (klass);
+
+  device_class->get_history = gdk_device_core_get_history;
+  device_class->get_state = gdk_device_core_get_state;
+  device_class->set_window_cursor = gdk_device_core_set_window_cursor;
+  device_class->warp = gdk_device_core_warp;
+  device_class->query_state = gdk_device_core_query_state;
+  device_class->grab = gdk_device_core_grab;
+  device_class->ungrab = gdk_device_core_ungrab;
+  device_class->window_at_position = gdk_device_core_window_at_position;
+  device_class->select_window_events = gdk_device_core_select_window_events;
+}
+
+static void
+gdk_device_core_init (GdkDeviceCore *device_core)
+{
+  GdkDevice *device;
+
+  device = GDK_DEVICE (device_core);
+
+  _gdk_device_add_axis (device, GDK_NONE, GDK_AXIS_X, 0, 0, 1);
+  _gdk_device_add_axis (device, GDK_NONE, GDK_AXIS_Y, 0, 0, 1);
+}
+
+static gboolean
+impl_coord_in_window (GdkWindow *window,
+		      int        impl_x,
+		      int        impl_y)
+{
+  GdkWindowObject *priv = (GdkWindowObject *) window;
+
+  if (impl_x < priv->abs_x ||
+      impl_x > priv->abs_x + priv->width)
+    return FALSE;
+
+  if (impl_y < priv->abs_y ||
+      impl_y > priv->abs_y + priv->height)
+    return FALSE;
+
+  return TRUE;
+}
+
+static gboolean
+gdk_device_core_get_history (GdkDevice      *device,
+                             GdkWindow      *window,
+                             guint32         start,
+                             guint32         stop,
+                             GdkTimeCoord ***events,
+                             guint          *n_events)
+{
+  GdkWindowObject *priv;
+  XTimeCoord *xcoords;
+  GdkTimeCoord **coords;
+  GdkWindow *impl_window;
+  int tmp_n_events;
+  int i, j;
+
+  impl_window = _gdk_window_get_impl_window (window);
+  xcoords = XGetMotionEvents (GDK_DRAWABLE_XDISPLAY (window),
+                              GDK_DRAWABLE_XID (impl_window),
+                              start, stop, &tmp_n_events);
+  if (!xcoords)
+    return FALSE;
+
+  priv = (GdkWindowObject *) window;
+  coords = _gdk_device_allocate_history (device, tmp_n_events);
+
+  for (i = 0, j = 0; i < tmp_n_events; i++)
+    {
+      if (impl_coord_in_window (window, xcoords[i].x, xcoords[i].y))
+        {
+          coords[j]->time = xcoords[i].time;
+          coords[j]->axes[0] = xcoords[i].x - priv->abs_x;
+          coords[j]->axes[1] = xcoords[i].y - priv->abs_y;
+          j++;
+        }
+    }
+
+  XFree (xcoords);
+
+  /* free the events we allocated too much */
+  for (i = j; i < tmp_n_events; i++)
+    {
+      g_free (coords[i]);
+      coords[i] = NULL;
+    }
+
+  tmp_n_events = j;
+
+  if (tmp_n_events == 0)
+    {
+      gdk_device_free_history (coords, tmp_n_events);
+      return FALSE;
+    }
+
+  if (n_events)
+    *n_events = tmp_n_events;
+
+  if (events)
+    *events = coords;
+  else if (coords)
+    gdk_device_free_history (coords, tmp_n_events);
+
+  return TRUE;
+}
+
+static void
+gdk_device_core_get_state (GdkDevice       *device,
+                           GdkWindow       *window,
+                           gdouble         *axes,
+                           GdkModifierType *mask)
+{
+  gint x_int, y_int;
+
+  gdk_window_get_pointer (window, &x_int, &y_int, mask);
+
+  if (axes)
+    {
+      axes[0] = x_int;
+      axes[1] = y_int;
+    }
+}
+
+static void
+gdk_device_core_set_window_cursor (GdkDevice *device,
+                                   GdkWindow *window,
+                                   GdkCursor *cursor)
+{
+  GdkCursorPrivate *cursor_private;
+  Cursor xcursor;
+
+  cursor_private = (GdkCursorPrivate*) cursor;
+
+  if (!cursor)
+    xcursor = None;
+  else
+    xcursor = cursor_private->xcursor;
+
+  XDefineCursor (GDK_WINDOW_XDISPLAY (window),
+                 GDK_WINDOW_XID (window),
+                 xcursor);
+}
+
+static void
+gdk_device_core_warp (GdkDevice *device,
+                      GdkScreen *screen,
+                      gint       x,
+                      gint       y)
+{
+  Display *xdisplay;
+  Window dest;
+
+  xdisplay = GDK_DISPLAY_XDISPLAY (gdk_device_get_display (device));
+  dest = GDK_WINDOW_XWINDOW (gdk_screen_get_root_window (screen));
+
+  XWarpPointer (xdisplay, None, dest, 0, 0, 0, 0, x, y);
+}
+
+static gboolean
+gdk_device_core_query_state (GdkDevice        *device,
+                             GdkWindow        *window,
+                             GdkWindow       **root_window,
+                             GdkWindow       **child_window,
+                             gint             *root_x,
+                             gint             *root_y,
+                             gint             *win_x,
+                             gint             *win_y,
+                             GdkModifierType  *mask)
+{
+  GdkDisplay *display;
+  Window xroot_window, xchild_window;
+  int xroot_x, xroot_y, xwin_x, xwin_y;
+  unsigned int xmask;
+
+  display = gdk_drawable_get_display (window);
+
+  if (!XQueryPointer (GDK_WINDOW_XDISPLAY (window),
+                      GDK_WINDOW_XID (window),
+                      &xroot_window,
+                      &xchild_window,
+                      &xroot_x,
+                      &xroot_y,
+                      &xwin_x,
+                      &xwin_y,
+                      &xmask))
+    {
+      return FALSE;
+    }
+
+  if (root_window)
+    *root_window = gdk_window_lookup_for_display (display, xroot_window);
+
+  if (child_window)
+    *child_window = gdk_window_lookup_for_display (display, xchild_window);
+
+  if (root_x)
+    *root_x = xroot_x;
+
+  if (root_y)
+    *root_y = xroot_y;
+
+  if (win_x)
+    *win_x = xwin_x;
+
+  if (win_y)
+    *win_y = xwin_y;
+
+  if (mask)
+    *mask = xmask;
+
+  return TRUE;
+}
+
+static GdkGrabStatus
+gdk_device_core_grab (GdkDevice    *device,
+                      GdkWindow    *window,
+                      gboolean      owner_events,
+                      GdkEventMask  event_mask,
+                      GdkWindow    *confine_to,
+                      GdkCursor    *cursor,
+                      guint32       time_)
+{
+  GdkDisplay *display;
+  Window xwindow, xconfine_to;
+  int status;
+
+  display = gdk_device_get_display (device);
+
+  xwindow = GDK_WINDOW_XID (window);
+
+  if (confine_to)
+    confine_to = _gdk_window_get_impl_window (confine_to);
+
+  if (!confine_to || GDK_WINDOW_DESTROYED (confine_to))
+    xconfine_to = None;
+  else
+    xconfine_to = GDK_WINDOW_XID (confine_to);
+
+  if (device->source == GDK_SOURCE_KEYBOARD)
+    {
+      /* Device is a keyboard */
+      status = XGrabKeyboard (GDK_DISPLAY_XDISPLAY (display),
+                              xwindow,
+                              owner_events,
+                              GrabModeAsync, GrabModeAsync,
+                              time_);
+    }
+  else
+    {
+      Cursor xcursor;
+      guint xevent_mask;
+      gint i;
+
+      /* Device is a pointer */
+      if (!cursor)
+        xcursor = None;
+      else
+        {
+          _gdk_x11_cursor_update_theme (cursor);
+          xcursor = ((GdkCursorPrivate *) cursor)->xcursor;
+        }
+
+      xevent_mask = 0;
+
+      for (i = 0; i < _gdk_nenvent_masks; i++)
+        {
+          if (event_mask & (1 << (i + 1)))
+            xevent_mask |= _gdk_event_mask_table[i];
+        }
+
+      /* We don't want to set a native motion hint mask, as we're emulating motion
+       * hints. If we set a native one we just wouldn't get any events.
+       */
+      xevent_mask &= ~PointerMotionHintMask;
+
+      status = XGrabPointer (GDK_DISPLAY_XDISPLAY (display),
+                             xwindow,
+                             owner_events,
+                             xevent_mask,
+                             GrabModeAsync, GrabModeAsync,
+                             xconfine_to,
+                             xcursor,
+                             time_);
+    }
+
+  return gdk_x11_convert_grab_status (status);
+}
+
+static void
+gdk_device_core_ungrab (GdkDevice *device,
+                        guint32    time_)
+{
+  GdkDisplay *display;
+
+  display = gdk_device_get_display (device);
+
+  if (device->source == GDK_SOURCE_KEYBOARD)
+    XUngrabKeyboard (GDK_DISPLAY_XDISPLAY (display), time_);
+  else
+    XUngrabPointer (GDK_DISPLAY_XDISPLAY (display), time_);
+}
+
+static GdkWindow *
+gdk_device_core_window_at_position (GdkDevice       *device,
+                                    gint            *win_x,
+                                    gint            *win_y,
+                                    GdkModifierType *mask,
+                                    gboolean         get_toplevel)
+{
+  GdkDisplay *display;
+  GdkScreen *screen;
+  Display *xdisplay;
+  GdkWindow *window;
+  Window xwindow, root, child, last;
+  int xroot_x, xroot_y, xwin_x, xwin_y;
+  unsigned int xmask;
+
+  last = None;
+  display = gdk_device_get_display (device);
+  screen = gdk_display_get_default_screen (display);
+
+  /* This function really only works if the mouse pointer is held still
+   * during its operation. If it moves from one leaf window to another
+   * than we'll end up with inaccurate values for win_x, win_y
+   * and the result.
+   */
+  gdk_x11_display_grab (display);
+
+  xdisplay = GDK_SCREEN_XDISPLAY (screen);
+  xwindow = GDK_SCREEN_XROOTWIN (screen);
+
+  XQueryPointer (xdisplay, xwindow,
+                 &root, &child,
+                 &xroot_x, &xroot_y,
+                 &xwin_x, &xwin_y,
+                 &xmask);
+
+  if (root == xwindow)
+    xwindow = child;
+  else
+    xwindow = root;
+
+  while (xwindow)
+    {
+      last = xwindow;
+      XQueryPointer (xdisplay, xwindow,
+                     &root, &xwindow,
+                     &xroot_x, &xroot_y,
+                     &xwin_x, &xwin_y,
+                     &xmask);
+
+      if (get_toplevel &&
+          (window = gdk_window_lookup_for_display (display, last)) != NULL &&
+          GDK_WINDOW_TYPE (window) != GDK_WINDOW_FOREIGN)
+        break;
+    }
+
+  gdk_x11_display_ungrab (display);
+
+  window = gdk_window_lookup_for_display (display, last);
+
+  if (win_x)
+    *win_x = (window) ? xwin_x : -1;
+
+  if (win_y)
+    *win_y = (window) ? xwin_y : -1;
+
+  if (mask)
+    *mask = xmask;
+
+  return window;
+}
+
+static void
+gdk_device_core_select_window_events (GdkDevice    *device,
+                                      GdkWindow    *window,
+                                      GdkEventMask  event_mask)
+{
+  GdkEventMask filter_mask, window_mask;
+  guint xmask = 0;
+  gint i;
+
+  window_mask = gdk_window_get_events (window);
+  filter_mask = (GDK_POINTER_MOTION_MASK &
+                 GDK_POINTER_MOTION_HINT_MASK &
+                 GDK_BUTTON_MOTION_MASK &
+                 GDK_BUTTON1_MOTION_MASK &
+                 GDK_BUTTON2_MOTION_MASK &
+                 GDK_BUTTON3_MOTION_MASK &
+                 GDK_BUTTON_PRESS_MASK &
+                 GDK_BUTTON_RELEASE_MASK &
+                 GDK_KEY_PRESS_MASK &
+                 GDK_KEY_RELEASE_MASK &
+                 GDK_ENTER_NOTIFY_MASK &
+                 GDK_LEAVE_NOTIFY_MASK &
+                 GDK_FOCUS_CHANGE_MASK &
+                 GDK_PROXIMITY_IN_MASK &
+                 GDK_PROXIMITY_OUT_MASK &
+                 GDK_SCROLL_MASK);
+
+  /* Filter out non-device events */
+  event_mask &= filter_mask;
+
+  /* Unset device events on window mask */
+  window_mask &= ~(filter_mask);
+
+  /* Combine masks */
+  event_mask |= window_mask;
+
+  for (i = 0; i < _gdk_nenvent_masks; i++)
+    {
+      if (event_mask & (1 << (i + 1)))
+        xmask |= _gdk_event_mask_table[i];
+    }
+
+  if (GDK_WINDOW_XID (window) != GDK_WINDOW_XROOTWIN (window))
+    xmask |= StructureNotifyMask | PropertyChangeMask;
+
+  XSelectInput (GDK_WINDOW_XDISPLAY (window),
+                GDK_WINDOW_XWINDOW (window),
+                xmask);
+}
diff --git a/gdk/x11/gdkdevice-core.h b/gdk/x11/gdkdevice-core.h
new file mode 100644
index 0000000..8f53d6f
--- /dev/null
+++ b/gdk/x11/gdkdevice-core.h
@@ -0,0 +1,51 @@
+/* GDK - The GIMP Drawing Kit
+ * Copyright (C) 2009 Carlos Garnacho <carlosg@gnome.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * 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.
+ */
+
+#ifndef __GDK_DEVICE_CORE_H__
+#define __GDK_DEVICE_CORE_H__
+
+#include <gdk/gdkdeviceprivate.h>
+
+G_BEGIN_DECLS
+
+#define GDK_TYPE_DEVICE_CORE         (gdk_device_core_get_type ())
+#define GDK_DEVICE_CORE(o)           (G_TYPE_CHECK_INSTANCE_CAST ((o), GDK_TYPE_DEVICE_CORE, GdkDeviceCore))
+#define GDK_DEVICE_CORE_CLASS(c)     (G_TYPE_CHECK_CLASS_CAST ((c), GDK_TYPE_DEVICE_CORE, GdkDeviceCoreClass))
+#define GDK_IS_DEVICE_CORE(o)        (G_TYPE_CHECK_INSTANCE_TYPE ((o), GDK_TYPE_DEVICE_CORE))
+#define GDK_IS_DEVICE_CORE_CLASS(c)  (G_TYPE_CHECK_CLASS_TYPE ((c), GDK_TYPE_DEVICE_CORE))
+#define GDK_DEVICE_CORE_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GDK_TYPE_DEVICE_CORE, GdkDeviceCoreClass))
+
+typedef struct _GdkDeviceCore GdkDeviceCore;
+typedef struct _GdkDeviceCoreClass GdkDeviceCoreClass;
+
+struct _GdkDeviceCore
+{
+  GdkDevice parent_instance;
+};
+
+struct _GdkDeviceCoreClass
+{
+  GdkDeviceClass parent_class;
+};
+
+GType gdk_device_core_get_type (void) G_GNUC_CONST;
+
+G_END_DECLS
+
+#endif /* __GDK_DEVICE_CORE_H__ */
diff --git a/gdk/x11/gdkdevice-xi.c b/gdk/x11/gdkdevice-xi.c
new file mode 100644
index 0000000..ca98321
--- /dev/null
+++ b/gdk/x11/gdkdevice-xi.c
@@ -0,0 +1,622 @@
+/* GDK - The GIMP Drawing Kit
+ * Copyright (C) 2009 Carlos Garnacho <carlosg@gnome.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * 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.
+ */
+
+#include <gdk/gdkwindow.h>
+#include "gdkdeviceprivate.h"
+#include "gdkdevice-xi.h"
+#include "gdkprivate-x11.h"
+#include "gdkintl.h"
+#include "gdkx.h"
+
+#define MAX_DEVICE_CLASSES 13
+
+static GQuark quark_window_input_info = 0;
+
+typedef struct
+{
+  gdouble root_x;
+  gdouble root_y;
+} GdkWindowInputInfo;
+
+static void gdk_device_xi_constructed  (GObject *object);
+static void gdk_device_xi_dispose      (GObject *object);
+
+static void gdk_device_xi_set_property (GObject      *object,
+                                        guint         prop_id,
+                                        const GValue *value,
+                                        GParamSpec   *pspec);
+static void gdk_device_xi_get_property (GObject      *object,
+                                        guint         prop_id,
+                                        GValue       *value,
+                                        GParamSpec   *pspec);
+
+static gboolean gdk_device_xi_get_history (GdkDevice      *device,
+                                           GdkWindow      *window,
+                                           guint32         start,
+                                           guint32         stop,
+                                           GdkTimeCoord ***events,
+                                           guint          *n_events);
+
+static void gdk_device_xi_get_state       (GdkDevice       *device,
+                                           GdkWindow       *window,
+                                           gdouble         *axes,
+                                           GdkModifierType *mask);
+static void gdk_device_xi_set_window_cursor (GdkDevice *device,
+                                             GdkWindow *window,
+                                             GdkCursor *cursor);
+static void gdk_device_xi_warp              (GdkDevice *device,
+                                             GdkScreen *screen,
+                                             gint       x,
+                                             gint       y);
+static gboolean gdk_device_xi_query_state   (GdkDevice        *device,
+                                             GdkWindow        *window,
+                                             GdkWindow       **root_window,
+                                             GdkWindow       **child_window,
+                                             gint             *root_x,
+                                             gint             *root_y,
+                                             gint             *win_x,
+                                             gint             *win_y,
+                                             GdkModifierType  *mask);
+static GdkGrabStatus gdk_device_xi_grab     (GdkDevice    *device,
+                                             GdkWindow    *window,
+                                             gboolean      owner_events,
+                                             GdkEventMask  event_mask,
+                                             GdkWindow    *confine_to,
+                                             GdkCursor    *cursor,
+                                             guint32       time_);
+static void          gdk_device_xi_ungrab   (GdkDevice    *device,
+                                             guint32       time_);
+
+static GdkWindow* gdk_device_xi_window_at_position (GdkDevice       *device,
+                                                    gint            *win_x,
+                                                    gint            *win_y,
+                                                    GdkModifierType *mask,
+                                                    gboolean         get_toplevel);
+
+static void gdk_device_xi_select_window_events (GdkDevice    *device,
+                                                GdkWindow    *window,
+                                                GdkEventMask  mask);
+
+
+G_DEFINE_TYPE (GdkDeviceXI, gdk_device_xi, GDK_TYPE_DEVICE)
+
+enum {
+  PROP_0,
+  PROP_DEVICE_ID
+};
+
+static void
+gdk_device_xi_class_init (GdkDeviceXIClass *klass)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+  GdkDeviceClass *device_class = GDK_DEVICE_CLASS (klass);
+
+  quark_window_input_info = g_quark_from_static_string ("gdk-window-input-info");
+
+  object_class->constructed = gdk_device_xi_constructed;
+  object_class->set_property = gdk_device_xi_set_property;
+  object_class->get_property = gdk_device_xi_get_property;
+  object_class->dispose = gdk_device_xi_dispose;
+
+  device_class->get_history = gdk_device_xi_get_history;
+  device_class->get_state = gdk_device_xi_get_state;
+  device_class->set_window_cursor = gdk_device_xi_set_window_cursor;
+  device_class->warp = gdk_device_xi_warp;
+  device_class->query_state = gdk_device_xi_query_state;
+  device_class->grab = gdk_device_xi_grab;
+  device_class->ungrab = gdk_device_xi_ungrab;
+  device_class->window_at_position = gdk_device_xi_window_at_position;
+  device_class->select_window_events = gdk_device_xi_select_window_events;
+
+  g_object_class_install_property (object_class,
+				   PROP_DEVICE_ID,
+				   g_param_spec_int ("device-id",
+                                                     P_("Device ID"),
+                                                     P_("Device ID"),
+                                                     0, G_MAXINT, 0,
+                                                     G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+}
+
+static void
+gdk_device_xi_init (GdkDeviceXI *device)
+{
+}
+
+static void
+gdk_device_xi_constructed (GObject *object)
+{
+  GdkDeviceXI *device;
+  GdkDisplay *display;
+
+  device = GDK_DEVICE_XI (object);
+  display = gdk_device_get_display (GDK_DEVICE (object));
+
+  gdk_error_trap_push ();
+  device->xdevice = XOpenDevice (GDK_DISPLAY_XDISPLAY (display),
+                                 device->device_id);
+
+  if (gdk_error_trap_pop ())
+    g_warning ("Device %s can't be opened", GDK_DEVICE (device)->name);
+
+  if (G_OBJECT_CLASS (gdk_device_xi_parent_class)->constructed)
+    G_OBJECT_CLASS (gdk_device_xi_parent_class)->constructed (object);
+}
+
+static void
+gdk_device_xi_set_property (GObject      *object,
+                            guint         prop_id,
+                            const GValue *value,
+                            GParamSpec   *pspec)
+{
+  GdkDeviceXI *device = GDK_DEVICE_XI (object);
+
+  switch (prop_id)
+    {
+    case PROP_DEVICE_ID:
+      device->device_id = g_value_get_int (value);
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+    }
+}
+
+static void
+gdk_device_xi_get_property (GObject    *object,
+                            guint       prop_id,
+                            GValue     *value,
+                            GParamSpec *pspec)
+{
+  GdkDeviceXI *device = GDK_DEVICE_XI (object);
+
+  switch (prop_id)
+    {
+    case PROP_DEVICE_ID:
+      g_value_set_int (value, device->device_id);
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+    }
+}
+
+static void
+gdk_device_xi_dispose (GObject *object)
+{
+  GdkDeviceXI *device_xi;
+  GdkDisplay *display;
+
+  device_xi = GDK_DEVICE_XI (object);
+  display = gdk_device_get_display (GDK_DEVICE (device_xi));
+
+  if (device_xi->xdevice)
+    {
+      XCloseDevice (GDK_DISPLAY_XDISPLAY (display), device_xi->xdevice);
+      device_xi->xdevice = NULL;
+    }
+
+  if (device_xi->axis_data)
+    {
+      g_free (device_xi->axis_data);
+      device_xi->axis_data = NULL;
+    }
+
+  G_OBJECT_CLASS (gdk_device_xi_parent_class)->dispose (object);
+}
+
+static gboolean
+gdk_device_xi_get_history (GdkDevice      *device,
+                           GdkWindow      *window,
+                           guint32         start,
+                           guint32         stop,
+                           GdkTimeCoord ***events,
+                           guint          *n_events)
+{
+  GdkTimeCoord **coords;
+  XDeviceTimeCoord *device_coords;
+  GdkWindow *impl_window;
+  GdkDeviceXI *device_xi;
+  gint n_events_return;
+  gint mode_return;
+  gint axis_count_return;
+  gint i;
+
+  device_xi = GDK_DEVICE_XI (device);
+  impl_window = _gdk_window_get_impl_window (window);
+
+  device_coords = XGetDeviceMotionEvents (GDK_WINDOW_XDISPLAY (impl_window),
+					  device_xi->xdevice,
+					  start, stop,
+					  &n_events_return,
+                                          &mode_return,
+					  &axis_count_return);
+
+  if (!device_coords)
+    return FALSE;
+
+  *n_events = (guint) n_events_return;
+  coords = _gdk_device_allocate_history (device, *n_events);
+
+  for (i = 0; i < *n_events; i++)
+    {
+      coords[i]->time = device_coords[i].time;
+      gdk_device_xi_translate_axes (device, window,
+                                    device_coords[i].data,
+                                    coords[i]->axes,
+                                    NULL, NULL);
+    }
+
+  XFreeDeviceMotionEvents (device_coords);
+
+  *events = coords;
+
+  return TRUE;
+}
+
+static void
+gdk_device_xi_get_state (GdkDevice       *device,
+                         GdkWindow       *window,
+                         gdouble         *axes,
+                         GdkModifierType *mask)
+{
+  GdkDeviceXI *device_xi;
+  XDeviceState *state;
+  XInputClass *input_class;
+  gint i;
+
+  if (mask)
+    gdk_window_get_pointer (window, NULL, NULL, mask);
+
+  device_xi = GDK_DEVICE_XI (device);
+  state = XQueryDeviceState (GDK_WINDOW_XDISPLAY (window),
+                             device_xi->xdevice);
+  input_class = state->data;
+
+  for (i = 0; i < state->num_classes; i++)
+    {
+      switch (input_class->class)
+        {
+        case ValuatorClass:
+          if (axes)
+            gdk_device_xi_translate_axes (device, window,
+                                          ((XValuatorState *) input_class)->valuators,
+                                          axes, NULL, NULL);
+          break;
+
+        case ButtonClass:
+          if (mask)
+            {
+              *mask &= 0xFF;
+              if (((XButtonState *)input_class)->num_buttons > 0)
+                *mask |= ((XButtonState *)input_class)->buttons[0] << 7;
+              /* GDK_BUTTON1_MASK = 1 << 8, and button n is stored
+               * in bit 1<<(n%8) in byte n/8. n = 1,2,... */
+            }
+          break;
+        }
+
+      input_class = (XInputClass *)(((char *)input_class)+input_class->length);
+    }
+
+  XFreeDeviceState (state);
+}
+
+static void
+gdk_device_xi_set_window_cursor (GdkDevice *device,
+                                 GdkWindow *window,
+                                 GdkCursor *cursor)
+{
+}
+
+static void
+gdk_device_xi_warp (GdkDevice *device,
+                    GdkScreen *screen,
+                    gint       x,
+                    gint       y)
+{
+}
+
+static void
+find_events (GdkDevice    *device,
+             GdkEventMask  mask,
+             XEventClass  *classes,
+             int          *num_classes)
+{
+  GdkDeviceXI *device_xi;
+  XEventClass class;
+  gint i;
+
+  device_xi = GDK_DEVICE_XI (device);
+  i = 0;
+
+  if (mask & GDK_BUTTON_PRESS_MASK)
+    {
+      DeviceButtonPress (device_xi->xdevice, device_xi->button_press_type, class);
+      if (class != 0)
+        classes[i++] = class;
+
+      DeviceButtonPressGrab (device_xi->xdevice, 0, class);
+      if (class != 0)
+        classes[i++] = class;
+    }
+
+  if (mask & GDK_BUTTON_RELEASE_MASK)
+    {
+      DeviceButtonRelease (device_xi->xdevice, device_xi->button_release_type, class);
+      if (class != 0)
+        classes[i++] = class;
+    }
+
+  if (mask & (GDK_POINTER_MOTION_MASK |
+              GDK_BUTTON1_MOTION_MASK | GDK_BUTTON2_MOTION_MASK |
+              GDK_BUTTON3_MOTION_MASK | GDK_BUTTON_MOTION_MASK))
+    {
+      /* Make sure device->motionnotify_type is set */
+      DeviceMotionNotify (device_xi->xdevice, device_xi->motion_notify_type, class);
+      if (class != 0)
+	  classes[i++] = class;
+      DeviceStateNotify (device_xi->xdevice, device_xi->state_notify_type, class);
+      if (class != 0)
+	  classes[i++] = class;
+    }
+
+  if (mask & GDK_KEY_PRESS_MASK)
+    {
+      DeviceKeyPress (device_xi->xdevice, device_xi->key_press_type, class);
+      if (class != 0)
+        classes[i++] = class;
+    }
+
+  if (mask & GDK_KEY_RELEASE_MASK)
+    {
+      DeviceKeyRelease (device_xi->xdevice, device_xi->key_release_type, class);
+      if (class != 0)
+        classes[i++] = class;
+    }
+
+  if (mask & GDK_PROXIMITY_IN_MASK)
+    {
+      ProximityIn (device_xi->xdevice, device_xi->proximity_in_type, class);
+      if (class != 0)
+        classes[i++] = class;
+    }
+
+  if (mask & GDK_PROXIMITY_OUT_MASK)
+    {
+      ProximityOut (device_xi->xdevice, device_xi->proximity_out_type, class);
+      if (class != 0)
+        classes[i++] = class;
+    }
+
+  *num_classes = i;
+}
+
+static gboolean
+gdk_device_xi_query_state (GdkDevice        *device,
+                           GdkWindow        *window,
+                           GdkWindow       **root_window,
+                           GdkWindow       **child_window,
+                           gint             *root_x,
+                           gint             *root_y,
+                           gint             *win_x,
+                           gint             *win_y,
+                           GdkModifierType  *mask)
+{
+  return FALSE;
+}
+
+static GdkGrabStatus
+gdk_device_xi_grab (GdkDevice    *device,
+                    GdkWindow    *window,
+                    gboolean      owner_events,
+                    GdkEventMask  event_mask,
+                    GdkWindow    *confine_to,
+                    GdkCursor    *cursor,
+                    guint32       time_)
+{
+  XEventClass event_classes[MAX_DEVICE_CLASSES];
+  gint status, num_classes;
+  GdkDeviceXI *device_xi;
+
+  device_xi = GDK_DEVICE_XI (device);
+  find_events (device, event_mask, event_classes, &num_classes);
+
+  status = XGrabDevice (GDK_WINDOW_XDISPLAY (window),
+                        device_xi->xdevice,
+                        GDK_WINDOW_XWINDOW (window),
+                        owner_events,
+                        num_classes, event_classes,
+                        GrabModeAsync, GrabModeAsync,
+                        time_);
+
+  return gdk_x11_convert_grab_status (status);
+}
+
+static void
+gdk_device_xi_ungrab (GdkDevice *device,
+                      guint32    time_)
+{
+  GdkDisplay *display;
+  GdkDeviceXI *device_xi;
+
+  device_xi = GDK_DEVICE_XI (device);
+  display = gdk_device_get_display (device);
+
+  XUngrabDevice (GDK_DISPLAY_XDISPLAY (device),
+                 device_xi->xdevice,
+                 time_);
+}
+
+static GdkWindow*
+gdk_device_xi_window_at_position (GdkDevice       *device,
+                                  gint            *win_x,
+                                  gint            *win_y,
+                                  GdkModifierType *mask,
+                                  gboolean         get_toplevel)
+{
+  return NULL;
+}
+static void
+gdk_device_xi_select_window_events (GdkDevice    *device,
+                                    GdkWindow    *window,
+                                    GdkEventMask  event_mask)
+{
+  XEventClass event_classes[MAX_DEVICE_CLASSES];
+  GdkDeviceXI *device_xi;
+  gint num_classes;
+
+  event_mask |= (GDK_PROXIMITY_IN_MASK |
+                 GDK_PROXIMITY_OUT_MASK);
+
+  device_xi = GDK_DEVICE_XI (device);
+  find_events (device, event_mask, event_classes, &num_classes);
+
+  XSelectExtensionEvent (GDK_WINDOW_XDISPLAY (window),
+			 GDK_WINDOW_XWINDOW (window),
+			 event_classes, num_classes);
+
+  if (event_mask)
+    {
+      GdkWindowInputInfo *info;
+
+      info = g_new0 (GdkWindowInputInfo, 1);
+      g_object_set_qdata_full (G_OBJECT (window),
+                               quark_window_input_info,
+                               info,
+                               (GDestroyNotify) g_free);
+    }
+  else
+    g_object_set_qdata (G_OBJECT (window),
+                        quark_window_input_info,
+                        NULL);
+}
+
+void
+gdk_device_xi_update_window_info (GdkWindow *window)
+{
+  GdkWindowInputInfo *info;
+  gint root_x, root_y;
+
+  info = g_object_get_qdata (G_OBJECT (window),
+                             quark_window_input_info);
+
+  if (!info)
+    return;
+
+  gdk_window_get_origin (window, &root_x, &root_y);
+  info->root_x = (gdouble) root_x;
+  info->root_y = (gdouble) root_y;
+}
+
+static gboolean
+gdk_device_xi_get_window_info (GdkWindow *window,
+                               gdouble   *root_x,
+                               gdouble   *root_y)
+{
+  GdkWindowInputInfo *info;
+
+  info = g_object_get_qdata (G_OBJECT (window),
+                             quark_window_input_info);
+
+  if (!info)
+    return FALSE;
+
+  *root_x = info->root_x;
+  *root_y = info->root_y;
+
+  return TRUE;
+}
+
+void
+gdk_device_xi_update_axes (GdkDevice *device,
+                           gint       axes_count,
+                           gint       first_axis,
+                           gint      *axis_data)
+{
+  GdkDeviceXI *device_xi;
+  int i;
+
+  device_xi = GDK_DEVICE_XI (device);
+  g_return_if_fail (first_axis >= 0 && first_axis + axes_count <= device->num_axes);
+
+  if (!device_xi->axis_data)
+    device_xi->axis_data = g_new0 (gint, device->num_axes);
+
+  for (i = 0; i < axes_count; i++)
+    device_xi->axis_data[first_axis + i] = axis_data[i];
+}
+
+void
+gdk_device_xi_translate_axes (GdkDevice *device,
+                              GdkWindow *window,
+                              gint      *axis_data,
+                              gdouble   *axes,
+                              gdouble   *x,
+                              gdouble   *y)
+{
+  GdkDeviceXI *device_xi;
+  GdkWindow *impl_window;
+  gdouble root_x, root_y;
+  gdouble temp_x, temp_y;
+  gint i;
+
+  device_xi = GDK_DEVICE_XI (device);
+  impl_window = _gdk_window_get_impl_window (window);
+  temp_x = temp_y = 0;
+
+  if (!gdk_device_xi_get_window_info (impl_window, &root_x, &root_y))
+    return;
+
+  for (i = 0; i < device->num_axes; i++)
+    {
+      GdkAxisUse use;
+
+      use = _gdk_device_get_axis_use (device, i);
+
+      switch (use)
+        {
+        case GDK_AXIS_X:
+        case GDK_AXIS_Y:
+          if (device->mode == GDK_MODE_WINDOW)
+            _gdk_device_translate_window_coord (device, window,
+                                                i, axis_data[i],
+                                                &axes[i]);
+          else
+            _gdk_device_translate_screen_coord (device, window,
+                                                root_x, root_y,
+                                                i, axis_data[i],
+                                                &axes[i]);
+          if (use == GDK_AXIS_X)
+            temp_x = axes[i];
+          else if (use == GDK_AXIS_Y)
+            temp_y = axes[i];
+
+          break;
+        default:
+          _gdk_device_translate_axis (device, i, axis_data[i], &axes[i]);
+          break;
+        }
+    }
+
+  if (x)
+    *x = temp_x;
+
+  if (y)
+    *y = temp_y;
+}
diff --git a/gdk/x11/gdkdevice-xi.h b/gdk/x11/gdkdevice-xi.h
new file mode 100644
index 0000000..5f8f586
--- /dev/null
+++ b/gdk/x11/gdkdevice-xi.h
@@ -0,0 +1,84 @@
+/* GDK - The GIMP Drawing Kit
+ * Copyright (C) 2009 Carlos Garnacho <carlosg@gnome.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * 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.
+ */
+
+#ifndef __GDK_DEVICE_XI_H__
+#define __GDK_DEVICE_XI_H__
+
+#include <gdk/gdkdeviceprivate.h>
+#include <X11/extensions/XInput.h>
+
+G_BEGIN_DECLS
+
+#define GDK_TYPE_DEVICE_XI         (gdk_device_xi_get_type ())
+#define GDK_DEVICE_XI(o)           (G_TYPE_CHECK_INSTANCE_CAST ((o), GDK_TYPE_DEVICE_XI, GdkDeviceXI))
+#define GDK_DEVICE_XI_CLASS(c)     (G_TYPE_CHECK_CLASS_CAST ((c), GDK_TYPE_DEVICE_XI, GdkDeviceXIClass))
+#define GDK_IS_DEVICE_XI(o)        (G_TYPE_CHECK_INSTANCE_TYPE ((o), GDK_TYPE_DEVICE_XI))
+#define GDK_IS_DEVICE_XI_CLASS(c)  (G_TYPE_CHECK_CLASS_TYPE ((c), GDK_TYPE_DEVICE_XI))
+#define GDK_DEVICE_XI_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GDK_TYPE_DEVICE_XI, GdkDeviceXIClass))
+
+typedef struct _GdkDeviceXI GdkDeviceXI;
+typedef struct _GdkDeviceXIClass GdkDeviceXIClass;
+
+struct _GdkDeviceXI
+{
+  GdkDevice parent_instance;
+
+  guint32 device_id;
+  XDevice *xdevice;
+
+  gint button_press_type;
+  gint button_release_type;
+  gint key_press_type;
+  gint key_release_type;
+  gint motion_notify_type;
+  gint proximity_in_type;
+  gint proximity_out_type;
+  gint state_notify_type;
+
+  /* minimum key code for device */
+  gint min_keycode;
+
+  gint *axis_data;
+
+  guint in_proximity : 1;
+};
+
+struct _GdkDeviceXIClass
+{
+  GdkDeviceClass parent_class;
+};
+
+GType gdk_device_xi_get_type (void) G_GNUC_CONST;
+
+void     gdk_device_xi_update_window_info (GdkWindow *window);
+
+void     gdk_device_xi_update_axes (GdkDevice *device,
+                                    gint       axes_count,
+                                    gint       first_axis,
+                                    gint      *axis_data);
+void     gdk_device_xi_translate_axes     (GdkDevice *device,
+                                           GdkWindow *window,
+                                           gint      *axis_data,
+                                           gdouble   *axes,
+                                           gdouble   *x,
+                                           gdouble   *y);
+
+G_END_DECLS
+
+#endif /* __GDK_DEVICE_XI_H__ */
diff --git a/gdk/x11/gdkdevice-xi2.c b/gdk/x11/gdkdevice-xi2.c
new file mode 100644
index 0000000..1d5e84e
--- /dev/null
+++ b/gdk/x11/gdkdevice-xi2.c
@@ -0,0 +1,562 @@
+/* GDK - The GIMP Drawing Kit
+ * Copyright (C) 2009 Carlos Garnacho <carlosg@gnome.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * 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.
+ */
+
+#include <X11/extensions/XInput2.h>
+#include "gdkdevice-xi2.h"
+#include "gdkintl.h"
+#include "gdkx.h"
+
+#define BIT_IS_ON(ptr, bit) (((unsigned char *) (ptr))[(bit)>>3] & (1 << ((bit) & 7)))
+#define GDK_DEVICE_XI2_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GDK_TYPE_DEVICE_XI2, GdkDeviceXI2Private))
+
+typedef struct GdkDeviceXI2Private GdkDeviceXI2Private;
+
+struct GdkDeviceXI2Private
+{
+  int device_id;
+};
+
+static void gdk_device_xi2_get_property (GObject      *object,
+                                         guint         prop_id,
+                                         GValue       *value,
+                                         GParamSpec   *pspec);
+static void gdk_device_xi2_set_property (GObject      *object,
+                                         guint         prop_id,
+                                         const GValue *value,
+                                         GParamSpec   *pspec);
+
+static void gdk_device_xi2_get_state (GdkDevice       *device,
+                                      GdkWindow       *window,
+                                      gdouble         *axes,
+                                      GdkModifierType *mask);
+static void gdk_device_xi2_set_window_cursor (GdkDevice *device,
+                                              GdkWindow *window,
+                                              GdkCursor *cursor);
+static void gdk_device_xi2_warp (GdkDevice *device,
+                                 GdkScreen *screen,
+                                 gint       x,
+                                 gint       y);
+static gboolean gdk_device_xi2_query_state (GdkDevice        *device,
+                                            GdkWindow        *window,
+                                            GdkWindow       **root_window,
+                                            GdkWindow       **child_window,
+                                            gint             *root_x,
+                                            gint             *root_y,
+                                            gint             *win_x,
+                                            gint             *win_y,
+                                            GdkModifierType  *mask);
+
+static GdkGrabStatus gdk_device_xi2_grab   (GdkDevice     *device,
+                                            GdkWindow     *window,
+                                            gboolean       owner_events,
+                                            GdkEventMask   event_mask,
+                                            GdkWindow     *confine_to,
+                                            GdkCursor     *cursor,
+                                            guint32        time_);
+static void          gdk_device_xi2_ungrab (GdkDevice     *device,
+                                            guint32        time_);
+
+static GdkWindow * gdk_device_xi2_window_at_position (GdkDevice       *device,
+                                                      gint            *win_x,
+                                                      gint            *win_y,
+                                                      GdkModifierType *mask,
+                                                      gboolean         get_toplevel);
+static void  gdk_device_xi2_select_window_events (GdkDevice    *device,
+                                                  GdkWindow    *window,
+                                                  GdkEventMask  event_mask);
+
+
+G_DEFINE_TYPE (GdkDeviceXI2, gdk_device_xi2, GDK_TYPE_DEVICE)
+
+enum {
+  PROP_0,
+  PROP_DEVICE_ID
+};
+
+static void
+gdk_device_xi2_class_init (GdkDeviceXI2Class *klass)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+  GdkDeviceClass *device_class = GDK_DEVICE_CLASS (klass);
+
+  object_class->get_property = gdk_device_xi2_get_property;
+  object_class->set_property = gdk_device_xi2_set_property;
+
+  device_class->get_state = gdk_device_xi2_get_state;
+  device_class->set_window_cursor = gdk_device_xi2_set_window_cursor;
+  device_class->warp = gdk_device_xi2_warp;
+  device_class->query_state = gdk_device_xi2_query_state;
+  device_class->grab = gdk_device_xi2_grab;
+  device_class->ungrab = gdk_device_xi2_ungrab;
+  device_class->window_at_position = gdk_device_xi2_window_at_position;
+  device_class->select_window_events = gdk_device_xi2_select_window_events;
+
+  g_object_class_install_property (object_class,
+				   PROP_DEVICE_ID,
+				   g_param_spec_int ("device-id",
+                                                     P_("Device ID"),
+                                                     P_("Device identifier"),
+                                                     0, G_MAXINT, 0,
+                                                     G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+
+  g_type_class_add_private (object_class, sizeof (GdkDeviceXI2Private));
+}
+
+static void
+gdk_device_xi2_init (GdkDeviceXI2 *device)
+{
+}
+
+static void
+gdk_device_xi2_get_property (GObject    *object,
+                             guint       prop_id,
+                             GValue     *value,
+                             GParamSpec *pspec)
+{
+  GdkDeviceXI2Private *priv;
+
+  priv = GDK_DEVICE_XI2_GET_PRIVATE (object);
+
+  switch (prop_id)
+    {
+    case PROP_DEVICE_ID:
+      g_value_set_int (value, priv->device_id);
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+    }
+}
+
+static void
+gdk_device_xi2_set_property (GObject      *object,
+                             guint         prop_id,
+                             const GValue *value,
+                             GParamSpec   *pspec)
+{
+  GdkDeviceXI2Private *priv;
+
+  priv = GDK_DEVICE_XI2_GET_PRIVATE (object);
+
+  switch (prop_id)
+    {
+    case PROP_DEVICE_ID:
+      priv->device_id = g_value_get_int (value);
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+    }
+}
+
+static void
+gdk_device_xi2_get_state (GdkDevice       *device,
+                          GdkWindow       *window,
+                          gdouble         *axes,
+                          GdkModifierType *mask)
+{
+  /* FIXME: Axes are not being translated, there doesn't
+   * seem to be any function to get valuators state in XI2.
+   */
+  gdk_device_xi2_query_state (device, window,
+                              NULL, NULL,
+                              NULL, NULL,
+                              NULL, NULL,
+                              mask);
+}
+
+static void
+gdk_device_xi2_set_window_cursor (GdkDevice *device,
+                                  GdkWindow *window,
+                                  GdkCursor *cursor)
+{
+  GdkDeviceXI2Private *priv;
+  GdkCursorPrivate *cursor_private;
+
+  priv = GDK_DEVICE_XI2_GET_PRIVATE (device);
+
+  /* Non-master devices don't have a cursor */
+  if (gdk_device_get_device_type (device) != GDK_DEVICE_TYPE_MASTER)
+    return;
+
+  if (cursor)
+    {
+      cursor_private = (GdkCursorPrivate*) cursor;
+
+      XIDefineCursor (GDK_WINDOW_XDISPLAY (window),
+                      priv->device_id,
+                      GDK_WINDOW_XWINDOW (window),
+                      cursor_private->xcursor);
+    }
+  else
+    XIUndefineCursor (GDK_WINDOW_XDISPLAY (window),
+                      priv->device_id,
+                      GDK_WINDOW_XWINDOW (window));
+}
+
+static void
+gdk_device_xi2_warp (GdkDevice *device,
+                     GdkScreen *screen,
+                     gint       x,
+                     gint       y)
+{
+  GdkDeviceXI2Private *priv;
+  Window dest;
+
+  priv = GDK_DEVICE_XI2_GET_PRIVATE (device);
+  dest = GDK_WINDOW_XWINDOW (gdk_screen_get_root_window (screen));
+
+  XIWarpPointer (GDK_SCREEN_XDISPLAY (screen),
+                 priv->device_id,
+                 None, dest,
+                 0, 0, 0, 0, x, y);
+}
+
+static gboolean
+gdk_device_xi2_query_state (GdkDevice        *device,
+                            GdkWindow        *window,
+                            GdkWindow       **root_window,
+                            GdkWindow       **child_window,
+                            gint             *root_x,
+                            gint             *root_y,
+                            gint             *win_x,
+                            gint             *win_y,
+                            GdkModifierType  *mask)
+{
+  GdkDisplay *display;
+  GdkDeviceXI2Private *priv;
+  Window xroot_window, xchild_window;
+  gdouble xroot_x, xroot_y, xwin_x, xwin_y;
+  XIButtonState button_state;
+  XIModifierState mod_state;
+  XIGroupState group_state;
+
+  if (!window || GDK_WINDOW_DESTROYED (window))
+    return FALSE;
+
+  priv = GDK_DEVICE_XI2_GET_PRIVATE (device);
+  display = gdk_drawable_get_display (window);
+
+  if (!XIQueryPointer (GDK_WINDOW_XDISPLAY (window),
+                       priv->device_id,
+                       GDK_WINDOW_XID (window),
+                       &xroot_window,
+                       &xchild_window,
+                       &xroot_x,
+                       &xroot_y,
+                       &xwin_x,
+                       &xwin_y,
+                       &button_state,
+                       &mod_state,
+                       &group_state))
+    {
+      return FALSE;
+    }
+
+  if (root_window)
+    *root_window = gdk_window_lookup_for_display (display, xroot_window);
+
+  if (child_window)
+    *child_window = gdk_window_lookup_for_display (display, xchild_window);
+
+  if (root_x)
+    *root_x = (gint) xroot_x;
+
+  if (root_y)
+    *root_y = (gint) xroot_y;
+
+  if (win_x)
+    *win_x = (gint) xwin_x;
+
+  if (win_y)
+    *win_y = (gint) xwin_y;
+
+  if (mask)
+    *mask = gdk_device_xi2_translate_state (&mod_state, &button_state);
+
+  return TRUE;
+}
+
+static GdkGrabStatus
+gdk_device_xi2_grab (GdkDevice    *device,
+                     GdkWindow    *window,
+                     gboolean      owner_events,
+                     GdkEventMask  event_mask,
+                     GdkWindow    *confine_to,
+                     GdkCursor    *cursor,
+                     guint32       time_)
+{
+  GdkDeviceXI2Private *priv;
+  GdkDisplay *display;
+  XIEventMask mask;
+  Window xwindow;
+  Cursor xcursor;
+  int status;
+
+  priv = GDK_DEVICE_XI2_GET_PRIVATE (device);
+  display = gdk_device_get_display (device);
+
+  /* FIXME: confine_to is actually unused */
+
+  xwindow = GDK_WINDOW_XID (window);
+
+  if (!cursor)
+    xcursor = None;
+  else
+    {
+      _gdk_x11_cursor_update_theme (cursor);
+      xcursor = ((GdkCursorPrivate *) cursor)->xcursor;
+    }
+
+  mask.deviceid = priv->device_id;
+  mask.mask = gdk_device_xi2_translate_event_mask (event_mask, &mask.mask_len);
+
+  status = XIGrabDevice (GDK_DISPLAY_XDISPLAY (display),
+                         priv->device_id,
+                         xwindow,
+                         time_,
+                         xcursor,
+                         GrabModeAsync, GrabModeAsync,
+                         owner_events,
+                         &mask);
+
+  g_free (mask.mask);
+
+  return gdk_x11_convert_grab_status (status);
+}
+
+static void
+gdk_device_xi2_ungrab (GdkDevice *device,
+                       guint32    time_)
+{
+  GdkDeviceXI2Private *priv;
+  GdkDisplay *display;
+
+  priv = GDK_DEVICE_XI2_GET_PRIVATE (device);
+  display = gdk_device_get_display (device);
+
+  XIUngrabDevice (GDK_DISPLAY_XDISPLAY (display),
+                  priv->device_id,
+                  time_);
+}
+
+static GdkWindow *
+gdk_device_xi2_window_at_position (GdkDevice       *device,
+                                   gint            *win_x,
+                                   gint            *win_y,
+                                   GdkModifierType *mask,
+                                   gboolean         get_toplevel)
+{
+  GdkDeviceXI2Private *priv;
+  GdkDisplay *display;
+  GdkScreen *screen;
+  Display *xdisplay;
+  GdkWindow *window;
+  Window xwindow, root, child, last = None;
+  gdouble xroot_x, xroot_y, xwin_x, xwin_y;
+  XIButtonState button_state;
+  XIModifierState mod_state;
+  XIGroupState group_state;
+
+  priv = GDK_DEVICE_XI2_GET_PRIVATE (device);
+  display = gdk_device_get_display (device);
+  screen = gdk_display_get_default_screen (display);
+
+  /* This function really only works if the mouse pointer is held still
+   * during its operation. If it moves from one leaf window to another
+   * than we'll end up with inaccurate values for win_x, win_y
+   * and the result.
+   */
+  gdk_x11_display_grab (display);
+
+  xdisplay = GDK_SCREEN_XDISPLAY (screen);
+  xwindow = GDK_SCREEN_XROOTWIN (screen);
+
+  XIQueryPointer (xdisplay,
+                  priv->device_id,
+                  xwindow,
+                  &root, &child,
+                  &xroot_x, &xroot_y,
+                  &xwin_x, &xwin_y,
+                  &button_state,
+                  &mod_state,
+                  &group_state);
+
+  if (root == xwindow)
+    xwindow = child;
+  else
+    xwindow = root;
+
+  while (xwindow)
+    {
+      last = xwindow;
+      XIQueryPointer (xdisplay,
+                      priv->device_id,
+                      xwindow,
+                      &root, &xwindow,
+                      &xroot_x, &xroot_y,
+                      &xwin_x, &xwin_y,
+                      &button_state,
+                      &mod_state,
+                      &group_state);
+
+      if (get_toplevel &&
+          (window = gdk_window_lookup_for_display (display, last)) != NULL &&
+          GDK_WINDOW_TYPE (window) != GDK_WINDOW_FOREIGN)
+        break;
+    }
+
+  gdk_x11_display_ungrab (display);
+
+  window = gdk_window_lookup_for_display (display, last);
+
+  if (win_x)
+    *win_x = (window) ? (gint) xwin_x : -1;
+
+  if (win_y)
+    *win_y = (window) ? (gint) xwin_y : -1;
+
+  if (mask)
+    *mask = gdk_device_xi2_translate_state (&mod_state, &button_state);
+
+  return window;
+}
+
+static void
+gdk_device_xi2_select_window_events (GdkDevice    *device,
+                                     GdkWindow    *window,
+                                     GdkEventMask  event_mask)
+{
+  GdkDeviceXI2Private *priv;
+  XIEventMask evmask;
+
+  priv = GDK_DEVICE_XI2_GET_PRIVATE (device);
+
+  evmask.deviceid = priv->device_id;
+  evmask.mask = gdk_device_xi2_translate_event_mask (event_mask, &evmask.mask_len);
+
+  XISelectEvents (GDK_WINDOW_XDISPLAY (window),
+                  GDK_WINDOW_XWINDOW (window),
+                  &evmask, 1);
+
+  g_free (evmask.mask);
+}
+
+guchar *
+gdk_device_xi2_translate_event_mask (GdkEventMask  event_mask,
+                                     int          *len)
+{
+  guchar *mask;
+
+  *len = XIMaskLen (XI_LASTEVENT);
+  mask = g_new0 (guchar, *len);
+
+  if (event_mask & GDK_POINTER_MOTION_MASK ||
+      event_mask & GDK_POINTER_MOTION_HINT_MASK)
+    XISetMask (mask, XI_Motion);
+
+  if (event_mask & GDK_BUTTON_MOTION_MASK ||
+      event_mask & GDK_BUTTON1_MOTION_MASK ||
+      event_mask & GDK_BUTTON2_MOTION_MASK ||
+      event_mask & GDK_BUTTON3_MOTION_MASK)
+    {
+      XISetMask (mask, XI_ButtonPress);
+      XISetMask (mask, XI_ButtonRelease);
+      XISetMask (mask, XI_Motion);
+    }
+
+  if (event_mask & GDK_SCROLL_MASK)
+    {
+      XISetMask (mask, XI_ButtonPress);
+      XISetMask (mask, XI_ButtonRelease);
+    }
+
+  if (event_mask & GDK_BUTTON_PRESS_MASK)
+    XISetMask (mask, XI_ButtonPress);
+
+  if (event_mask & GDK_BUTTON_RELEASE_MASK)
+    XISetMask (mask, XI_ButtonRelease);
+
+  if (event_mask & GDK_KEY_PRESS_MASK)
+    XISetMask (mask, XI_KeyPress);
+
+  if (event_mask & GDK_KEY_RELEASE_MASK)
+    XISetMask (mask, XI_KeyRelease);
+
+  if (event_mask & GDK_ENTER_NOTIFY_MASK)
+    XISetMask (mask, XI_Enter);
+
+  if (event_mask & GDK_LEAVE_NOTIFY_MASK)
+    XISetMask (mask, XI_Leave);
+
+  if (event_mask & GDK_FOCUS_CHANGE_MASK)
+    {
+      XISetMask (mask, XI_FocusIn);
+      XISetMask (mask, XI_FocusOut);
+    }
+
+  return mask;
+}
+
+guint
+gdk_device_xi2_translate_state (XIModifierState *mods_state,
+                                XIButtonState   *buttons_state)
+{
+  guint state = 0;
+
+  if (mods_state)
+    state = (guint) mods_state->effective;
+
+  if (buttons_state)
+    {
+      gint len, i;
+
+      /* We're only interested in the first 5 buttons */
+      len = MIN (5, buttons_state->mask_len * 8);
+
+      for (i = 0; i < len; i++)
+        {
+          if (!BIT_IS_ON (buttons_state->mask, i))
+            continue;
+
+          switch (i)
+            {
+            case 1:
+              state |= GDK_BUTTON1_MASK;
+              break;
+            case 2:
+              state |= GDK_BUTTON2_MASK;
+              break;
+            case 3:
+              state |= GDK_BUTTON3_MASK;
+              break;
+            case 4:
+              state |= GDK_BUTTON4_MASK;
+              break;
+            case 5:
+              state |= GDK_BUTTON5_MASK;
+              break;
+            default:
+              break;
+            }
+        }
+    }
+
+  return state;
+}
diff --git a/gdk/x11/gdkdevice-xi2.h b/gdk/x11/gdkdevice-xi2.h
new file mode 100644
index 0000000..2d2f031
--- /dev/null
+++ b/gdk/x11/gdkdevice-xi2.h
@@ -0,0 +1,57 @@
+/* GDK - The GIMP Drawing Kit
+ * Copyright (C) 2009 Carlos Garnacho <carlosg@gnome.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * 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.
+ */
+
+#ifndef __GDK_DEVICE_XI2_H__
+#define __GDK_DEVICE_XI2_H__
+
+#include <gdk/gdkdeviceprivate.h>
+
+G_BEGIN_DECLS
+
+#define GDK_TYPE_DEVICE_XI2         (gdk_device_xi2_get_type ())
+#define GDK_DEVICE_XI2(o)           (G_TYPE_CHECK_INSTANCE_CAST ((o), GDK_TYPE_DEVICE_XI2, GdkDeviceXI2))
+#define GDK_DEVICE_XI2_CLASS(c)     (G_TYPE_CHECK_CLASS_CAST ((c), GDK_TYPE_DEVICE_XI2, GdkDeviceXI2Class))
+#define GDK_IS_DEVICE_XI2(o)        (G_TYPE_CHECK_INSTANCE_TYPE ((o), GDK_TYPE_DEVICE_XI2))
+#define GDK_IS_DEVICE_XI2_CLASS(c)  (G_TYPE_CHECK_CLASS_TYPE ((c), GDK_TYPE_DEVICE_XI2))
+#define GDK_DEVICE_XI2_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GDK_TYPE_DEVICE_XI2, GdkDeviceXI2Class))
+
+typedef struct _GdkDeviceXI2 GdkDeviceXI2;
+typedef struct _GdkDeviceXI2Class GdkDeviceXI2Class;
+
+struct _GdkDeviceXI2
+{
+  GdkDevice parent_instance;
+};
+
+struct _GdkDeviceXI2Class
+{
+  GdkDeviceClass parent_class;
+};
+
+GType gdk_device_xi2_get_type (void) G_GNUC_CONST;
+
+guchar * gdk_device_xi2_translate_event_mask (GdkEventMask  event_mask,
+                                              int          *len);
+guint    gdk_device_xi2_translate_state      (XIModifierState *mods_state,
+                                              XIButtonState   *buttons_state);
+
+
+G_END_DECLS
+
+#endif /* __GDK_DEVICE_XI2_H__ */
diff --git a/gdk/x11/gdkdevicemanager-core.c b/gdk/x11/gdkdevicemanager-core.c
new file mode 100644
index 0000000..04e7f93
--- /dev/null
+++ b/gdk/x11/gdkdevicemanager-core.c
@@ -0,0 +1,913 @@
+/* GDK - The GIMP Drawing Kit
+ * Copyright (C) 2009 Carlos Garnacho <carlosg@gnome.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * 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.
+ */
+
+#include <gdk/gdktypes.h>
+#include <gdk/gdkdevicemanager.h>
+#include "gdkdevicemanager-core.h"
+#include "gdkeventtranslator.h"
+#include "gdkdevice-core.h"
+#include "gdkkeysyms.h"
+#include "gdkx.h"
+
+#ifdef HAVE_XKB
+#include <X11/XKBlib.h>
+#endif
+
+
+#define HAS_FOCUS(toplevel)                           \
+  ((toplevel)->has_focus || (toplevel)->has_pointer_focus)
+
+static void    gdk_device_manager_core_finalize    (GObject *object);
+static void    gdk_device_manager_core_constructed (GObject *object);
+
+static GList * gdk_device_manager_core_get_devices (GdkDeviceManager *device_manager,
+                                                    GdkDeviceType     type);
+
+static void     gdk_device_manager_event_translator_init (GdkEventTranslatorIface *iface);
+
+static gboolean gdk_device_manager_core_translate_event  (GdkEventTranslator *translator,
+                                                          GdkDisplay         *display,
+                                                          GdkEvent           *event,
+                                                          XEvent             *xevent);
+
+
+G_DEFINE_TYPE_WITH_CODE (GdkDeviceManagerCore, gdk_device_manager_core, GDK_TYPE_DEVICE_MANAGER,
+                         G_IMPLEMENT_INTERFACE (GDK_TYPE_EVENT_TRANSLATOR,
+                                                gdk_device_manager_event_translator_init))
+
+static void
+gdk_device_manager_core_class_init (GdkDeviceManagerCoreClass *klass)
+{
+  GdkDeviceManagerClass *device_manager_class = GDK_DEVICE_MANAGER_CLASS (klass);
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+  object_class->finalize = gdk_device_manager_core_finalize;
+  object_class->constructed = gdk_device_manager_core_constructed;
+  device_manager_class->get_devices = gdk_device_manager_core_get_devices;
+}
+
+static void
+gdk_device_manager_event_translator_init (GdkEventTranslatorIface *iface)
+{
+  iface->translate_event = gdk_device_manager_core_translate_event;
+}
+
+static GdkDevice *
+create_core_pointer (GdkDeviceManager *device_manager,
+                     GdkDisplay       *display)
+{
+  return g_object_new (GDK_TYPE_DEVICE_CORE,
+                       "name", "Core Pointer",
+                       "type", GDK_DEVICE_TYPE_MASTER,
+                       "input-source", GDK_SOURCE_MOUSE,
+                       "input-mode", GDK_MODE_SCREEN,
+                       "has-cursor", TRUE,
+                       "display", display,
+                       "device-manager", device_manager,
+                       NULL);
+}
+
+static GdkDevice *
+create_core_keyboard (GdkDeviceManager *device_manager,
+                      GdkDisplay       *display)
+{
+  return g_object_new (GDK_TYPE_DEVICE_CORE,
+                       "name", "Core Keyboard",
+                       "type", GDK_DEVICE_TYPE_MASTER,
+                       "input-source", GDK_SOURCE_KEYBOARD,
+                       "input-mode", GDK_MODE_SCREEN,
+                       "has-cursor", FALSE,
+                       "display", display,
+                       "device-manager", device_manager,
+                       NULL);
+}
+
+static void
+gdk_device_manager_core_init (GdkDeviceManagerCore *device_manager)
+{
+}
+
+static void
+gdk_device_manager_core_finalize (GObject *object)
+{
+  GdkDeviceManagerCore *device_manager_core;
+
+  device_manager_core = GDK_DEVICE_MANAGER_CORE (object);
+
+  g_object_unref (device_manager_core->core_pointer);
+  g_object_unref (device_manager_core->core_keyboard);
+
+  G_OBJECT_CLASS (gdk_device_manager_core_parent_class)->finalize (object);
+}
+
+static void
+gdk_device_manager_core_constructed (GObject *object)
+{
+  GdkDeviceManagerCore *device_manager;
+  GdkDisplay *display;
+
+  device_manager = GDK_DEVICE_MANAGER_CORE (object);
+  display = gdk_device_manager_get_display (GDK_DEVICE_MANAGER (object));
+  device_manager->core_pointer = create_core_pointer (GDK_DEVICE_MANAGER (device_manager), display);
+  device_manager->core_keyboard = create_core_keyboard (GDK_DEVICE_MANAGER (device_manager), display);
+
+  _gdk_device_set_relative (device_manager->core_pointer, device_manager->core_keyboard);
+  _gdk_device_set_relative (device_manager->core_keyboard, device_manager->core_pointer);
+}
+
+static void
+translate_key_event (GdkDisplay           *display,
+                     GdkDeviceManagerCore *device_manager,
+		     GdkEvent             *event,
+		     XEvent               *xevent)
+{
+  GdkKeymap *keymap = gdk_keymap_get_for_display (display);
+  gunichar c = 0;
+  gchar buf[7];
+
+  event->key.type = xevent->xany.type == KeyPress ? GDK_KEY_PRESS : GDK_KEY_RELEASE;
+  event->key.time = xevent->xkey.time;
+  event->key.device = device_manager->core_keyboard;
+
+  event->key.state = (GdkModifierType) xevent->xkey.state;
+  event->key.group = _gdk_x11_get_group_for_state (display, xevent->xkey.state);
+  event->key.hardware_keycode = xevent->xkey.keycode;
+
+  event->key.keyval = GDK_VoidSymbol;
+
+  gdk_keymap_translate_keyboard_state (keymap,
+				       event->key.hardware_keycode,
+				       event->key.state,
+				       event->key.group,
+				       &event->key.keyval,
+				       NULL, NULL, NULL);
+
+  _gdk_keymap_add_virtual_modifiers (keymap, &event->key.state);
+  event->key.is_modifier = _gdk_keymap_key_is_modifier (keymap, event->key.hardware_keycode);
+
+  /* Fill in event->string crudely, since various programs
+   * depend on it.
+   */
+  event->key.string = NULL;
+
+  if (event->key.keyval != GDK_VoidSymbol)
+    c = gdk_keyval_to_unicode (event->key.keyval);
+
+  if (c)
+    {
+      gsize bytes_written;
+      gint len;
+
+      /* Apply the control key - Taken from Xlib
+       */
+      if (event->key.state & GDK_CONTROL_MASK)
+	{
+	  if ((c >= '@' && c < '\177') || c == ' ') c &= 0x1F;
+	  else if (c == '2')
+	    {
+	      event->key.string = g_memdup ("\0\0", 2);
+	      event->key.length = 1;
+	      buf[0] = '\0';
+	      goto out;
+	    }
+	  else if (c >= '3' && c <= '7') c -= ('3' - '\033');
+	  else if (c == '8') c = '\177';
+	  else if (c == '/') c = '_' & 0x1F;
+	}
+
+      len = g_unichar_to_utf8 (c, buf);
+      buf[len] = '\0';
+
+      event->key.string = g_locale_from_utf8 (buf, len,
+					      NULL, &bytes_written,
+					      NULL);
+      if (event->key.string)
+	event->key.length = bytes_written;
+    }
+  else if (event->key.keyval == GDK_Escape)
+    {
+      event->key.length = 1;
+      event->key.string = g_strdup ("\033");
+    }
+  else if (event->key.keyval == GDK_Return ||
+	  event->key.keyval == GDK_KP_Enter)
+    {
+      event->key.length = 1;
+      event->key.string = g_strdup ("\r");
+    }
+
+  if (!event->key.string)
+    {
+      event->key.length = 0;
+      event->key.string = g_strdup ("");
+    }
+
+ out:
+#ifdef G_ENABLE_DEBUG
+  if (_gdk_debug_flags & GDK_DEBUG_EVENTS)
+    {
+      g_message ("%s:\t\twindow: %ld	 key: %12s  %d",
+		 event->type == GDK_KEY_PRESS ? "key press  " : "key release",
+		 xevent->xkey.window,
+		 event->key.keyval ? gdk_keyval_name (event->key.keyval) : "(none)",
+		 event->key.keyval);
+
+      if (event->key.length > 0)
+	g_message ("\t\tlength: %4d string: \"%s\"",
+		   event->key.length, buf);
+    }
+#endif /* G_ENABLE_DEBUG */
+  return;
+}
+
+#ifdef G_ENABLE_DEBUG
+static const char notify_modes[][19] = {
+  "NotifyNormal",
+  "NotifyGrab",
+  "NotifyUngrab",
+  "NotifyWhileGrabbed"
+};
+
+static const char notify_details[][23] = {
+  "NotifyAncestor",
+  "NotifyVirtual",
+  "NotifyInferior",
+  "NotifyNonlinear",
+  "NotifyNonlinearVirtual",
+  "NotifyPointer",
+  "NotifyPointerRoot",
+  "NotifyDetailNone"
+};
+#endif
+
+static void
+set_user_time (GdkWindow *window,
+	       GdkEvent  *event)
+{
+  g_return_if_fail (event != NULL);
+
+  window = gdk_window_get_toplevel (event->client.window);
+  g_return_if_fail (GDK_IS_WINDOW (window));
+
+  /* If an event doesn't have a valid timestamp, we shouldn't use it
+   * to update the latest user interaction time.
+   */
+  if (gdk_event_get_time (event) != GDK_CURRENT_TIME)
+    gdk_x11_window_set_user_time (gdk_window_get_toplevel (window),
+                                  gdk_event_get_time (event));
+}
+
+static void
+generate_focus_event (GdkDeviceManagerCore *device_manager,
+                      GdkWindow            *window,
+		      gboolean              in)
+{
+  GdkEvent event;
+
+  event.type = GDK_FOCUS_CHANGE;
+  event.focus_change.window = window;
+  event.focus_change.send_event = FALSE;
+  event.focus_change.in = in;
+  event.focus_change.device = device_manager->core_keyboard;
+
+  gdk_event_put (&event);
+}
+
+static gboolean
+set_screen_from_root (GdkDisplay *display,
+		      GdkEvent   *event,
+		      Window      xrootwin)
+{
+  GdkScreen *screen;
+
+  screen = _gdk_x11_display_screen_for_xrootwin (display, xrootwin);
+
+  if (screen)
+    {
+      gdk_event_set_screen (event, screen);
+
+      return TRUE;
+    }
+
+  return FALSE;
+}
+
+static GdkCrossingMode
+translate_crossing_mode (int mode)
+{
+  switch (mode)
+    {
+    case NotifyNormal:
+      return GDK_CROSSING_NORMAL;
+    case NotifyGrab:
+      return GDK_CROSSING_GRAB;
+    case NotifyUngrab:
+      return GDK_CROSSING_UNGRAB;
+    default:
+      g_assert_not_reached ();
+    }
+}
+
+static GdkNotifyType
+translate_notify_type (int detail)
+{
+  switch (detail)
+    {
+    case NotifyInferior:
+      return GDK_NOTIFY_INFERIOR;
+    case NotifyAncestor:
+      return GDK_NOTIFY_ANCESTOR;
+    case NotifyVirtual:
+      return GDK_NOTIFY_VIRTUAL;
+    case NotifyNonlinear:
+      return GDK_NOTIFY_NONLINEAR;
+    case NotifyNonlinearVirtual:
+      return GDK_NOTIFY_NONLINEAR_VIRTUAL;
+    default:
+      g_assert_not_reached ();
+    }
+}
+
+static gboolean
+is_parent_of (GdkWindow *parent,
+              GdkWindow *child)
+{
+  GdkWindow *w;
+
+  w = child;
+  while (w != NULL)
+    {
+      if (w == parent)
+	return TRUE;
+
+      w = gdk_window_get_parent (w);
+    }
+
+  return FALSE;
+}
+
+static GdkWindow *
+get_event_window (GdkEventTranslator *translator,
+                  XEvent             *xevent)
+{
+  GdkDeviceManager *device_manager;
+  GdkDisplay *display;
+  GdkWindow *window;
+
+  device_manager = GDK_DEVICE_MANAGER (translator);
+  display = gdk_device_manager_get_display (device_manager);
+  window = gdk_window_lookup_for_display (display, xevent->xany.window);
+
+  /* Apply keyboard grabs to non-native windows */
+  if (xevent->type == KeyPress || xevent->type == KeyRelease)
+    {
+      GdkDeviceGrabInfo *info;
+      gulong serial;
+
+      serial = _gdk_windowing_window_get_next_serial (display);
+      info = _gdk_display_has_device_grab (display,
+                                           GDK_DEVICE_MANAGER_CORE (device_manager)->core_keyboard,
+                                           serial);
+      if (info &&
+          (!is_parent_of (info->window, window) ||
+           !info->owner_events))
+        {
+          /* Report key event against grab window */
+          window = info->window;
+        }
+    }
+
+  return window;
+}
+
+static gboolean
+gdk_device_manager_core_translate_event (GdkEventTranslator *translator,
+                                         GdkDisplay         *display,
+                                         GdkEvent           *event,
+                                         XEvent             *xevent)
+{
+  GdkDeviceManagerCore *device_manager;
+  GdkWindow *window;
+  GdkWindowObject *window_private;
+  GdkWindowImplX11 *window_impl = NULL;
+  gboolean return_val;
+  GdkToplevelX11 *toplevel = NULL;
+  GdkDisplayX11 *display_x11 = GDK_DISPLAY_X11 (display);
+
+  device_manager = GDK_DEVICE_MANAGER_CORE (translator);
+  return_val = FALSE;
+
+  window = get_event_window (translator, xevent);
+  window_private = (GdkWindowObject *) window;
+
+  if (window && GDK_WINDOW_DESTROYED (window))
+    return FALSE;
+
+  if (window)
+    {
+      toplevel = _gdk_x11_window_get_toplevel (window);
+      window_impl = GDK_WINDOW_IMPL_X11 (window_private->impl);
+      g_object_ref (window);
+    }
+
+  event->any.window = window;
+  event->any.send_event = xevent->xany.send_event ? TRUE : FALSE;
+
+  if (window_private && GDK_WINDOW_DESTROYED (window))
+    {
+      if (xevent->type != DestroyNotify)
+	{
+	  return_val = FALSE;
+	  goto done;
+	}
+    }
+
+  if (window &&
+      (xevent->type == MotionNotify ||
+       xevent->type == ButtonRelease))
+    {
+      if (_gdk_moveresize_handle_event (xevent))
+	{
+          return_val = FALSE;
+          goto done;
+        }
+    }
+
+  /* We do a "manual" conversion of the XEvent to a
+   *  GdkEvent. The structures are mostly the same so
+   *  the conversion is fairly straightforward. We also
+   *  optionally print debugging info regarding events
+   *  received.
+   */
+
+  return_val = TRUE;
+
+  switch (xevent->type)
+    {
+    case KeyPress:
+      if (window_private == NULL)
+        {
+          return_val = FALSE;
+          break;
+        }
+      translate_key_event (display, device_manager, event, xevent);
+      set_user_time (window, event);
+      break;
+
+    case KeyRelease:
+      if (window_private == NULL)
+        {
+          return_val = FALSE;
+          break;
+        }
+
+      /* Emulate detectable auto-repeat by checking to see
+       * if the next event is a key press with the same
+       * keycode and timestamp, and if so, ignoring the event.
+       */
+
+      if (!display_x11->have_xkb_autorepeat && XPending (xevent->xkey.display))
+	{
+	  XEvent next_event;
+
+	  XPeekEvent (xevent->xkey.display, &next_event);
+
+	  if (next_event.type == KeyPress &&
+	      next_event.xkey.keycode == xevent->xkey.keycode &&
+	      next_event.xkey.time == xevent->xkey.time)
+	    {
+	      return_val = FALSE;
+	      break;
+	    }
+	}
+
+      translate_key_event (display, device_manager, event, xevent);
+      break;
+
+    case ButtonPress:
+      GDK_NOTE (EVENTS,
+		g_message ("button press:\t\twindow: %ld  x,y: %d %d  button: %d",
+			   xevent->xbutton.window,
+			   xevent->xbutton.x, xevent->xbutton.y,
+			   xevent->xbutton.button));
+
+      if (window_private == NULL)
+	{
+	  return_val = FALSE;
+	  break;
+	}
+
+      /* If we get a ButtonPress event where the button is 4 or 5,
+	 it's a Scroll event */
+      switch (xevent->xbutton.button)
+        {
+        case 4: /* up */
+        case 5: /* down */
+        case 6: /* left */
+        case 7: /* right */
+	  event->scroll.type = GDK_SCROLL;
+
+          if (xevent->xbutton.button == 4)
+            event->scroll.direction = GDK_SCROLL_UP;
+          else if (xevent->xbutton.button == 5)
+            event->scroll.direction = GDK_SCROLL_DOWN;
+          else if (xevent->xbutton.button == 6)
+            event->scroll.direction = GDK_SCROLL_LEFT;
+          else
+            event->scroll.direction = GDK_SCROLL_RIGHT;
+
+	  event->scroll.window = window;
+	  event->scroll.time = xevent->xbutton.time;
+	  event->scroll.x = xevent->xbutton.x;
+	  event->scroll.y = xevent->xbutton.y;
+	  event->scroll.x_root = (gfloat) xevent->xbutton.x_root;
+	  event->scroll.y_root = (gfloat) xevent->xbutton.y_root;
+	  event->scroll.state = (GdkModifierType) xevent->xbutton.state;
+	  event->scroll.device = device_manager->core_pointer;
+
+	  if (!set_screen_from_root (display, event, xevent->xbutton.root))
+	    {
+	      return_val = FALSE;
+	      break;
+	    }
+
+          break;
+
+        default:
+	  event->button.type = GDK_BUTTON_PRESS;
+	  event->button.window = window;
+	  event->button.time = xevent->xbutton.time;
+	  event->button.x = xevent->xbutton.x;
+	  event->button.y = xevent->xbutton.y;
+	  event->button.x_root = (gfloat) xevent->xbutton.x_root;
+	  event->button.y_root = (gfloat) xevent->xbutton.y_root;
+	  event->button.axes = NULL;
+	  event->button.state = (GdkModifierType) xevent->xbutton.state;
+	  event->button.button = xevent->xbutton.button;
+	  event->button.device = device_manager->core_pointer;
+
+	  if (!set_screen_from_root (display, event, xevent->xbutton.root))
+            return_val = FALSE;
+
+          break;
+	}
+
+      set_user_time (window, event);
+
+      break;
+
+    case ButtonRelease:
+      GDK_NOTE (EVENTS,
+		g_message ("button release:\twindow: %ld  x,y: %d %d  button: %d",
+			   xevent->xbutton.window,
+			   xevent->xbutton.x, xevent->xbutton.y,
+			   xevent->xbutton.button));
+
+      if (window_private == NULL)
+	{
+	  return_val = FALSE;
+	  break;
+	}
+
+      /* We treat button presses as scroll wheel events, so ignore the release */
+      if (xevent->xbutton.button == 4 || xevent->xbutton.button == 5 ||
+          xevent->xbutton.button == 6 || xevent->xbutton.button == 7)
+	{
+	  return_val = FALSE;
+	  break;
+	}
+
+      event->button.type = GDK_BUTTON_RELEASE;
+      event->button.window = window;
+      event->button.time = xevent->xbutton.time;
+      event->button.x = xevent->xbutton.x;
+      event->button.y = xevent->xbutton.y;
+      event->button.x_root = (gfloat) xevent->xbutton.x_root;
+      event->button.y_root = (gfloat) xevent->xbutton.y_root;
+      event->button.axes = NULL;
+      event->button.state = (GdkModifierType) xevent->xbutton.state;
+      event->button.button = xevent->xbutton.button;
+      event->button.device = device_manager->core_pointer;
+
+      if (!set_screen_from_root (display, event, xevent->xbutton.root))
+        return_val = FALSE;
+
+      break;
+
+    case MotionNotify:
+      GDK_NOTE (EVENTS,
+		g_message ("motion notify:\t\twindow: %ld  x,y: %d %d  hint: %s",
+			   xevent->xmotion.window,
+			   xevent->xmotion.x, xevent->xmotion.y,
+			   (xevent->xmotion.is_hint) ? "true" : "false"));
+
+      if (window_private == NULL)
+	{
+	  return_val = FALSE;
+	  break;
+	}
+
+      event->motion.type = GDK_MOTION_NOTIFY;
+      event->motion.window = window;
+      event->motion.time = xevent->xmotion.time;
+      event->motion.x = xevent->xmotion.x;
+      event->motion.y = xevent->xmotion.y;
+      event->motion.x_root = (gfloat) xevent->xmotion.x_root;
+      event->motion.y_root = (gfloat) xevent->xmotion.y_root;
+      event->motion.axes = NULL;
+      event->motion.state = (GdkModifierType) xevent->xmotion.state;
+      event->motion.is_hint = xevent->xmotion.is_hint;
+      event->motion.device = device_manager->core_pointer;
+
+      if (!set_screen_from_root (display, event, xevent->xbutton.root))
+	{
+	  return_val = FALSE;
+	  break;
+	}
+
+      break;
+
+    case EnterNotify:
+      GDK_NOTE (EVENTS,
+		g_message ("enter notify:\t\twindow: %ld  detail: %d subwin: %ld",
+			   xevent->xcrossing.window,
+			   xevent->xcrossing.detail,
+			   xevent->xcrossing.subwindow));
+
+      if (window_private == NULL)
+        {
+          return_val = FALSE;
+          break;
+        }
+
+      if (!set_screen_from_root (display, event, xevent->xbutton.root))
+	{
+	  return_val = FALSE;
+	  break;
+	}
+
+      event->crossing.type = GDK_ENTER_NOTIFY;
+      event->crossing.window = window;
+      event->crossing.device = device_manager->core_pointer;
+
+      /* If the subwindow field of the XEvent is non-NULL, then
+       *  lookup the corresponding GdkWindow.
+       */
+      if (xevent->xcrossing.subwindow != None)
+	event->crossing.subwindow = gdk_window_lookup_for_display (display, xevent->xcrossing.subwindow);
+      else
+	event->crossing.subwindow = NULL;
+
+      event->crossing.time = xevent->xcrossing.time;
+      event->crossing.x = xevent->xcrossing.x;
+      event->crossing.y = xevent->xcrossing.y;
+      event->crossing.x_root = xevent->xcrossing.x_root;
+      event->crossing.y_root = xevent->xcrossing.y_root;
+
+      event->crossing.mode = translate_crossing_mode (xevent->xcrossing.mode);
+      event->crossing.detail = translate_notify_type (xevent->xcrossing.detail);
+
+      event->crossing.focus = xevent->xcrossing.focus;
+      event->crossing.state = xevent->xcrossing.state;
+
+      break;
+
+    case LeaveNotify:
+      GDK_NOTE (EVENTS,
+		g_message ("leave notify:\t\twindow: %ld  detail: %d subwin: %ld",
+			   xevent->xcrossing.window,
+			   xevent->xcrossing.detail, xevent->xcrossing.subwindow));
+
+      if (window_private == NULL)
+        {
+          return_val = FALSE;
+          break;
+        }
+
+      if (!set_screen_from_root (display, event, xevent->xbutton.root))
+	{
+	  return_val = FALSE;
+	  break;
+	}
+
+      event->crossing.type = GDK_LEAVE_NOTIFY;
+      event->crossing.window = window;
+      event->crossing.device = device_manager->core_pointer;
+
+      /* If the subwindow field of the XEvent is non-NULL, then
+       *  lookup the corresponding GdkWindow.
+       */
+      if (xevent->xcrossing.subwindow != None)
+	event->crossing.subwindow = gdk_window_lookup_for_display (display, xevent->xcrossing.subwindow);
+      else
+	event->crossing.subwindow = NULL;
+
+      event->crossing.time = xevent->xcrossing.time;
+      event->crossing.x = xevent->xcrossing.x;
+      event->crossing.y = xevent->xcrossing.y;
+      event->crossing.x_root = xevent->xcrossing.x_root;
+      event->crossing.y_root = xevent->xcrossing.y_root;
+
+      event->crossing.mode = translate_crossing_mode (xevent->xcrossing.mode);
+      event->crossing.detail = translate_notify_type (xevent->xcrossing.detail);
+
+      event->crossing.focus = xevent->xcrossing.focus;
+      event->crossing.state = xevent->xcrossing.state;
+
+      break;
+
+      /* We only care about focus events that indicate that _this_
+       * window (not a ancestor or child) got or lost the focus
+       */
+    case FocusIn:
+      GDK_NOTE (EVENTS,
+		g_message ("focus in:\t\twindow: %ld, detail: %s, mode: %s",
+			   xevent->xfocus.window,
+			   notify_details[xevent->xfocus.detail],
+			   notify_modes[xevent->xfocus.mode]));
+
+      if (toplevel)
+	{
+	  gboolean had_focus = HAS_FOCUS (toplevel);
+
+	  switch (xevent->xfocus.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 &&
+		  xevent->xfocus.mode != NotifyGrab &&
+		  xevent->xfocus.mode != NotifyUngrab)
+		toplevel->has_pointer_focus = FALSE;
+
+	      /* fall through */
+	    case NotifyNonlinear:
+	    case NotifyNonlinearVirtual:
+	      if (xevent->xfocus.mode != NotifyGrab &&
+		  xevent->xfocus.mode != NotifyUngrab)
+		toplevel->has_focus_window = TRUE;
+	      /* We pretend that the focus moves to the grab
+	       * window, so we pay attention to NotifyGrab
+	       * NotifyUngrab, and ignore NotifyWhileGrabbed
+	       */
+	      if (xevent->xfocus.mode != NotifyWhileGrabbed)
+		toplevel->has_focus = TRUE;
+	      break;
+	    case NotifyPointer:
+	      /* The X server sends NotifyPointer/NotifyGrab,
+	       * but the pointer focus is ignored while a
+	       * grab is in effect
+	       */
+	      if (xevent->xfocus.mode != NotifyGrab &&
+		  xevent->xfocus.mode != NotifyUngrab)
+		toplevel->has_pointer_focus = TRUE;
+	      break;
+	    case NotifyInferior:
+	    case NotifyPointerRoot:
+	    case NotifyDetailNone:
+	      break;
+	    }
+
+	  if (HAS_FOCUS (toplevel) != had_focus)
+	    generate_focus_event (device_manager, window, TRUE);
+	}
+      break;
+    case FocusOut:
+      GDK_NOTE (EVENTS,
+		g_message ("focus out:\t\twindow: %ld, detail: %s, mode: %s",
+			   xevent->xfocus.window,
+			   notify_details[xevent->xfocus.detail],
+			   notify_modes[xevent->xfocus.mode]));
+
+      if (toplevel)
+	{
+	  gboolean had_focus = HAS_FOCUS (toplevel);
+
+	  switch (xevent->xfocus.detail)
+	    {
+	    case NotifyAncestor:
+	    case NotifyVirtual:
+	      /* When the focus moves from the window or a descendent
+	       * of the window to an ancestor of the window, *and* the
+	       * pointer is inside the window, then we were previously
+	       * receiving keystroke events in the has_focus_window
+	       * case and are now receiving them in the
+	       * has_pointer_focus case.
+	       */
+	      if (toplevel->has_pointer &&
+		  xevent->xfocus.mode != NotifyGrab &&
+		  xevent->xfocus.mode != NotifyUngrab)
+		toplevel->has_pointer_focus = TRUE;
+
+	      /* fall through */
+	    case NotifyNonlinear:
+	    case NotifyNonlinearVirtual:
+	      if (xevent->xfocus.mode != NotifyGrab &&
+		  xevent->xfocus.mode != NotifyUngrab)
+		toplevel->has_focus_window = FALSE;
+	      if (xevent->xfocus.mode != NotifyWhileGrabbed)
+		toplevel->has_focus = FALSE;
+	      break;
+	    case NotifyPointer:
+	      if (xevent->xfocus.mode != NotifyGrab &&
+		  xevent->xfocus.mode != NotifyUngrab)
+		toplevel->has_pointer_focus = FALSE;
+	    break;
+	    case NotifyInferior:
+	    case NotifyPointerRoot:
+	    case NotifyDetailNone:
+	      break;
+	    }
+
+	  if (HAS_FOCUS (toplevel) != had_focus)
+	    generate_focus_event (device_manager, window, FALSE);
+	}
+      break;
+
+    default:
+#ifdef HAVE_XKB
+      if (xevent->type == display_x11->xkb_event_type)
+	{
+	  XkbEvent *xkb_event = (XkbEvent *)xevent;
+
+	  switch (xkb_event->any.xkb_type)
+	    {
+	    case XkbNewKeyboardNotify:
+	    case XkbMapNotify:
+	      _gdk_keymap_keys_changed (display);
+
+	      return_val = FALSE;
+	      break;
+
+	    case XkbStateNotify:
+	      _gdk_keymap_state_changed (display, xevent);
+	      break;
+	    }
+	}
+      else
+#endif
+        return_val = FALSE;
+    }
+
+ done:
+  if (return_val)
+    {
+      if (event->any.window)
+	g_object_ref (event->any.window);
+
+      if (((event->any.type == GDK_ENTER_NOTIFY) ||
+	   (event->any.type == GDK_LEAVE_NOTIFY)) &&
+	  (event->crossing.subwindow != NULL))
+	g_object_ref (event->crossing.subwindow);
+    }
+  else
+    {
+      /* Mark this event as having no resources to be freed */
+      event->any.window = NULL;
+      event->any.type = GDK_NOTHING;
+    }
+
+  if (window)
+    g_object_unref (window);
+
+  return return_val;
+}
+
+static GList *
+gdk_device_manager_core_get_devices (GdkDeviceManager *device_manager,
+                                     GdkDeviceType     type)
+{
+  GdkDeviceManagerCore *device_manager_core;
+  GList *devices = NULL;
+
+  if (type == GDK_DEVICE_TYPE_MASTER)
+    {
+      device_manager_core = (GdkDeviceManagerCore *) device_manager;
+      devices = g_list_prepend (devices, device_manager_core->core_keyboard);
+      devices = g_list_prepend (devices, device_manager_core->core_pointer);
+    }
+
+  return devices;
+}
diff --git a/gdk/x11/gdkdevicemanager-core.h b/gdk/x11/gdkdevicemanager-core.h
new file mode 100644
index 0000000..0a337fc
--- /dev/null
+++ b/gdk/x11/gdkdevicemanager-core.h
@@ -0,0 +1,54 @@
+/* GDK - The GIMP Drawing Kit
+ * Copyright (C) 2009 Carlos Garnacho <carlosg@gnome.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * 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.
+ */
+
+#ifndef __GDK_DEVICE_MANAGER_CORE_H__
+#define __GDK_DEVICE_MANAGER_CORE_H__
+
+#include <gdk/gdkdevicemanager.h>
+
+G_BEGIN_DECLS
+
+#define GDK_TYPE_DEVICE_MANAGER_CORE         (gdk_device_manager_core_get_type ())
+#define GDK_DEVICE_MANAGER_CORE(o)           (G_TYPE_CHECK_INSTANCE_CAST ((o), GDK_TYPE_DEVICE_MANAGER_CORE, GdkDeviceManagerCore))
+#define GDK_DEVICE_MANAGER_CORE_CLASS(c)     (G_TYPE_CHECK_CLASS_CAST ((c), GDK_TYPE_DEVICE_MANAGER_CORE, GdkDeviceManagerCoreClass))
+#define GDK_IS_DEVICE_MANAGER_CORE(o)        (G_TYPE_CHECK_INSTANCE_TYPE ((o), GDK_TYPE_DEVICE_MANAGER_CORE))
+#define GDK_IS_DEVICE_MANAGER_CORE_CLASS(c)  (G_TYPE_CHECK_CLASS_TYPE ((c), GDK_TYPE_DEVICE_MANAGER_CORE))
+#define GDK_DEVICE_MANAGER_CORE_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GDK_TYPE_DEVICE_MANAGER_CORE, GdkDeviceManagerCoreClass))
+
+typedef struct _GdkDeviceManagerCore GdkDeviceManagerCore;
+typedef struct _GdkDeviceManagerCoreClass GdkDeviceManagerCoreClass;
+
+struct _GdkDeviceManagerCore
+{
+  GdkDeviceManager parent_object;
+  GdkDevice *core_pointer;
+  GdkDevice *core_keyboard;
+};
+
+struct _GdkDeviceManagerCoreClass
+{
+  GdkDeviceManagerClass parent_class;
+};
+
+GType gdk_device_manager_core_get_type (void) G_GNUC_CONST;
+
+
+G_END_DECLS
+
+#endif /* __GDK_DEVICE_MANAGER_CORE_H__ */
diff --git a/gdk/x11/gdkdevicemanager-x11.c b/gdk/x11/gdkdevicemanager-x11.c
new file mode 100644
index 0000000..02f4ca2
--- /dev/null
+++ b/gdk/x11/gdkdevicemanager-x11.c
@@ -0,0 +1,78 @@
+/* GDK - The GIMP Drawing Kit
+ * Copyright (C) 2009 Carlos Garnacho <carlosg@gnome.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * 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.
+ */
+
+#include "config.h"
+#include "gdkx.h"
+#include "gdkdevicemanager-core.h"
+
+#ifdef XINPUT_2
+#  include "gdkdevicemanager-xi2.h"
+#else
+#  ifdef XINPUT_XFREE
+#    include "gdkdevicemanager-xi.h"
+#  endif
+#endif
+
+GdkDeviceManager *
+_gdk_device_manager_new (GdkDisplay *display)
+{
+  GdkDeviceManager *device_manager;
+  int opcode, firstevent, firsterror;
+  int major, minor;
+  Display *xdisplay;
+
+  if (G_UNLIKELY (!g_getenv ("GDK_CORE_DEVICE_EVENTS")))
+    {
+#if defined (XINPUT_2) || defined (XINPUT_XFREE)
+      xdisplay = GDK_DISPLAY_XDISPLAY (display);
+
+      if (XQueryExtension (xdisplay, "XInputExtension",
+                           &opcode, &firstevent, &firsterror))
+        {
+#if defined (XINPUT_2)
+          major = 2;
+          minor = 0;
+
+          if (XIQueryVersion (xdisplay, &major, &minor) != BadRequest)
+            {
+              GdkDeviceManagerXI2 *device_manager_xi2;
+
+              device_manager = g_object_new (GDK_TYPE_DEVICE_MANAGER_XI2,
+                                             "display", display,
+                                             NULL);
+
+              device_manager_xi2 = GDK_DEVICE_MANAGER_XI2 (device_manager);
+              device_manager_xi2->opcode = opcode;
+
+              return device_manager;
+            }
+#else
+          return g_object_new (GDK_TYPE_DEVICE_MANAGER_XI,
+                               "display", display,
+                               "event-base", firstevent,
+                               NULL);
+#endif
+        }
+#endif /* XINPUT_2 || XINPUT_XFREE */
+    }
+
+  return g_object_new (GDK_TYPE_DEVICE_MANAGER_CORE,
+                       "display", display,
+                       NULL);
+}
diff --git a/gdk/x11/gdkdevicemanager-xi.c b/gdk/x11/gdkdevicemanager-xi.c
new file mode 100644
index 0000000..c857eb7
--- /dev/null
+++ b/gdk/x11/gdkdevicemanager-xi.c
@@ -0,0 +1,645 @@
+/* GDK - The GIMP Drawing Kit
+ * Copyright (C) 2009 Carlos Garnacho <carlosg@gnome.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * 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.
+ */
+
+#include "gdkdevicemanager-xi.h"
+#include "gdkeventtranslator.h"
+#include "gdkdevice-xi.h"
+#include "gdkintl.h"
+#include "gdkx.h"
+
+#include <X11/extensions/XInput.h>
+
+#define GDK_DEVICE_MANAGER_XI_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), GDK_TYPE_DEVICE_MANAGER_XI, GdkDeviceManagerXIPrivate))
+
+typedef struct GdkDeviceManagerXIPrivate GdkDeviceManagerXIPrivate;
+
+struct GdkDeviceManagerXIPrivate
+{
+  GHashTable *id_table;
+  gint event_base;
+  GList *devices;
+  gboolean ignore_core_events;
+};
+
+static void gdk_device_manager_xi_constructed  (GObject      *object);
+static void gdk_device_manager_xi_finalize     (GObject      *object);
+static void gdk_device_manager_xi_set_property (GObject      *object,
+                                                guint         prop_id,
+                                                const GValue *value,
+                                                GParamSpec   *pspec);
+static void gdk_device_manager_xi_get_property (GObject      *object,
+                                                guint         prop_id,
+                                                GValue       *value,
+                                                GParamSpec   *pspec);
+
+static void     gdk_device_manager_xi_event_translator_init  (GdkEventTranslatorIface *iface);
+static gboolean gdk_device_manager_xi_translate_event (GdkEventTranslator *translator,
+                                                       GdkDisplay         *display,
+                                                       GdkEvent           *event,
+                                                       XEvent             *xevent);
+static GList *  gdk_device_manager_xi_get_devices     (GdkDeviceManager  *device_manager,
+                                                       GdkDeviceType      type);
+
+
+G_DEFINE_TYPE_WITH_CODE (GdkDeviceManagerXI, gdk_device_manager_xi, GDK_TYPE_DEVICE_MANAGER_CORE,
+                         G_IMPLEMENT_INTERFACE (GDK_TYPE_EVENT_TRANSLATOR,
+                                                gdk_device_manager_xi_event_translator_init))
+
+enum {
+  PROP_0,
+  PROP_EVENT_BASE
+};
+
+static void
+gdk_device_manager_xi_class_init (GdkDeviceManagerXIClass *klass)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+  GdkDeviceManagerClass *device_manager_class = GDK_DEVICE_MANAGER_CLASS (klass);
+
+  object_class->constructed = gdk_device_manager_xi_constructed;
+  object_class->finalize = gdk_device_manager_xi_finalize;
+  object_class->set_property = gdk_device_manager_xi_set_property;
+  object_class->get_property = gdk_device_manager_xi_get_property;
+
+  device_manager_class->get_devices = gdk_device_manager_xi_get_devices;
+
+  g_object_class_install_property (object_class,
+				   PROP_EVENT_BASE,
+				   g_param_spec_int ("event-base",
+                                                     P_("Event base"),
+                                                     P_("Event base for XInput events"),
+                                                     0, G_MAXINT, 0,
+                                                     G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+
+  g_type_class_add_private (object_class, sizeof (GdkDeviceManagerXIPrivate));
+}
+
+static GdkFilterReturn
+window_input_info_filter (GdkXEvent *xevent,
+                          GdkEvent  *event,
+                          gpointer   user_data)
+{
+  GdkDeviceManager *device_manager;
+  GdkDisplay *display;
+  GdkWindow *window;
+  XEvent *xev;
+
+  device_manager = user_data;
+  xev = (XEvent *) xevent;
+
+  display = gdk_device_manager_get_display (device_manager);
+  window = gdk_window_lookup_for_display (display, xev->xany.window);
+
+  if (window && xev->type == ConfigureNotify)
+    gdk_device_xi_update_window_info (window);
+
+  return GDK_FILTER_CONTINUE;
+}
+
+static void
+gdk_device_manager_xi_init (GdkDeviceManagerXI *device_manager)
+{
+  GdkDeviceManagerXIPrivate *priv;
+
+  priv = GDK_DEVICE_MANAGER_XI_GET_PRIVATE (device_manager);
+  priv->id_table = g_hash_table_new_full (NULL, NULL, NULL,
+                                          (GDestroyNotify) g_object_unref);
+
+  gdk_window_add_filter (NULL, window_input_info_filter, device_manager);
+}
+
+static void
+translate_class_info (GdkDevice   *device,
+                      XDeviceInfo *info)
+{
+  GdkDeviceXI *device_xi;
+  XAnyClassPtr class;
+  gint i, j;
+
+  device_xi = GDK_DEVICE_XI (device);
+  class = info->inputclassinfo;
+
+  for (i = 0; i < info->num_classes; i++)
+    {
+      switch (class->class) {
+      case ButtonClass:
+	break;
+      case KeyClass:
+	{
+	  XKeyInfo *xki = (XKeyInfo *)class;
+          guint num_keys;
+
+          num_keys = xki->max_keycode - xki->min_keycode + 1;
+          _gdk_device_set_keys (device, num_keys);
+
+          device_xi->min_keycode = xki->min_keycode;
+
+	  break;
+	}
+      case ValuatorClass:
+	{
+	  XValuatorInfo *xvi = (XValuatorInfo *)class;
+
+          for (j = 0; j < xvi->num_axes; j++)
+            {
+              GdkAxisUse use;
+
+              switch (j)
+                {
+                case 0:
+                  use = GDK_AXIS_X;
+                  break;
+                case 1:
+                  use = GDK_AXIS_Y;
+                  break;
+                case 2:
+                  use = GDK_AXIS_PRESSURE;
+                  break;
+                case 3:
+                  use = GDK_AXIS_XTILT;
+                  break;
+                case 4:
+                  use = GDK_AXIS_YTILT;
+                  break;
+                case 5:
+                  use = GDK_AXIS_WHEEL;
+                  break;
+                default:
+                  use = GDK_AXIS_IGNORE;
+                }
+
+              _gdk_device_add_axis (device,
+                                    GDK_NONE,
+                                    use,
+                                    xvi->axes[j].min_value,
+                                    xvi->axes[j].max_value,
+                                    xvi->axes[j].resolution);
+            }
+
+	  break;
+	}
+      }
+
+      class = (XAnyClassPtr) (((char *) class) + class->length);
+    }
+}
+
+static GdkDevice *
+create_device (GdkDeviceManager *device_manager,
+               GdkDisplay       *display,
+               XDeviceInfo      *info)
+{
+  GdkInputSource input_source;
+  GdkDevice *device;
+
+  if (info->use != IsXExtensionPointer &&
+      info->use != IsXExtensionKeyboard)
+    return NULL;
+
+  if (info->use == IsXExtensionKeyboard)
+    input_source = GDK_SOURCE_KEYBOARD;
+  else
+    {
+      gchar *tmp_name;
+
+      tmp_name = g_ascii_strdown (info->name, -1);
+
+      if (strstr (tmp_name, "eraser"))
+        input_source = GDK_SOURCE_ERASER;
+      else if (strstr (tmp_name, "cursor"))
+        input_source = GDK_SOURCE_CURSOR;
+      else if (strstr (tmp_name, "wacom") ||
+               strstr (tmp_name, "pen"))
+        input_source = GDK_SOURCE_PEN;
+      else
+        input_source = GDK_SOURCE_MOUSE;
+
+      g_free (tmp_name);
+    }
+
+  device = g_object_new (GDK_TYPE_DEVICE_XI,
+                         "name", info->name,
+                         "type", GDK_DEVICE_TYPE_FLOATING,
+                         "input-source", input_source,
+                         "input-mode", GDK_MODE_DISABLED,
+                         "has-cursor", FALSE,
+                         "display", display,
+                         "device-manager", device_manager,
+                         "device-id", info->id,
+                         NULL);
+  translate_class_info (device, info);
+
+  return device;
+}
+
+static void
+gdk_device_manager_xi_constructed (GObject *object)
+{
+  GdkDeviceManagerXIPrivate *priv;
+  XDeviceInfo *devices;
+  gint i, num_devices;
+  GdkDisplay *display;
+
+  priv = GDK_DEVICE_MANAGER_XI_GET_PRIVATE (object);
+  display = gdk_device_manager_get_display (GDK_DEVICE_MANAGER (object));
+  devices = XListInputDevices(GDK_DISPLAY_XDISPLAY (display), &num_devices);
+
+  for(i = 0; i < num_devices; i++)
+    {
+      GdkDevice *device;
+
+      device = create_device (GDK_DEVICE_MANAGER (object),
+                              display, &devices[i]);
+      if (device)
+        {
+          priv->devices = g_list_prepend (priv->devices, device);
+          g_hash_table_insert (priv->id_table,
+                               GINT_TO_POINTER (devices[i].id),
+                               device);
+        }
+    }
+
+  XFreeDeviceList(devices);
+
+  gdk_x11_register_standard_event_type (display,
+                                        priv->event_base,
+                                        15 /* Number of events */);
+
+  if (G_OBJECT_CLASS (gdk_device_manager_xi_parent_class)->constructed)
+    G_OBJECT_CLASS (gdk_device_manager_xi_parent_class)->constructed (object);
+}
+
+static void
+gdk_device_manager_xi_finalize (GObject *object)
+{
+  GdkDeviceManagerXIPrivate *priv;
+
+  priv = GDK_DEVICE_MANAGER_XI_GET_PRIVATE (object);
+
+  g_list_foreach (priv->devices, (GFunc) g_object_unref, NULL);
+  g_list_free (priv->devices);
+
+  g_hash_table_destroy (priv->id_table);
+
+  gdk_window_remove_filter (NULL, window_input_info_filter, object);
+
+  G_OBJECT_CLASS (gdk_device_manager_xi_parent_class)->finalize (object);
+}
+
+static void
+gdk_device_manager_xi_set_property (GObject      *object,
+                                    guint         prop_id,
+                                    const GValue *value,
+                                    GParamSpec   *pspec)
+{
+  GdkDeviceManagerXIPrivate *priv;
+
+  priv = GDK_DEVICE_MANAGER_XI_GET_PRIVATE (object);
+
+  switch (prop_id)
+    {
+    case PROP_EVENT_BASE:
+      priv->event_base = g_value_get_int (value);
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+    }
+}
+
+static void
+gdk_device_manager_xi_get_property (GObject    *object,
+                                    guint       prop_id,
+                                    GValue     *value,
+                                    GParamSpec *pspec)
+{
+  GdkDeviceManagerXIPrivate *priv;
+
+  priv = GDK_DEVICE_MANAGER_XI_GET_PRIVATE (object);
+
+  switch (prop_id)
+    {
+    case PROP_EVENT_BASE:
+      g_value_set_int (value, priv->event_base);
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+    }
+}
+
+static void
+gdk_device_manager_xi_event_translator_init (GdkEventTranslatorIface *iface)
+{
+  iface->translate_event = gdk_device_manager_xi_translate_event;
+}
+
+/* combine the state of the core device and the device state
+ * into one - for now we do this in a simple-minded manner -
+ * we just take the keyboard portion of the core device and
+ * the button portion (all of?) the device state.
+ * Any button remapping should go on here.
+ */
+static guint
+translate_state (guint state, guint device_state)
+{
+  return device_state | (state & 0xFF);
+}
+
+static GdkDevice *
+lookup_device (GdkDeviceManagerXI *device_manager,
+               XEvent             *xevent)
+{
+  GdkDeviceManagerXIPrivate *priv;
+  guint32 device_id;
+
+  priv = GDK_DEVICE_MANAGER_XI_GET_PRIVATE (device_manager);
+
+  /* This is a sort of a hack, as there isn't any XDeviceAnyEvent -
+     but it's potentially faster than scanning through the types of
+     every device. If we were deceived, then it won't match any of
+     the types for the device anyways */
+  device_id = ((XDeviceButtonEvent *)xevent)->deviceid;
+
+  return g_hash_table_lookup (priv->id_table, GINT_TO_POINTER (device_id));
+}
+
+static gboolean
+gdk_device_manager_xi_translate_event (GdkEventTranslator *translator,
+                                       GdkDisplay         *display,
+                                       GdkEvent           *event,
+                                       XEvent             *xevent)
+{
+  GdkDeviceManagerXIPrivate *priv;
+  GdkEventTranslatorIface *parent_iface;
+  GdkDeviceXI *device_xi;
+  GdkDevice *device;
+  GdkWindow *window;
+
+  parent_iface = g_type_interface_peek_parent (GDK_EVENT_TRANSLATOR_GET_IFACE (translator));
+  priv = GDK_DEVICE_MANAGER_XI_GET_PRIVATE (translator);
+
+  if (!priv->ignore_core_events &&
+      parent_iface->translate_event (translator, display, event, xevent))
+    return TRUE;
+
+  device = lookup_device (GDK_DEVICE_MANAGER_XI (translator), xevent);
+  device_xi = GDK_DEVICE_XI (device);
+
+  if (!device)
+    return FALSE;
+
+  window = gdk_window_lookup_for_display (display, xevent->xany.window);
+
+  if (!window)
+    return FALSE;
+
+  if ((xevent->type == device_xi->button_press_type) ||
+      (xevent->type == device_xi->button_release_type))
+    {
+      XDeviceButtonEvent *xdbe = (XDeviceButtonEvent *) xevent;
+
+      event->button.type = (xdbe->type == device_xi->button_press_type) ?
+        GDK_BUTTON_PRESS : GDK_BUTTON_RELEASE;
+
+      event->button.device = device;
+      event->button.window = g_object_ref (window);
+      event->button.time = xdbe->time;
+
+      event->button.x_root = (gdouble) xdbe->x_root;
+      event->button.y_root = (gdouble) xdbe->y_root;
+
+      event->button.axes = g_new0 (gdouble, device->num_axes);
+      gdk_device_xi_update_axes (device, xdbe->axes_count,
+                                 xdbe->first_axis, xdbe->axis_data);
+      gdk_device_xi_translate_axes (device, window,
+                                    device_xi->axis_data,
+                                    event->button.axes,
+                                    &event->button.x,
+                                    &event->button.y);
+
+      event->button.state = translate_state (xdbe->state, xdbe->device_state);
+      event->button.button = xdbe->button;
+
+      if (event->button.type == GDK_BUTTON_PRESS)
+	_gdk_event_button_generate (gdk_drawable_get_display (event->button.window),
+				    event);
+
+      GDK_NOTE (EVENTS,
+	g_print ("button %s:\t\twindow: %ld  device: %ld  x,y: %f %f  button: %d\n",
+		 (event->button.type == GDK_BUTTON_PRESS) ? "press" : "release",
+		 xdbe->window,
+		 xdbe->deviceid,
+		 event->button.x, event->button.y,
+		 xdbe->button));
+
+      /* Update the timestamp of the latest user interaction, if the event has
+       * a valid timestamp.
+       */
+      if (gdk_event_get_time (event) != GDK_CURRENT_TIME)
+	gdk_x11_window_set_user_time (gdk_window_get_toplevel (window),
+				      gdk_event_get_time (event));
+      return TRUE;
+    }
+
+  if ((xevent->type == device_xi->key_press_type) ||
+      (xevent->type == device_xi->key_release_type))
+    {
+      XDeviceKeyEvent *xdke = (XDeviceKeyEvent *) xevent;
+
+      GDK_NOTE (EVENTS,
+	g_print ("device key %s:\twindow: %ld  device: %ld  keycode: %d\n",
+		 (event->key.type == GDK_KEY_PRESS) ? "press" : "release",
+		 xdke->window,
+		 xdke->deviceid,
+		 xdke->keycode));
+
+      if (xdke->keycode < device_xi->min_keycode ||
+	  xdke->keycode >= device_xi->min_keycode + device->num_keys)
+	{
+	  g_warning ("Invalid device key code received");
+	  return FALSE;
+	}
+
+      event->key.keyval = device->keys[xdke->keycode - device_xi->min_keycode].keyval;
+
+      if (event->key.keyval == 0)
+	{
+	  GDK_NOTE (EVENTS,
+	    g_print ("\t\ttranslation - NONE\n"));
+
+	  return FALSE;
+	}
+
+      event->key.type = (xdke->type == device_xi->key_press_type) ?
+	GDK_KEY_PRESS : GDK_KEY_RELEASE;
+
+      event->key.window = g_object_ref (window);
+      event->key.time = xdke->time;
+
+      event->key.state = translate_state (xdke->state, xdke->device_state)
+	| device->keys[xdke->keycode - device_xi->min_keycode].modifiers;
+
+      /* Add a string translation for the key event */
+      if ((event->key.keyval >= 0x20) && (event->key.keyval <= 0xFF))
+	{
+	  event->key.length = 1;
+	  event->key.string = g_new (gchar, 2);
+	  event->key.string[0] = (gchar) event->key.keyval;
+	  event->key.string[1] = 0;
+	}
+      else
+	{
+	  event->key.length = 0;
+	  event->key.string = g_new0 (gchar, 1);
+	}
+
+      GDK_NOTE (EVENTS,
+	g_print ("\t\ttranslation - keyval: %d modifiers: %#x\n",
+		 event->key.keyval,
+		 event->key.state));
+
+      /* Update the timestamp of the latest user interaction, if the event has
+       * a valid timestamp.
+       */
+      if (gdk_event_get_time (event) != GDK_CURRENT_TIME)
+	gdk_x11_window_set_user_time (gdk_window_get_toplevel (window),
+				      gdk_event_get_time (event));
+      return TRUE;
+    }
+
+  if (xevent->type == device_xi->motion_notify_type)
+    {
+      XDeviceMotionEvent *xdme = (XDeviceMotionEvent *) xevent;
+
+      event->motion.device = device;
+
+      if (device_xi->in_proximity)
+        priv->ignore_core_events = TRUE;
+
+      event->motion.x_root = (gdouble) xdme->x_root;
+      event->motion.y_root = (gdouble) xdme->y_root;
+
+      event->motion.axes = g_new0 (gdouble, device->num_axes);
+      gdk_device_xi_update_axes (device, xdme->axes_count,
+                                 xdme->first_axis, xdme->axis_data);
+      gdk_device_xi_translate_axes (device, window,
+                                    device_xi->axis_data,
+                                    event->motion.axes,
+                                    &event->motion.x,
+                                    &event->motion.y);
+
+      event->motion.type = GDK_MOTION_NOTIFY;
+      event->motion.window = g_object_ref (window);
+      event->motion.time = xdme->time;
+      event->motion.state = translate_state (xdme->state,
+                                             xdme->device_state);
+      event->motion.is_hint = xdme->is_hint;
+
+      GDK_NOTE (EVENTS,
+	g_print ("motion notify:\t\twindow: %ld  device: %ld  x,y: %f %f  state %#4x  hint: %s\n",
+		 xdme->window,
+		 xdme->deviceid,
+		 event->motion.x, event->motion.y,
+		 event->motion.state,
+		 (xdme->is_hint) ? "true" : "false"));
+
+
+      /* Update the timestamp of the latest user interaction, if the event has
+       * a valid timestamp.
+       */
+      if (gdk_event_get_time (event) != GDK_CURRENT_TIME)
+	gdk_x11_window_set_user_time (gdk_window_get_toplevel (window),
+				      gdk_event_get_time (event));
+      return TRUE;
+    }
+
+  if (xevent->type == device_xi->proximity_in_type ||
+      xevent->type == device_xi->proximity_out_type)
+    {
+      XProximityNotifyEvent *xpne = (XProximityNotifyEvent *) xevent;
+
+      if (xevent->type == device_xi->proximity_in_type)
+        {
+          event->proximity.type = GDK_PROXIMITY_IN;
+          device_xi->in_proximity = TRUE;
+          priv->ignore_core_events = TRUE;
+        }
+      else
+        {
+          event->proximity.type = GDK_PROXIMITY_OUT;
+          device_xi->in_proximity = FALSE;
+          priv->ignore_core_events = FALSE;
+        }
+
+      event->proximity.device = device;
+      event->proximity.window = g_object_ref (window);
+      event->proximity.time = xpne->time;
+
+      /* Update the timestamp of the latest user interaction, if the event has
+       * a valid timestamp.
+       */
+      if (gdk_event_get_time (event) != GDK_CURRENT_TIME)
+	gdk_x11_window_set_user_time (gdk_window_get_toplevel (window),
+				      gdk_event_get_time (event));
+      return TRUE;
+    }
+
+  if (xevent->type == device_xi->state_notify_type)
+    {
+      XDeviceStateNotifyEvent *xdse = (XDeviceStateNotifyEvent *) xevent;
+      XInputClass *input_class = (XInputClass *) xdse->data;
+      int i;
+
+      for (i = 0; i < xdse->num_classes; i++)
+        {
+          if (input_class->class == ValuatorClass)
+            gdk_device_xi_update_axes (device, device->num_axes, 0,
+                                       ((XValuatorState *)input_class)->valuators);
+
+          input_class = (XInputClass *)(((char *)input_class)+input_class->length);
+        }
+
+      GDK_NOTE (EVENTS,
+                g_print ("device state notify:\t\twindow: %ld  device: %ld\n",
+                         xdse->window,
+                         xdse->deviceid));
+
+      return FALSE;
+    }
+
+  return FALSE;
+}
+
+static GList *
+gdk_device_manager_xi_get_devices (GdkDeviceManager *device_manager,
+                                   GdkDeviceType     type)
+{
+  GdkDeviceManagerXIPrivate *priv;
+
+  if (type == GDK_DEVICE_TYPE_MASTER)
+    return GDK_DEVICE_MANAGER_CLASS (gdk_device_manager_xi_parent_class)->get_devices (device_manager, type);
+  else if (type == GDK_DEVICE_TYPE_FLOATING)
+    {
+      priv = GDK_DEVICE_MANAGER_XI_GET_PRIVATE (device_manager);
+      return g_list_copy (priv->devices);
+    }
+  else
+    return NULL;
+}
diff --git a/gdk/x11/gdkdevicemanager-xi.h b/gdk/x11/gdkdevicemanager-xi.h
new file mode 100644
index 0000000..3b0e976
--- /dev/null
+++ b/gdk/x11/gdkdevicemanager-xi.h
@@ -0,0 +1,53 @@
+/* GDK - The GIMP Drawing Kit
+ * Copyright (C) 2009 Carlos Garnacho <carlosg@gnome.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * 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.
+ */
+
+#ifndef __GDK_DEVICE_MANAGER_XI_H__
+#define __GDK_DEVICE_MANAGER_XI_H__
+
+#include "gdkdevicemanager-core.h"
+
+G_BEGIN_DECLS
+
+#define GDK_TYPE_DEVICE_MANAGER_XI         (gdk_device_manager_xi_get_type ())
+#define GDK_DEVICE_MANAGER_XI(o)           (G_TYPE_CHECK_INSTANCE_CAST ((o), GDK_TYPE_DEVICE_MANAGER_XI, GdkDeviceManagerXI))
+#define GDK_DEVICE_MANAGER_XI_CLASS(c)     (G_TYPE_CHECK_CLASS_CAST ((c), GDK_TYPE_DEVICE_MANAGER_XI, GdkDeviceManagerXIClass))
+#define GDK_IS_DEVICE_MANAGER_XI(o)        (G_TYPE_CHECK_INSTANCE_TYPE ((o), GDK_TYPE_DEVICE_MANAGER_XI))
+#define GDK_IS_DEVICE_MANAGER_XI_CLASS(c)  (G_TYPE_CHECK_CLASS_TYPE ((c), GDK_TYPE_DEVICE_MANAGER_XI))
+#define GDK_DEVICE_MANAGER_XI_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GDK_TYPE_DEVICE_MANAGER_XI, GdkDeviceManagerXIClass))
+
+typedef struct _GdkDeviceManagerXI GdkDeviceManagerXI;
+typedef struct _GdkDeviceManagerXIClass GdkDeviceManagerXIClass;
+
+struct _GdkDeviceManagerXI
+{
+  GdkDeviceManagerCore parent_object;
+  GdkDevice *core_pointer;
+  GdkDevice *core_keyboard;
+};
+
+struct _GdkDeviceManagerXIClass
+{
+  GdkDeviceManagerCoreClass parent_class;
+};
+
+GType gdk_device_manager_xi_get_type (void) G_GNUC_CONST;
+
+G_END_DECLS
+
+#endif /* __GDK_DEVICE_MANAGER_XI_H__ */
diff --git a/gdk/x11/gdkdevicemanager-xi2.c b/gdk/x11/gdkdevicemanager-xi2.c
new file mode 100644
index 0000000..d0ccb2a
--- /dev/null
+++ b/gdk/x11/gdkdevicemanager-xi2.c
@@ -0,0 +1,1131 @@
+/* GDK - The GIMP Drawing Kit
+ * Copyright (C) 2009 Carlos Garnacho <carlosg@gnome.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * 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.
+ */
+
+#include <string.h>
+
+#include <gdk/gdkdeviceprivate.h>
+#include "gdkdevicemanager-xi2.h"
+#include "gdkeventtranslator.h"
+#include "gdkdevice-xi2.h"
+#include "gdkkeysyms.h"
+#include "gdkx.h"
+
+#define HAS_FOCUS(toplevel) ((toplevel)->has_focus || (toplevel)->has_pointer_focus)
+
+
+static void    gdk_device_manager_xi2_constructed (GObject *object);
+static void    gdk_device_manager_xi2_finalize    (GObject *object);
+
+static GList * gdk_device_manager_xi2_get_devices (GdkDeviceManager *device_manager,
+                                                   GdkDeviceType     type);
+
+static void     gdk_device_manager_xi2_event_translator_init (GdkEventTranslatorIface *iface);
+
+static gboolean gdk_device_manager_xi2_translate_event (GdkEventTranslator *translator,
+                                                        GdkDisplay         *display,
+                                                        GdkEvent           *event,
+                                                        XEvent             *xevent);
+static GdkEventMask gdk_device_manager_xi2_get_handled_events   (GdkEventTranslator *translator);
+static void         gdk_device_manager_xi2_select_window_events (GdkEventTranslator *translator,
+                                                                 Window              window,
+                                                                 GdkEventMask        event_mask);
+
+
+G_DEFINE_TYPE_WITH_CODE (GdkDeviceManagerXI2, gdk_device_manager_xi2, GDK_TYPE_DEVICE_MANAGER,
+                         G_IMPLEMENT_INTERFACE (GDK_TYPE_EVENT_TRANSLATOR,
+                                                gdk_device_manager_xi2_event_translator_init))
+
+
+static void
+gdk_device_manager_xi2_class_init (GdkDeviceManagerXI2Class *klass)
+{
+  GdkDeviceManagerClass *device_manager_class = GDK_DEVICE_MANAGER_CLASS (klass);
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+  object_class->constructed = gdk_device_manager_xi2_constructed;
+  object_class->finalize = gdk_device_manager_xi2_finalize;
+
+  device_manager_class->get_devices = gdk_device_manager_xi2_get_devices;
+}
+
+static void
+gdk_device_manager_xi2_init (GdkDeviceManagerXI2 *device_manager)
+{
+  device_manager->id_table = g_hash_table_new_full (g_direct_hash,
+                                                    g_direct_equal,
+                                                    NULL,
+                                                    (GDestroyNotify) g_object_unref);
+}
+
+static void
+_gdk_device_manager_xi2_select_events (GdkDeviceManager *device_manager,
+                                       Window            xwindow,
+                                       XIEventMask      *event_mask)
+{
+  GdkDisplay *display;
+  Display *xdisplay;
+
+  display = gdk_device_manager_get_display (device_manager);
+  xdisplay = GDK_DISPLAY_XDISPLAY (display);
+
+  XISelectEvents (xdisplay, xwindow, event_mask, 1);
+}
+
+static void
+translate_valuator_class (GdkDisplay          *display,
+                          GdkDevice           *device,
+                          XIValuatorClassInfo *info,
+                          gint                 n_valuator)
+{
+  static gboolean initialized = FALSE;
+  static Atom label_atoms [GDK_AXIS_LAST] = { 0 };
+  GdkAxisUse use = GDK_AXIS_IGNORE;
+  GdkAtom label;
+  gint i;
+
+  if (!initialized)
+    {
+      label_atoms [GDK_AXIS_X] = gdk_x11_get_xatom_by_name_for_display (display, "Abs X");
+      label_atoms [GDK_AXIS_Y] = gdk_x11_get_xatom_by_name_for_display (display, "Abs Y");
+      label_atoms [GDK_AXIS_PRESSURE] = gdk_x11_get_xatom_by_name_for_display (display, "Abs Pressure");
+      label_atoms [GDK_AXIS_XTILT] = gdk_x11_get_xatom_by_name_for_display (display, "Abs Tilt X");
+      label_atoms [GDK_AXIS_YTILT] = gdk_x11_get_xatom_by_name_for_display (display, "Abs Tilt Y");
+      label_atoms [GDK_AXIS_WHEEL] = gdk_x11_get_xatom_by_name_for_display (display, "Abs Wheel");
+      initialized = TRUE;
+    }
+
+  for (i = GDK_AXIS_IGNORE; i <= GDK_AXIS_LAST; i++)
+    {
+      if (label_atoms[i] == info->label)
+        {
+          use = i;
+          break;
+        }
+    }
+
+  if (info->label != None)
+    label = gdk_x11_xatom_to_atom_for_display (display, info->label);
+  else
+    label = GDK_NONE;
+
+  _gdk_device_add_axis (device,
+                        label,
+                        use,
+                        info->min,
+                        info->max,
+                        info->resolution);
+}
+
+static void
+translate_device_classes (GdkDisplay      *display,
+                          GdkDevice       *device,
+                          XIAnyClassInfo **classes,
+                          guint            n_classes)
+{
+  gint i, n_valuator = 0;
+
+  g_object_freeze_notify (G_OBJECT (device));
+
+  for (i = 0; i < n_classes; i++)
+    {
+      XIAnyClassInfo *class_info = classes[i];
+
+      switch (class_info->type)
+        {
+        case XIKeyClass:
+          {
+            XIKeyClassInfo *key_info = (XIKeyClassInfo *) class_info;
+            gint i;
+
+            _gdk_device_set_keys (device, key_info->num_keycodes);
+
+            for (i = 0; i < key_info->num_keycodes; i++)
+              gdk_device_set_key (device, i, key_info->keycodes[i], 0);
+          }
+          break;
+        case XIValuatorClass:
+          translate_valuator_class (display, device,
+                                    (XIValuatorClassInfo *) class_info,
+                                    n_valuator);
+          n_valuator++;
+          break;
+        default:
+          /* Ignore */
+          break;
+        }
+    }
+
+  g_object_thaw_notify (G_OBJECT (device));
+}
+
+static GdkDevice *
+create_device (GdkDeviceManager *device_manager,
+               GdkDisplay       *display,
+               XIDeviceInfo     *dev)
+{
+  GdkInputSource input_source;
+  GdkDeviceType type;
+  GdkDevice *device;
+  GdkInputMode mode;
+
+  if (dev->use == XIMasterKeyboard || dev->use == XISlaveKeyboard)
+    input_source = GDK_SOURCE_KEYBOARD;
+  else
+    {
+      gchar *tmp_name;
+
+      tmp_name = g_ascii_strdown (dev->name, -1);
+
+      if (strstr (tmp_name, "eraser"))
+        input_source = GDK_SOURCE_ERASER;
+      else if (strstr (tmp_name, "cursor"))
+        input_source = GDK_SOURCE_CURSOR;
+      else if (strstr (tmp_name, "wacom") ||
+               strstr (tmp_name, "pen"))
+        input_source = GDK_SOURCE_PEN;
+      else
+        input_source = GDK_SOURCE_MOUSE;
+
+      g_free (tmp_name);
+    }
+
+  switch (dev->use)
+    {
+    case XIMasterKeyboard:
+    case XIMasterPointer:
+      type = GDK_DEVICE_TYPE_MASTER;
+      mode = GDK_MODE_SCREEN;
+      break;
+    case XISlaveKeyboard:
+    case XISlavePointer:
+      type = GDK_DEVICE_TYPE_SLAVE;
+      mode = GDK_MODE_DISABLED;
+      break;
+    case XIFloatingSlave:
+    default:
+      type = GDK_DEVICE_TYPE_FLOATING;
+      mode = GDK_MODE_DISABLED;
+      break;
+    }
+
+  device = g_object_new (GDK_TYPE_DEVICE_XI2,
+                         "name", dev->name,
+                         "type", type,
+                         "input-source", input_source,
+                         "input-mode", mode,
+                         "has-cursor", (dev->use == XIMasterPointer),
+                         "display", display,
+                         "device-manager", device_manager,
+                         "device-id", dev->deviceid,
+                         NULL);
+
+  translate_device_classes (display, device, dev->classes, dev->num_classes);
+
+  return device;
+}
+
+static GdkDevice *
+add_device (GdkDeviceManagerXI2 *device_manager,
+            XIDeviceInfo        *dev,
+            gboolean             emit_signal)
+{
+  GdkDisplay *display;
+  GdkDevice *device;
+
+  display = gdk_device_manager_get_display (GDK_DEVICE_MANAGER (device_manager));
+  device = create_device (GDK_DEVICE_MANAGER (device_manager), display, dev);
+
+  g_hash_table_replace (device_manager->id_table,
+                        GINT_TO_POINTER (dev->deviceid),
+                        device);
+
+  if (dev->use == XIMasterPointer || dev->use == XIMasterKeyboard)
+    device_manager->master_devices = g_list_append (device_manager->master_devices, device);
+  else if (dev->use == XISlavePointer || dev->use == XISlaveKeyboard)
+    device_manager->slave_devices = g_list_append (device_manager->slave_devices, device);
+  else if (dev->use == XIFloatingSlave)
+    device_manager->floating_devices = g_list_append (device_manager->floating_devices, device);
+  else
+    g_warning ("Unhandled device: %s\n", device->name);
+
+  if (emit_signal)
+    g_signal_emit_by_name (device_manager, "device-added", device);
+
+  return device;
+}
+
+static void
+remove_device (GdkDeviceManagerXI2 *device_manager,
+               int                  device_id)
+{
+  GdkDevice *device;
+
+  device = g_hash_table_lookup (device_manager->id_table,
+                                GINT_TO_POINTER (device_id));
+
+  if (device)
+    {
+      device_manager->master_devices = g_list_remove (device_manager->master_devices, device);
+      device_manager->slave_devices = g_list_remove (device_manager->slave_devices, device);
+      device_manager->floating_devices = g_list_remove (device_manager->floating_devices, device);
+
+      g_signal_emit_by_name (device_manager, "device-removed", device);
+
+      g_object_run_dispose (G_OBJECT (device));
+
+      g_hash_table_remove (device_manager->id_table,
+                           GINT_TO_POINTER (device_id));
+    }
+}
+
+static void
+relate_devices (gpointer key,
+                gpointer value,
+                gpointer user_data)
+{
+  GdkDeviceManagerXI2 *device_manager;
+  GdkDevice *device, *relative;
+
+  device_manager = user_data;
+  device = g_hash_table_lookup (device_manager->id_table, key);
+  relative = g_hash_table_lookup (device_manager->id_table, value);
+
+  _gdk_device_set_relative (device, relative);
+  _gdk_device_set_relative (relative, device);
+}
+
+static void
+gdk_device_manager_xi2_constructed (GObject *object)
+{
+  GdkDeviceManagerXI2 *device_manager_xi2;
+  GdkDisplay *display;
+  GdkScreen *screen;
+  GHashTable *relations;
+  Display *xdisplay;
+  XIDeviceInfo *info, *dev;
+  int ndevices, i;
+  XIEventMask event_mask;
+  unsigned char mask[2] = { 0 };
+
+  device_manager_xi2 = GDK_DEVICE_MANAGER_XI2 (object);
+  display = gdk_device_manager_get_display (GDK_DEVICE_MANAGER (object));
+  xdisplay = GDK_DISPLAY_XDISPLAY (display);
+  relations = g_hash_table_new (NULL, NULL);
+
+  info = XIQueryDevice(xdisplay, XIAllDevices, &ndevices);
+
+  /* Initialize devices list */
+  for (i = 0; i < ndevices; i++)
+    {
+      GdkDevice *device;
+
+      dev = &info[i];
+      device = add_device (device_manager_xi2, dev, FALSE);
+
+      if (dev->use == XIMasterPointer ||
+          dev->use == XIMasterKeyboard)
+        {
+          g_hash_table_insert (relations,
+                               GINT_TO_POINTER (dev->deviceid),
+                               GINT_TO_POINTER (dev->attachment));
+        }
+    }
+
+  XIFreeDeviceInfo(info);
+
+  /* Stablish relationships between devices */
+  g_hash_table_foreach (relations, relate_devices, object);
+  g_hash_table_destroy (relations);
+
+  /* Connect to hierarchy change events */
+  screen = gdk_display_get_default_screen (display);
+  XISetMask (mask, XI_HierarchyChanged);
+  XISetMask (mask, XI_DeviceChanged);
+
+  event_mask.deviceid = XIAllDevices;
+  event_mask.mask_len = sizeof (mask);
+  event_mask.mask = mask;
+
+  _gdk_device_manager_xi2_select_events (GDK_DEVICE_MANAGER (object),
+                                         GDK_WINDOW_XWINDOW (gdk_screen_get_root_window (screen)),
+                                         &event_mask);
+}
+
+static void
+gdk_device_manager_xi2_finalize (GObject *object)
+{
+  GdkDeviceManagerXI2 *device_manager_xi2;
+
+  device_manager_xi2 = GDK_DEVICE_MANAGER_XI2 (object);
+
+  g_list_foreach (device_manager_xi2->master_devices, (GFunc) g_object_unref, NULL);
+  g_list_free (device_manager_xi2->master_devices);
+
+  g_list_foreach (device_manager_xi2->slave_devices, (GFunc) g_object_unref, NULL);
+  g_list_free (device_manager_xi2->slave_devices);
+
+  g_list_foreach (device_manager_xi2->floating_devices, (GFunc) g_object_unref, NULL);
+  g_list_free (device_manager_xi2->floating_devices);
+
+  g_hash_table_destroy (device_manager_xi2->id_table);
+
+  G_OBJECT_CLASS (gdk_device_manager_xi2_parent_class)->finalize (object);
+}
+
+static GList *
+gdk_device_manager_xi2_get_devices (GdkDeviceManager *device_manager,
+                                    GdkDeviceType     type)
+{
+  GdkDeviceManagerXI2 *device_manager_xi2;
+  GList *list = NULL;
+
+  device_manager_xi2 = GDK_DEVICE_MANAGER_XI2 (device_manager);
+
+  switch (type)
+    {
+    case GDK_DEVICE_TYPE_MASTER:
+      list = device_manager_xi2->master_devices;
+      break;
+    case GDK_DEVICE_TYPE_SLAVE:
+      list = device_manager_xi2->slave_devices;
+      break;
+    case GDK_DEVICE_TYPE_FLOATING:
+      list = device_manager_xi2->floating_devices;
+      break;
+    default:
+      g_assert_not_reached ();
+    }
+
+  return g_list_copy (list);
+}
+
+static void
+gdk_device_manager_xi2_event_translator_init (GdkEventTranslatorIface *iface)
+{
+  iface->translate_event = gdk_device_manager_xi2_translate_event;
+  iface->get_handled_events = gdk_device_manager_xi2_get_handled_events;
+  iface->select_window_events = gdk_device_manager_xi2_select_window_events;
+}
+
+static void
+handle_hierarchy_changed (GdkDeviceManagerXI2 *device_manager,
+                          XIHierarchyEvent    *ev)
+{
+  GdkDevice *device;
+  gint i;
+
+  /* We only care about enabled devices */
+  if (!(ev->flags & XIDeviceEnabled) &&
+      !(ev->flags & XIDeviceDisabled))
+    return;
+
+  for (i = 0; i < ev->num_info; i++)
+    {
+      if (ev->info[i].flags & XIDeviceEnabled)
+        {
+          GdkDisplay *display;
+          Display *xdisplay;
+          XIDeviceInfo *info;
+          int ndevices;
+
+          display = gdk_device_manager_get_display (GDK_DEVICE_MANAGER (device_manager));
+          xdisplay = GDK_DISPLAY_XDISPLAY (display);
+
+          info = XIQueryDevice(xdisplay, ev->info[i].deviceid, &ndevices);
+          device = add_device (device_manager, &info[0], TRUE);
+          XIFreeDeviceInfo(info);
+        }
+      else if (ev->info[i].flags & XIDeviceDisabled)
+        remove_device (device_manager, ev->info[i].deviceid);
+    }
+}
+
+static void
+handle_device_changed (GdkDeviceManagerXI2  *device_manager,
+                       XIDeviceChangedEvent *ev)
+{
+  GdkDisplay *display;
+  GdkDevice *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));
+
+  _gdk_device_reset_axes (device);
+  translate_device_classes (display, device, ev->classes, ev->num_classes);
+}
+
+static GdkCrossingMode
+translate_crossing_mode (int mode)
+{
+  switch (mode)
+    {
+    case NotifyNormal:
+      return GDK_CROSSING_NORMAL;
+    case NotifyGrab:
+      return GDK_CROSSING_GRAB;
+    case NotifyUngrab:
+      return GDK_CROSSING_UNGRAB;
+    default:
+      g_assert_not_reached ();
+    }
+}
+
+static GdkNotifyType
+translate_notify_type (int detail)
+{
+  switch (detail)
+    {
+    case NotifyInferior:
+      return GDK_NOTIFY_INFERIOR;
+    case NotifyAncestor:
+      return GDK_NOTIFY_ANCESTOR;
+    case NotifyVirtual:
+      return GDK_NOTIFY_VIRTUAL;
+    case NotifyNonlinear:
+      return GDK_NOTIFY_NONLINEAR;
+    case NotifyNonlinearVirtual:
+      return GDK_NOTIFY_NONLINEAR_VIRTUAL;
+    default:
+      g_assert_not_reached ();
+    }
+}
+
+static gboolean
+set_screen_from_root (GdkDisplay *display,
+		      GdkEvent   *event,
+		      Window      xrootwin)
+{
+  GdkScreen *screen;
+
+  screen = _gdk_x11_display_screen_for_xrootwin (display, xrootwin);
+
+  if (screen)
+    {
+      gdk_event_set_screen (event, screen);
+
+      return TRUE;
+    }
+
+  return FALSE;
+}
+
+static void
+set_user_time (GdkEvent *event)
+{
+  GdkWindow *window;
+  guint32 time;
+
+  window = gdk_window_get_toplevel (event->any.window);
+  g_return_if_fail (GDK_IS_WINDOW (window));
+
+  time = gdk_event_get_time (event);
+
+  /* If an event doesn't have a valid timestamp, we shouldn't use it
+   * to update the latest user interaction time.
+   */
+  if (time != GDK_CURRENT_TIME)
+    gdk_x11_window_set_user_time (window, time);
+}
+
+static void
+translate_keyboard_string (GdkEventKey *event)
+{
+  gunichar c = 0;
+  gchar buf[7];
+
+  /* Fill in event->string crudely, since various programs
+   * depend on it.
+   */
+  event->string = NULL;
+
+  if (event->keyval != GDK_VoidSymbol)
+    c = gdk_keyval_to_unicode (event->keyval);
+
+  if (c)
+    {
+      gsize bytes_written;
+      gint len;
+
+      /* Apply the control key - Taken from Xlib
+       */
+      if (event->state & GDK_CONTROL_MASK)
+	{
+	  if ((c >= '@' && c < '\177') || c == ' ') c &= 0x1F;
+	  else if (c == '2')
+	    {
+	      event->string = g_memdup ("\0\0", 2);
+	      event->length = 1;
+	      buf[0] = '\0';
+              return;
+	    }
+	  else if (c >= '3' && c <= '7') c -= ('3' - '\033');
+	  else if (c == '8') c = '\177';
+	  else if (c == '/') c = '_' & 0x1F;
+	}
+
+      len = g_unichar_to_utf8 (c, buf);
+      buf[len] = '\0';
+
+      event->string = g_locale_from_utf8 (buf, len,
+                                          NULL, &bytes_written,
+                                          NULL);
+      if (event->string)
+	event->length = bytes_written;
+    }
+  else if (event->keyval == GDK_Escape)
+    {
+      event->length = 1;
+      event->string = g_strdup ("\033");
+    }
+  else if (event->keyval == GDK_Return ||
+	  event->keyval == GDK_KP_Enter)
+    {
+      event->length = 1;
+      event->string = g_strdup ("\r");
+    }
+
+  if (!event->string)
+    {
+      event->length = 0;
+      event->string = g_strdup ("");
+    }
+}
+
+static void
+generate_focus_event (GdkWindow *window,
+		      gboolean   in)
+{
+  GdkEvent event;
+
+  event.type = GDK_FOCUS_CHANGE;
+  event.focus_change.window = window;
+  event.focus_change.send_event = FALSE;
+  event.focus_change.in = in;
+
+  gdk_event_put (&event);
+}
+
+static void
+handle_focus_change (GdkWindow *window,
+                     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, (in) ? TRUE : FALSE);
+}
+
+static gdouble *
+translate_axes (GdkDevice       *device,
+                gdouble          x,
+                gdouble          y,
+                GdkWindow       *window,
+                XIValuatorState *valuators)
+{
+  guint n_axes, i;
+  gint width, height;
+  gdouble *axes;
+  double *vals;
+
+  g_object_get (device, "n-axes", &n_axes, NULL);
+
+  axes = g_new0 (gdouble, n_axes);
+  vals = valuators->values;
+
+  gdk_drawable_get_size (GDK_DRAWABLE (window), &width, &height);
+
+  for (i = 0; i < valuators->mask_len * 8; i++)
+    {
+      GdkAxisUse use;
+      gdouble val;
+
+      if (!XIMaskIsSet (valuators->mask, i))
+        continue;
+
+      use = _gdk_device_get_axis_use (device, i);
+      val = *vals++;
+
+      switch (use)
+        {
+        case GDK_AXIS_X:
+        case GDK_AXIS_Y:
+          if (device->mode == GDK_MODE_WINDOW)
+            _gdk_device_translate_window_coord (device, window, i, val, &axes[i]);
+          else
+            {
+              if (use == GDK_AXIS_X)
+                axes[i] = x;
+              else
+                axes[i] = y;
+            }
+          break;
+        default:
+          _gdk_device_translate_axis (device, i, val, &axes[i]);
+          break;
+        }
+    }
+
+  return axes;
+}
+
+static gboolean
+is_parent_of (GdkWindow *parent,
+              GdkWindow *child)
+{
+  GdkWindow *w;
+
+  w = child;
+  while (w != NULL)
+    {
+      if (w == parent)
+	return TRUE;
+
+      w = gdk_window_get_parent (w);
+    }
+
+  return FALSE;
+}
+
+static GdkWindow *
+get_event_window (GdkEventTranslator *translator,
+                  XIEvent            *ev)
+{
+  GdkDisplay *display;
+  GdkWindow *window = NULL;
+
+  display = gdk_device_manager_get_display (GDK_DEVICE_MANAGER (translator));
+
+  switch (ev->evtype)
+    {
+    case XI_KeyPress:
+    case XI_KeyRelease:
+    case XI_ButtonPress:
+    case XI_ButtonRelease:
+    case XI_Motion:
+      {
+        XIDeviceEvent *xev = (XIDeviceEvent *) ev;
+
+        window = gdk_window_lookup_for_display (display, xev->event);
+
+        /* Apply keyboard grabs to non-native windows */
+        if (ev->evtype == XI_KeyPress || ev->evtype == XI_KeyRelease)
+          {
+            GdkDeviceGrabInfo *info;
+            GdkDevice *device;
+            gulong serial;
+
+            device = g_hash_table_lookup (GDK_DEVICE_MANAGER_XI2 (translator)->id_table,
+                                          GUINT_TO_POINTER (((XIDeviceEvent *) ev)->deviceid));
+
+            serial = _gdk_windowing_window_get_next_serial (display);
+            info = _gdk_display_has_device_grab (display, device, serial);
+
+            if (info &&
+                (!is_parent_of (info->window, window) ||
+                 !info->owner_events))
+              {
+                /* Report key event against grab window */
+                window = info->window;
+              }
+          }
+      }
+      break;
+    case XI_Enter:
+    case XI_Leave:
+    case XI_FocusIn:
+    case XI_FocusOut:
+      {
+        XIEnterEvent *xev = (XIEnterEvent *) ev;
+
+        window = gdk_window_lookup_for_display (display, xev->event);
+      }
+      break;
+    }
+
+  return window;
+}
+
+static gboolean
+gdk_device_manager_xi2_translate_event (GdkEventTranslator *translator,
+                                        GdkDisplay         *display,
+                                        GdkEvent           *event,
+                                        XEvent             *xevent)
+{
+  GdkDeviceManagerXI2 *device_manager;
+  XGenericEventCookie *cookie;
+  gboolean return_val = TRUE;
+  GdkWindow *window;
+  XIEvent *ev;
+  Display *dpy;
+
+  dpy = GDK_DISPLAY_XDISPLAY (display);
+  device_manager = (GdkDeviceManagerXI2 *) translator;
+  cookie = &xevent->xcookie;
+
+  if (!XGetEventData (dpy, cookie))
+    return FALSE;
+
+  if (cookie->type != GenericEvent ||
+      cookie->extension != device_manager->opcode)
+    {
+      XFreeEventData (dpy, cookie);
+      return FALSE;
+    }
+
+  ev = (XIEvent *) cookie->data;
+
+  window = get_event_window (translator, ev);
+
+  if (window && GDK_WINDOW_DESTROYED (window))
+    {
+      XFreeEventData (dpy, cookie);
+      return FALSE;
+    }
+
+  if (ev->evtype == XI_Motion ||
+      ev->evtype == XI_ButtonRelease)
+    {
+      if (_gdk_moveresize_handle_event (xevent))
+        {
+          XFreeEventData (dpy, cookie);
+          return FALSE;
+        }
+    }
+
+  switch (ev->evtype)
+    {
+    case XI_HierarchyChanged:
+      handle_hierarchy_changed (device_manager,
+                                (XIHierarchyEvent *) ev);
+      return_val = FALSE;
+      break;
+    case XI_DeviceChanged:
+      handle_device_changed (device_manager,
+                             (XIDeviceChangedEvent *) ev);
+      return_val = FALSE;
+      break;
+    case XI_KeyPress:
+    case XI_KeyRelease:
+      {
+        XIDeviceEvent *xev = (XIDeviceEvent *) ev;
+        GdkKeymap *keymap = gdk_keymap_get_for_display (display);
+
+        event->key.type = xev->evtype == XI_KeyPress ? GDK_KEY_PRESS : GDK_KEY_RELEASE;
+
+        event->key.window = window;
+
+        event->key.time = xev->time;
+        event->key.state = gdk_device_xi2_translate_state (&xev->mods, &xev->buttons);
+        event->key.group = _gdk_x11_get_group_for_state (display, event->key.state);
+
+        event->key.hardware_keycode = xev->detail;
+        event->key.is_modifier = _gdk_keymap_key_is_modifier (keymap, event->key.hardware_keycode);
+
+        event->key.device = g_hash_table_lookup (device_manager->id_table,
+                                                 GUINT_TO_POINTER (xev->deviceid));
+
+        _gdk_keymap_add_virtual_modifiers (keymap, &event->key.state);
+
+        event->key.keyval = GDK_VoidSymbol;
+
+        gdk_keymap_translate_keyboard_state (keymap,
+                                             event->key.hardware_keycode,
+                                             event->key.state,
+                                             event->key.group,
+                                             &event->key.keyval,
+                                             NULL, NULL, NULL);
+
+        translate_keyboard_string ((GdkEventKey *) event);
+
+        if (ev->evtype == XI_KeyPress)
+          set_user_time (event);
+
+        /* FIXME: emulate autorepeat on key
+         * release? XI2 seems attached to Xkb.
+         */
+      }
+
+      break;
+    case XI_ButtonPress:
+    case XI_ButtonRelease:
+      {
+        XIDeviceEvent *xev = (XIDeviceEvent *) ev;
+
+        switch (xev->detail)
+          {
+          case 4:
+          case 5:
+          case 6:
+          case 7:
+            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));
+
+            event->scroll.state = gdk_device_xi2_translate_state (&xev->mods, &xev->buttons);
+            break;
+          default:
+            event->button.type = (ev->evtype == XI_ButtonPress) ? GDK_BUTTON_PRESS : GDK_BUTTON_RELEASE;
+
+            event->button.window = window;
+            event->button.time = xev->time;
+            event->button.x = (gdouble) xev->event_x;
+            event->button.y = (gdouble) xev->event_y;
+            event->button.x_root = (gdouble) xev->root_x;
+            event->button.y_root = (gdouble) xev->root_y;
+
+            event->button.device = g_hash_table_lookup (device_manager->id_table,
+                                                        GUINT_TO_POINTER (xev->deviceid));
+
+            event->button.axes = translate_axes (event->button.device,
+                                                 event->button.x,
+                                                 event->button.y,
+                                                 event->button.window,
+                                                 &xev->valuators);
+
+            if (event->button.device->mode == GDK_MODE_WINDOW)
+              {
+                GdkDevice *device = event->button.device;
+
+                /* Update event coordinates from axes */
+                gdk_device_get_axis (device, event->button.axes, GDK_AXIS_X, &event->button.x);
+                gdk_device_get_axis (device, event->button.axes, GDK_AXIS_Y, &event->button.y);
+              }
+
+            event->button.state = gdk_device_xi2_translate_state (&xev->mods, &xev->buttons);
+            event->button.button = xev->detail;
+          }
+
+        if (!set_screen_from_root (display, event, xev->root))
+          {
+            return_val = FALSE;
+            break;
+          }
+
+        set_user_time (event);
+
+        break;
+      }
+    case XI_Motion:
+      {
+        XIDeviceEvent *xev = (XIDeviceEvent *) ev;
+
+        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));
+
+        event->motion.state = gdk_device_xi2_translate_state (&xev->mods, &xev->buttons);
+
+        /* There doesn't seem to be motion hints in XI */
+        event->motion.is_hint = FALSE;
+
+        event->motion.axes = translate_axes (event->motion.device,
+                                             event->motion.x,
+                                             event->motion.y,
+                                             event->motion.window,
+                                             &xev->valuators);
+
+        if (event->motion.device->mode == GDK_MODE_WINDOW)
+          {
+            GdkDevice *device = event->motion.device;
+
+            /* Update event coordinates from axes */
+            gdk_device_get_axis (device, event->motion.axes, GDK_AXIS_X, &event->motion.x);
+            gdk_device_get_axis (device, event->motion.axes, GDK_AXIS_Y, &event->motion.y);
+          }
+      }
+      break;
+    case XI_Enter:
+    case XI_Leave:
+      {
+        XIEnterEvent *xev = (XIEnterEvent *) ev;
+
+        event->crossing.type = (ev->evtype == XI_Enter) ? GDK_ENTER_NOTIFY : GDK_LEAVE_NOTIFY;
+
+        event->crossing.x = (gdouble) xev->event_x;
+        event->crossing.y = (gdouble) xev->event_y;
+        event->crossing.x_root = (gdouble) xev->root_x;
+        event->crossing.y_root = (gdouble) xev->root_y;
+        event->crossing.time = xev->time;
+        event->crossing.focus = xev->focus;
+
+        event->crossing.window = window;
+        event->crossing.subwindow = gdk_window_lookup_for_display (display, xev->child);
+        event->crossing.device = g_hash_table_lookup (device_manager->id_table,
+                                                      GINT_TO_POINTER (xev->deviceid));
+
+        event->crossing.mode = translate_crossing_mode (xev->mode);
+        event->crossing.detail = translate_notify_type (xev->detail);
+        event->crossing.state = gdk_device_xi2_translate_state (&xev->mods, &xev->buttons);
+      }
+      break;
+    case XI_FocusIn:
+    case XI_FocusOut:
+      {
+        XIEnterEvent *xev = (XIEnterEvent *) ev;
+
+        handle_focus_change (window, xev->detail, xev->mode,
+                             (ev->evtype == XI_FocusIn) ? TRUE : FALSE);
+
+        return_val = FALSE;
+      }
+    default:
+      return_val = FALSE;
+      break;
+    }
+
+  event->any.send_event = cookie->send_event;
+
+  if (return_val)
+    {
+      if (event->any.window)
+        g_object_ref (event->any.window);
+
+      if (((event->any.type == GDK_ENTER_NOTIFY) ||
+	   (event->any.type == GDK_LEAVE_NOTIFY)) &&
+	  (event->crossing.subwindow != NULL))
+        g_object_ref (event->crossing.subwindow);
+    }
+  else
+    {
+      /* Mark this event as having no resources to be freed */
+      event->any.window = NULL;
+      event->any.type = GDK_NOTHING;
+    }
+
+  XFreeEventData (dpy, cookie);
+
+  return return_val;
+}
+
+static GdkEventMask
+gdk_device_manager_xi2_get_handled_events (GdkEventTranslator *translator)
+{
+  return (GDK_KEY_PRESS_MASK |
+          GDK_KEY_RELEASE_MASK |
+          GDK_BUTTON_PRESS_MASK |
+          GDK_BUTTON_RELEASE_MASK |
+          GDK_SCROLL_MASK |
+          GDK_ENTER_NOTIFY_MASK |
+          GDK_LEAVE_NOTIFY_MASK |
+          GDK_POINTER_MOTION_MASK |
+          GDK_POINTER_MOTION_HINT_MASK |
+          GDK_BUTTON1_MOTION_MASK |
+          GDK_BUTTON2_MOTION_MASK |
+          GDK_BUTTON3_MOTION_MASK |
+          GDK_BUTTON_MOTION_MASK |
+          GDK_FOCUS_CHANGE_MASK);
+}
+
+static void
+gdk_device_manager_xi2_select_window_events (GdkEventTranslator *translator,
+                                             Window              window,
+                                             GdkEventMask        evmask)
+{
+  GdkDeviceManager *device_manager;
+  XIEventMask event_mask;
+
+  device_manager = GDK_DEVICE_MANAGER (translator);
+
+  event_mask.deviceid = XIAllMasterDevices;
+  event_mask.mask = gdk_device_xi2_translate_event_mask (evmask, &event_mask.mask_len);
+
+  _gdk_device_manager_xi2_select_events (device_manager, window, &event_mask);
+  g_free (event_mask.mask);
+}
diff --git a/gdk/x11/gdkdevicemanager-xi2.h b/gdk/x11/gdkdevicemanager-xi2.h
new file mode 100644
index 0000000..d47bcdb
--- /dev/null
+++ b/gdk/x11/gdkdevicemanager-xi2.h
@@ -0,0 +1,61 @@
+/* GDK - The GIMP Drawing Kit
+ * Copyright (C) 2009 Carlos Garnacho <carlosg@gnome.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * 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.
+ */
+
+#ifndef __GDK_DEVICE_MANAGER_XI2_H__
+#define __GDK_DEVICE_MANAGER_XI2_H__
+
+#include <gdk/gdkdevicemanager.h>
+#include <X11/extensions/XInput2.h>
+
+G_BEGIN_DECLS
+
+#define GDK_TYPE_DEVICE_MANAGER_XI2         (gdk_device_manager_xi2_get_type ())
+#define GDK_DEVICE_MANAGER_XI2(o)           (G_TYPE_CHECK_INSTANCE_CAST ((o), GDK_TYPE_DEVICE_MANAGER_XI2, GdkDeviceManagerXI2))
+#define GDK_DEVICE_MANAGER_XI2_CLASS(c)     (G_TYPE_CHECK_CLASS_CAST ((c), GDK_TYPE_DEVICE_MANAGER_XI2, GdkDeviceManagerXI2Class))
+#define GDK_IS_DEVICE_MANAGER_XI2(o)        (G_TYPE_CHECK_INSTANCE_TYPE ((o), GDK_TYPE_DEVICE_MANAGER_XI2))
+#define GDK_IS_DEVICE_MANAGER_XI2_CLASS(c)  (G_TYPE_CHECK_CLASS_TYPE ((c), GDK_TYPE_DEVICE_MANAGER_XI2))
+#define GDK_DEVICE_MANAGER_XI2_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GDK_TYPE_DEVICE_MANAGER_XI2, GdkDeviceManagerXI2Class))
+
+typedef struct _GdkDeviceManagerXI2 GdkDeviceManagerXI2;
+typedef struct _GdkDeviceManagerXI2Class GdkDeviceManagerXI2Class;
+
+struct _GdkDeviceManagerXI2
+{
+  GdkDeviceManager parent_object;
+
+  GHashTable *id_table;
+
+  GList *master_devices;
+  GList *slave_devices;
+  GList *floating_devices;
+
+  int opcode;
+};
+
+struct _GdkDeviceManagerXI2Class
+{
+  GdkDeviceManagerClass parent_class;
+};
+
+GType gdk_device_manager_xi2_get_type (void) G_GNUC_CONST;
+
+
+G_END_DECLS
+
+#endif /* __GDK_DEVICE_MANAGER_CORE_H__ */
diff --git a/gdk/x11/gdkdisplay-x11.c b/gdk/x11/gdkdisplay-x11.c
index 2b10a93..b2d10d9 100644
--- a/gdk/x11/gdkdisplay-x11.c
+++ b/gdk/x11/gdkdisplay-x11.c
@@ -34,10 +34,13 @@
 #include "gdkasync.h"
 #include "gdkdisplay.h"
 #include "gdkdisplay-x11.h"
+#include "gdkeventsource.h"
+#include "gdkeventtranslator.h"
 #include "gdkscreen.h"
 #include "gdkscreen-x11.h"
 #include "gdkinternals.h"
-#include "gdkinputprivate.h"
+#include "gdkdeviceprivate.h"
+#include "gdkdevicemanager.h"
 #include "xsettings-client.h"
 #include "gdkalias.h"
 
@@ -69,6 +72,13 @@
 static void   gdk_display_x11_dispose            (GObject            *object);
 static void   gdk_display_x11_finalize           (GObject            *object);
 
+static void     gdk_display_x11_event_translator_init (GdkEventTranslatorIface *iface);
+
+static gboolean gdk_display_x11_translate_event (GdkEventTranslator *translator,
+                                                 GdkDisplay         *display,
+                                                 GdkEvent           *event,
+                                                 XEvent             *xevent);
+
 #ifdef HAVE_X11R6
 static void gdk_internal_connection_watch (Display  *display,
 					   XPointer  arg,
@@ -77,6 +87,14 @@ static void gdk_internal_connection_watch (Display  *display,
 					   XPointer *watch_data);
 #endif /* HAVE_X11R6 */
 
+typedef struct _GdkEventTypeX11 GdkEventTypeX11;
+
+struct _GdkEventTypeX11
+{
+  gint base;
+  gint n_events;
+};
+
 /* Note that we never *directly* use WM_LOCALE_NAME, WM_PROTOCOLS,
  * but including them here has the side-effect of getting them
  * into the internal Xlib cache
@@ -120,7 +138,10 @@ static const char *const precache_atoms[] = {
   "_NET_VIRTUAL_ROOTS"
 };
 
-G_DEFINE_TYPE (GdkDisplayX11, _gdk_display_x11, GDK_TYPE_DISPLAY)
+G_DEFINE_TYPE_WITH_CODE (GdkDisplayX11, _gdk_display_x11, GDK_TYPE_DISPLAY,
+                         G_IMPLEMENT_INTERFACE (GDK_TYPE_EVENT_TRANSLATOR,
+                                                gdk_display_x11_event_translator_init))
+
 
 static void
 _gdk_display_x11_class_init (GdkDisplayX11Class * class)
@@ -136,6 +157,992 @@ _gdk_display_x11_init (GdkDisplayX11 *display)
 {
 }
 
+static void
+gdk_display_x11_event_translator_init (GdkEventTranslatorIface *iface)
+{
+  iface->translate_event = gdk_display_x11_translate_event;
+}
+
+static void
+do_net_wm_state_changes (GdkWindow *window)
+{
+  GdkToplevelX11 *toplevel = _gdk_x11_window_get_toplevel (window);
+  GdkWindowState old_state;
+
+  if (GDK_WINDOW_DESTROYED (window) ||
+      gdk_window_get_window_type (window) != GDK_WINDOW_TOPLEVEL)
+    return;
+
+  old_state = gdk_window_get_state (window);
+
+  /* For found_sticky to remain TRUE, we have to also be on desktop
+   * 0xFFFFFFFF
+   */
+  if (old_state & GDK_WINDOW_STATE_STICKY)
+    {
+      if (!(toplevel->have_sticky && toplevel->on_all_desktops))
+        gdk_synthesize_window_state (window,
+                                     GDK_WINDOW_STATE_STICKY,
+                                     0);
+    }
+  else
+    {
+      if (toplevel->have_sticky || toplevel->on_all_desktops)
+        gdk_synthesize_window_state (window,
+                                     0,
+                                     GDK_WINDOW_STATE_STICKY);
+    }
+
+  if (old_state & GDK_WINDOW_STATE_FULLSCREEN)
+    {
+      if (!toplevel->have_fullscreen)
+        gdk_synthesize_window_state (window,
+                                     GDK_WINDOW_STATE_FULLSCREEN,
+                                     0);
+    }
+  else
+    {
+      if (toplevel->have_fullscreen)
+        gdk_synthesize_window_state (window,
+                                     0,
+                                     GDK_WINDOW_STATE_FULLSCREEN);
+    }
+
+  /* Our "maximized" means both vertical and horizontal; if only one,
+   * we don't expose that via GDK
+   */
+  if (old_state & GDK_WINDOW_STATE_MAXIMIZED)
+    {
+      if (!(toplevel->have_maxvert && toplevel->have_maxhorz))
+        gdk_synthesize_window_state (window,
+                                     GDK_WINDOW_STATE_MAXIMIZED,
+                                     0);
+    }
+  else
+    {
+      if (toplevel->have_maxvert && toplevel->have_maxhorz)
+        gdk_synthesize_window_state (window,
+                                     0,
+                                     GDK_WINDOW_STATE_MAXIMIZED);
+    }
+}
+
+static void
+gdk_check_wm_desktop_changed (GdkWindow *window)
+{
+  GdkToplevelX11 *toplevel = _gdk_x11_window_get_toplevel (window);
+  GdkDisplay *display = GDK_WINDOW_DISPLAY (window);
+
+  Atom type;
+  gint format;
+  gulong nitems;
+  gulong bytes_after;
+  guchar *data;
+  gulong *desktop;
+
+  type = None;
+  gdk_error_trap_push ();
+  XGetWindowProperty (GDK_DISPLAY_XDISPLAY (display),
+                      GDK_WINDOW_XID (window),
+                      gdk_x11_get_xatom_by_name_for_display (display, "_NET_WM_DESKTOP"),
+                      0, G_MAXLONG, False, XA_CARDINAL, &type,
+                      &format, &nitems,
+                      &bytes_after, &data);
+  gdk_error_trap_pop ();
+
+  if (type != None)
+    {
+      desktop = (gulong *)data;
+      toplevel->on_all_desktops = (*desktop == 0xFFFFFFFF);
+      XFree (desktop);
+    }
+  else
+    toplevel->on_all_desktops = FALSE;
+
+  do_net_wm_state_changes (window);
+}
+
+static void
+gdk_check_wm_state_changed (GdkWindow *window)
+{
+  GdkToplevelX11 *toplevel = _gdk_x11_window_get_toplevel (window);
+  GdkDisplay *display = GDK_WINDOW_DISPLAY (window);
+
+  Atom type;
+  gint format;
+  gulong nitems;
+  gulong bytes_after;
+  guchar *data;
+  Atom *atoms = NULL;
+  gulong i;
+
+  gboolean had_sticky = toplevel->have_sticky;
+
+  toplevel->have_sticky = FALSE;
+  toplevel->have_maxvert = FALSE;
+  toplevel->have_maxhorz = FALSE;
+  toplevel->have_fullscreen = FALSE;
+
+  type = None;
+  gdk_error_trap_push ();
+  XGetWindowProperty (GDK_DISPLAY_XDISPLAY (display), GDK_WINDOW_XID (window),
+		      gdk_x11_get_xatom_by_name_for_display (display, "_NET_WM_STATE"),
+		      0, G_MAXLONG, False, XA_ATOM, &type, &format, &nitems,
+		      &bytes_after, &data);
+  gdk_error_trap_pop ();
+
+  if (type != None)
+    {
+      Atom sticky_atom = gdk_x11_get_xatom_by_name_for_display (display, "_NET_WM_STATE_STICKY");
+      Atom maxvert_atom = gdk_x11_get_xatom_by_name_for_display (display, "_NET_WM_STATE_MAXIMIZED_VERT");
+      Atom maxhorz_atom	= gdk_x11_get_xatom_by_name_for_display (display, "_NET_WM_STATE_MAXIMIZED_HORZ");
+      Atom fullscreen_atom = gdk_x11_get_xatom_by_name_for_display (display, "_NET_WM_STATE_FULLSCREEN");
+
+      atoms = (Atom *)data;
+
+      i = 0;
+      while (i < nitems)
+        {
+          if (atoms[i] == sticky_atom)
+            toplevel->have_sticky = TRUE;
+          else if (atoms[i] == maxvert_atom)
+            toplevel->have_maxvert = TRUE;
+          else if (atoms[i] == maxhorz_atom)
+            toplevel->have_maxhorz = TRUE;
+          else if (atoms[i] == fullscreen_atom)
+            toplevel->have_fullscreen = TRUE;
+
+          ++i;
+        }
+
+      XFree (atoms);
+    }
+
+  /* When have_sticky is turned on, we have to check the DESKTOP property
+   * as well.
+   */
+  if (toplevel->have_sticky && !had_sticky)
+    gdk_check_wm_desktop_changed (window);
+  else
+    do_net_wm_state_changes (window);
+}
+
+static GdkWindow *
+get_event_window (GdkEventTranslator *translator,
+                  XEvent             *xevent)
+{
+  GdkDisplay *display;
+  Window xwindow;
+
+  display = (GdkDisplay *) translator;
+
+  switch (xevent->type)
+    {
+    case DestroyNotify:
+      xwindow = xevent->xdestroywindow.window;
+      break;
+    case UnmapNotify:
+      xwindow = xevent->xunmap.window;
+      break;
+    case MapNotify:
+      xwindow = xevent->xmap.window;
+      break;
+    case ConfigureNotify:
+      xwindow = xevent->xconfigure.window;
+      break;
+    default:
+      xwindow = xevent->xany.window;
+    }
+
+  return gdk_window_lookup_for_display (display, xwindow);
+}
+
+static gboolean
+gdk_display_x11_translate_event (GdkEventTranslator *translator,
+                                 GdkDisplay         *display,
+                                 GdkEvent           *event,
+                                 XEvent             *xevent)
+{
+  GdkWindow *window;
+  GdkWindowObject *window_private;
+  GdkWindowImplX11 *window_impl = NULL;
+  GdkScreen *screen = NULL;
+  GdkScreenX11 *screen_x11 = NULL;
+  GdkToplevelX11 *toplevel = NULL;
+  GdkDisplayX11 *display_x11 = GDK_DISPLAY_X11 (display);
+  gboolean return_val;
+  Window xwindow = None;
+
+  /* Find the GdkWindow that this event relates to.
+   * Basically this means substructure events
+   * are reported same as structure events
+   */
+  window = get_event_window (translator, xevent);
+  window_private = (GdkWindowObject *) window;
+
+  if (window)
+    {
+      /* We may receive events such as NoExpose/GraphicsExpose
+       * and ShmCompletion for pixmaps
+       */
+      if (!GDK_IS_WINDOW (window))
+        return FALSE;
+
+      screen = GDK_WINDOW_SCREEN (window);
+      screen_x11 = GDK_SCREEN_X11 (screen);
+      toplevel = _gdk_x11_window_get_toplevel (window);
+      window_impl = GDK_WINDOW_IMPL_X11 (window_private->impl);
+      xwindow = GDK_WINDOW_XID (window);
+
+      g_object_ref (window);
+    }
+
+  event->any.window = window;
+  event->any.send_event = xevent->xany.send_event ? TRUE : FALSE;
+
+  if (window_private && GDK_WINDOW_DESTROYED (window))
+    {
+      if (xevent->type != DestroyNotify)
+	{
+	  return_val = FALSE;
+	  goto done;
+	}
+    }
+
+  if (xevent->type == DestroyNotify)
+    {
+      int i, n;
+
+      n = gdk_display_get_n_screens (display);
+      for (i = 0; i < n; i++)
+        {
+          screen = gdk_display_get_screen (display, i);
+          screen_x11 = GDK_SCREEN_X11 (screen);
+
+          if (screen_x11->wmspec_check_window == xwindow)
+            {
+              screen_x11->wmspec_check_window = None;
+              screen_x11->last_wmspec_check_time = 0;
+              g_free (screen_x11->window_manager_name);
+              screen_x11->window_manager_name = g_strdup ("unknown");
+
+              /* careful, reentrancy */
+              _gdk_x11_screen_window_manager_changed (screen);
+
+              return_val = FALSE;
+              goto done;
+            }
+        }
+    }
+
+  /* We do a "manual" conversion of the XEvent to a
+   *  GdkEvent. The structures are mostly the same so
+   *  the conversion is fairly straightforward. We also
+   *  optionally print debugging info regarding events
+   *  received.
+   */
+
+  return_val = TRUE;
+
+  switch (xevent->type)
+    {
+    case KeymapNotify:
+      GDK_NOTE (EVENTS,
+		g_message ("keymap notify"));
+
+      /* Not currently handled */
+      return_val = FALSE;
+      break;
+
+    case Expose:
+      GDK_NOTE (EVENTS,
+		g_message ("expose:\t\twindow: %ld  %d	x,y: %d %d  w,h: %d %d%s",
+			   xevent->xexpose.window, xevent->xexpose.count,
+			   xevent->xexpose.x, xevent->xexpose.y,
+			   xevent->xexpose.width, xevent->xexpose.height,
+			   event->any.send_event ? " (send)" : ""));
+
+      if (window_private == NULL)
+        {
+          return_val = FALSE;
+          break;
+        }
+
+      {
+	GdkRectangle expose_rect;
+
+	expose_rect.x = xevent->xexpose.x;
+	expose_rect.y = xevent->xexpose.y;
+	expose_rect.width = xevent->xexpose.width;
+	expose_rect.height = xevent->xexpose.height;
+
+        _gdk_window_process_expose (window, xevent->xexpose.serial, &expose_rect);
+        return_val = FALSE;
+      }
+
+      break;
+
+    case GraphicsExpose:
+      {
+	GdkRectangle expose_rect;
+
+        GDK_NOTE (EVENTS,
+		  g_message ("graphics expose:\tdrawable: %ld",
+			     xevent->xgraphicsexpose.drawable));
+
+        if (window_private == NULL)
+          {
+            return_val = FALSE;
+            break;
+          }
+
+	expose_rect.x = xevent->xgraphicsexpose.x;
+	expose_rect.y = xevent->xgraphicsexpose.y;
+	expose_rect.width = xevent->xgraphicsexpose.width;
+	expose_rect.height = xevent->xgraphicsexpose.height;
+
+        _gdk_window_process_expose (window, xevent->xgraphicsexpose.serial, &expose_rect);
+        return_val = FALSE;
+      }
+      break;
+
+    case NoExpose:
+      GDK_NOTE (EVENTS,
+		g_message ("no expose:\t\tdrawable: %ld",
+			   xevent->xnoexpose.drawable));
+
+      event->no_expose.type = GDK_NO_EXPOSE;
+      event->no_expose.window = window;
+
+      break;
+
+    case VisibilityNotify:
+#ifdef G_ENABLE_DEBUG
+      if (_gdk_debug_flags & GDK_DEBUG_EVENTS)
+	switch (xevent->xvisibility.state)
+	  {
+	  case VisibilityFullyObscured:
+	    g_message ("visibility notify:\twindow: %ld	 none",
+		       xevent->xvisibility.window);
+	    break;
+	  case VisibilityPartiallyObscured:
+	    g_message ("visibility notify:\twindow: %ld	 partial",
+		       xevent->xvisibility.window);
+	    break;
+	  case VisibilityUnobscured:
+	    g_message ("visibility notify:\twindow: %ld	 full",
+		       xevent->xvisibility.window);
+	    break;
+	  }
+#endif /* G_ENABLE_DEBUG */
+
+      if (window_private == NULL)
+        {
+          return_val = FALSE;
+          break;
+        }
+
+      event->visibility.type = GDK_VISIBILITY_NOTIFY;
+      event->visibility.window = window;
+
+      switch (xevent->xvisibility.state)
+	{
+	case VisibilityFullyObscured:
+	  event->visibility.state = GDK_VISIBILITY_FULLY_OBSCURED;
+	  break;
+
+	case VisibilityPartiallyObscured:
+	  event->visibility.state = GDK_VISIBILITY_PARTIAL;
+	  break;
+
+	case VisibilityUnobscured:
+	  event->visibility.state = GDK_VISIBILITY_UNOBSCURED;
+	  break;
+	}
+
+      break;
+
+    case CreateNotify:
+      GDK_NOTE (EVENTS,
+		g_message ("create notify:\twindow: %ld  x,y: %d %d	w,h: %d %d  b-w: %d  parent: %ld	 ovr: %d",
+			   xevent->xcreatewindow.window,
+			   xevent->xcreatewindow.x,
+			   xevent->xcreatewindow.y,
+			   xevent->xcreatewindow.width,
+			   xevent->xcreatewindow.height,
+			   xevent->xcreatewindow.border_width,
+			   xevent->xcreatewindow.parent,
+			   xevent->xcreatewindow.override_redirect));
+      /* not really handled */
+      break;
+
+    case DestroyNotify:
+      GDK_NOTE (EVENTS,
+		g_message ("destroy notify:\twindow: %ld",
+			   xevent->xdestroywindow.window));
+
+      /* Ignore DestroyNotify from SubstructureNotifyMask */
+      if (xevent->xdestroywindow.window == xevent->xdestroywindow.event)
+	{
+	  event->any.type = GDK_DESTROY;
+	  event->any.window = window;
+
+	  return_val = window_private && !GDK_WINDOW_DESTROYED (window);
+
+	  if (window && GDK_WINDOW_XID (window) != screen_x11->xroot_window)
+	    gdk_window_destroy_notify (window);
+	}
+      else
+	return_val = FALSE;
+
+      break;
+
+    case UnmapNotify:
+      GDK_NOTE (EVENTS,
+		g_message ("unmap notify:\t\twindow: %ld",
+			   xevent->xmap.window));
+
+      event->any.type = GDK_UNMAP;
+      event->any.window = window;
+
+      /* If we are shown (not withdrawn) and get an unmap, it means we
+       * were iconified in the X sense. If we are withdrawn, and get
+       * an unmap, it means we hid the window ourselves, so we
+       * will have already flipped the iconified bit off.
+       */
+      if (window)
+	{
+	  if (GDK_WINDOW_IS_MAPPED (window))
+	    gdk_synthesize_window_state (window,
+					 0,
+					 GDK_WINDOW_STATE_ICONIFIED);
+
+	  _gdk_xgrab_check_unmap (window, xevent->xany.serial);
+	}
+
+      break;
+
+    case MapNotify:
+      GDK_NOTE (EVENTS,
+		g_message ("map notify:\t\twindow: %ld",
+			   xevent->xmap.window));
+
+      event->any.type = GDK_MAP;
+      event->any.window = window;
+
+      /* Unset iconified if it was set */
+      if (window && (((GdkWindowObject*)window)->state & GDK_WINDOW_STATE_ICONIFIED))
+        gdk_synthesize_window_state (window,
+                                     GDK_WINDOW_STATE_ICONIFIED,
+                                     0);
+
+      break;
+
+    case ReparentNotify:
+      GDK_NOTE (EVENTS,
+		g_message ("reparent notify:\twindow: %ld  x,y: %d %d  parent: %ld	ovr: %d",
+			   xevent->xreparent.window,
+			   xevent->xreparent.x,
+			   xevent->xreparent.y,
+			   xevent->xreparent.parent,
+			   xevent->xreparent.override_redirect));
+
+      /* Not currently handled */
+      return_val = FALSE;
+      break;
+
+    case ConfigureNotify:
+      GDK_NOTE (EVENTS,
+		g_message ("configure notify:\twindow: %ld  x,y: %d %d	w,h: %d %d  b-w: %d  above: %ld	 ovr: %d%s",
+			   xevent->xconfigure.window,
+			   xevent->xconfigure.x,
+			   xevent->xconfigure.y,
+			   xevent->xconfigure.width,
+			   xevent->xconfigure.height,
+			   xevent->xconfigure.border_width,
+			   xevent->xconfigure.above,
+			   xevent->xconfigure.override_redirect,
+			   !window
+			   ? " (discarding)"
+			   : GDK_WINDOW_TYPE (window) == GDK_WINDOW_CHILD
+			   ? " (discarding child)"
+			   : xevent->xconfigure.event != xevent->xconfigure.window
+			   ? " (discarding substructure)"
+			   : ""));
+      if (window && GDK_WINDOW_TYPE (window) == GDK_WINDOW_ROOT)
+        {
+	  window_private->width = xevent->xconfigure.width;
+	  window_private->height = xevent->xconfigure.height;
+
+	  _gdk_window_update_size (window);
+	  _gdk_x11_drawable_update_size (window_private->impl);
+	  _gdk_x11_screen_size_changed (screen, xevent);
+        }
+
+#ifdef HAVE_XSYNC
+      if (toplevel && display_x11->use_sync && !XSyncValueIsZero (toplevel->pending_counter_value))
+	{
+	  toplevel->current_counter_value = toplevel->pending_counter_value;
+	  XSyncIntToValue (&toplevel->pending_counter_value, 0);
+	}
+#endif
+
+    if (!window ||
+	  xevent->xconfigure.event != xevent->xconfigure.window ||
+          GDK_WINDOW_TYPE (window) == GDK_WINDOW_CHILD ||
+          GDK_WINDOW_TYPE (window) == GDK_WINDOW_ROOT)
+	return_val = FALSE;
+      else
+	{
+	  event->configure.type = GDK_CONFIGURE;
+	  event->configure.window = window;
+	  event->configure.width = xevent->xconfigure.width;
+	  event->configure.height = xevent->xconfigure.height;
+
+	  if (!xevent->xconfigure.send_event &&
+	      !xevent->xconfigure.override_redirect &&
+	      !GDK_WINDOW_DESTROYED (window))
+	    {
+	      gint tx = 0;
+	      gint ty = 0;
+	      Window child_window = 0;
+
+	      gdk_error_trap_push ();
+	      if (XTranslateCoordinates (GDK_DRAWABLE_XDISPLAY (window),
+					 GDK_DRAWABLE_XID (window),
+					 screen_x11->xroot_window,
+					 0, 0,
+					 &tx, &ty,
+					 &child_window))
+		{
+		  event->configure.x = tx;
+		  event->configure.y = ty;
+		}
+	      gdk_error_trap_pop ();
+	    }
+	  else
+	    {
+	      event->configure.x = xevent->xconfigure.x;
+	      event->configure.y = xevent->xconfigure.y;
+	    }
+	  window_private->x = event->configure.x;
+	  window_private->y = event->configure.y;
+	  window_private->width = xevent->xconfigure.width;
+	  window_private->height = xevent->xconfigure.height;
+
+	  _gdk_window_update_size (window);
+	  _gdk_x11_drawable_update_size (window_private->impl);
+
+	  if (window_private->resize_count >= 1)
+	    {
+	      window_private->resize_count -= 1;
+
+	      if (window_private->resize_count == 0)
+		_gdk_moveresize_configure_done (display, window);
+	    }
+	}
+      break;
+
+    case PropertyNotify:
+      GDK_NOTE (EVENTS,
+		g_message ("property notify:\twindow: %ld, atom(%ld): %s%s%s",
+			   xevent->xproperty.window,
+			   xevent->xproperty.atom,
+			   "\"",
+			   gdk_x11_get_xatom_name_for_display (display, xevent->xproperty.atom),
+			   "\""));
+
+      if (window_private == NULL)
+        {
+	  return_val = FALSE;
+          break;
+        }
+
+      /* We compare with the serial of the last time we mapped the
+       * window to avoid refetching properties that we set ourselves
+       */
+      if (toplevel &&
+	  xevent->xproperty.serial >= toplevel->map_serial)
+	{
+	  if (xevent->xproperty.atom == gdk_x11_get_xatom_by_name_for_display (display, "_NET_WM_STATE"))
+	    gdk_check_wm_state_changed (window);
+
+	  if (xevent->xproperty.atom == gdk_x11_get_xatom_by_name_for_display (display, "_NET_WM_DESKTOP"))
+	    gdk_check_wm_desktop_changed (window);
+	}
+
+      if (window_private->event_mask & GDK_PROPERTY_CHANGE_MASK)
+	{
+	  event->property.type = GDK_PROPERTY_NOTIFY;
+	  event->property.window = window;
+	  event->property.atom = gdk_x11_xatom_to_atom_for_display (display, xevent->xproperty.atom);
+	  event->property.time = xevent->xproperty.time;
+	  event->property.state = xevent->xproperty.state;
+	}
+      else
+	return_val = FALSE;
+
+      break;
+
+    case SelectionClear:
+      GDK_NOTE (EVENTS,
+		g_message ("selection clear:\twindow: %ld",
+			   xevent->xproperty.window));
+
+      if (_gdk_selection_filter_clear_event (&xevent->xselectionclear))
+	{
+	  event->selection.type = GDK_SELECTION_CLEAR;
+	  event->selection.window = window;
+	  event->selection.selection = gdk_x11_xatom_to_atom_for_display (display, xevent->xselectionclear.selection);
+	  event->selection.time = xevent->xselectionclear.time;
+	}
+      else
+	return_val = FALSE;
+
+      break;
+
+    case SelectionRequest:
+      GDK_NOTE (EVENTS,
+		g_message ("selection request:\twindow: %ld",
+			   xevent->xproperty.window));
+
+      event->selection.type = GDK_SELECTION_REQUEST;
+      event->selection.window = window;
+      event->selection.selection = gdk_x11_xatom_to_atom_for_display (display, xevent->xselectionrequest.selection);
+      event->selection.target = gdk_x11_xatom_to_atom_for_display (display, xevent->xselectionrequest.target);
+      event->selection.property = gdk_x11_xatom_to_atom_for_display (display, xevent->xselectionrequest.property);
+      event->selection.requestor = xevent->xselectionrequest.requestor;
+      event->selection.time = xevent->xselectionrequest.time;
+
+      break;
+
+    case SelectionNotify:
+      GDK_NOTE (EVENTS,
+		g_message ("selection notify:\twindow: %ld",
+			   xevent->xproperty.window));
+
+      event->selection.type = GDK_SELECTION_NOTIFY;
+      event->selection.window = window;
+      event->selection.selection = gdk_x11_xatom_to_atom_for_display (display, xevent->xselection.selection);
+      event->selection.target = gdk_x11_xatom_to_atom_for_display (display, xevent->xselection.target);
+      if (xevent->xselection.property == None)
+        event->selection.property = GDK_NONE;
+      else
+        event->selection.property = gdk_x11_xatom_to_atom_for_display (display, xevent->xselection.property);
+      event->selection.time = xevent->xselection.time;
+
+      break;
+
+    case ColormapNotify:
+      GDK_NOTE (EVENTS,
+		g_message ("colormap notify:\twindow: %ld",
+			   xevent->xcolormap.window));
+
+      /* Not currently handled */
+      return_val = FALSE;
+      break;
+
+    case ClientMessage:
+      {
+	GList *tmp_list;
+	GdkFilterReturn result = GDK_FILTER_CONTINUE;
+	GdkAtom message_type = gdk_x11_xatom_to_atom_for_display (display, xevent->xclient.message_type);
+
+	GDK_NOTE (EVENTS,
+		  g_message ("client message:\twindow: %ld",
+			     xevent->xclient.window));
+
+	tmp_list = display_x11->client_filters;
+	while (tmp_list)
+	  {
+	    GdkClientFilter *filter = tmp_list->data;
+	    tmp_list = tmp_list->next;
+
+	    if (filter->type == message_type)
+	      {
+		result = (*filter->function) (xevent, event, filter->data);
+		if (result != GDK_FILTER_CONTINUE)
+		  break;
+	      }
+	  }
+
+	switch (result)
+	  {
+	  case GDK_FILTER_REMOVE:
+	    return_val = FALSE;
+	    break;
+	  case GDK_FILTER_TRANSLATE:
+	    return_val = TRUE;
+	    break;
+	  case GDK_FILTER_CONTINUE:
+	    /* Send unknown ClientMessage's on to Gtk for it to use */
+            if (window_private == NULL)
+              {
+                return_val = FALSE;
+              }
+            else
+              {
+                event->client.type = GDK_CLIENT_EVENT;
+                event->client.window = window;
+                event->client.message_type = message_type;
+                event->client.data_format = xevent->xclient.format;
+                memcpy(&event->client.data, &xevent->xclient.data,
+                       sizeof(event->client.data));
+              }
+            break;
+          }
+      }
+
+      break;
+
+    case MappingNotify:
+      GDK_NOTE (EVENTS,
+		g_message ("mapping notify"));
+
+      /* Let XLib know that there is a new keyboard mapping.
+       */
+      XRefreshKeyboardMapping (&xevent->xmapping);
+      _gdk_keymap_keys_changed (display);
+      return_val = FALSE;
+      break;
+
+    default:
+#ifdef HAVE_XFIXES
+      if (xevent->type - display_x11->xfixes_event_base == XFixesSelectionNotify)
+	{
+	  XFixesSelectionNotifyEvent *selection_notify = (XFixesSelectionNotifyEvent *)xevent;
+
+	  _gdk_x11_screen_process_owner_change (screen, xevent);
+	  
+	  event->owner_change.type = GDK_OWNER_CHANGE;
+	  event->owner_change.window = window;
+	  event->owner_change.owner = selection_notify->owner;
+	  event->owner_change.reason = selection_notify->subtype;
+	  event->owner_change.selection = 
+	    gdk_x11_xatom_to_atom_for_display (display, 
+					       selection_notify->selection);
+	  event->owner_change.time = selection_notify->timestamp;
+	  event->owner_change.selection_time = selection_notify->selection_timestamp;
+	  
+	  return_val = TRUE;
+	}
+      else
+#endif
+#ifdef HAVE_RANDR
+      if (xevent->type - display_x11->xrandr_event_base == RRScreenChangeNotify ||
+          xevent->type - display_x11->xrandr_event_base == RRNotify)
+	{
+          if (screen)
+            _gdk_x11_screen_size_changed (screen, xevent);
+	}
+      else
+#endif
+#if defined(HAVE_XCOMPOSITE) && defined (HAVE_XDAMAGE) && defined (HAVE_XFIXES)
+      if (display_x11->have_xdamage && window_private && window_private->composited &&
+	  xevent->type == display_x11->xdamage_event_base + XDamageNotify &&
+	  ((XDamageNotifyEvent *) xevent)->damage == window_impl->damage)
+	{
+	  XDamageNotifyEvent *damage_event = (XDamageNotifyEvent *) xevent;
+	  XserverRegion repair;
+	  GdkRectangle rect;
+
+	  rect.x = window_private->x + damage_event->area.x;
+	  rect.y = window_private->y + damage_event->area.y;
+	  rect.width = damage_event->area.width;
+	  rect.height = damage_event->area.height;
+
+	  repair = XFixesCreateRegion (display_x11->xdisplay,
+				       &damage_event->area, 1);
+	  XDamageSubtract (display_x11->xdisplay,
+			   window_impl->damage,
+			   repair, None);
+	  XFixesDestroyRegion (display_x11->xdisplay, repair);
+
+	  if (window_private->parent != NULL)
+	    _gdk_window_process_expose (GDK_WINDOW (window_private->parent),
+					damage_event->serial, &rect);
+
+	  return_val = TRUE;
+	}
+      else
+#endif
+        return_val = FALSE;
+    }
+
+ done:
+  if (return_val)
+    {
+      if (event->any.window)
+	g_object_ref (event->any.window);
+    }
+  else
+    {
+      /* Mark this event as having no resources to be freed */
+      event->any.window = NULL;
+      event->any.type = GDK_NOTHING;
+    }
+
+  if (window)
+    g_object_unref (window);
+
+  return return_val;
+}
+
+static GdkFilterReturn
+gdk_wm_protocols_filter (GdkXEvent *xev,
+			 GdkEvent  *event,
+			 gpointer data)
+{
+  XEvent *xevent = (XEvent *)xev;
+  GdkWindow *win = event->any.window;
+  GdkDisplay *display;
+  Atom atom;
+
+  if (!win)
+      return GDK_FILTER_REMOVE;
+
+  display = GDK_WINDOW_DISPLAY (win);
+  atom = (Atom)xevent->xclient.data.l[0];
+
+  if (atom == gdk_x11_get_xatom_by_name_for_display (display, "WM_DELETE_WINDOW"))
+    {
+  /* The delete window request specifies a window
+   *  to delete. We don't actually destroy the
+   *  window because "it is only a request". (The
+   *  window might contain vital data that the
+   *  program does not want destroyed). Instead
+   *  the event is passed along to the program,
+   *  which should then destroy the window.
+   */
+      GDK_NOTE (EVENTS,
+		g_message ("delete window:\t\twindow: %ld",
+			   xevent->xclient.window));
+
+      event->any.type = GDK_DELETE;
+
+      gdk_x11_window_set_user_time (win, xevent->xclient.data.l[1]);
+
+      return GDK_FILTER_TRANSLATE;
+    }
+  else if (atom == gdk_x11_get_xatom_by_name_for_display (display, "WM_TAKE_FOCUS"))
+    {
+      GdkToplevelX11 *toplevel = _gdk_x11_window_get_toplevel (event->any.window);
+      GdkWindowObject *private = (GdkWindowObject *)win;
+
+      /* There is no way of knowing reliably whether we are viewable;
+       * _gdk_x11_set_input_focus_safe() traps errors asynchronously.
+       */
+      if (toplevel && private->accept_focus)
+	_gdk_x11_set_input_focus_safe (display, toplevel->focus_window,
+				       RevertToParent,
+				       xevent->xclient.data.l[1]);
+
+      return GDK_FILTER_REMOVE;
+    }
+  else if (atom == gdk_x11_get_xatom_by_name_for_display (display, "_NET_WM_PING") &&
+	   !_gdk_x11_display_is_root_window (display,
+					     xevent->xclient.window))
+    {
+      XClientMessageEvent xclient = xevent->xclient;
+
+      xclient.window = GDK_WINDOW_XROOTWIN (win);
+      XSendEvent (GDK_WINDOW_XDISPLAY (win),
+		  xclient.window,
+		  False,
+		  SubstructureRedirectMask | SubstructureNotifyMask, (XEvent *)&xclient);
+
+      return GDK_FILTER_REMOVE;
+    }
+  else if (atom == gdk_x11_get_xatom_by_name_for_display (display, "_NET_WM_SYNC_REQUEST") &&
+	   GDK_DISPLAY_X11 (display)->use_sync)
+    {
+      GdkToplevelX11 *toplevel = _gdk_x11_window_get_toplevel (event->any.window);
+      if (toplevel)
+	{
+#ifdef HAVE_XSYNC
+	  XSyncIntsToValue (&toplevel->pending_counter_value,
+			    xevent->xclient.data.l[2],
+			    xevent->xclient.data.l[3]);
+#endif
+	}
+      return GDK_FILTER_REMOVE;
+    }
+
+  return GDK_FILTER_CONTINUE;
+}
+
+static void
+_gdk_event_init (GdkDisplay *display)
+{
+  GdkDisplayX11 *display_x11;
+  GdkDeviceManager *device_manager;
+
+  display_x11 = GDK_DISPLAY_X11 (display);
+  display_x11->event_source = gdk_event_source_new (display);
+
+  gdk_event_source_add_translator ((GdkEventSource *) display_x11->event_source,
+                                   GDK_EVENT_TRANSLATOR (display));
+
+  device_manager = gdk_display_get_device_manager (display);
+  gdk_event_source_add_translator ((GdkEventSource *) display_x11->event_source,
+                                   GDK_EVENT_TRANSLATOR (device_manager));
+
+  gdk_display_add_client_message_filter (display,
+					 gdk_atom_intern_static_string ("WM_PROTOCOLS"),
+					 gdk_wm_protocols_filter,
+					 NULL);
+}
+
+static void
+_gdk_input_init (GdkDisplay *display)
+{
+  GdkDisplayX11 *display_x11;
+  GdkDeviceManager *device_manager;
+  GdkDevice *device;
+  GList *list, *l;
+
+  display_x11 = GDK_DISPLAY_X11 (display);
+  device_manager = gdk_display_get_device_manager (display);
+
+  /* For backwards compatibility, just add
+   * floating devices that are not keyboards.
+   */
+  list = gdk_device_manager_get_devices (device_manager, GDK_DEVICE_TYPE_FLOATING);
+
+  for (l = list; l; l = l->next)
+    {
+      device = l->data;
+
+      if (device->source == GDK_SOURCE_KEYBOARD)
+        continue;
+
+      display_x11->input_devices = g_list_prepend (display_x11->input_devices, l->data);
+    }
+
+  g_list_free (list);
+
+  /* Now set "core" pointer to the first
+   * master device that is a pointer.
+   */
+  list = gdk_device_manager_get_devices (device_manager, GDK_DEVICE_TYPE_MASTER);
+
+  for (l = list; l; l = l->next)
+    {
+      device = list->data;
+
+      if (device->source != GDK_SOURCE_MOUSE)
+        continue;
+
+      display->core_pointer = device;
+      break;
+    }
+
+  /* Add the core pointer to the devices list */
+  display_x11->input_devices = g_list_prepend (display_x11->input_devices, display->core_pointer);
+
+  g_list_free (list);
+}
+
 /**
  * gdk_display_open:
  * @display_name: the name of the display to open
@@ -206,11 +1213,15 @@ gdk_display_open (const gchar *display_name)
    * structures in places
    */
   for (i = 0; i < ScreenCount (display_x11->xdisplay); i++)
-    _gdk_x11_events_init_screen (display_x11->screens[i]);
-  
+    _gdk_screen_x11_events_init (display_x11->screens[i]);
+
   /*set the default screen */
   display_x11->default_screen = display_x11->screens[DefaultScreen (display_x11->xdisplay)];
 
+  display->device_manager = _gdk_device_manager_new (display);
+
+  _gdk_event_init (display);
+
   attr.window_type = GDK_WINDOW_TOPLEVEL;
   attr.wclass = GDK_INPUT_OUTPUT;
   attr.x = 10;
@@ -395,15 +1406,15 @@ gdk_display_open (const gchar *display_name)
       display_x11->use_sync = TRUE;
   }
 #endif
-  
+
   _gdk_windowing_image_init (display);
-  _gdk_events_init (display);
   _gdk_input_init (display);
   _gdk_dnd_init (display);
 
   for (i = 0; i < ScreenCount (display_x11->xdisplay); i++)
     _gdk_x11_screen_setup (display_x11->screens[i]);
 
+  g_signal_emit_by_name (display, "opened");
   g_signal_emit_by_name (gdk_display_manager_get(),
 			 "display_opened", display);
 
@@ -592,11 +1603,13 @@ struct XPointerUngrabInfo {
 };
 
 static void
-pointer_ungrab_callback (GdkDisplay *display,
-			 gpointer data,
-			 gulong serial)
+device_ungrab_callback (GdkDisplay *display,
+                        gpointer    data,
+                        gulong      serial)
 {
-  _gdk_display_pointer_grab_update (display, serial);
+  GdkDevice *device = data;
+
+  _gdk_display_device_grab_update (display, device, serial);
 }
 
 
@@ -606,78 +1619,45 @@ pointer_ungrab_callback (GdkDisplay *display,
   )
 
 /**
- * gdk_display_pointer_ungrab:
- * @display: a #GdkDisplay.
+ * gdk_device_ungrab:
+ * @device: a #GdkDevice
  * @time_: a timestap (e.g. %GDK_CURRENT_TIME).
  *
- * Release any pointer grab.
- *
- * Since: 2.2
+ * Release any grab on @device.
  */
 void
-gdk_display_pointer_ungrab (GdkDisplay *display,
-			    guint32     time_)
+gdk_device_ungrab (GdkDevice  *device,
+                   guint32     time_)
 {
+  GdkDisplay *display;
   Display *xdisplay;
-  GdkDisplayX11 *display_x11;
-  GdkPointerGrabInfo *grab;
+  GdkDeviceGrabInfo *grab;
   unsigned long serial;
 
-  g_return_if_fail (GDK_IS_DISPLAY (display));
+  g_return_if_fail (GDK_IS_DEVICE (device));
 
-  display_x11 = GDK_DISPLAY_X11 (display);
+  display = gdk_device_get_display (device);
   xdisplay = GDK_DISPLAY_XDISPLAY (display);
 
   serial = NextRequest (xdisplay);
-  
-  _gdk_input_ungrab_pointer (display, time_);
-  XUngrabPointer (xdisplay, time_);
+
+  GDK_DEVICE_GET_CLASS (device)->ungrab (device, time_);
   XFlush (xdisplay);
 
-  grab = _gdk_display_get_last_pointer_grab (display);
+  grab = _gdk_display_get_last_device_grab (display, device);
   if (grab &&
       (time_ == GDK_CURRENT_TIME ||
        grab->time == GDK_CURRENT_TIME ||
        !XSERVER_TIME_IS_LATER (grab->time, time_)))
     {
       grab->serial_end = serial;
-      _gdk_x11_roundtrip_async (display, 
-				pointer_ungrab_callback,
-				NULL);
+      _gdk_x11_roundtrip_async (display,
+				device_ungrab_callback,
+				device);
     }
 }
 
 /**
- * gdk_display_keyboard_ungrab:
- * @display: a #GdkDisplay.
- * @time_: a timestap (e.g #GDK_CURRENT_TIME).
- *
- * Release any keyboard grab
- *
- * Since: 2.2
- */
-void
-gdk_display_keyboard_ungrab (GdkDisplay *display,
-			     guint32     time)
-{
-  Display *xdisplay;
-  GdkDisplayX11 *display_x11;
-  
-  g_return_if_fail (GDK_IS_DISPLAY (display));
-
-  display_x11 = GDK_DISPLAY_X11 (display);
-  xdisplay = GDK_DISPLAY_XDISPLAY (display);
-  
-  XUngrabKeyboard (xdisplay, time);
-  XFlush (xdisplay);
-  
-  if (time == GDK_CURRENT_TIME || 
-      display->keyboard_grab.time == GDK_CURRENT_TIME ||
-      !XSERVER_TIME_IS_LATER (display->keyboard_grab.time, time))
-    _gdk_display_unset_has_keyboard_grab (display, FALSE);
-}
-
-/**
  * gdk_display_beep:
  * @display: a #GdkDisplay
  *
@@ -825,7 +1805,12 @@ gdk_display_x11_dispose (GObject *object)
   for (i = 0; i < ScreenCount (display_x11->xdisplay); i++)
     _gdk_screen_close (display_x11->screens[i]);
 
-  _gdk_events_uninit (GDK_DISPLAY_OBJECT (object));
+  if (display_x11->event_source)
+    {
+      g_source_destroy (display_x11->event_source);
+      g_source_unref (display_x11->event_source);
+      display_x11->event_source = NULL;
+    }
 
   G_OBJECT_CLASS (_gdk_display_x11_parent_class)->dispose (object);
 }
@@ -1475,6 +2460,228 @@ gdk_display_supports_composite (GdkDisplay *display)
 	 x11_display->have_xfixes;
 }
 
+/**
+ * gdk_display_list_devices:
+ * @display: a #GdkDisplay
+ *
+ * Returns the list of available input devices attached to @display.
+ * The list is statically allocated and should not be freed.
+ *
+ * Return value: a list of #GdkDevice
+ *
+ * Since: 2.2
+ **/
+GList *
+gdk_display_list_devices (GdkDisplay *display)
+{
+  g_return_val_if_fail (GDK_IS_DISPLAY (display), NULL);
+
+  return GDK_DISPLAY_X11 (display)->input_devices;
+}
+
+/**
+ * gdk_event_send_client_message_for_display:
+ * @display: the #GdkDisplay for the window where the message is to be sent.
+ * @event: the #GdkEvent to send, which should be a #GdkEventClient.
+ * @winid: the window to send the client message to.
+ *
+ * On X11, sends an X ClientMessage event to a given window. On
+ * Windows, sends a message registered with the name
+ * GDK_WIN32_CLIENT_MESSAGE.
+ *
+ * This could be used for communicating between different
+ * applications, though the amount of data is limited to 20 bytes on
+ * X11, and to just four bytes on Windows.
+ *
+ * Returns: non-zero on success.
+ *
+ * Since: 2.2
+ */
+gboolean
+gdk_event_send_client_message_for_display (GdkDisplay     *display,
+					   GdkEvent       *event,
+					   GdkNativeWindow winid)
+{
+  XEvent sev;
+
+  g_return_val_if_fail(event != NULL, FALSE);
+
+  /* Set up our event to send, with the exception of its target window */
+  sev.xclient.type = ClientMessage;
+  sev.xclient.display = GDK_DISPLAY_XDISPLAY (display);
+  sev.xclient.format = event->client.data_format;
+  sev.xclient.window = winid;
+  memcpy(&sev.xclient.data, &event->client.data, sizeof (sev.xclient.data));
+  sev.xclient.message_type = gdk_x11_atom_to_xatom_for_display (display, event->client.message_type);
+
+  return _gdk_send_xevent (display, winid, False, NoEventMask, &sev);
+}
+
+/**
+ * gdk_display_add_client_message_filter:
+ * @display: a #GdkDisplay for which this message filter applies
+ * @message_type: the type of ClientMessage events to receive.
+ *   This will be checked against the @message_type field
+ *   of the XClientMessage event struct.
+ * @func: the function to call to process the event.
+ * @data: user data to pass to @func.
+ *
+ * Adds a filter to be called when X ClientMessage events are received.
+ * See gdk_window_add_filter() if you are interested in filtering other
+ * types of events.
+ *
+ * Since: 2.2
+ **/
+void
+gdk_display_add_client_message_filter (GdkDisplay   *display,
+				       GdkAtom       message_type,
+				       GdkFilterFunc func,
+				       gpointer      data)
+{
+  GdkClientFilter *filter;
+  g_return_if_fail (GDK_IS_DISPLAY (display));
+  filter = g_new (GdkClientFilter, 1);
+
+  filter->type = message_type;
+  filter->function = func;
+  filter->data = data;
+
+  GDK_DISPLAY_X11(display)->client_filters =
+    g_list_append (GDK_DISPLAY_X11 (display)->client_filters,
+		   filter);
+}
+
+/**
+ * gdk_add_client_message_filter:
+ * @message_type: the type of ClientMessage events to receive. This will be
+ *     checked against the <structfield>message_type</structfield> field of the
+ *     XClientMessage event struct.
+ * @func: the function to call to process the event.
+ * @data: user data to pass to @func.
+ *
+ * Adds a filter to the default display to be called when X ClientMessage events
+ * are received. See gdk_display_add_client_message_filter().
+ **/
+void
+gdk_add_client_message_filter (GdkAtom       message_type,
+			       GdkFilterFunc func,
+			       gpointer      data)
+{
+  gdk_display_add_client_message_filter (gdk_display_get_default (),
+					 message_type, func, data);
+}
+
+/*
+ *--------------------------------------------------------------
+ * gdk_flush
+ *
+ *   Flushes the Xlib output buffer and then waits
+ *   until all requests have been received and processed
+ *   by the X server. The only real use for this function
+ *   is in dealing with XShm.
+ *
+ * Arguments:
+ *
+ * Results:
+ *
+ * Side effects:
+ *
+ *--------------------------------------------------------------
+ */
+void
+gdk_flush (void)
+{
+  GSList *tmp_list = _gdk_displays;
+
+  while (tmp_list)
+    {
+      XSync (GDK_DISPLAY_XDISPLAY (tmp_list->data), False);
+      tmp_list = tmp_list->next;
+    }
+}
+
+/**
+ * gdk_x11_register_standard_event_type:
+ * @display: a #GdkDisplay
+ * @event_base: first event type code to register
+ * @n_events: number of event type codes to register
+ *
+ * Registers interest in receiving extension events with type codes
+ * between @event_base and <literal>event_base + n_events - 1</literal>.
+ * The registered events must have the window field in the same place
+ * as core X events (this is not the case for e.g. XKB extension events).
+ *
+ * If an event type is registered, events of this type will go through
+ * global and window-specific filters (see gdk_window_add_filter()).
+ * Unregistered events will only go through global filters.
+ * GDK may register the events of some X extensions on its own.
+ *
+ * This function should only be needed in unusual circumstances, e.g.
+ * when filtering XInput extension events on the root window.
+ *
+ * Since: 2.4
+ **/
+void
+gdk_x11_register_standard_event_type (GdkDisplay *display,
+				      gint        event_base,
+				      gint        n_events)
+{
+  GdkEventTypeX11 *event_type;
+  GdkDisplayX11 *display_x11;
+
+  display_x11 = GDK_DISPLAY_X11 (display);
+  event_type = g_new (GdkEventTypeX11, 1);
+
+  event_type->base = event_base;
+  event_type->n_events = n_events;
+
+  display_x11->event_types = g_slist_prepend (display_x11->event_types, event_type);
+}
+
+static Bool
+graphics_expose_predicate (Display  *display,
+			   XEvent   *xevent,
+			   XPointer  arg)
+{
+  if (xevent->xany.window == GDK_DRAWABLE_XID ((GdkDrawable *)arg) &&
+      (xevent->xany.type == GraphicsExpose ||
+       xevent->xany.type == NoExpose))
+    return True;
+  else
+    return False;
+}
+
+/**
+ * gdk_event_get_graphics_expose:
+ * @window: the #GdkWindow to wait for the events for.
+ *
+ * Waits for a GraphicsExpose or NoExpose event from the X server.
+ * This is used in the #GtkText and #GtkCList widgets in GTK+ to make sure any
+ * GraphicsExpose events are handled before the widget is scrolled.
+ *
+ * Return value:  a #GdkEventExpose if a GraphicsExpose was received, or %NULL if a
+ * NoExpose event was received.
+ *
+ * Deprecated:2.18
+ **/
+GdkEvent*
+gdk_event_get_graphics_expose (GdkWindow *window)
+{
+  GdkDisplay *display;
+  XEvent xevent;
+
+  g_return_val_if_fail (window != NULL, NULL);
+
+  display = gdk_drawable_get_display (GDK_DRAWABLE (window));
+
+  XIfEvent (GDK_WINDOW_XDISPLAY (window), &xevent,
+	    graphics_expose_predicate, (XPointer) window);
+
+  if (xevent.xany.type == GraphicsExpose)
+    return gdk_event_translator_translate (GDK_EVENT_TRANSLATOR (display),
+                                           display, &xevent);
+  return NULL;
+}
 
 #define __GDK_DISPLAY_X11_C__
 #include "gdkaliasdef.c"
diff --git a/gdk/x11/gdkevents-x11.c b/gdk/x11/gdkevents-x11.c
deleted file mode 100644
index e2cae06..0000000
--- a/gdk/x11/gdkevents-x11.c
+++ /dev/null
@@ -1,3123 +0,0 @@
-/* GDK - The GIMP Drawing Kit
- * Copyright (C) 1995-2007 Peter Mattis, Spencer Kimball,
- * Josh MacDonald, Ryan Lortie
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.	 See the GNU
- * 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.
- */
-
-/*
- * Modified by the GTK+ Team and others 1997-2007.  See the AUTHORS
- * file for a list of people on the GTK+ Team.  See the ChangeLog
- * files for a list of changes.  These files are distributed with
- * GTK+ at ftp://ftp.gtk.org/pub/gtk/. 
- */
-
-#include "config.h"
-
-#include "gdk.h"
-#include "gdkprivate-x11.h"
-#include "gdkinternals.h"
-#include "gdkx.h"
-#include "gdkscreen-x11.h"
-#include "gdkdisplay-x11.h"
-#include "gdkasync.h"
-
-#include "gdkkeysyms.h"
-
-#include "xsettings-client.h"
-
-#include <string.h>
-
-#include "gdkinputprivate.h"
-#include "gdksettings.c"
-#include "gdkalias.h"
-
-
-#ifdef HAVE_XKB
-#include <X11/XKBlib.h>
-#endif
-
-#ifdef HAVE_XSYNC
-#include <X11/extensions/sync.h>
-#endif
-
-#ifdef HAVE_XFIXES
-#include <X11/extensions/Xfixes.h>
-#endif
-
-#ifdef HAVE_RANDR
-#include <X11/extensions/Xrandr.h>
-#endif
-
-#include <X11/Xatom.h>
-
-typedef struct _GdkIOClosure GdkIOClosure;
-typedef struct _GdkDisplaySource GdkDisplaySource;
-typedef struct _GdkEventTypeX11 GdkEventTypeX11;
-
-struct _GdkIOClosure
-{
-  GdkInputFunction function;
-  GdkInputCondition condition;
-  GDestroyNotify notify;
-  gpointer data;
-};
-
-struct _GdkDisplaySource
-{
-  GSource source;
-  
-  GdkDisplay *display;
-  GPollFD event_poll_fd;
-};
-
-struct _GdkEventTypeX11
-{
-  gint base;
-  gint n_events;
-};
-
-/* 
- * Private function declarations
- */
-
-static gint	 gdk_event_apply_filters (XEvent   *xevent,
-					  GdkEvent *event,
-					  GList    *filters);
-static gboolean	 gdk_event_translate	 (GdkDisplay *display,
-					  GdkEvent   *event, 
-					  XEvent     *xevent,
-					  gboolean    return_exposes);
-
-static gboolean gdk_event_prepare  (GSource     *source,
-				    gint        *timeout);
-static gboolean gdk_event_check    (GSource     *source);
-static gboolean gdk_event_dispatch (GSource     *source,
-				    GSourceFunc  callback,
-				    gpointer     user_data);
-
-static GdkFilterReturn gdk_wm_protocols_filter (GdkXEvent *xev,
-						GdkEvent  *event,
-						gpointer   data);
-
-static GSource *gdk_display_source_new (GdkDisplay *display);
-static gboolean gdk_check_xpending     (GdkDisplay *display);
-
-static Bool gdk_xsettings_watch_cb  (Window            window,
-				     Bool              is_start,
-				     long              mask,
-				     void             *cb_data);
-static void gdk_xsettings_notify_cb (const char       *name,
-				     XSettingsAction   action,
-				     XSettingsSetting *setting,
-				     void             *data);
-
-/* Private variable declarations
- */
-
-static GList *display_sources;
-
-static GSourceFuncs event_funcs = {
-  gdk_event_prepare,
-  gdk_event_check,
-  gdk_event_dispatch,
-  NULL
-};
-
-static GSource *
-gdk_display_source_new (GdkDisplay *display)
-{
-  GSource *source = g_source_new (&event_funcs, sizeof (GdkDisplaySource));
-  GdkDisplaySource *display_source = (GdkDisplaySource *)source;
-  
-  display_source->display = display;
-  
-  return source;
-}
-
-static gboolean
-gdk_check_xpending (GdkDisplay *display)
-{
-  return XPending (GDK_DISPLAY_XDISPLAY (display));
-}
-
-/*********************************************
- * Functions for maintaining the event queue *
- *********************************************/
-
-static void
-refcounted_grab_server (Display *xdisplay)
-{
-  GdkDisplay *display = gdk_x11_lookup_xdisplay (xdisplay);
-
-  gdk_x11_display_grab (display);
-}
-
-static void
-refcounted_ungrab_server (Display *xdisplay)
-{
-  GdkDisplay *display = gdk_x11_lookup_xdisplay (xdisplay);
-  
-  gdk_x11_display_ungrab (display);
-}
-
-void
-_gdk_x11_events_init_screen (GdkScreen *screen)
-{
-  GdkScreenX11 *screen_x11 = GDK_SCREEN_X11 (screen);
-
-  /* Keep a flag to avoid extra notifies that we don't need
-   */
-  screen_x11->xsettings_in_init = TRUE;
-  screen_x11->xsettings_client = xsettings_client_new_with_grab_funcs (screen_x11->xdisplay,
-						                       screen_x11->screen_num,
-						                       gdk_xsettings_notify_cb,
-						                       gdk_xsettings_watch_cb,
-						                       screen,
-                                                                       refcounted_grab_server,
-                                                                       refcounted_ungrab_server);
-  screen_x11->xsettings_in_init = FALSE;
-}
-
-void
-_gdk_x11_events_uninit_screen (GdkScreen *screen)
-{
-  GdkScreenX11 *screen_x11 = GDK_SCREEN_X11 (screen);
-
-  if (screen_x11->xsettings_client)
-    {
-      xsettings_client_destroy (screen_x11->xsettings_client);
-      screen_x11->xsettings_client = NULL;
-    }
-}
-
-void 
-_gdk_events_init (GdkDisplay *display)
-{
-  GSource *source;
-  GdkDisplaySource *display_source;
-  GdkDisplayX11 *display_x11 = GDK_DISPLAY_X11 (display);
-  
-  int connection_number = ConnectionNumber (display_x11->xdisplay);
-  GDK_NOTE (MISC, g_message ("connection number: %d", connection_number));
-
-
-  source = display_x11->event_source = gdk_display_source_new (display);
-  display_source = (GdkDisplaySource*) source;
-  g_source_set_priority (source, GDK_PRIORITY_EVENTS);
-  
-  display_source->event_poll_fd.fd = connection_number;
-  display_source->event_poll_fd.events = G_IO_IN;
-  
-  g_source_add_poll (source, &display_source->event_poll_fd);
-  g_source_set_can_recurse (source, TRUE);
-  g_source_attach (source, NULL);
-
-  display_sources = g_list_prepend (display_sources,display_source);
-
-  gdk_display_add_client_message_filter (display,
-					 gdk_atom_intern_static_string ("WM_PROTOCOLS"), 
-					 gdk_wm_protocols_filter,   
-					 NULL);
-}
-
-void
-_gdk_events_uninit (GdkDisplay *display)
-{
-  GdkDisplayX11 *display_x11 = GDK_DISPLAY_X11 (display);
-
-  if (display_x11->event_source)
-    {
-      display_sources = g_list_remove (display_sources,
-                                       display_x11->event_source);
-      g_source_destroy (display_x11->event_source);
-      g_source_unref (display_x11->event_source);
-      display_x11->event_source = NULL;
-    }
-}
-
-/**
- * gdk_events_pending:
- * 
- * Checks if any events are ready to be processed for any display.
- * 
- * Return value:  %TRUE if any events are pending.
- **/
-gboolean
-gdk_events_pending (void)
-{
-  GList *tmp_list;
-
-  for (tmp_list = display_sources; tmp_list; tmp_list = tmp_list->next)
-    {
-      GdkDisplaySource *tmp_source = tmp_list->data;
-      GdkDisplay *display = tmp_source->display;
-      
-      if (_gdk_event_queue_find_first (display))
-	return TRUE;
-    }
-
-  for (tmp_list = display_sources; tmp_list; tmp_list = tmp_list->next)
-    {
-      GdkDisplaySource *tmp_source = tmp_list->data;
-      GdkDisplay *display = tmp_source->display;
-      
-      if (gdk_check_xpending (display))
-	return TRUE;
-    }
-  
-  return FALSE;
-}
-
-static Bool
-graphics_expose_predicate (Display  *display,
-			   XEvent   *xevent,
-			   XPointer  arg)
-{
-  if (xevent->xany.window == GDK_DRAWABLE_XID ((GdkDrawable *)arg) &&
-      (xevent->xany.type == GraphicsExpose ||
-       xevent->xany.type == NoExpose))
-    return True;
-  else
-    return False;
-}
-
-/**
- * gdk_event_get_graphics_expose:
- * @window: the #GdkWindow to wait for the events for.
- * 
- * Waits for a GraphicsExpose or NoExpose event from the X server.
- * This is used in the #GtkText and #GtkCList widgets in GTK+ to make sure any
- * GraphicsExpose events are handled before the widget is scrolled.
- *
- * Return value:  a #GdkEventExpose if a GraphicsExpose was received, or %NULL if a
- * NoExpose event was received.
- *
- * Deprecated:2.18
- **/
-GdkEvent*
-gdk_event_get_graphics_expose (GdkWindow *window)
-{
-  XEvent xevent;
-  GdkEvent *event;
-  
-  g_return_val_if_fail (window != NULL, NULL);
-
-  XIfEvent (GDK_WINDOW_XDISPLAY (window), &xevent, 
-	    graphics_expose_predicate, (XPointer) window);
-  
-  if (xevent.xany.type == GraphicsExpose)
-    {
-      event = gdk_event_new (GDK_NOTHING);
-      
-      if (gdk_event_translate (GDK_WINDOW_DISPLAY (window), event,
-			       &xevent, TRUE))
-	return event;
-      else
-	gdk_event_free (event);
-    }
-  
-  return NULL;	
-}
-
-static gint
-gdk_event_apply_filters (XEvent *xevent,
-			 GdkEvent *event,
-			 GList *filters)
-{
-  GList *tmp_list;
-  GdkFilterReturn result;
-  
-  tmp_list = filters;
-  
-  while (tmp_list)
-    {
-      GdkEventFilter *filter = (GdkEventFilter*) tmp_list->data;
-      
-      tmp_list = tmp_list->next;
-      result = filter->function (xevent, event, filter->data);
-      if (result !=  GDK_FILTER_CONTINUE)
-	return result;
-    }
-  
-  return GDK_FILTER_CONTINUE;
-}
-
-/**
- * gdk_display_add_client_message_filter:
- * @display: a #GdkDisplay for which this message filter applies
- * @message_type: the type of ClientMessage events to receive.
- *   This will be checked against the @message_type field 
- *   of the XClientMessage event struct.
- * @func: the function to call to process the event.
- * @data: user data to pass to @func.
- *
- * Adds a filter to be called when X ClientMessage events are received.
- * See gdk_window_add_filter() if you are interested in filtering other
- * types of events.
- *
- * Since: 2.2
- **/ 
-void 
-gdk_display_add_client_message_filter (GdkDisplay   *display,
-				       GdkAtom       message_type,
-				       GdkFilterFunc func,
-				       gpointer      data)
-{
-  GdkClientFilter *filter;
-  g_return_if_fail (GDK_IS_DISPLAY (display));
-  filter = g_new (GdkClientFilter, 1);
-
-  filter->type = message_type;
-  filter->function = func;
-  filter->data = data;
-  
-  GDK_DISPLAY_X11(display)->client_filters = 
-    g_list_append (GDK_DISPLAY_X11 (display)->client_filters,
-		   filter);
-}
-
-/**
- * gdk_add_client_message_filter:
- * @message_type: the type of ClientMessage events to receive. This will be
- *     checked against the <structfield>message_type</structfield> field of the
- *     XClientMessage event struct.
- * @func: the function to call to process the event.
- * @data: user data to pass to @func. 
- * 
- * Adds a filter to the default display to be called when X ClientMessage events
- * are received. See gdk_display_add_client_message_filter().
- **/
-void 
-gdk_add_client_message_filter (GdkAtom       message_type,
-			       GdkFilterFunc func,
-			       gpointer      data)
-{
-  gdk_display_add_client_message_filter (gdk_display_get_default (),
-					 message_type, func, data);
-}
-
-static void
-do_net_wm_state_changes (GdkWindow *window)
-{
-  GdkToplevelX11 *toplevel = _gdk_x11_window_get_toplevel (window);
-  GdkWindowState old_state;
-  
-  if (GDK_WINDOW_DESTROYED (window) ||
-      gdk_window_get_window_type (window) != GDK_WINDOW_TOPLEVEL)
-    return;
-  
-  old_state = gdk_window_get_state (window);
-
-  /* For found_sticky to remain TRUE, we have to also be on desktop
-   * 0xFFFFFFFF
-   */
-  if (old_state & GDK_WINDOW_STATE_STICKY)
-    {
-      if (!(toplevel->have_sticky && toplevel->on_all_desktops))
-        gdk_synthesize_window_state (window,
-                                     GDK_WINDOW_STATE_STICKY,
-                                     0);
-    }
-  else
-    {
-      if (toplevel->have_sticky || toplevel->on_all_desktops)
-        gdk_synthesize_window_state (window,
-                                     0,
-                                     GDK_WINDOW_STATE_STICKY);
-    }
-
-  if (old_state & GDK_WINDOW_STATE_FULLSCREEN)
-    {
-      if (!toplevel->have_fullscreen)
-        gdk_synthesize_window_state (window,
-                                     GDK_WINDOW_STATE_FULLSCREEN,
-                                     0);
-    }
-  else
-    {
-      if (toplevel->have_fullscreen)
-        gdk_synthesize_window_state (window,
-                                     0,
-                                     GDK_WINDOW_STATE_FULLSCREEN);
-    }
-  
-  /* Our "maximized" means both vertical and horizontal; if only one,
-   * we don't expose that via GDK
-   */
-  if (old_state & GDK_WINDOW_STATE_MAXIMIZED)
-    {
-      if (!(toplevel->have_maxvert && toplevel->have_maxhorz))
-        gdk_synthesize_window_state (window,
-                                     GDK_WINDOW_STATE_MAXIMIZED,
-                                     0);
-    }
-  else
-    {
-      if (toplevel->have_maxvert && toplevel->have_maxhorz)
-        gdk_synthesize_window_state (window,
-                                     0,
-                                     GDK_WINDOW_STATE_MAXIMIZED);
-    }
-}
-
-static void
-gdk_check_wm_desktop_changed (GdkWindow *window)
-{
-  GdkToplevelX11 *toplevel = _gdk_x11_window_get_toplevel (window);
-  GdkDisplay *display = GDK_WINDOW_DISPLAY (window);
-
-  Atom type;
-  gint format;
-  gulong nitems;
-  gulong bytes_after;
-  guchar *data;
-  gulong *desktop;
-
-  type = None;
-  gdk_error_trap_push ();
-  XGetWindowProperty (GDK_DISPLAY_XDISPLAY (display), 
-                      GDK_WINDOW_XID (window),
-                      gdk_x11_get_xatom_by_name_for_display (display, "_NET_WM_DESKTOP"),
-                      0, G_MAXLONG, False, XA_CARDINAL, &type, 
-                      &format, &nitems,
-                      &bytes_after, &data);
-  gdk_error_trap_pop ();
-
-  if (type != None)
-    {
-      desktop = (gulong *)data;
-      toplevel->on_all_desktops = (*desktop == 0xFFFFFFFF);
-      XFree (desktop);
-    }
-  else
-    toplevel->on_all_desktops = FALSE;
-      
-  do_net_wm_state_changes (window);
-}
-
-static void
-gdk_check_wm_state_changed (GdkWindow *window)
-{
-  GdkToplevelX11 *toplevel = _gdk_x11_window_get_toplevel (window);
-  GdkDisplay *display = GDK_WINDOW_DISPLAY (window);
-  
-  Atom type;
-  gint format;
-  gulong nitems;
-  gulong bytes_after;
-  guchar *data;
-  Atom *atoms = NULL;
-  gulong i;
-
-  gboolean had_sticky = toplevel->have_sticky;
-
-  toplevel->have_sticky = FALSE;
-  toplevel->have_maxvert = FALSE;
-  toplevel->have_maxhorz = FALSE;
-  toplevel->have_fullscreen = FALSE;
-
-  type = None;
-  gdk_error_trap_push ();
-  XGetWindowProperty (GDK_DISPLAY_XDISPLAY (display), GDK_WINDOW_XID (window),
-		      gdk_x11_get_xatom_by_name_for_display (display, "_NET_WM_STATE"),
-		      0, G_MAXLONG, False, XA_ATOM, &type, &format, &nitems,
-		      &bytes_after, &data);
-  gdk_error_trap_pop ();
-
-  if (type != None)
-    {
-      Atom sticky_atom = gdk_x11_get_xatom_by_name_for_display (display, "_NET_WM_STATE_STICKY");
-      Atom maxvert_atom = gdk_x11_get_xatom_by_name_for_display (display, "_NET_WM_STATE_MAXIMIZED_VERT");
-      Atom maxhorz_atom	= gdk_x11_get_xatom_by_name_for_display (display, "_NET_WM_STATE_MAXIMIZED_HORZ");
-      Atom fullscreen_atom = gdk_x11_get_xatom_by_name_for_display (display, "_NET_WM_STATE_FULLSCREEN");
-
-      atoms = (Atom *)data;
-
-      i = 0;
-      while (i < nitems)
-        {
-          if (atoms[i] == sticky_atom)
-            toplevel->have_sticky = TRUE;
-          else if (atoms[i] == maxvert_atom)
-            toplevel->have_maxvert = TRUE;
-          else if (atoms[i] == maxhorz_atom)
-            toplevel->have_maxhorz = TRUE;
-          else if (atoms[i] == fullscreen_atom)
-            toplevel->have_fullscreen = TRUE;
-          
-          ++i;
-        }
-
-      XFree (atoms);
-    }
-
-  /* When have_sticky is turned on, we have to check the DESKTOP property
-   * as well.
-   */
-  if (toplevel->have_sticky && !had_sticky)
-    gdk_check_wm_desktop_changed (window);
-  else
-    do_net_wm_state_changes (window);
-}
-
-#define HAS_FOCUS(toplevel)                           \
-  ((toplevel)->has_focus || (toplevel)->has_pointer_focus)
-
-static void
-generate_focus_event (GdkWindow *window,
-		      gboolean   in)
-{
-  GdkEvent event;
-  
-  event.type = GDK_FOCUS_CHANGE;
-  event.focus_change.window = window;
-  event.focus_change.send_event = FALSE;
-  event.focus_change.in = in;
-  
-  gdk_event_put (&event);
-}
-
-static gboolean
-set_screen_from_root (GdkDisplay *display,
-		      GdkEvent   *event,
-		      Window      xrootwin)
-{
-  GdkScreen *screen;
-
-  screen = _gdk_x11_display_screen_for_xrootwin (display, xrootwin);
-
-  if (screen)
-    {
-      gdk_event_set_screen (event, screen);
-
-      return TRUE;
-    }
-  
-  return FALSE;
-}
-
-static void
-translate_key_event (GdkDisplay *display,
-		     GdkEvent   *event,
-		     XEvent     *xevent)
-{
-  GdkKeymap *keymap = gdk_keymap_get_for_display (display);
-  gunichar c = 0;
-  gchar buf[7];
-
-  event->key.type = xevent->xany.type == KeyPress ? GDK_KEY_PRESS : GDK_KEY_RELEASE;
-  event->key.time = xevent->xkey.time;
-
-  event->key.state = (GdkModifierType) xevent->xkey.state;
-  event->key.group = _gdk_x11_get_group_for_state (display, xevent->xkey.state);
-  event->key.hardware_keycode = xevent->xkey.keycode;
-
-  event->key.keyval = GDK_VoidSymbol;
-
-  gdk_keymap_translate_keyboard_state (keymap,
-				       event->key.hardware_keycode,
-				       event->key.state,
-				       event->key.group,
-				       &event->key.keyval,
-				       NULL, NULL, NULL);
-
-  _gdk_keymap_add_virtual_modifiers (keymap, &event->key.state);
-  event->key.is_modifier = _gdk_keymap_key_is_modifier (keymap, event->key.hardware_keycode);
-
-  /* Fill in event->string crudely, since various programs
-   * depend on it.
-   */
-  event->key.string = NULL;
-  
-  if (event->key.keyval != GDK_VoidSymbol)
-    c = gdk_keyval_to_unicode (event->key.keyval);
-
-  if (c)
-    {
-      gsize bytes_written;
-      gint len;
-
-      /* Apply the control key - Taken from Xlib
-       */
-      if (event->key.state & GDK_CONTROL_MASK)
-	{
-	  if ((c >= '@' && c < '\177') || c == ' ') c &= 0x1F;
-	  else if (c == '2')
-	    {
-	      event->key.string = g_memdup ("\0\0", 2);
-	      event->key.length = 1;
-	      buf[0] = '\0';
-	      goto out;
-	    }
-	  else if (c >= '3' && c <= '7') c -= ('3' - '\033');
-	  else if (c == '8') c = '\177';
-	  else if (c == '/') c = '_' & 0x1F;
-	}
-      
-      len = g_unichar_to_utf8 (c, buf);
-      buf[len] = '\0';
-      
-      event->key.string = g_locale_from_utf8 (buf, len,
-					      NULL, &bytes_written,
-					      NULL);
-      if (event->key.string)
-	event->key.length = bytes_written;
-    }
-  else if (event->key.keyval == GDK_Escape)
-    {
-      event->key.length = 1;
-      event->key.string = g_strdup ("\033");
-    }
-  else if (event->key.keyval == GDK_Return ||
-	  event->key.keyval == GDK_KP_Enter)
-    {
-      event->key.length = 1;
-      event->key.string = g_strdup ("\r");
-    }
-
-  if (!event->key.string)
-    {
-      event->key.length = 0;
-      event->key.string = g_strdup ("");
-    }
-  
- out:
-#ifdef G_ENABLE_DEBUG
-  if (_gdk_debug_flags & GDK_DEBUG_EVENTS)
-    {
-      g_message ("%s:\t\twindow: %ld	 key: %12s  %d",
-		 event->type == GDK_KEY_PRESS ? "key press  " : "key release",
-		 xevent->xkey.window,
-		 event->key.keyval ? gdk_keyval_name (event->key.keyval) : "(none)",
-		 event->key.keyval);
-  
-      if (event->key.length > 0)
-	g_message ("\t\tlength: %4d string: \"%s\"",
-		   event->key.length, buf);
-    }
-#endif /* G_ENABLE_DEBUG */  
-  return;
-}
-
-/**
- * gdk_x11_register_standard_event_type:
- * @display: a #GdkDisplay
- * @event_base: first event type code to register
- * @n_events: number of event type codes to register
- * 
- * Registers interest in receiving extension events with type codes
- * between @event_base and <literal>event_base + n_events - 1</literal>.
- * The registered events must have the window field in the same place
- * as core X events (this is not the case for e.g. XKB extension events).
- *
- * If an event type is registered, events of this type will go through
- * global and window-specific filters (see gdk_window_add_filter()). 
- * Unregistered events will only go through global filters.
- * GDK may register the events of some X extensions on its own.
- * 
- * This function should only be needed in unusual circumstances, e.g.
- * when filtering XInput extension events on the root window.
- *
- * Since: 2.4
- **/
-void
-gdk_x11_register_standard_event_type (GdkDisplay          *display,
-				      gint                 event_base,
-				      gint                 n_events)
-{
-  GdkEventTypeX11 *event_type;
-  GdkDisplayX11 *display_x11;
-
-  display_x11 = GDK_DISPLAY_X11 (display);
-  event_type = g_new (GdkEventTypeX11, 1);
-
-  event_type->base = event_base;
-  event_type->n_events = n_events;
-
-  display_x11->event_types = g_slist_prepend (display_x11->event_types, event_type);
-}
-
-/* Return the window this has to do with, if any, rather
- * than the frame or root window that was selecting
- * for substructure
- */
-static void
-get_real_window (GdkDisplay *display,
-		 XEvent     *event,
-		 Window     *event_window,
-		 Window     *filter_window)
-{
-  /* Core events all have an event->xany.window field, but that's
-   * not true for extension events
-   */
-  if (event->type >= KeyPress &&
-      event->type <= MappingNotify)
-    {
-      *filter_window = event->xany.window;
-      switch (event->type)
-	{      
-	case CreateNotify:
-	  *event_window = event->xcreatewindow.window;
-	  break;
-	case DestroyNotify:
-	  *event_window = event->xdestroywindow.window;
-	  break;
-	case UnmapNotify:
-	  *event_window = event->xunmap.window;
-	  break;
-	case MapNotify:
-	  *event_window = event->xmap.window;
-	  break;
-	case MapRequest:
-	  *event_window = event->xmaprequest.window;
-	  break;
-	case ReparentNotify:
-	  *event_window = event->xreparent.window;
-	  break;
-	case ConfigureNotify:
-	  *event_window = event->xconfigure.window;
-	  break;
-	case ConfigureRequest:
-	  *event_window = event->xconfigurerequest.window;
-	  break;
-	case GravityNotify:
-	  *event_window = event->xgravity.window;
-	  break;
-	case CirculateNotify:
-	  *event_window = event->xcirculate.window;
-	  break;
-	case CirculateRequest:
-	  *event_window = event->xcirculaterequest.window;
-	  break;
-	default:
-	  *event_window = event->xany.window;
-	}
-    }
-  else
-    {
-      GdkDisplayX11 *display_x11 = GDK_DISPLAY_X11 (display);
-      GSList *tmp_list;
-
-      for (tmp_list = display_x11->event_types;
-	   tmp_list;
-	   tmp_list = tmp_list->next)
-	{
-	  GdkEventTypeX11 *event_type = tmp_list->data;
-
-	  if (event->type >= event_type->base &&
-	      event->type < event_type->base + event_type->n_events)
-	    {
-	      *event_window = event->xany.window;
-	      *filter_window = event->xany.window;
-	      return;
-	    }
-	}
-	   
-      *event_window = None;
-      *filter_window = None;
-    }
-}
-
-#ifdef G_ENABLE_DEBUG
-static const char notify_modes[][19] = {
-  "NotifyNormal",
-  "NotifyGrab",
-  "NotifyUngrab",
-  "NotifyWhileGrabbed"
-};
-
-static const char notify_details[][23] = {
-  "NotifyAncestor",
-  "NotifyVirtual",
-  "NotifyInferior",
-  "NotifyNonlinear",
-  "NotifyNonlinearVirtual",
-  "NotifyPointer",
-  "NotifyPointerRoot",
-  "NotifyDetailNone"
-};
-#endif
-
-static void
-set_user_time (GdkWindow *window,
-	       GdkEvent  *event)
-{
-  g_return_if_fail (event != NULL);
-
-  window = gdk_window_get_toplevel (event->client.window);
-  g_return_if_fail (GDK_IS_WINDOW (window));
-
-  /* If an event doesn't have a valid timestamp, we shouldn't use it
-   * to update the latest user interaction time.
-   */
-  if (gdk_event_get_time (event) != GDK_CURRENT_TIME)
-    gdk_x11_window_set_user_time (gdk_window_get_toplevel (window),
-                                  gdk_event_get_time (event));
-}
-
-static gboolean
-is_parent_of (GdkWindow *parent,
-              GdkWindow *child)
-{
-  GdkWindow *w;
-
-  w = child;
-  while (w != NULL)
-    {
-      if (w == parent)
-	return TRUE;
-
-      w = gdk_window_get_parent (w);
-    }
-
-  return FALSE;
-}
-
-static gboolean
-gdk_event_translate (GdkDisplay *display,
-		     GdkEvent   *event,
-		     XEvent     *xevent,
-		     gboolean    return_exposes)
-{
-  
-  GdkWindow *window;
-  GdkWindowObject *window_private;
-  GdkWindow *filter_window;
-  GdkWindowImplX11 *window_impl = NULL;
-  gboolean return_val;
-  GdkScreen *screen = NULL;
-  GdkScreenX11 *screen_x11 = NULL;
-  GdkToplevelX11 *toplevel = NULL;
-  GdkDisplayX11 *display_x11 = GDK_DISPLAY_X11 (display);
-  Window xwindow, filter_xwindow;
-  
-  return_val = FALSE;
-
-  /* init these, since the done: block uses them */
-  window = NULL;
-  window_private = NULL;
-  event->any.window = NULL;
-
-  if (_gdk_default_filters)
-    {
-      /* Apply global filters */
-      GdkFilterReturn result;
-      result = gdk_event_apply_filters (xevent, event,
-                                        _gdk_default_filters);
-      
-      if (result != GDK_FILTER_CONTINUE)
-        {
-          return_val = (result == GDK_FILTER_TRANSLATE) ? TRUE : FALSE;
-          goto done;
-        }
-    }  
-
-  /* Find the GdkWindow that this event relates to.
-   * Basically this means substructure events
-   * are reported same as structure events
-   */
-  get_real_window (display, xevent, &xwindow, &filter_xwindow);
-  
-  window = gdk_window_lookup_for_display (display, xwindow);
-  /* We may receive events such as NoExpose/GraphicsExpose
-   * and ShmCompletion for pixmaps
-   */
-  if (window && !GDK_IS_WINDOW (window))
-    window = NULL;
-  window_private = (GdkWindowObject *) window;
-
-  /* We always run the filters for the window where the event
-   * is delivered, not the window that it relates to
-   */
-  if (filter_xwindow == xwindow)
-    filter_window = window;
-  else
-    {
-      filter_window = gdk_window_lookup_for_display (display, filter_xwindow);
-      if (filter_window && !GDK_IS_WINDOW (filter_window))
-	filter_window = NULL;
-    }
-
-  if (window)
-    {
-      screen = GDK_WINDOW_SCREEN (window);
-      screen_x11 = GDK_SCREEN_X11 (screen);
-      toplevel = _gdk_x11_window_get_toplevel (window);
-    }
-    
-  if (window != NULL)
-    {
-      /* Apply keyboard grabs to non-native windows */
-      if (/* Is key event */
-	  (xevent->type == KeyPress || xevent->type == KeyRelease) &&
-	  /* And we have a grab */
-	  display->keyboard_grab.window != NULL &&
-	  (
-	   /* The window is not a descendant of the grabbed window */
-	   !is_parent_of ((GdkWindow *)display->keyboard_grab.window, window) ||
-	   /* Or owner event is false */
-	   !display->keyboard_grab.owner_events
-	   )
-	  )
-        {
-	  /* Report key event against grab window */
-          window = display->keyboard_grab.window;;
-          window_private = (GdkWindowObject *) window;
-        }
-
-      window_impl = GDK_WINDOW_IMPL_X11 (window_private->impl);
-      
-      /* Move key events on focus window to the real toplevel, and
-       * filter out all other events on focus window
-       */          
-      if (toplevel && xwindow == toplevel->focus_window)
-	{
-	  switch (xevent->type)
-	    {
-	    case KeyPress:
-	    case KeyRelease:
-	      xwindow = GDK_WINDOW_XID (window);
-	      xevent->xany.window = xwindow;
-	      break;
-	    default:
-	      return FALSE;
-	    }
-	}
-
-      g_object_ref (window);
-    }
-
-  event->any.window = window;
-  event->any.send_event = xevent->xany.send_event ? TRUE : FALSE;
-  
-  if (window_private && GDK_WINDOW_DESTROYED (window))
-    {
-      if (xevent->type != DestroyNotify)
-	{
-	  return_val = FALSE;
-	  goto done;
-	}
-    }
-  else if (filter_window)
-    {
-      /* Apply per-window filters */
-      GdkWindowObject *filter_private = (GdkWindowObject *) filter_window;
-      GdkFilterReturn result;
-
-      if (filter_private->filters)
-	{
-	  g_object_ref (filter_window);
-	  
-	  result = gdk_event_apply_filters (xevent, event,
-					    filter_private->filters);
-	  
-	  g_object_unref (filter_window);
-      
-	  if (result != GDK_FILTER_CONTINUE)
-	    {
-	      return_val = (result == GDK_FILTER_TRANSLATE) ? TRUE : FALSE;
-	      goto done;
-	    }
-	}
-    }
-
-  if (xevent->type == DestroyNotify)
-    {
-      int i, n;
-
-      n = gdk_display_get_n_screens (display);
-      for (i = 0; i < n; i++)
-        {
-          screen = gdk_display_get_screen (display, i);
-          screen_x11 = GDK_SCREEN_X11 (screen);
-
-          if (screen_x11->wmspec_check_window == xwindow)
-            {
-              screen_x11->wmspec_check_window = None;
-              screen_x11->last_wmspec_check_time = 0;
-              g_free (screen_x11->window_manager_name);
-              screen_x11->window_manager_name = g_strdup ("unknown");
-
-              /* careful, reentrancy */
-              _gdk_x11_screen_window_manager_changed (screen);
-
-              return_val = FALSE;
-              goto done;
-            }
-        }
-    }
-
-  if (window &&
-      (xevent->xany.type == MotionNotify ||
-       xevent->xany.type == ButtonRelease))
-    {
-      if (_gdk_moveresize_handle_event (xevent))
-	{
-          return_val = FALSE;
-          goto done;
-        }
-    }
-  
-  /* We do a "manual" conversion of the XEvent to a
-   *  GdkEvent. The structures are mostly the same so
-   *  the conversion is fairly straightforward. We also
-   *  optionally print debugging info regarding events
-   *  received.
-   */
-
-  return_val = TRUE;
-
-  switch (xevent->type)
-    {
-    case KeyPress:
-      if (window_private == NULL)
-        {
-          return_val = FALSE;
-          break;
-        }
-      translate_key_event (display, event, xevent);
-      set_user_time (window, event);
-      break;
-
-    case KeyRelease:
-      if (window_private == NULL)
-        {
-          return_val = FALSE;
-          break;
-        }
-      
-      /* Emulate detectable auto-repeat by checking to see
-       * if the next event is a key press with the same
-       * keycode and timestamp, and if so, ignoring the event.
-       */
-
-      if (!display_x11->have_xkb_autorepeat && XPending (xevent->xkey.display))
-	{
-	  XEvent next_event;
-
-	  XPeekEvent (xevent->xkey.display, &next_event);
-
-	  if (next_event.type == KeyPress &&
-	      next_event.xkey.keycode == xevent->xkey.keycode &&
-	      next_event.xkey.time == xevent->xkey.time)
-	    {
-	      return_val = FALSE;
-	      break;
-	    }
-	}
-
-      translate_key_event (display, event, xevent);
-      break;
-      
-    case ButtonPress:
-      GDK_NOTE (EVENTS, 
-		g_message ("button press:\t\twindow: %ld  x,y: %d %d  button: %d",
-			   xevent->xbutton.window,
-			   xevent->xbutton.x, xevent->xbutton.y,
-			   xevent->xbutton.button));
-      
-      if (window_private == NULL)
-	{
-	  return_val = FALSE;
-	  break;
-	}
-      
-      /* If we get a ButtonPress event where the button is 4 or 5,
-	 it's a Scroll event */
-      switch (xevent->xbutton.button)
-        {
-        case 4: /* up */
-        case 5: /* down */
-        case 6: /* left */
-        case 7: /* right */
-	  event->scroll.type = GDK_SCROLL;
-
-          if (xevent->xbutton.button == 4)
-            event->scroll.direction = GDK_SCROLL_UP;
-          else if (xevent->xbutton.button == 5)
-            event->scroll.direction = GDK_SCROLL_DOWN;
-          else if (xevent->xbutton.button == 6)
-            event->scroll.direction = GDK_SCROLL_LEFT;
-          else
-            event->scroll.direction = GDK_SCROLL_RIGHT;
-
-	  event->scroll.window = window;
-	  event->scroll.time = xevent->xbutton.time;
-	  event->scroll.x = xevent->xbutton.x;
-	  event->scroll.y = xevent->xbutton.y;
-	  event->scroll.x_root = (gfloat)xevent->xbutton.x_root;
-	  event->scroll.y_root = (gfloat)xevent->xbutton.y_root;
-	  event->scroll.state = (GdkModifierType) xevent->xbutton.state;
-	  event->scroll.device = display->core_pointer;
-	  
-	  if (!set_screen_from_root (display, event, xevent->xbutton.root))
-	    {
-	      return_val = FALSE;
-	      break;
-	    }
-	  
-          break;
-          
-        default:
-	  event->button.type = GDK_BUTTON_PRESS;
-	  event->button.window = window;
-	  event->button.time = xevent->xbutton.time;
-	  event->button.x = xevent->xbutton.x;
-	  event->button.y = xevent->xbutton.y;
-	  event->button.x_root = (gfloat)xevent->xbutton.x_root;
-	  event->button.y_root = (gfloat)xevent->xbutton.y_root;
-	  event->button.axes = NULL;
-	  event->button.state = (GdkModifierType) xevent->xbutton.state;
-	  event->button.button = xevent->xbutton.button;
-	  event->button.device = display->core_pointer;
-	  
-	  if (!set_screen_from_root (display, event, xevent->xbutton.root))
-	    {
-	      return_val = FALSE;
-	      break;
-	    }
-          break;
-	}
-
-      set_user_time (window, event);
-
-      break;
-      
-    case ButtonRelease:
-      GDK_NOTE (EVENTS, 
-		g_message ("button release:\twindow: %ld  x,y: %d %d  button: %d",
-			   xevent->xbutton.window,
-			   xevent->xbutton.x, xevent->xbutton.y,
-			   xevent->xbutton.button));
-      
-      if (window_private == NULL)
-	{
-	  return_val = FALSE;
-	  break;
-	}
-      
-      /* We treat button presses as scroll wheel events, so ignore the release */
-      if (xevent->xbutton.button == 4 || xevent->xbutton.button == 5 ||
-          xevent->xbutton.button == 6 || xevent->xbutton.button ==7)
-	{
-	  return_val = FALSE;
-	  break;
-	}
-
-      event->button.type = GDK_BUTTON_RELEASE;
-      event->button.window = window;
-      event->button.time = xevent->xbutton.time;
-      event->button.x = xevent->xbutton.x;
-      event->button.y = xevent->xbutton.y;
-      event->button.x_root = (gfloat)xevent->xbutton.x_root;
-      event->button.y_root = (gfloat)xevent->xbutton.y_root;
-      event->button.axes = NULL;
-      event->button.state = (GdkModifierType) xevent->xbutton.state;
-      event->button.button = xevent->xbutton.button;
-      event->button.device = display->core_pointer;
-
-      if (!set_screen_from_root (display, event, xevent->xbutton.root))
-	return_val = FALSE;
-      
-      break;
-      
-    case MotionNotify:
-      GDK_NOTE (EVENTS,
-		g_message ("motion notify:\t\twindow: %ld  x,y: %d %d  hint: %s", 
-			   xevent->xmotion.window,
-			   xevent->xmotion.x, xevent->xmotion.y,
-			   (xevent->xmotion.is_hint) ? "true" : "false"));
-      
-      if (window_private == NULL)
-	{
-	  return_val = FALSE;
-	  break;
-	}
-
-      event->motion.type = GDK_MOTION_NOTIFY;
-      event->motion.window = window;
-      event->motion.time = xevent->xmotion.time;
-      event->motion.x = xevent->xmotion.x;
-      event->motion.y = xevent->xmotion.y;
-      event->motion.x_root = (gfloat)xevent->xmotion.x_root;
-      event->motion.y_root = (gfloat)xevent->xmotion.y_root;
-      event->motion.axes = NULL;
-      event->motion.state = (GdkModifierType) xevent->xmotion.state;
-      event->motion.is_hint = xevent->xmotion.is_hint;
-      event->motion.device = display->core_pointer;
-      
-      if (!set_screen_from_root (display, event, xevent->xbutton.root))
-	{
-	  return_val = FALSE;
-	  break;
-	}
-            
-      break;
-      
-    case EnterNotify:
-      GDK_NOTE (EVENTS,
-                g_message ("enter notify:\t\twindow: %ld  detail: %d subwin: %ld mode: %d",
-                           xevent->xcrossing.window,
-                           xevent->xcrossing.detail,
-                           xevent->xcrossing.subwindow,
-                           xevent->xcrossing.mode));
-
-      if (window_private == NULL)
-        {
-          return_val = FALSE;
-          break;
-        }
-      
-      if (!set_screen_from_root (display, event, xevent->xbutton.root))
-	{
-	  return_val = FALSE;
-	  break;
-	}
-      
-      /* Handle focusing (in the case where no window manager is running */
-      if (toplevel && xevent->xcrossing.detail != NotifyInferior)
-	{
-	  toplevel->has_pointer = TRUE;
-
-	  if (xevent->xcrossing.focus && !toplevel->has_focus_window)
-	    {
-	      gboolean had_focus = HAS_FOCUS (toplevel);
-	      
-	      toplevel->has_pointer_focus = TRUE;
-	      
-	      if (HAS_FOCUS (toplevel) != had_focus)
-		generate_focus_event (window, TRUE);
-	    }
-	}
-
-      event->crossing.type = GDK_ENTER_NOTIFY;
-      event->crossing.window = window;
-      
-      /* If the subwindow field of the XEvent is non-NULL, then
-       *  lookup the corresponding GdkWindow.
-       */
-      if (xevent->xcrossing.subwindow != None)
-	event->crossing.subwindow = gdk_window_lookup_for_display (display, xevent->xcrossing.subwindow);
-      else
-	event->crossing.subwindow = NULL;
-      
-      event->crossing.time = xevent->xcrossing.time;
-      event->crossing.x = xevent->xcrossing.x;
-      event->crossing.y = xevent->xcrossing.y;
-      event->crossing.x_root = xevent->xcrossing.x_root;
-      event->crossing.y_root = xevent->xcrossing.y_root;
-      
-      /* Translate the crossing mode into Gdk terms.
-       */
-      switch (xevent->xcrossing.mode)
-	{
-	case NotifyNormal:
-	  event->crossing.mode = GDK_CROSSING_NORMAL;
-	  break;
-	case NotifyGrab:
-	  event->crossing.mode = GDK_CROSSING_GRAB;
-	  break;
-	case NotifyUngrab:
-	  event->crossing.mode = GDK_CROSSING_UNGRAB;
-	  break;
-	};
-      
-      /* Translate the crossing detail into Gdk terms.
-       */
-      switch (xevent->xcrossing.detail)
-	{
-	case NotifyInferior:
-	  event->crossing.detail = GDK_NOTIFY_INFERIOR;
-	  break;
-	case NotifyAncestor:
-	  event->crossing.detail = GDK_NOTIFY_ANCESTOR;
-	  break;
-	case NotifyVirtual:
-	  event->crossing.detail = GDK_NOTIFY_VIRTUAL;
-	  break;
-	case NotifyNonlinear:
-	  event->crossing.detail = GDK_NOTIFY_NONLINEAR;
-	  break;
-	case NotifyNonlinearVirtual:
-	  event->crossing.detail = GDK_NOTIFY_NONLINEAR_VIRTUAL;
-	  break;
-	default:
-	  event->crossing.detail = GDK_NOTIFY_UNKNOWN;
-	  break;
-	}
-      
-      event->crossing.focus = xevent->xcrossing.focus;
-      event->crossing.state = xevent->xcrossing.state;
-  
-      break;
-      
-    case LeaveNotify:
-      GDK_NOTE (EVENTS, 
-                g_message ("leave notify:\t\twindow: %ld  detail: %d subwin: %ld mode: %d",
-                           xevent->xcrossing.window,
-                           xevent->xcrossing.detail,
-                           xevent->xcrossing.subwindow,
-                           xevent->xcrossing.mode));
-
-      if (window_private == NULL)
-        {
-          return_val = FALSE;
-          break;
-        }
-
-      if (!set_screen_from_root (display, event, xevent->xbutton.root))
-	{
-	  return_val = FALSE;
-	  break;
-	}
-                  
-      /* Handle focusing (in the case where no window manager is running */
-      if (toplevel && xevent->xcrossing.detail != NotifyInferior)
-	{
-	  toplevel->has_pointer = FALSE;
-
-	  if (xevent->xcrossing.focus && !toplevel->has_focus_window)
-	    {
-	      gboolean had_focus = HAS_FOCUS (toplevel);
-	      
-	      toplevel->has_pointer_focus = FALSE;
-	      
-	      if (HAS_FOCUS (toplevel) != had_focus)
-		generate_focus_event (window, FALSE);
-	    }
-	}
-
-      event->crossing.type = GDK_LEAVE_NOTIFY;
-      event->crossing.window = window;
-      
-      /* If the subwindow field of the XEvent is non-NULL, then
-       *  lookup the corresponding GdkWindow.
-       */
-      if (xevent->xcrossing.subwindow != None)
-	event->crossing.subwindow = gdk_window_lookup_for_display (display, xevent->xcrossing.subwindow);
-      else
-	event->crossing.subwindow = NULL;
-      
-      event->crossing.time = xevent->xcrossing.time;
-      event->crossing.x = xevent->xcrossing.x;
-      event->crossing.y = xevent->xcrossing.y;
-      event->crossing.x_root = xevent->xcrossing.x_root;
-      event->crossing.y_root = xevent->xcrossing.y_root;
-      
-      /* Translate the crossing mode into Gdk terms.
-       */
-      switch (xevent->xcrossing.mode)
-	{
-	case NotifyNormal:
-	  event->crossing.mode = GDK_CROSSING_NORMAL;
-	  break;
-	case NotifyGrab:
-	  event->crossing.mode = GDK_CROSSING_GRAB;
-	  break;
-	case NotifyUngrab:
-	  event->crossing.mode = GDK_CROSSING_UNGRAB;
-	  break;
-	};
-      
-      /* Translate the crossing detail into Gdk terms.
-       */
-      switch (xevent->xcrossing.detail)
-	{
-	case NotifyInferior:
-	  event->crossing.detail = GDK_NOTIFY_INFERIOR;
-	  break;
-	case NotifyAncestor:
-	  event->crossing.detail = GDK_NOTIFY_ANCESTOR;
-	  break;
-	case NotifyVirtual:
-	  event->crossing.detail = GDK_NOTIFY_VIRTUAL;
-	  break;
-	case NotifyNonlinear:
-	  event->crossing.detail = GDK_NOTIFY_NONLINEAR;
-	  break;
-	case NotifyNonlinearVirtual:
-	  event->crossing.detail = GDK_NOTIFY_NONLINEAR_VIRTUAL;
-	  break;
-	default:
-	  event->crossing.detail = GDK_NOTIFY_UNKNOWN;
-	  break;
-	}
-      
-      event->crossing.focus = xevent->xcrossing.focus;
-      event->crossing.state = xevent->xcrossing.state;
-      
-      break;
-      
-      /* We only care about focus events that indicate that _this_
-       * window (not a ancestor or child) got or lost the focus
-       */
-    case FocusIn:
-      GDK_NOTE (EVENTS,
-		g_message ("focus in:\t\twindow: %ld, detail: %s, mode: %s",
-			   xevent->xfocus.window,
-			   notify_details[xevent->xfocus.detail],
-			   notify_modes[xevent->xfocus.mode]));
-      
-      if (toplevel)
-	{
-	  gboolean had_focus = HAS_FOCUS (toplevel);
-	  
-	  switch (xevent->xfocus.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 &&
-		  xevent->xfocus.mode != NotifyGrab &&
-		  xevent->xfocus.mode != NotifyUngrab)
-		toplevel->has_pointer_focus = FALSE;
-	      
-	      /* fall through */
-	    case NotifyNonlinear:
-	    case NotifyNonlinearVirtual:
-	      if (xevent->xfocus.mode != NotifyGrab &&
-		  xevent->xfocus.mode != NotifyUngrab)
-		toplevel->has_focus_window = TRUE;
-	      /* We pretend that the focus moves to the grab
-	       * window, so we pay attention to NotifyGrab
-	       * NotifyUngrab, and ignore NotifyWhileGrabbed
-	       */
-	      if (xevent->xfocus.mode != NotifyWhileGrabbed)
-		toplevel->has_focus = TRUE;
-	      break;
-	    case NotifyPointer:
-	      /* The X server sends NotifyPointer/NotifyGrab,
-	       * but the pointer focus is ignored while a
-	       * grab is in effect
-	       */
-	      if (xevent->xfocus.mode != NotifyGrab &&
-		  xevent->xfocus.mode != NotifyUngrab)
-		toplevel->has_pointer_focus = TRUE;
-	      break;
-	    case NotifyInferior:
-	    case NotifyPointerRoot:
-	    case NotifyDetailNone:
-	      break;
-	    }
-
-	  if (HAS_FOCUS (toplevel) != had_focus)
-	    generate_focus_event (window, TRUE);
-	}
-      break;
-    case FocusOut:
-      GDK_NOTE (EVENTS,
-		g_message ("focus out:\t\twindow: %ld, detail: %s, mode: %s",
-			   xevent->xfocus.window,
-			   notify_details[xevent->xfocus.detail],
-			   notify_modes[xevent->xfocus.mode]));
-      
-      if (toplevel)
-	{
-	  gboolean had_focus = HAS_FOCUS (toplevel);
-	    
-	  switch (xevent->xfocus.detail)
-	    {
-	    case NotifyAncestor:
-	    case NotifyVirtual:
-	      /* When the focus moves from the window or a descendent
-	       * of the window to an ancestor of the window, *and* the
-	       * pointer is inside the window, then we were previously
-	       * receiving keystroke events in the has_focus_window
-	       * case and are now receiving them in the
-	       * has_pointer_focus case.
-	       */
-	      if (toplevel->has_pointer &&
-		  xevent->xfocus.mode != NotifyGrab &&
-		  xevent->xfocus.mode != NotifyUngrab)
-		toplevel->has_pointer_focus = TRUE;
-
-	      /* fall through */
-	    case NotifyNonlinear:
-	    case NotifyNonlinearVirtual:
-	      if (xevent->xfocus.mode != NotifyGrab &&
-		  xevent->xfocus.mode != NotifyUngrab)
-		toplevel->has_focus_window = FALSE;
-	      if (xevent->xfocus.mode != NotifyWhileGrabbed)
-		toplevel->has_focus = FALSE;
-	      break;
-	    case NotifyPointer:
-	      if (xevent->xfocus.mode != NotifyGrab &&
-		  xevent->xfocus.mode != NotifyUngrab)
-		toplevel->has_pointer_focus = FALSE;
-	    break;
-	    case NotifyInferior:
-	    case NotifyPointerRoot:
-	    case NotifyDetailNone:
-	      break;
-	    }
-
-	  if (HAS_FOCUS (toplevel) != had_focus)
-	    generate_focus_event (window, FALSE);
-	}
-      break;
-
-#if 0      
- 	  /* gdk_keyboard_grab() causes following events. These events confuse
- 	   * the XIM focus, so ignore them.
- 	   */
- 	  if (xevent->xfocus.mode == NotifyGrab ||
- 	      xevent->xfocus.mode == NotifyUngrab)
- 	    break;
-#endif
-
-    case KeymapNotify:
-      GDK_NOTE (EVENTS,
-		g_message ("keymap notify"));
-
-      /* Not currently handled */
-      return_val = FALSE;
-      break;
-      
-    case Expose:
-      GDK_NOTE (EVENTS,
-		g_message ("expose:\t\twindow: %ld  %d	x,y: %d %d  w,h: %d %d%s",
-			   xevent->xexpose.window, xevent->xexpose.count,
-			   xevent->xexpose.x, xevent->xexpose.y,
-			   xevent->xexpose.width, xevent->xexpose.height,
-			   event->any.send_event ? " (send)" : ""));
-     
-      if (window_private == NULL)
-        {
-          return_val = FALSE;
-          break;
-        }
-
-      {
-	GdkRectangle expose_rect;
-
-	expose_rect.x = xevent->xexpose.x;
-	expose_rect.y = xevent->xexpose.y;
-	expose_rect.width = xevent->xexpose.width;
-	expose_rect.height = xevent->xexpose.height;
-
-	_gdk_window_process_expose (window, xevent->xexpose.serial, &expose_rect);
-	 return_val = FALSE;
-      }
-
-      break;
-      
-    case GraphicsExpose:
-      {
-	GdkRectangle expose_rect;
-
-        GDK_NOTE (EVENTS,
-		  g_message ("graphics expose:\tdrawable: %ld",
-			     xevent->xgraphicsexpose.drawable));
- 
-        if (window_private == NULL)
-          {
-            return_val = FALSE;
-            break;
-          }
-        
-	expose_rect.x = xevent->xgraphicsexpose.x;
-	expose_rect.y = xevent->xgraphicsexpose.y;
-	expose_rect.width = xevent->xgraphicsexpose.width;
-	expose_rect.height = xevent->xgraphicsexpose.height;
-	    
-	if (return_exposes)
-	  {
-	    event->expose.type = GDK_EXPOSE;
-	    event->expose.area = expose_rect;
-	    event->expose.region = gdk_region_rectangle (&expose_rect);
-	    event->expose.window = window;
-	    event->expose.count = xevent->xgraphicsexpose.count;
-
-	    return_val = TRUE;
-	  }
-	else
-	  {
-	    _gdk_window_process_expose (window, xevent->xgraphicsexpose.serial, &expose_rect);
-	    
-	    return_val = FALSE;
-	  }
-	
-      }
-      break;
-      
-    case NoExpose:
-      GDK_NOTE (EVENTS,
-		g_message ("no expose:\t\tdrawable: %ld",
-			   xevent->xnoexpose.drawable));
-      
-      event->no_expose.type = GDK_NO_EXPOSE;
-      event->no_expose.window = window;
-      
-      break;
-      
-    case VisibilityNotify:
-#ifdef G_ENABLE_DEBUG
-      if (_gdk_debug_flags & GDK_DEBUG_EVENTS)
-	switch (xevent->xvisibility.state)
-	  {
-	  case VisibilityFullyObscured:
-	    g_message ("visibility notify:\twindow: %ld	 none",
-		       xevent->xvisibility.window);
-	    break;
-	  case VisibilityPartiallyObscured:
-	    g_message ("visibility notify:\twindow: %ld	 partial",
-		       xevent->xvisibility.window);
-	    break;
-	  case VisibilityUnobscured:
-	    g_message ("visibility notify:\twindow: %ld	 full",
-		       xevent->xvisibility.window);
-	    break;
-	  }
-#endif /* G_ENABLE_DEBUG */
-      
-      if (window_private == NULL)
-        {
-          return_val = FALSE;
-          break;
-        }
-      
-      event->visibility.type = GDK_VISIBILITY_NOTIFY;
-      event->visibility.window = window;
-      
-      switch (xevent->xvisibility.state)
-	{
-	case VisibilityFullyObscured:
-	  event->visibility.state = GDK_VISIBILITY_FULLY_OBSCURED;
-	  break;
-	  
-	case VisibilityPartiallyObscured:
-	  event->visibility.state = GDK_VISIBILITY_PARTIAL;
-	  break;
-	  
-	case VisibilityUnobscured:
-	  event->visibility.state = GDK_VISIBILITY_UNOBSCURED;
-	  break;
-	}
-      
-      break;
-      
-    case CreateNotify:
-      GDK_NOTE (EVENTS,
-		g_message ("create notify:\twindow: %ld  x,y: %d %d	w,h: %d %d  b-w: %d  parent: %ld	 ovr: %d",
-			   xevent->xcreatewindow.window,
-			   xevent->xcreatewindow.x,
-			   xevent->xcreatewindow.y,
-			   xevent->xcreatewindow.width,
-			   xevent->xcreatewindow.height,
-			   xevent->xcreatewindow.border_width,
-			   xevent->xcreatewindow.parent,
-			   xevent->xcreatewindow.override_redirect));
-      /* not really handled */
-      break;
-      
-    case DestroyNotify:
-      GDK_NOTE (EVENTS,
-		g_message ("destroy notify:\twindow: %ld",
-			   xevent->xdestroywindow.window));
-
-      /* Ignore DestroyNotify from SubstructureNotifyMask */
-      if (xevent->xdestroywindow.window == xevent->xdestroywindow.event)
-	{
-	  event->any.type = GDK_DESTROY;
-	  event->any.window = window;
-	  
-	  return_val = window_private && !GDK_WINDOW_DESTROYED (window);
-	  
-	  if (window && GDK_WINDOW_XID (window) != screen_x11->xroot_window)
-	    gdk_window_destroy_notify (window);
-	}
-      else
-	return_val = FALSE;
-      
-      break;
-      
-    case UnmapNotify:
-      GDK_NOTE (EVENTS,
-		g_message ("unmap notify:\t\twindow: %ld",
-			   xevent->xmap.window));
-      
-      event->any.type = GDK_UNMAP;
-      event->any.window = window;      
-
-      /* If we are shown (not withdrawn) and get an unmap, it means we
-       * were iconified in the X sense. If we are withdrawn, and get
-       * an unmap, it means we hid the window ourselves, so we
-       * will have already flipped the iconified bit off.
-       */
-      if (window)
-	{
-	  if (GDK_WINDOW_IS_MAPPED (window))
-	    gdk_synthesize_window_state (window,
-					 0,
-					 GDK_WINDOW_STATE_ICONIFIED);
-
-	  _gdk_xgrab_check_unmap (window, xevent->xany.serial);
-	}
-      
-      break;
-      
-    case MapNotify:
-      GDK_NOTE (EVENTS,
-		g_message ("map notify:\t\twindow: %ld",
-			   xevent->xmap.window));
-      
-      event->any.type = GDK_MAP;
-      event->any.window = window;
-
-      /* Unset iconified if it was set */
-      if (window && (((GdkWindowObject*)window)->state & GDK_WINDOW_STATE_ICONIFIED))
-        gdk_synthesize_window_state (window,
-                                     GDK_WINDOW_STATE_ICONIFIED,
-                                     0);
-      
-      break;
-      
-    case ReparentNotify:
-      GDK_NOTE (EVENTS,
-		g_message ("reparent notify:\twindow: %ld  x,y: %d %d  parent: %ld	ovr: %d",
-			   xevent->xreparent.window,
-			   xevent->xreparent.x,
-			   xevent->xreparent.y,
-			   xevent->xreparent.parent,
-			   xevent->xreparent.override_redirect));
-
-      /* Not currently handled */
-      return_val = FALSE;
-      break;
-      
-    case ConfigureNotify:
-      GDK_NOTE (EVENTS,
-		g_message ("configure notify:\twindow: %ld  x,y: %d %d	w,h: %d %d  b-w: %d  above: %ld	 ovr: %d%s",
-			   xevent->xconfigure.window,
-			   xevent->xconfigure.x,
-			   xevent->xconfigure.y,
-			   xevent->xconfigure.width,
-			   xevent->xconfigure.height,
-			   xevent->xconfigure.border_width,
-			   xevent->xconfigure.above,
-			   xevent->xconfigure.override_redirect,
-			   !window
-			   ? " (discarding)"
-			   : GDK_WINDOW_TYPE (window) == GDK_WINDOW_CHILD
-			   ? " (discarding child)"
-			   : xevent->xconfigure.event != xevent->xconfigure.window
-			   ? " (discarding substructure)"
-			   : ""));
-      if (window && GDK_WINDOW_TYPE (window) == GDK_WINDOW_ROOT)
-        { 
-	  window_private->width = xevent->xconfigure.width;
-	  window_private->height = xevent->xconfigure.height;
-
-	  _gdk_window_update_size (window);
-	  _gdk_x11_drawable_update_size (window_private->impl);
-	  _gdk_x11_screen_size_changed (screen, xevent);
-        }
-
-      if (window &&
-	  xevent->xconfigure.event == xevent->xconfigure.window &&
-	  !GDK_WINDOW_DESTROYED (window) &&
-	  window_private->input_window != NULL)
-	_gdk_input_configure_event (&xevent->xconfigure, window);
-      
-#ifdef HAVE_XSYNC
-      if (toplevel && display_x11->use_sync && !XSyncValueIsZero (toplevel->pending_counter_value))
-	{
-	  toplevel->current_counter_value = toplevel->pending_counter_value;
-	  XSyncIntToValue (&toplevel->pending_counter_value, 0);
-	}
-#endif
-
-      if (!window ||
-	  xevent->xconfigure.event != xevent->xconfigure.window ||
-          GDK_WINDOW_TYPE (window) == GDK_WINDOW_CHILD ||
-          GDK_WINDOW_TYPE (window) == GDK_WINDOW_ROOT)
-	return_val = FALSE;
-      else
-	{
-	  event->configure.type = GDK_CONFIGURE;
-	  event->configure.window = window;
-	  event->configure.width = xevent->xconfigure.width;
-	  event->configure.height = xevent->xconfigure.height;
-	  
-	  if (!xevent->xconfigure.send_event &&
-	      !xevent->xconfigure.override_redirect &&
-	      !GDK_WINDOW_DESTROYED (window))
-	    {
-	      gint tx = 0;
-	      gint ty = 0;
-	      Window child_window = 0;
-
-	      gdk_error_trap_push ();
-	      if (XTranslateCoordinates (GDK_DRAWABLE_XDISPLAY (window),
-					 GDK_DRAWABLE_XID (window),
-					 screen_x11->xroot_window,
-					 0, 0,
-					 &tx, &ty,
-					 &child_window))
-		{
-		  event->configure.x = tx;
-		  event->configure.y = ty;
-		}
-	      gdk_error_trap_pop ();
-	    }
-	  else
-	    {
-	      event->configure.x = xevent->xconfigure.x;
-	      event->configure.y = xevent->xconfigure.y;
-	    }
-	  window_private->x = event->configure.x;
-	  window_private->y = event->configure.y;
-	  window_private->width = xevent->xconfigure.width;
-	  window_private->height = xevent->xconfigure.height;
-	  
-	  _gdk_window_update_size (window);
-	  _gdk_x11_drawable_update_size (window_private->impl);
-	  
-	  if (window_private->resize_count >= 1)
-	    {
-	      window_private->resize_count -= 1;
-
-	      if (window_private->resize_count == 0)
-		_gdk_moveresize_configure_done (display, window);
-	    }
-	}
-      break;
-      
-    case PropertyNotify:
-      GDK_NOTE (EVENTS,
-		g_message ("property notify:\twindow: %ld, atom(%ld): %s%s%s",
-			   xevent->xproperty.window,
-			   xevent->xproperty.atom,
-			   "\"",
-			   gdk_x11_get_xatom_name_for_display (display, xevent->xproperty.atom),
-			   "\""));
-
-      if (window_private == NULL)
-        {
-	  return_val = FALSE;
-          break;
-        }
-
-      /* We compare with the serial of the last time we mapped the
-       * window to avoid refetching properties that we set ourselves
-       */
-      if (toplevel &&
-	  xevent->xproperty.serial >= toplevel->map_serial)
-	{
-	  if (xevent->xproperty.atom == gdk_x11_get_xatom_by_name_for_display (display, "_NET_WM_STATE"))
-	    gdk_check_wm_state_changed (window);
-	  
-	  if (xevent->xproperty.atom == gdk_x11_get_xatom_by_name_for_display (display, "_NET_WM_DESKTOP"))
-	    gdk_check_wm_desktop_changed (window);
-	}
-      
-      if (window_private->event_mask & GDK_PROPERTY_CHANGE_MASK) 
-	{
-	  event->property.type = GDK_PROPERTY_NOTIFY;
-	  event->property.window = window;
-	  event->property.atom = gdk_x11_xatom_to_atom_for_display (display, xevent->xproperty.atom);
-	  event->property.time = xevent->xproperty.time;
-	  event->property.state = xevent->xproperty.state;
-	}
-      else
-	return_val = FALSE;
-
-      break;
-      
-    case SelectionClear:
-      GDK_NOTE (EVENTS,
-		g_message ("selection clear:\twindow: %ld",
-			   xevent->xproperty.window));
-
-      if (_gdk_selection_filter_clear_event (&xevent->xselectionclear))
-	{
-	  event->selection.type = GDK_SELECTION_CLEAR;
-	  event->selection.window = window;
-	  event->selection.selection = gdk_x11_xatom_to_atom_for_display (display, xevent->xselectionclear.selection);
-	  event->selection.time = xevent->xselectionclear.time;
-	}
-      else
-	return_val = FALSE;
-	  
-      break;
-      
-    case SelectionRequest:
-      GDK_NOTE (EVENTS,
-		g_message ("selection request:\twindow: %ld",
-			   xevent->xproperty.window));
-      
-      event->selection.type = GDK_SELECTION_REQUEST;
-      event->selection.window = window;
-      event->selection.selection = gdk_x11_xatom_to_atom_for_display (display, xevent->xselectionrequest.selection);
-      event->selection.target = gdk_x11_xatom_to_atom_for_display (display, xevent->xselectionrequest.target);
-      event->selection.property = gdk_x11_xatom_to_atom_for_display (display, xevent->xselectionrequest.property);
-      event->selection.requestor = xevent->xselectionrequest.requestor;
-      event->selection.time = xevent->xselectionrequest.time;
-      
-      break;
-      
-    case SelectionNotify:
-      GDK_NOTE (EVENTS,
-		g_message ("selection notify:\twindow: %ld",
-			   xevent->xproperty.window));
-      
-      
-      event->selection.type = GDK_SELECTION_NOTIFY;
-      event->selection.window = window;
-      event->selection.selection = gdk_x11_xatom_to_atom_for_display (display, xevent->xselection.selection);
-      event->selection.target = gdk_x11_xatom_to_atom_for_display (display, xevent->xselection.target);
-      if (xevent->xselection.property == None)
-        event->selection.property = GDK_NONE;
-      else
-        event->selection.property = gdk_x11_xatom_to_atom_for_display (display, xevent->xselection.property);
-      event->selection.time = xevent->xselection.time;
-      
-      break;
-      
-    case ColormapNotify:
-      GDK_NOTE (EVENTS,
-		g_message ("colormap notify:\twindow: %ld",
-			   xevent->xcolormap.window));
-      
-      /* Not currently handled */
-      return_val = FALSE;
-      break;
-      
-    case ClientMessage:
-      {
-	GList *tmp_list;
-	GdkFilterReturn result = GDK_FILTER_CONTINUE;
-	GdkAtom message_type = gdk_x11_xatom_to_atom_for_display (display, xevent->xclient.message_type);
-
-	GDK_NOTE (EVENTS,
-		  g_message ("client message:\twindow: %ld",
-			     xevent->xclient.window));
-	
-	tmp_list = display_x11->client_filters;
-	while (tmp_list)
-	  {
-	    GdkClientFilter *filter = tmp_list->data;
-	    tmp_list = tmp_list->next;
-	    
-	    if (filter->type == message_type)
-	      {
-		result = (*filter->function) (xevent, event, filter->data);
-		if (result != GDK_FILTER_CONTINUE)
-		  break;
-	      }
-	  }
-
-	switch (result)
-	  {
-	  case GDK_FILTER_REMOVE:
-	    return_val = FALSE;
-	    break;
-	  case GDK_FILTER_TRANSLATE:
-	    return_val = TRUE;
-	    break;
-	  case GDK_FILTER_CONTINUE:
-	    /* Send unknown ClientMessage's on to Gtk for it to use */
-            if (window_private == NULL)
-              {
-                return_val = FALSE;
-              }
-            else
-              {
-                event->client.type = GDK_CLIENT_EVENT;
-                event->client.window = window;
-                event->client.message_type = message_type;
-                event->client.data_format = xevent->xclient.format;
-                memcpy(&event->client.data, &xevent->xclient.data,
-                       sizeof(event->client.data));
-              }
-            break;
-          }
-      }
-      
-      break;
-      
-    case MappingNotify:
-      GDK_NOTE (EVENTS,
-		g_message ("mapping notify"));
-      
-      /* Let XLib know that there is a new keyboard mapping.
-       */
-      XRefreshKeyboardMapping (&xevent->xmapping);
-      _gdk_keymap_keys_changed (display);
-      return_val = FALSE;
-      break;
-
-    default:
-#ifdef HAVE_XKB
-      if (xevent->type == display_x11->xkb_event_type)
-	{
-	  XkbEvent *xkb_event = (XkbEvent *)xevent;
-
-	  switch (xkb_event->any.xkb_type)
-	    {
-	    case XkbNewKeyboardNotify:
-	    case XkbMapNotify:
-	      _gdk_keymap_keys_changed (display);
-
-	      return_val = FALSE;
-	      break;
-	      
-	    case XkbStateNotify:
-	      _gdk_keymap_state_changed (display, xevent);
-	      break;
-	    }
-	}
-      else
-#endif
-#ifdef HAVE_XFIXES
-      if (xevent->type - display_x11->xfixes_event_base == XFixesSelectionNotify)
-	{
-	  XFixesSelectionNotifyEvent *selection_notify = (XFixesSelectionNotifyEvent *)xevent;
-
-	  _gdk_x11_screen_process_owner_change (screen, xevent);
-	  
-	  event->owner_change.type = GDK_OWNER_CHANGE;
-	  event->owner_change.window = window;
-	  event->owner_change.owner = selection_notify->owner;
-	  event->owner_change.reason = selection_notify->subtype;
-	  event->owner_change.selection = 
-	    gdk_x11_xatom_to_atom_for_display (display, 
-					       selection_notify->selection);
-	  event->owner_change.time = selection_notify->timestamp;
-	  event->owner_change.selection_time = selection_notify->selection_timestamp;
-	  
-	  return_val = TRUE;
-	}
-      else
-#endif
-#ifdef HAVE_RANDR
-      if (xevent->type - display_x11->xrandr_event_base == RRScreenChangeNotify ||
-          xevent->type - display_x11->xrandr_event_base == RRNotify)
-	{
-          if (screen)
-            _gdk_x11_screen_size_changed (screen, xevent);
-	}
-      else
-#endif
-#if defined(HAVE_XCOMPOSITE) && defined (HAVE_XDAMAGE) && defined (HAVE_XFIXES)
-      if (display_x11->have_xdamage && window_private && window_private->composited &&
-	  xevent->type == display_x11->xdamage_event_base + XDamageNotify &&
-	  ((XDamageNotifyEvent *) xevent)->damage == window_impl->damage)
-	{
-	  XDamageNotifyEvent *damage_event = (XDamageNotifyEvent *) xevent;
-	  XserverRegion repair;
-	  GdkRectangle rect;
-
-	  rect.x = window_private->x + damage_event->area.x;
-	  rect.y = window_private->y + damage_event->area.y;
-	  rect.width = damage_event->area.width;
-	  rect.height = damage_event->area.height;
-
-	  repair = XFixesCreateRegion (display_x11->xdisplay,
-				       &damage_event->area, 1);
-	  XDamageSubtract (display_x11->xdisplay,
-			   window_impl->damage,
-			   repair, None);
-	  XFixesDestroyRegion (display_x11->xdisplay, repair);
-
-	  if (window_private->parent != NULL)
-	    _gdk_window_process_expose (GDK_WINDOW (window_private->parent),
-					damage_event->serial, &rect);
-
-	  return_val = TRUE;
-	}
-      else
-#endif
-	{
-	  /* something else - (e.g., a Xinput event) */
-	  
-	  if (window_private &&
-	      !GDK_WINDOW_DESTROYED (window_private) &&
-	      window_private->input_window)
-	    return_val = _gdk_input_other_event (event, xevent, window);
-	  else
-	    return_val = FALSE;
-	  
-	  break;
-	}
-    }
-
- done:
-  if (return_val)
-    {
-      if (event->any.window)
-	g_object_ref (event->any.window);
-      if (((event->any.type == GDK_ENTER_NOTIFY) ||
-	   (event->any.type == GDK_LEAVE_NOTIFY)) &&
-	  (event->crossing.subwindow != NULL))
-	g_object_ref (event->crossing.subwindow);
-    }
-  else
-    {
-      /* Mark this event as having no resources to be freed */
-      event->any.window = NULL;
-      event->any.type = GDK_NOTHING;
-    }
-  
-  if (window)
-    g_object_unref (window);
-
-  return return_val;
-}
-
-static GdkFilterReturn
-gdk_wm_protocols_filter (GdkXEvent *xev,
-			 GdkEvent  *event,
-			 gpointer data)
-{
-  XEvent *xevent = (XEvent *)xev;
-  GdkWindow *win = event->any.window;
-  GdkDisplay *display;
-  Atom atom;
-
-  if (!win)
-      return GDK_FILTER_REMOVE;    
-
-  display = GDK_WINDOW_DISPLAY (win);
-  atom = (Atom)xevent->xclient.data.l[0];
-
-  if (atom == gdk_x11_get_xatom_by_name_for_display (display, "WM_DELETE_WINDOW"))
-    {
-  /* The delete window request specifies a window
-   *  to delete. We don't actually destroy the
-   *  window because "it is only a request". (The
-   *  window might contain vital data that the
-   *  program does not want destroyed). Instead
-   *  the event is passed along to the program,
-   *  which should then destroy the window.
-   */
-      GDK_NOTE (EVENTS,
-		g_message ("delete window:\t\twindow: %ld",
-			   xevent->xclient.window));
-      
-      event->any.type = GDK_DELETE;
-
-      gdk_x11_window_set_user_time (win, xevent->xclient.data.l[1]);
-
-      return GDK_FILTER_TRANSLATE;
-    }
-  else if (atom == gdk_x11_get_xatom_by_name_for_display (display, "WM_TAKE_FOCUS"))
-    {
-      GdkToplevelX11 *toplevel = _gdk_x11_window_get_toplevel (event->any.window);
-      GdkWindowObject *private = (GdkWindowObject *)win;
-
-      /* There is no way of knowing reliably whether we are viewable;
-       * _gdk_x11_set_input_focus_safe() traps errors asynchronously.
-       */
-      if (toplevel && private->accept_focus)
-	_gdk_x11_set_input_focus_safe (display, toplevel->focus_window,
-				       RevertToParent,
-				       xevent->xclient.data.l[1]);
-
-      return GDK_FILTER_REMOVE;
-    }
-  else if (atom == gdk_x11_get_xatom_by_name_for_display (display, "_NET_WM_PING") &&
-	   !_gdk_x11_display_is_root_window (display,
-					     xevent->xclient.window))
-    {
-      XClientMessageEvent xclient = xevent->xclient;
-      
-      xclient.window = GDK_WINDOW_XROOTWIN (win);
-      XSendEvent (GDK_WINDOW_XDISPLAY (win), 
-		  xclient.window,
-		  False, 
-		  SubstructureRedirectMask | SubstructureNotifyMask, (XEvent *)&xclient);
-
-      return GDK_FILTER_REMOVE;
-    }
-  else if (atom == gdk_x11_get_xatom_by_name_for_display (display, "_NET_WM_SYNC_REQUEST") &&
-	   GDK_DISPLAY_X11 (display)->use_sync)
-    {
-      GdkToplevelX11 *toplevel = _gdk_x11_window_get_toplevel (event->any.window);
-      if (toplevel)
-	{
-#ifdef HAVE_XSYNC
-	  XSyncIntsToValue (&toplevel->pending_counter_value, 
-			    xevent->xclient.data.l[2], 
-			    xevent->xclient.data.l[3]);
-#endif
-	}
-      return GDK_FILTER_REMOVE;
-    }
-  
-  return GDK_FILTER_CONTINUE;
-}
-
-void
-_gdk_events_queue (GdkDisplay *display)
-{
-  GList *node;
-  GdkEvent *event;
-  XEvent xevent;
-  Display *xdisplay = GDK_DISPLAY_XDISPLAY (display);
-
-  while (!_gdk_event_queue_find_first(display) && XPending (xdisplay))
-    {
-      XNextEvent (xdisplay, &xevent);
-
-      switch (xevent.type)
-	{
-	case KeyPress:
-	case KeyRelease:
-	  break;
-	default:
-	  if (XFilterEvent (&xevent, None))
-	    continue;
-	}
-      
-      event = gdk_event_new (GDK_NOTHING);
-      
-      event->any.window = NULL;
-      event->any.send_event = xevent.xany.send_event ? TRUE : FALSE;
-
-      ((GdkEventPrivate *)event)->flags |= GDK_EVENT_PENDING;
-
-      node = _gdk_event_queue_append (display, event);
-
-      if (gdk_event_translate (display, event, &xevent, FALSE))
-	{
-	  ((GdkEventPrivate *)event)->flags &= ~GDK_EVENT_PENDING;
-          _gdk_windowing_got_event (display, node, event, xevent.xany.serial);
-	}
-      else
-	{
-	  _gdk_event_queue_remove_link (display, node);
-	  g_list_free_1 (node);
-	  gdk_event_free (event);
-	}
-    }
-}
-
-static gboolean  
-gdk_event_prepare (GSource  *source,
-		   gint     *timeout)
-{
-  GdkDisplay *display = ((GdkDisplaySource*)source)->display;
-  gboolean retval;
-  
-  GDK_THREADS_ENTER ();
-
-  *timeout = -1;
-  retval = (_gdk_event_queue_find_first (display) != NULL || 
-	    gdk_check_xpending (display));
-  
-  GDK_THREADS_LEAVE ();
-
-  return retval;
-}
-
-static gboolean  
-gdk_event_check (GSource *source) 
-{
-  GdkDisplaySource *display_source = (GdkDisplaySource*)source;
-  gboolean retval;
-
-  GDK_THREADS_ENTER ();
-
-  if (display_source->event_poll_fd.revents & G_IO_IN)
-    retval = (_gdk_event_queue_find_first (display_source->display) != NULL || 
-	      gdk_check_xpending (display_source->display));
-  else
-    retval = FALSE;
-
-  GDK_THREADS_LEAVE ();
-
-  return retval;
-}
-
-static gboolean  
-gdk_event_dispatch (GSource    *source,
-		    GSourceFunc callback,
-		    gpointer    user_data)
-{
-  GdkDisplay *display = ((GdkDisplaySource*)source)->display;
-  GdkEvent *event;
- 
-  GDK_THREADS_ENTER ();
-
-  _gdk_events_queue (display);
-  event = _gdk_event_unqueue (display);
-
-  if (event)
-    {
-      if (_gdk_event_func)
-	(*_gdk_event_func) (event, _gdk_event_data);
-      
-      gdk_event_free (event);
-    }
-  
-  GDK_THREADS_LEAVE ();
-
-  return TRUE;
-}
-
-/**
- * gdk_event_send_client_message_for_display:
- * @display: the #GdkDisplay for the window where the message is to be sent.
- * @event: the #GdkEvent to send, which should be a #GdkEventClient.
- * @winid: the window to send the client message to.
- *
- * On X11, sends an X ClientMessage event to a given window. On
- * Windows, sends a message registered with the name
- * GDK_WIN32_CLIENT_MESSAGE.
- *
- * This could be used for communicating between different
- * applications, though the amount of data is limited to 20 bytes on
- * X11, and to just four bytes on Windows.
- *
- * Returns: non-zero on success.
- *
- * Since: 2.2
- */
-gboolean
-gdk_event_send_client_message_for_display (GdkDisplay     *display,
-					   GdkEvent       *event,
-					   GdkNativeWindow winid)
-{
-  XEvent sev;
-  
-  g_return_val_if_fail(event != NULL, FALSE);
-
-  /* Set up our event to send, with the exception of its target window */
-  sev.xclient.type = ClientMessage;
-  sev.xclient.display = GDK_DISPLAY_XDISPLAY (display);
-  sev.xclient.format = event->client.data_format;
-  sev.xclient.window = winid;
-  memcpy(&sev.xclient.data, &event->client.data, sizeof (sev.xclient.data));
-  sev.xclient.message_type = gdk_x11_atom_to_xatom_for_display (display, event->client.message_type);
-  
-  return _gdk_send_xevent (display, winid, False, NoEventMask, &sev);
-}
-
-
-
-/* Sends a ClientMessage to all toplevel client windows */
-static gboolean
-gdk_event_send_client_message_to_all_recurse (GdkDisplay *display,
-					      XEvent     *xev, 
-					      guint32     xid,
-					      guint       level)
-{
-  Atom type = None;
-  int format;
-  unsigned long nitems, after;
-  unsigned char *data;
-  Window *ret_children, ret_root, ret_parent;
-  unsigned int ret_nchildren;
-  gboolean send = FALSE;
-  gboolean found = FALSE;
-  gboolean result = FALSE;
-  int i;
-  
-  gdk_error_trap_push ();
-  
-  if (XGetWindowProperty (GDK_DISPLAY_XDISPLAY (display), xid, 
-			  gdk_x11_get_xatom_by_name_for_display (display, "WM_STATE"),
-			  0, 0, False, AnyPropertyType,
-			  &type, &format, &nitems, &after, &data) != Success)
-    goto out;
-  
-  if (type)
-    {
-      send = TRUE;
-      XFree (data);
-    }
-  else
-    {
-      /* OK, we're all set, now let's find some windows to send this to */
-      if (!XQueryTree (GDK_DISPLAY_XDISPLAY (display), xid,
-		      &ret_root, &ret_parent,
-		      &ret_children, &ret_nchildren))	
-	goto out;
-
-      for(i = 0; i < ret_nchildren; i++)
-	if (gdk_event_send_client_message_to_all_recurse (display, xev, ret_children[i], level + 1))
-	  found = TRUE;
-
-      XFree (ret_children);
-    }
-
-  if (send || (!found && (level == 1)))
-    {
-      xev->xclient.window = xid;
-      _gdk_send_xevent (display, xid, False, NoEventMask, xev);
-    }
-
-  result = send || found;
-
- out:
-  gdk_error_trap_pop ();
-
-  return result;
-}
-
-/**
- * gdk_screen_broadcast_client_message:
- * @screen: the #GdkScreen where the event will be broadcasted.
- * @event: the #GdkEvent.
- *
- * On X11, sends an X ClientMessage event to all toplevel windows on
- * @screen. 
- *
- * Toplevel windows are determined by checking for the WM_STATE property, 
- * as described in the Inter-Client Communication Conventions Manual (ICCCM).
- * If no windows are found with the WM_STATE property set, the message is 
- * sent to all children of the root window.
- *
- * On Windows, broadcasts a message registered with the name
- * GDK_WIN32_CLIENT_MESSAGE to all top-level windows. The amount of
- * data is limited to one long, i.e. four bytes.
- *
- * Since: 2.2
- */
-
-void
-gdk_screen_broadcast_client_message (GdkScreen *screen, 
-				     GdkEvent  *event)
-{
-  XEvent sev;
-  GdkWindow *root_window;
-
-  g_return_if_fail (event != NULL);
-  
-  root_window = gdk_screen_get_root_window (screen);
-  
-  /* Set up our event to send, with the exception of its target window */
-  sev.xclient.type = ClientMessage;
-  sev.xclient.display = GDK_WINDOW_XDISPLAY (root_window);
-  sev.xclient.format = event->client.data_format;
-  memcpy(&sev.xclient.data, &event->client.data, sizeof (sev.xclient.data));
-  sev.xclient.message_type = 
-    gdk_x11_atom_to_xatom_for_display (GDK_WINDOW_DISPLAY (root_window),
-				       event->client.message_type);
-
-  gdk_event_send_client_message_to_all_recurse (gdk_screen_get_display (screen),
-						&sev, 
-						GDK_WINDOW_XID (root_window), 
-						0);
-}
-
-/*
- *--------------------------------------------------------------
- * gdk_flush
- *
- *   Flushes the Xlib output buffer and then waits
- *   until all requests have been received and processed
- *   by the X server. The only real use for this function
- *   is in dealing with XShm.
- *
- * Arguments:
- *
- * Results:
- *
- * Side effects:
- *
- *--------------------------------------------------------------
- */
-
-void
-gdk_flush (void)
-{
-  GSList *tmp_list = _gdk_displays;
-  
-  while (tmp_list)
-    {
-      XSync (GDK_DISPLAY_XDISPLAY (tmp_list->data), False);
-      tmp_list = tmp_list->next;
-    }
-}
-
-static Bool
-timestamp_predicate (Display *display,
-		     XEvent  *xevent,
-		     XPointer arg)
-{
-  Window xwindow = GPOINTER_TO_UINT (arg);
-  GdkDisplay *gdk_display = gdk_x11_lookup_xdisplay (display);
-
-  if (xevent->type == PropertyNotify &&
-      xevent->xproperty.window == xwindow &&
-      xevent->xproperty.atom == gdk_x11_get_xatom_by_name_for_display (gdk_display,
-								       "GDK_TIMESTAMP_PROP"))
-    return True;
-
-  return False;
-}
-
-/**
- * gdk_x11_get_server_time:
- * @window: a #GdkWindow, used for communication with the server.
- *          The window must have GDK_PROPERTY_CHANGE_MASK in its
- *          events mask or a hang will result.
- * 
- * Routine to get the current X server time stamp. 
- * 
- * Return value: the time stamp.
- **/
-guint32
-gdk_x11_get_server_time (GdkWindow *window)
-{
-  Display *xdisplay;
-  Window   xwindow;
-  guchar c = 'a';
-  XEvent xevent;
-  Atom timestamp_prop_atom;
-
-  g_return_val_if_fail (GDK_IS_WINDOW (window), 0);
-  g_return_val_if_fail (!GDK_WINDOW_DESTROYED (window), 0);
-
-  xdisplay = GDK_WINDOW_XDISPLAY (window);
-  xwindow = GDK_WINDOW_XWINDOW (window);
-  timestamp_prop_atom = 
-    gdk_x11_get_xatom_by_name_for_display (GDK_WINDOW_DISPLAY (window),
-					   "GDK_TIMESTAMP_PROP");
-  
-  XChangeProperty (xdisplay, xwindow, timestamp_prop_atom,
-		   timestamp_prop_atom,
-		   8, PropModeReplace, &c, 1);
-
-  XIfEvent (xdisplay, &xevent,
-	    timestamp_predicate, GUINT_TO_POINTER(xwindow));
-
-  return xevent.xproperty.time;
-}
-
-static void
-fetch_net_wm_check_window (GdkScreen *screen)
-{
-  GdkScreenX11 *screen_x11;
-  GdkDisplay *display;
-  Atom type;
-  gint format;
-  gulong n_items;
-  gulong bytes_after;
-  guchar *data;
-  Window *xwindow;
-  GTimeVal tv;
-  gint error;
-
-  screen_x11 = GDK_SCREEN_X11 (screen);
-  display = screen_x11->display;
-
-  g_return_if_fail (GDK_DISPLAY_X11 (display)->trusted_client);
-  
-  g_get_current_time (&tv);
-
-  if (ABS  (tv.tv_sec - screen_x11->last_wmspec_check_time) < 15)
-    return; /* we've checked recently */
-
-  screen_x11->last_wmspec_check_time = tv.tv_sec;
-
-  data = NULL;
-  XGetWindowProperty (screen_x11->xdisplay, screen_x11->xroot_window,
-		      gdk_x11_get_xatom_by_name_for_display (display, "_NET_SUPPORTING_WM_CHECK"),
-		      0, G_MAXLONG, False, XA_WINDOW, &type, &format,
-		      &n_items, &bytes_after, &data);
-  
-  if (type != XA_WINDOW)
-    {
-      if (data)
-        XFree (data);
-      return;
-    }
-
-  xwindow = (Window *)data;
-
-  if (screen_x11->wmspec_check_window == *xwindow)
-    {
-      XFree (xwindow);
-      return;
-    }
-
-  gdk_error_trap_push ();
-
-  /* Find out if this WM goes away, so we can reset everything. */
-  XSelectInput (screen_x11->xdisplay, *xwindow, StructureNotifyMask);
-  gdk_display_sync (display);
-
-  error = gdk_error_trap_pop ();
-  if (!error)
-    {
-      screen_x11->wmspec_check_window = *xwindow;
-      screen_x11->need_refetch_net_supported = TRUE;
-      screen_x11->need_refetch_wm_name = TRUE;
-
-      /* Careful, reentrancy */
-      _gdk_x11_screen_window_manager_changed (GDK_SCREEN (screen_x11));
-    }
-  else if (error == BadWindow)
-    {
-      /* Leftover property, try again immediately, new wm may be starting up */
-      screen_x11->last_wmspec_check_time = 0;
-    }
-
-  XFree (xwindow);
-}
-
-/**
- * gdk_x11_screen_get_window_manager_name:
- * @screen: a #GdkScreen 
- * 
- * Returns the name of the window manager for @screen. 
- * 
- * Return value: the name of the window manager screen @screen, or 
- * "unknown" if the window manager is unknown. The string is owned by GDK
- * and should not be freed.
- *
- * Since: 2.2
- **/
-const char*
-gdk_x11_screen_get_window_manager_name (GdkScreen *screen)
-{
-  GdkScreenX11 *screen_x11;
-
-  screen_x11 = GDK_SCREEN_X11 (screen);
-  
-  if (!G_LIKELY (GDK_DISPLAY_X11 (screen_x11->display)->trusted_client))
-    return screen_x11->window_manager_name;
-
-  fetch_net_wm_check_window (screen);
-
-  if (screen_x11->need_refetch_wm_name)
-    {
-      /* Get the name of the window manager */
-      screen_x11->need_refetch_wm_name = FALSE;
-
-      g_free (screen_x11->window_manager_name);
-      screen_x11->window_manager_name = g_strdup ("unknown");
-      
-      if (screen_x11->wmspec_check_window != None)
-        {
-          Atom type;
-          gint format;
-          gulong n_items;
-          gulong bytes_after;
-          gchar *name;
-          
-          name = NULL;
-
-	  gdk_error_trap_push ();
-          
-          XGetWindowProperty (GDK_DISPLAY_XDISPLAY (screen_x11->display),
-                              screen_x11->wmspec_check_window,
-                              gdk_x11_get_xatom_by_name_for_display (screen_x11->display,
-                                                                     "_NET_WM_NAME"),
-                              0, G_MAXLONG, False,
-                              gdk_x11_get_xatom_by_name_for_display (screen_x11->display,
-                                                                     "UTF8_STRING"),
-                              &type, &format, 
-                              &n_items, &bytes_after,
-                              (guchar **)&name);
-          
-          gdk_display_sync (screen_x11->display);
-          
-          gdk_error_trap_pop ();
-          
-          if (name != NULL)
-            {
-              g_free (screen_x11->window_manager_name);
-              screen_x11->window_manager_name = g_strdup (name);
-              XFree (name);
-            }
-        }
-    }
-  
-  return GDK_SCREEN_X11 (screen)->window_manager_name;
-}
-
-typedef struct _NetWmSupportedAtoms NetWmSupportedAtoms;
-
-struct _NetWmSupportedAtoms
-{
-  Atom *atoms;
-  gulong n_atoms;
-};
-
-static void
-cleanup_atoms(gpointer data)
-{
-  NetWmSupportedAtoms *supported_atoms = data;
-  if (supported_atoms->atoms)
-      XFree (supported_atoms->atoms);
-  g_free (supported_atoms);
-}
-
-/**
- * gdk_x11_screen_supports_net_wm_hint:
- * @screen: the relevant #GdkScreen.
- * @property: a property atom.
- * 
- * This function is specific to the X11 backend of GDK, and indicates
- * whether the window manager supports a certain hint from the
- * Extended Window Manager Hints Specification. You can find this
- * specification on 
- * <ulink url="http://www.freedesktop.org">http://www.freedesktop.org</ulink>.
- *
- * When using this function, keep in mind that the window manager
- * can change over time; so you shouldn't use this function in
- * a way that impacts persistent application state. A common bug
- * is that your application can start up before the window manager
- * does when the user logs in, and before the window manager starts
- * gdk_x11_screen_supports_net_wm_hint() will return %FALSE for every property.
- * You can monitor the window_manager_changed signal on #GdkScreen to detect
- * a window manager change.
- * 
- * Return value: %TRUE if the window manager supports @property
- *
- * Since: 2.2
- **/
-gboolean
-gdk_x11_screen_supports_net_wm_hint (GdkScreen *screen,
-				     GdkAtom    property)
-{
-  gulong i;
-  GdkScreenX11 *screen_x11;
-  NetWmSupportedAtoms *supported_atoms;
-  GdkDisplay *display;
-
-  g_return_val_if_fail (GDK_IS_SCREEN (screen), FALSE);
-  
-  screen_x11 = GDK_SCREEN_X11 (screen);
-  display = screen_x11->display;
-
-  if (!G_LIKELY (GDK_DISPLAY_X11 (display)->trusted_client))
-    return FALSE;
-
-  supported_atoms = g_object_get_data (G_OBJECT (screen), "gdk-net-wm-supported-atoms");
-  if (!supported_atoms)
-    {
-      supported_atoms = g_new0 (NetWmSupportedAtoms, 1);
-      g_object_set_data_full (G_OBJECT (screen), "gdk-net-wm-supported-atoms", supported_atoms, cleanup_atoms);
-    }
-
-  fetch_net_wm_check_window (screen);
-
-  if (screen_x11->wmspec_check_window == None)
-    return FALSE;
-  
-  if (screen_x11->need_refetch_net_supported)
-    {
-      /* WM has changed since we last got the supported list,
-       * refetch it.
-       */
-      Atom type;
-      gint format;
-      gulong bytes_after;
-
-      screen_x11->need_refetch_net_supported = FALSE;
-      
-      if (supported_atoms->atoms)
-        XFree (supported_atoms->atoms);
-      
-      supported_atoms->atoms = NULL;
-      supported_atoms->n_atoms = 0;
-      
-      XGetWindowProperty (GDK_DISPLAY_XDISPLAY (display), screen_x11->xroot_window,
-                          gdk_x11_get_xatom_by_name_for_display (display, "_NET_SUPPORTED"),
-                          0, G_MAXLONG, False, XA_ATOM, &type, &format, 
-                          &supported_atoms->n_atoms, &bytes_after,
-                          (guchar **)&supported_atoms->atoms);
-      
-      if (type != XA_ATOM)
-        return FALSE;
-    }
-  
-  if (supported_atoms->atoms == NULL)
-    return FALSE;
-  
-  i = 0;
-  while (i < supported_atoms->n_atoms)
-    {
-      if (supported_atoms->atoms[i] == gdk_x11_atom_to_xatom_for_display (display, property))
-        return TRUE;
-      
-      ++i;
-    }
-  
-  return FALSE;
-}
-
-/**
- * gdk_net_wm_supports:
- * @property: a property atom.
- * 
- * This function is specific to the X11 backend of GDK, and indicates
- * whether the window manager for the default screen supports a certain
- * hint from the Extended Window Manager Hints Specification. See
- * gdk_x11_screen_supports_net_wm_hint() for complete details.
- * 
- * Return value: %TRUE if the window manager supports @property
- **/
-gboolean
-gdk_net_wm_supports (GdkAtom property)
-{
-  return gdk_x11_screen_supports_net_wm_hint (gdk_screen_get_default (), property);
-}
-
-
-static void
-gdk_xsettings_notify_cb (const char       *name,
-			 XSettingsAction   action,
-			 XSettingsSetting *setting,
-			 void             *data)
-{
-  GdkEvent new_event;
-  GdkScreen *screen = data;
-  GdkScreenX11 *screen_x11 = data;
-  int i;
-
-  if (screen_x11->xsettings_in_init)
-    return;
-  
-  new_event.type = GDK_SETTING;
-  new_event.setting.window = gdk_screen_get_root_window (screen);
-  new_event.setting.send_event = FALSE;
-  new_event.setting.name = NULL;
-
-  for (i = 0; i < GDK_SETTINGS_N_ELEMENTS() ; i++)
-    if (strcmp (GDK_SETTINGS_X_NAME (i), name) == 0)
-      {
-	new_event.setting.name = (char*) GDK_SETTINGS_GDK_NAME (i);
-	break;
-      }
-  
-  if (!new_event.setting.name)
-    return;
-  
-  switch (action)
-    {
-    case XSETTINGS_ACTION_NEW:
-      new_event.setting.action = GDK_SETTING_ACTION_NEW;
-      break;
-    case XSETTINGS_ACTION_CHANGED:
-      new_event.setting.action = GDK_SETTING_ACTION_CHANGED;
-      break;
-    case XSETTINGS_ACTION_DELETED:
-      new_event.setting.action = GDK_SETTING_ACTION_DELETED;
-      break;
-    }
-
-  gdk_event_put (&new_event);
-}
-
-static gboolean
-check_transform (const gchar *xsettings_name,
-		 GType        src_type,
-		 GType        dest_type)
-{
-  if (!g_value_type_transformable (src_type, dest_type))
-    {
-      g_warning ("Cannot transform xsetting %s of type %s to type %s\n",
-		 xsettings_name,
-		 g_type_name (src_type),
-		 g_type_name (dest_type));
-      return FALSE;
-    }
-  else
-    return TRUE;
-}
-
-/**
- * gdk_screen_get_setting:
- * @screen: the #GdkScreen where the setting is located
- * @name: the name of the setting
- * @value: location to store the value of the setting
- *
- * Retrieves a desktop-wide setting such as double-click time
- * for the #GdkScreen @screen. 
- *
- * FIXME needs a list of valid settings here, or a link to 
- * more information.
- * 
- * Returns: %TRUE if the setting existed and a value was stored
- *   in @value, %FALSE otherwise.
- *
- * Since: 2.2
- **/
-gboolean
-gdk_screen_get_setting (GdkScreen   *screen,
-			const gchar *name,
-			GValue      *value)
-{
-
-  const char *xsettings_name = NULL;
-  XSettingsResult result;
-  XSettingsSetting *setting = NULL;
-  GdkScreenX11 *screen_x11;
-  gboolean success = FALSE;
-  gint i;
-  GValue tmp_val = { 0, };
-  
-  g_return_val_if_fail (GDK_IS_SCREEN (screen), FALSE);
-  
-  screen_x11 = GDK_SCREEN_X11 (screen);
-
-  for (i = 0; i < GDK_SETTINGS_N_ELEMENTS(); i++)
-    if (strcmp (GDK_SETTINGS_GDK_NAME (i), name) == 0)
-      {
-	xsettings_name = GDK_SETTINGS_X_NAME (i);
-	break;
-      }
-
-  if (!xsettings_name)
-    goto out;
-
-  result = xsettings_client_get_setting (screen_x11->xsettings_client, 
-					 xsettings_name, &setting);
-  if (result != XSETTINGS_SUCCESS)
-    goto out;
-
-  switch (setting->type)
-    {
-    case XSETTINGS_TYPE_INT:
-      if (check_transform (xsettings_name, G_TYPE_INT, G_VALUE_TYPE (value)))
-	{
-	  g_value_init (&tmp_val, G_TYPE_INT);
-	  g_value_set_int (&tmp_val, setting->data.v_int);
-	  g_value_transform (&tmp_val, value);
-
-	  success = TRUE;
-	}
-      break;
-    case XSETTINGS_TYPE_STRING:
-      if (check_transform (xsettings_name, G_TYPE_STRING, G_VALUE_TYPE (value)))
-	{
-	  g_value_init (&tmp_val, G_TYPE_STRING);
-	  g_value_set_string (&tmp_val, setting->data.v_string);
-	  g_value_transform (&tmp_val, value);
-
-	  success = TRUE;
-	}
-      break;
-    case XSETTINGS_TYPE_COLOR:
-      if (!check_transform (xsettings_name, GDK_TYPE_COLOR, G_VALUE_TYPE (value)))
-	{
-	  GdkColor color;
-	  
-	  g_value_init (&tmp_val, GDK_TYPE_COLOR);
-
-	  color.pixel = 0;
-	  color.red = setting->data.v_color.red;
-	  color.green = setting->data.v_color.green;
-	  color.blue = setting->data.v_color.blue;
-	  
-	  g_value_set_boxed (&tmp_val, &color);
-	  
-	  g_value_transform (&tmp_val, value);
-	  
-	  success = TRUE;
-	}
-      break;
-    }
-  
-  g_value_unset (&tmp_val);
-
- out:
-  if (setting)
-    xsettings_setting_free (setting);
-
-  if (success)
-    return TRUE;
-  else
-    return _gdk_x11_get_xft_setting (screen, name, value);
-}
-
-static GdkFilterReturn 
-gdk_xsettings_client_event_filter (GdkXEvent *xevent,
-				   GdkEvent  *event,
-				   gpointer   data)
-{
-  GdkScreenX11 *screen = data;
-  
-  if (xsettings_client_process_event (screen->xsettings_client, (XEvent *)xevent))
-    return GDK_FILTER_REMOVE;
-  else
-    return GDK_FILTER_CONTINUE;
-}
-
-static Bool
-gdk_xsettings_watch_cb (Window   window,
-			Bool	 is_start,
-			long     mask,
-			void    *cb_data)
-{
-  GdkWindow *gdkwin;
-  GdkScreen *screen = cb_data;
-
-  gdkwin = gdk_window_lookup_for_display (gdk_screen_get_display (screen), window);
-
-  if (is_start)
-    {
-      if (gdkwin)
-	g_object_ref (gdkwin);
-      else
-	{
-	  gdkwin = gdk_window_foreign_new_for_display (gdk_screen_get_display (screen), window);
-	  
-	  /* gdk_window_foreign_new_for_display() can fail and return NULL if the
-	   * window has already been destroyed.
-	   */
-	  if (!gdkwin)
-	    return False;
-	}
-
-      gdk_window_add_filter (gdkwin, gdk_xsettings_client_event_filter, screen);
-    }
-  else
-    {
-      if (!gdkwin)
-	{
-	  /* gdkwin should not be NULL here, since if starting the watch succeeded
-	   * we have a reference on the window. It might mean that the caller didn't
-	   * remove the watch when it got a DestroyNotify event. Or maybe the
-	   * caller ignored the return value when starting the watch failed.
-	   */
-	  g_warning ("gdk_xsettings_watch_cb(): Couldn't find window to unwatch");
-	  return False;
-	}
-      
-      gdk_window_remove_filter (gdkwin, gdk_xsettings_client_event_filter, screen);
-      g_object_unref (gdkwin);
-    }
-
-  return True;
-}
-
-void
-_gdk_windowing_event_data_copy (const GdkEvent *src,
-                                GdkEvent       *dst)
-{
-}
-
-void
-_gdk_windowing_event_data_free (GdkEvent *event)
-{
-}
-
-#define __GDK_EVENTS_X11_C__
-#include "gdkaliasdef.c"
diff --git a/gdk/x11/gdkeventsource.c b/gdk/x11/gdkeventsource.c
new file mode 100644
index 0000000..30da4f4
--- /dev/null
+++ b/gdk/x11/gdkeventsource.c
@@ -0,0 +1,423 @@
+/* GDK - The GIMP Drawing Kit
+ * Copyright (C) 2009 Carlos Garnacho <carlosg@gnome.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * 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.
+ */
+
+#include "gdkeventsource.h"
+#include "gdkinternals.h"
+#include "gdkx.h"
+#include "gdkalias.h"
+
+static gboolean gdk_event_source_prepare  (GSource     *source,
+                                           gint        *timeout);
+static gboolean gdk_event_source_check    (GSource     *source);
+static gboolean gdk_event_source_dispatch (GSource     *source,
+                                           GSourceFunc  callback,
+                                           gpointer     user_data);
+static void     gdk_event_source_finalize (GSource     *source);
+
+#define HAS_FOCUS(toplevel)                           \
+  ((toplevel)->has_focus || (toplevel)->has_pointer_focus)
+
+struct _GdkEventSource
+{
+  GSource source;
+
+  GdkDisplay *display;
+  GPollFD event_poll_fd;
+  GList *translators;
+};
+
+static GSourceFuncs event_funcs = {
+  gdk_event_source_prepare,
+  gdk_event_source_check,
+  gdk_event_source_dispatch,
+  gdk_event_source_finalize
+};
+
+static GList *event_sources = NULL;
+
+static gint
+gdk_event_apply_filters (XEvent *xevent,
+			 GdkEvent *event,
+			 GList *filters)
+{
+  GList *tmp_list;
+  GdkFilterReturn result;
+
+  tmp_list = filters;
+
+  while (tmp_list)
+    {
+      GdkEventFilter *filter = (GdkEventFilter*) tmp_list->data;
+
+      tmp_list = tmp_list->next;
+      result = filter->function (xevent, event, filter->data);
+
+      if (result !=  GDK_FILTER_CONTINUE)
+	return result;
+    }
+
+  return GDK_FILTER_CONTINUE;
+}
+
+static GdkWindow *
+gdk_event_source_get_filter_window (GdkEventSource *event_source,
+                                    XEvent         *xevent)
+{
+  GdkWindow *window;
+
+  window = gdk_window_lookup_for_display (event_source->display,
+                                          xevent->xany.window);
+
+  if (window && !GDK_IS_WINDOW (window))
+    window = NULL;
+
+  return window;
+}
+
+static void
+handle_focus_change (GdkEventCrossing *event)
+{
+  GdkToplevelX11 *toplevel;
+  gboolean focus_in, had_focus;
+
+  toplevel = _gdk_x11_window_get_toplevel (event->window);
+  focus_in = (event->type == GDK_ENTER_NOTIFY);
+
+  if (!toplevel || event->detail == GDK_NOTIFY_INFERIOR)
+    return;
+
+  toplevel->has_pointer = focus_in;
+
+  if (!event->focus || toplevel->has_focus_window)
+    return;
+
+  had_focus = HAS_FOCUS (toplevel);
+  toplevel->has_pointer_focus = focus_in;
+
+  if (HAS_FOCUS (toplevel) != had_focus)
+    {
+      GdkEvent focus_event;
+
+      focus_event.type = GDK_FOCUS_CHANGE;
+      focus_event.focus_change.window = event->window;
+      focus_event.focus_change.send_event = FALSE;
+      focus_event.focus_change.in = focus_in;
+      focus_event.focus_change.device = event->device;
+
+      gdk_event_put (&focus_event);
+    }
+}
+
+static GdkEvent *
+gdk_event_source_translate_event (GdkEventSource *event_source,
+                                  XEvent         *xevent)
+{
+  GdkEvent *event = gdk_event_new (GDK_NOTHING);
+  GList *list = event_source->translators;
+  GdkFilterReturn result;
+  GdkWindow *filter_window;
+
+  /* Run default filters */
+  if (_gdk_default_filters)
+    {
+      /* Apply global filters */
+
+      result = gdk_event_apply_filters (xevent, event,
+                                        _gdk_default_filters);
+
+      if (result == GDK_FILTER_REMOVE)
+        {
+          gdk_event_free (event);
+          return NULL;
+        }
+      else if (result == GDK_FILTER_TRANSLATE)
+        return event;
+    }
+
+  filter_window = gdk_event_source_get_filter_window (event_source, xevent);
+  event->any.window = g_object_ref (filter_window);
+
+  if (filter_window)
+    {
+      /* Apply per-window filters */
+      GdkWindowObject *filter_private = (GdkWindowObject *) filter_window;
+      GdkFilterReturn result;
+
+      if (filter_private->filters)
+	{
+	  result = gdk_event_apply_filters (xevent, event,
+					    filter_private->filters);
+
+          if (result == GDK_FILTER_REMOVE)
+            {
+              gdk_event_free (event);
+              return NULL;
+            }
+          else if (result == GDK_FILTER_TRANSLATE)
+            return event;
+	}
+    }
+
+  gdk_event_free (event);
+  event = NULL;
+
+  while (list && !event)
+    {
+      GdkEventTranslator *translator = list->data;
+
+      list = list->next;
+      event = gdk_event_translator_translate (translator,
+                                              event_source->display,
+                                              xevent);
+    }
+
+  if (event &&
+      (event->type == GDK_ENTER_NOTIFY ||
+       event->type == GDK_LEAVE_NOTIFY) &&
+      event->crossing.window != NULL)
+    {
+      /* Handle focusing (in the case where no window manager is running */
+      handle_focus_change (&event->crossing);
+    }
+
+  return event;
+}
+
+static gboolean
+gdk_check_xpending (GdkDisplay *display)
+{
+  return XPending (GDK_DISPLAY_XDISPLAY (display));
+}
+
+static gboolean
+gdk_event_source_prepare (GSource *source,
+                          gint    *timeout)
+{
+  GdkDisplay *display = ((GdkEventSource*) source)->display;
+  gboolean retval;
+
+  GDK_THREADS_ENTER ();
+
+  *timeout = -1;
+  retval = (_gdk_event_queue_find_first (display) != NULL ||
+	    gdk_check_xpending (display));
+
+  GDK_THREADS_LEAVE ();
+
+  return retval;
+}
+
+static gboolean
+gdk_event_source_check (GSource *source)
+{
+  GdkEventSource *event_source = (GdkEventSource*) source;
+  gboolean retval;
+
+  GDK_THREADS_ENTER ();
+
+  if (event_source->event_poll_fd.revents & G_IO_IN)
+    retval = (_gdk_event_queue_find_first (event_source->display) != NULL ||
+	      gdk_check_xpending (event_source->display));
+  else
+    retval = FALSE;
+
+  GDK_THREADS_LEAVE ();
+
+  return retval;
+}
+
+void
+_gdk_events_queue (GdkDisplay *display)
+{
+  GdkEvent *event;
+  XEvent xevent;
+  Display *xdisplay = GDK_DISPLAY_XDISPLAY (display);
+  GdkEventSource *event_source;
+  GdkDisplayX11 *display_x11;
+
+  display_x11 = GDK_DISPLAY_X11 (display);
+  event_source = (GdkEventSource *) display_x11->event_source;
+
+  while (!_gdk_event_queue_find_first(display) && XPending (xdisplay))
+    {
+      XNextEvent (xdisplay, &xevent);
+
+      switch (xevent.type)
+	{
+	case KeyPress:
+	case KeyRelease:
+	  break;
+	default:
+	  if (XFilterEvent (&xevent, None))
+	    continue;
+	}
+
+      event = gdk_event_source_translate_event (event_source, &xevent);
+
+      if (event)
+        {
+          GList *node;
+
+          node = _gdk_event_queue_append (display, event);
+          _gdk_windowing_got_event (display, node, event, xevent.xany.serial);
+        }
+    }
+}
+
+static gboolean
+gdk_event_source_dispatch (GSource     *source,
+                           GSourceFunc  callback,
+                           gpointer     user_data)
+{
+  GdkDisplay *display = ((GdkEventSource*) source)->display;
+  GdkEvent *event;
+
+  GDK_THREADS_ENTER ();
+
+  event = gdk_display_get_event (display);
+
+  if (event)
+    {
+      if (_gdk_event_func)
+	(*_gdk_event_func) (event, _gdk_event_data);
+
+      gdk_event_free (event);
+    }
+
+  GDK_THREADS_LEAVE ();
+
+  return TRUE;
+}
+
+static void
+gdk_event_source_finalize (GSource *source)
+{
+  event_sources = g_list_remove (event_sources, source);
+}
+
+GSource *
+gdk_event_source_new (GdkDisplay *display)
+{
+  GSource *source;
+  GdkEventSource *event_source;
+  GdkDisplayX11 *display_x11;
+  int connection_number;
+
+  source = g_source_new (&event_funcs, sizeof (GdkEventSource));
+  event_source = (GdkEventSource *) source;
+  event_source->display = display;
+
+  display_x11 = GDK_DISPLAY_X11 (display);
+  connection_number = ConnectionNumber (display_x11->xdisplay);
+
+  event_source->event_poll_fd.fd = connection_number;
+  event_source->event_poll_fd.events = G_IO_IN;
+  g_source_add_poll (source, &event_source->event_poll_fd);
+
+  g_source_set_priority (source, GDK_PRIORITY_EVENTS);
+  g_source_set_can_recurse (source, TRUE);
+  g_source_attach (source, NULL);
+
+  event_sources = g_list_prepend (event_sources, source);
+
+  return source;
+}
+
+void
+gdk_event_source_add_translator (GdkEventSource     *source,
+                                 GdkEventTranslator *translator)
+{
+  g_return_if_fail (GDK_IS_EVENT_TRANSLATOR (translator));
+
+  source->translators = g_list_prepend (source->translators, translator);
+}
+
+void
+gdk_event_source_select_events (GdkEventSource *source,
+                                Window          window,
+                                GdkEventMask    event_mask,
+                                unsigned int    extra_x_mask)
+{
+  unsigned int xmask = extra_x_mask;
+  GList *list;
+  gint i;
+
+  list = source->translators;
+
+  while (list)
+    {
+      GdkEventTranslator *translator = list->data;
+      GdkEventMask translator_mask, mask;
+
+      translator_mask = gdk_event_translator_get_handled_events (translator);
+      mask = event_mask & translator_mask;
+
+      if (mask != 0)
+        {
+          gdk_event_translator_select_window_events (translator, window, mask);
+          event_mask &= ~(mask);
+        }
+
+      list = list->next;
+    }
+
+  for (i = 0; i < _gdk_nenvent_masks; i++)
+    {
+      if (event_mask & (1 << (i + 1)))
+        xmask |= _gdk_event_mask_table[i];
+    }
+
+  XSelectInput (GDK_DISPLAY_XDISPLAY (source->display), window, xmask);
+}
+
+/**
+ * gdk_events_pending:
+ *
+ * Checks if any events are ready to be processed for any display.
+ *
+ * Return value:  %TRUE if any events are pending.
+ **/
+gboolean
+gdk_events_pending (void)
+{
+  GList *tmp_list;
+
+  for (tmp_list = event_sources; tmp_list; tmp_list = tmp_list->next)
+    {
+      GdkEventSource *tmp_source = tmp_list->data;
+      GdkDisplay *display = tmp_source->display;
+
+      if (_gdk_event_queue_find_first (display))
+	return TRUE;
+    }
+
+  for (tmp_list = event_sources; tmp_list; tmp_list = tmp_list->next)
+    {
+      GdkEventSource *tmp_source = tmp_list->data;
+      GdkDisplay *display = tmp_source->display;
+
+      if (gdk_check_xpending (display))
+	return TRUE;
+    }
+
+  return FALSE;
+}
+
+#define __GDK_EVENT_SOURCE_C__
+#include "gdkaliasdef.c"
diff --git a/gdk/x11/gdkeventsource.h b/gdk/x11/gdkeventsource.h
new file mode 100644
index 0000000..c1e5239
--- /dev/null
+++ b/gdk/x11/gdkeventsource.h
@@ -0,0 +1,43 @@
+/* GDK - The GIMP Drawing Kit
+ * Copyright (C) 2009 Carlos Garnacho <carlosg@gnome.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * 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.
+ */
+
+#ifndef __GDK_EVENT_SOURCE_H__
+#define __GDK_EVENT_SOURCE_H__
+
+#include "gdkeventtranslator.h"
+#include "gdkprivate-x11.h"
+
+G_BEGIN_DECLS
+
+typedef struct _GdkEventSource GdkEventSource;
+
+GSource * gdk_event_source_new            (GdkDisplay *display);
+
+void      gdk_event_source_add_translator (GdkEventSource     *source,
+                                           GdkEventTranslator *translator);
+
+void      gdk_event_source_select_events  (GdkEventSource *source,
+                                           Window          window,
+                                           GdkEventMask    event_mask,
+                                           unsigned int    extra_x_mask);
+
+
+G_END_DECLS
+
+#endif /* __GDK_EVENT_SOURCE_H__ */
diff --git a/gdk/x11/gdkeventtranslator.c b/gdk/x11/gdkeventtranslator.c
new file mode 100644
index 0000000..d41d7c3
--- /dev/null
+++ b/gdk/x11/gdkeventtranslator.c
@@ -0,0 +1,94 @@
+/* GDK - The GIMP Drawing Kit
+ * Copyright (C) 2009 Carlos Garnacho <carlosg@gnome.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * 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.
+ */
+
+#include "gdkeventtranslator.h"
+
+GType
+gdk_event_translator_get_type (void)
+{
+  static GType translator_type = 0;
+
+  if (G_UNLIKELY (!translator_type))
+    {
+      translator_type = g_type_register_static_simple (G_TYPE_INTERFACE,
+                                                       g_intern_static_string ("GdkEventTranslator"),
+                                                       sizeof (GdkEventTranslatorIface),
+                                                       NULL, 0, NULL, 0);
+
+      g_type_interface_add_prerequisite (translator_type, G_TYPE_OBJECT);
+    }
+
+  return translator_type;
+}
+
+GdkEvent *
+gdk_event_translator_translate (GdkEventTranslator *translator,
+                                GdkDisplay         *display,
+                                XEvent             *xevent)
+{
+  GdkEventTranslatorIface *iface;
+  GdkEvent *event;
+
+  g_return_val_if_fail (GDK_IS_EVENT_TRANSLATOR (translator), NULL);
+  g_return_val_if_fail (GDK_IS_DISPLAY (display), NULL);
+
+  iface = GDK_EVENT_TRANSLATOR_GET_IFACE (translator);
+
+  if (!iface->translate_event)
+    return NULL;
+
+  event = gdk_event_new (GDK_NOTHING);
+
+  if ((iface->translate_event) (translator, display, event, xevent))
+    return event;
+
+  gdk_event_free (event);
+
+  return NULL;
+}
+
+GdkEventMask
+gdk_event_translator_get_handled_events (GdkEventTranslator *translator)
+{
+  GdkEventTranslatorIface *iface;
+
+  g_return_val_if_fail (GDK_IS_EVENT_TRANSLATOR (translator), 0);
+
+  iface = GDK_EVENT_TRANSLATOR_GET_IFACE (translator);
+
+  if (iface->get_handled_events)
+    return iface->get_handled_events (translator);
+
+  return 0;
+}
+
+void
+gdk_event_translator_select_window_events (GdkEventTranslator *translator,
+                                           Window              window,
+                                           GdkEventMask        event_mask)
+{
+  GdkEventTranslatorIface *iface;
+
+  g_return_if_fail (GDK_IS_EVENT_TRANSLATOR (translator));
+
+  iface = GDK_EVENT_TRANSLATOR_GET_IFACE (translator);
+
+  if (iface->select_window_events)
+    iface->select_window_events (translator, window, event_mask);
+}
diff --git a/gdk/x11/gdkeventtranslator.h b/gdk/x11/gdkeventtranslator.h
new file mode 100644
index 0000000..c6d0acc
--- /dev/null
+++ b/gdk/x11/gdkeventtranslator.h
@@ -0,0 +1,66 @@
+/* GDK - The GIMP Drawing Kit
+ * Copyright (C) 2009 Carlos Garnacho <carlosg@gnome.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * 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.
+ */
+
+#ifndef __GDK_EVENT_TRANSLATOR_H__
+#define __GDK_EVENT_TRANSLATOR_H__
+
+#include <gdk/gdktypes.h>
+#include <gdk/gdkdisplay.h>
+#include "gdkprivate-x11.h"
+
+G_BEGIN_DECLS
+
+#define GDK_TYPE_EVENT_TRANSLATOR         (gdk_event_translator_get_type ())
+#define GDK_EVENT_TRANSLATOR(o)           (G_TYPE_CHECK_INSTANCE_CAST ((o), GDK_TYPE_EVENT_TRANSLATOR, GdkEventTranslator))
+#define GDK_IS_EVENT_TRANSLATOR(o)        (G_TYPE_CHECK_INSTANCE_TYPE ((o), GDK_TYPE_EVENT_TRANSLATOR))
+#define GDK_EVENT_TRANSLATOR_GET_IFACE(o) (G_TYPE_INSTANCE_GET_INTERFACE  ((o), GDK_TYPE_EVENT_TRANSLATOR, GdkEventTranslatorIface))
+
+typedef struct _GdkEventTranslatorIface GdkEventTranslatorIface;
+typedef struct _GdkEventTranslator GdkEventTranslator; /* Dummy typedef */
+
+struct _GdkEventTranslatorIface
+{
+  GTypeInterface iface;
+
+  /* VMethods */
+  gboolean (* translate_event) (GdkEventTranslator *translator,
+                                GdkDisplay         *display,
+                                GdkEvent           *event,
+                                XEvent             *xevent);
+
+  GdkEventMask (* get_handled_events)   (GdkEventTranslator *translator);
+  void         (* select_window_events) (GdkEventTranslator *translator,
+                                         Window              window,
+                                         GdkEventMask        event_mask);
+};
+
+GType      gdk_event_translator_get_type (void) G_GNUC_CONST;
+
+GdkEvent * gdk_event_translator_translate (GdkEventTranslator *translator,
+                                           GdkDisplay         *display,
+                                           XEvent             *xevent);
+
+GdkEventMask gdk_event_translator_get_handled_events   (GdkEventTranslator *translator);
+void         gdk_event_translator_select_window_events (GdkEventTranslator *translator,
+                                                        Window              window,
+                                                        GdkEventMask        event_mask);
+
+G_END_DECLS
+
+#endif /* __GDK_EVENT_TRANSLATOR_H__ */
diff --git a/gdk/x11/gdkinput-none.c b/gdk/x11/gdkinput-none.c
deleted file mode 100644
index 4d21bef..0000000
--- a/gdk/x11/gdkinput-none.c
+++ /dev/null
@@ -1,126 +0,0 @@
-/* GDK - The GIMP Drawing Kit
- * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * 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.
- */
-
-#include "config.h"
-#include "gdkinputprivate.h"
-#include "gdkdisplay-x11.h"
-#include "gdkalias.h"
-
-/*
- * Modified by the GTK+ Team and others 1997-2000.  See the AUTHORS
- * file for a list of people on the GTK+ Team.  See the ChangeLog
- * files for a list of changes.  These files are distributed with
- * GTK+ at ftp://ftp.gtk.org/pub/gtk/. 
- */
-
-void
-_gdk_input_init (GdkDisplay *display)
-{
-  GdkDisplayX11 *display_x11 = GDK_DISPLAY_X11 (display);
-  
-  _gdk_init_input_core (display);
-  
-  display_x11->input_devices = g_list_append (NULL, display->core_pointer);
-  display->ignore_core_events = FALSE;
-}
-
-void 
-gdk_device_get_state (GdkDevice       *device,
-		      GdkWindow       *window,
-		      gdouble         *axes,
-		      GdkModifierType *mask)
-{
-  gint x_int, y_int;
-
-  g_return_if_fail (device != NULL);
-  g_return_if_fail (GDK_IS_WINDOW (window));
-
-  gdk_window_get_pointer (window, &x_int, &y_int, mask);
-
-  if (axes)
-    {
-      axes[0] = x_int;
-      axes[1] = y_int;
-    }
-}
-
-gboolean
-_gdk_device_get_history (GdkDevice         *device,
-			 GdkWindow         *window,
-			 guint32            start,
-			 guint32            stop,
-			 GdkTimeCoord    ***events,
-			 gint              *n_events)
-{
-  g_warning ("gdk_device_get_history() called for invalid device");
-  return FALSE;
-}
-
-void
-_gdk_input_select_events (GdkWindow        *impl_window,
-			  GdkDevicePrivate *gdkdev)
-{
-}
-
-gboolean
-_gdk_input_other_event (GdkEvent *event, 
-			XEvent *xevent, 
-			GdkWindow *window)
-{
-  return FALSE;
-}
-
-void
-_gdk_input_configure_event (XConfigureEvent *xevent,
-			    GdkWindow       *window)
-{
-}
-
-void 
-_gdk_input_crossing_event (GdkWindow *window,
-			   gboolean enter)
-{
-}
-
-gint 
-_gdk_input_grab_pointer (GdkWindow *     window,
-			 GdkWindow      *native_window,
-			 gint            owner_events,
-			 GdkEventMask    event_mask,
-			 GdkWindow *     confine_to,
-			 guint32         time)
-{
-  return Success;
-}
-
-void
-_gdk_input_ungrab_pointer (GdkDisplay *display,
-			   guint32     time)
-{
-}
-
-gboolean
-gdk_device_set_mode (GdkDevice   *device,
-		     GdkInputMode mode)
-{
-  return FALSE;
-}
-
-#define __GDK_INPUT_NONE_C__
-#include "gdkaliasdef.c"
diff --git a/gdk/x11/gdkinput-x11.c b/gdk/x11/gdkinput-x11.c
deleted file mode 100644
index a793a42..0000000
--- a/gdk/x11/gdkinput-x11.c
+++ /dev/null
@@ -1,927 +0,0 @@
-/* GDK - The GIMP Drawing Kit
- * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * 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.
- */
-
-/*
- * Modified by the GTK+ Team and others 1997-2000.  See the AUTHORS
- * file for a list of people on the GTK+ Team.  See the ChangeLog
- * files for a list of changes.  These files are distributed with
- * GTK+ at ftp://ftp.gtk.org/pub/gtk/.
- */
-
-#include "config.h"
-#include "gdkinputprivate.h"
-#include "gdkinternals.h"
-#include "gdkx.h"
-#include "gdk.h"		/* For gdk_error_trap_push()/pop() */
-#include "gdkdisplay-x11.h"
-#include "gdkalias.h"
-
-#include <string.h>
-
-/* Forward declarations */
-static GdkDevicePrivate *gdk_input_device_new            (GdkDisplay       *display,
-							  XDeviceInfo      *device,
-							  gint              include_core);
-static void              gdk_input_translate_coordinates (GdkDevicePrivate *gdkdev,
-							  GdkWindow        *window,
-							  gint             *axis_data,
-							  gdouble          *axis_out,
-							  gdouble          *x_out,
-							  gdouble          *y_out);
-static void              gdk_input_update_axes           (GdkDevicePrivate *gdkdev,
-							  gint             axes_count,
-							  gint             first_axis,
-							  gint             *axis_data);
-static guint             gdk_input_translate_state       (guint             state,
-							  guint             device_state);
-
-GdkDevicePrivate *
-_gdk_input_find_device (GdkDisplay *display,
-			guint32     id)
-{
-  GList *tmp_list = GDK_DISPLAY_X11 (display)->input_devices;
-  GdkDevicePrivate *gdkdev;
-  while (tmp_list)
-    {
-      gdkdev = (GdkDevicePrivate *)(tmp_list->data);
-      if (gdkdev->deviceid == id)
-	return gdkdev;
-      tmp_list = tmp_list->next;
-    }
-  return NULL;
-}
-
-void
-_gdk_input_get_root_relative_geometry (GdkWindow *window,
-				       int *x_ret, int *y_ret)
-{
-  Window child;
-  gint x,y;
-
-  XTranslateCoordinates (GDK_WINDOW_XDISPLAY (window),
-			 GDK_WINDOW_XWINDOW (window),
-			 GDK_WINDOW_XROOTWIN (window),
-			 0, 0, &x, &y, &child);
-
-  if (x_ret)
-    *x_ret = x;
-  if (y_ret)
-    *y_ret = y;
-}
-
-static GdkDevicePrivate *
-gdk_input_device_new (GdkDisplay  *display,
-		      XDeviceInfo *device,
-		      gint         include_core)
-{
-  GdkDevicePrivate *gdkdev;
-  gchar *tmp_name;
-  XAnyClassPtr class;
-  gint i,j;
-
-  gdkdev = g_object_new (GDK_TYPE_DEVICE, NULL);
-
-  gdkdev->deviceid = device->id;
-  gdkdev->display = display;
-
-  if (device->name[0])
-    gdkdev->info.name = g_strdup (device->name);
- else
-   /* XFree86 3.2 gives an empty name to the default core devices,
-      (fixed in 3.2A) */
-   gdkdev->info.name = g_strdup ("pointer");
-
-  gdkdev->info.mode = GDK_MODE_DISABLED;
-
-  /* Try to figure out what kind of device this is by its name -
-     could invite a very, very, long list... Lowercase name
-     for comparison purposes */
-
-  tmp_name = g_ascii_strdown (gdkdev->info.name, -1);
-
-  if (strstr (tmp_name, "eraser"))
-    gdkdev->info.source = GDK_SOURCE_ERASER;
-  else if (strstr (tmp_name, "cursor"))
-    gdkdev->info.source = GDK_SOURCE_CURSOR;
-  else if (strstr (tmp_name, "wacom") ||
-	   strstr (tmp_name, "pen"))
-    gdkdev->info.source = GDK_SOURCE_PEN;
-  else
-    gdkdev->info.source = GDK_SOURCE_MOUSE;
-
-  g_free(tmp_name);
-
-  gdkdev->xdevice = NULL;
-
-  /* step through the classes */
-
-  gdkdev->info.num_axes = 0;
-  gdkdev->info.num_keys = 0;
-  gdkdev->info.axes = NULL;
-  gdkdev->info.keys = NULL;
-  gdkdev->axes = 0;
-  gdkdev->info.has_cursor = 0;
-  gdkdev->needs_update = FALSE;
-  gdkdev->claimed = FALSE;
-  memset(gdkdev->button_state, 0, sizeof (gdkdev->button_state));
-  gdkdev->button_count = 0;
-
-  class = device->inputclassinfo;
-  for (i=0;i<device->num_classes;i++)
-    {
-      switch (class->class) {
-      case ButtonClass:
-	break;
-      case KeyClass:
-	{
-	  XKeyInfo *xki = (XKeyInfo *)class;
-	  /* Hack to catch XFree86 3.3.1 bug. Other devices better
-	   * not have exactly 25 keys...
-	   */
-	  if ((xki->min_keycode == 8) && (xki->max_keycode == 32))
-	    {
-	      gdkdev->info.num_keys = 32;
-	      gdkdev->min_keycode = 1;
-	    }
-	  else
-	    {
-	      gdkdev->info.num_keys = xki->max_keycode - xki->min_keycode + 1;
-	      gdkdev->min_keycode = xki->min_keycode;
-	    }
-	  gdkdev->info.keys = g_new (GdkDeviceKey, gdkdev->info.num_keys);
-
-	  for (j=0; j<gdkdev->info.num_keys; j++)
-	    {
-	      gdkdev->info.keys[j].keyval = 0;
-	      gdkdev->info.keys[j].modifiers = 0;
-	    }
-
-	  break;
-	}
-      case ValuatorClass:
-	{
-	  XValuatorInfo *xvi = (XValuatorInfo *)class;
-	  gdkdev->info.num_axes = xvi->num_axes;
-	  gdkdev->axes = g_new (GdkAxisInfo, xvi->num_axes);
-	  gdkdev->axis_data = g_new0 (gint, xvi->num_axes);
-	  gdkdev->info.axes = g_new0 (GdkDeviceAxis, xvi->num_axes);
-	  for (j=0;j<xvi->num_axes;j++)
-	    {
-	      gdkdev->axes[j].resolution =
-		gdkdev->axes[j].xresolution = xvi->axes[j].resolution;
-	      gdkdev->axes[j].min_value =
-		gdkdev->axes[j].xmin_value = xvi->axes[j].min_value;
-	      gdkdev->axes[j].max_value =
-		gdkdev->axes[j].xmax_value = xvi->axes[j].max_value;
-	      gdkdev->info.axes[j].use = GDK_AXIS_IGNORE;
-	    }
-	  j=0;
-	  if (j<xvi->num_axes)
-	    gdk_device_set_axis_use (&gdkdev->info, j++, GDK_AXIS_X);
-	  if (j<xvi->num_axes)
-	    gdk_device_set_axis_use (&gdkdev->info, j++, GDK_AXIS_Y);
-	  if (j<xvi->num_axes)
-	    gdk_device_set_axis_use (&gdkdev->info, j++, GDK_AXIS_PRESSURE);
-	  if (j<xvi->num_axes)
-	    gdk_device_set_axis_use (&gdkdev->info, j++, GDK_AXIS_XTILT);
-	  if (j<xvi->num_axes)
-	    gdk_device_set_axis_use (&gdkdev->info, j++, GDK_AXIS_YTILT);
-	  if (j<xvi->num_axes)
-	    gdk_device_set_axis_use (&gdkdev->info, j++, GDK_AXIS_WHEEL);
-
-	  break;
-	}
-      }
-      class = (XAnyClassPtr)(((char *)class) + class->length);
-    }
-  /* return NULL if no axes */
-  if (!gdkdev->info.num_axes || !gdkdev->axes ||
-      (!include_core && device->use == IsXPointer))
-    goto error;
-
-  if (device->use != IsXPointer)
-    {
-      gdk_error_trap_push ();
-      gdkdev->xdevice = XOpenDevice (GDK_DISPLAY_XDISPLAY (display),
-				     gdkdev->deviceid);
-
-      /* return NULL if device is not ready */
-      if (gdk_error_trap_pop ())
-	goto error;
-    }
-
-  gdkdev->buttonpress_type = 0;
-  gdkdev->buttonrelease_type = 0;
-  gdkdev->keypress_type = 0;
-  gdkdev->keyrelease_type = 0;
-  gdkdev->motionnotify_type = 0;
-  gdkdev->proximityin_type = 0;
-  gdkdev->proximityout_type = 0;
-  gdkdev->changenotify_type = 0;
-
-  return gdkdev;
-
- error:
-
-  g_object_unref (gdkdev);
-
-  return NULL;
-}
-
-void
-_gdk_input_common_find_events (GdkDevicePrivate *gdkdev,
-			       gint mask,
-			       XEventClass *classes,
-			       int *num_classes)
-{
-  gint i;
-  XEventClass class;
-
-  i = 0;
-  if (mask & GDK_BUTTON_PRESS_MASK)
-    {
-      DeviceButtonPress (gdkdev->xdevice, gdkdev->buttonpress_type,
-			     class);
-      if (class != 0)
-	  classes[i++] = class;
-      DeviceButtonPressGrab (gdkdev->xdevice, 0, class);
-      if (class != 0)
-	  classes[i++] = class;
-    }
-  if (mask & GDK_BUTTON_RELEASE_MASK)
-    {
-      DeviceButtonRelease (gdkdev->xdevice, gdkdev->buttonrelease_type,
-			   class);
-      if (class != 0)
-	  classes[i++] = class;
-    }
-  if (mask & (GDK_POINTER_MOTION_MASK |
-	      GDK_BUTTON1_MOTION_MASK | GDK_BUTTON2_MOTION_MASK |
-	      GDK_BUTTON3_MOTION_MASK | GDK_BUTTON_MOTION_MASK))
-    {
-      DeviceMotionNotify (gdkdev->xdevice, gdkdev->motionnotify_type, class);
-      if (class != 0)
-	  classes[i++] = class;
-      DeviceStateNotify (gdkdev->xdevice, gdkdev->devicestatenotify_type, class);
-      if (class != 0)
-	  classes[i++] = class;
-    }
-  if (mask & GDK_KEY_PRESS_MASK)
-    {
-      DeviceKeyPress (gdkdev->xdevice, gdkdev->keypress_type, class);
-      if (class != 0)
-	  classes[i++] = class;
-    }
-  if (mask & GDK_KEY_RELEASE_MASK)
-    {
-      DeviceKeyRelease (gdkdev->xdevice, gdkdev->keyrelease_type, class);
-      if (class != 0)
-	  classes[i++] = class;
-    }
-  if (mask & GDK_PROXIMITY_IN_MASK)
-    {
-      ProximityIn   (gdkdev->xdevice, gdkdev->proximityin_type, class);
-      if (class != 0)
-	  classes[i++] = class;
-    }
-  if (mask & GDK_PROXIMITY_OUT_MASK)
-    {
-      ProximityOut  (gdkdev->xdevice, gdkdev->proximityout_type, class);
-      if (class != 0)
-	  classes[i++] = class;
-    }
-
-  *num_classes = i;
-}
-
-void
-_gdk_input_select_events (GdkWindow *impl_window,
-			  GdkDevicePrivate *gdkdev)
-{
-  XEventClass classes[GDK_MAX_DEVICE_CLASSES];
-  gint num_classes;
-  guint event_mask;
-  GdkWindowObject *w;
-  GdkInputWindow *iw;
-  GList *l;
-
-  event_mask = 0;
-  iw = ((GdkWindowObject *)impl_window)->input_window;
-
-  if (gdkdev->info.mode != GDK_MODE_DISABLED &&
-      iw != NULL)
-    {
-      for (l = iw->windows; l != NULL; l = l->next)
-	{
-	  w = l->data;
-	  if (gdkdev->info.has_cursor || (w->extension_events & GDK_ALL_DEVICES_MASK))
-	    event_mask |= w->extension_events;
-	}
-    }
-  event_mask &= ~GDK_ALL_DEVICES_MASK;
-
-  if (event_mask)
-    event_mask |= GDK_PROXIMITY_OUT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK;
-
-  _gdk_input_common_find_events (gdkdev, event_mask,
-				 classes, &num_classes);
-  XSelectExtensionEvent (GDK_WINDOW_XDISPLAY (impl_window),
-			 GDK_WINDOW_XWINDOW (impl_window),
-			 classes, num_classes);
-}
-
-gint
-_gdk_input_common_init (GdkDisplay *display,
-			gint        include_core)
-{
-  XDeviceInfo   *devices;
-  int num_devices, loop;
-  int ignore, event_base;
-  GdkDisplayX11 *display_x11 = GDK_DISPLAY_X11 (display);
-
-  /* Init XInput extension */
-
-  display_x11->input_devices = NULL;
-  if (XQueryExtension (display_x11->xdisplay, "XInputExtension",
-		       &ignore, &event_base, &ignore))
-    {
-      gdk_x11_register_standard_event_type (display,
-					    event_base, 15 /* Number of events */);
-
-      devices = XListInputDevices(display_x11->xdisplay, &num_devices);
-
-      for(loop=0; loop<num_devices; loop++)
-	{
-	  GdkDevicePrivate *gdkdev = gdk_input_device_new(display,
-							  &devices[loop],
-							  include_core);
-	  if (gdkdev)
-	    display_x11->input_devices = g_list_append(display_x11->input_devices, gdkdev);
-	}
-      XFreeDeviceList(devices);
-    }
-
-  display_x11->input_devices = g_list_append (display_x11->input_devices, display->core_pointer);
-
-  return TRUE;
-}
-
-static void
-gdk_input_update_axes (GdkDevicePrivate *gdkdev,
-		       gint             axes_count,
-		       gint             first_axis,
-		       gint             *axis_data)
-{
-  int i;
-  g_return_if_fail (first_axis >= 0 && first_axis + axes_count <= gdkdev->info.num_axes);
-
-  for (i = 0; i < axes_count; i++)
-    gdkdev->axis_data[first_axis + i] = axis_data[i];
-}
-
-static void
-gdk_input_translate_coordinates (GdkDevicePrivate *gdkdev,
-				 GdkWindow        *window,
-				 gint             *axis_data,
-				 gdouble          *axis_out,
-				 gdouble          *x_out,
-				 gdouble          *y_out)
-{
-  GdkWindowObject *priv, *impl_window;
-
-  int i;
-  int x_axis = 0;
-  int y_axis = 0;
-
-  double device_width, device_height, x_min, y_min;
-  double x_offset, y_offset, x_scale, y_scale;
-
-  priv = (GdkWindowObject *) window;
-  impl_window = (GdkWindowObject *)_gdk_window_get_impl_window (window);
-
-  for (i=0; i<gdkdev->info.num_axes; i++)
-    {
-      switch (gdkdev->info.axes[i].use)
-	{
-	case GDK_AXIS_X:
-	  x_axis = i;
-	  break;
-	case GDK_AXIS_Y:
-	  y_axis = i;
-	  break;
-	default:
-	  break;
-	}
-    }
-
-  device_width = gdkdev->axes[x_axis].max_value - gdkdev->axes[x_axis].min_value;
-  if (device_width > 0)
-    {
-      x_min = gdkdev->axes[x_axis].min_value;
-    }
-  else
-    {
-      device_width = gdk_screen_get_width (gdk_drawable_get_screen (window));
-      x_min = 0;
-    }
-
-  device_height = gdkdev->axes[y_axis].max_value - gdkdev->axes[y_axis].min_value;
-  if (device_height > 0)
-    {
-      y_min = gdkdev->axes[y_axis].min_value;
-    }
-  else
-    {
-      device_height = gdk_screen_get_height (gdk_drawable_get_screen (window));
-      y_min = 0;
-    }
-
-  if (gdkdev->info.mode == GDK_MODE_SCREEN)
-    {
-      x_scale = gdk_screen_get_width (gdk_drawable_get_screen (window)) / device_width;
-      y_scale = gdk_screen_get_height (gdk_drawable_get_screen (window)) / device_height;
-
-      x_offset = - impl_window->input_window->root_x - priv->abs_x;
-      y_offset = - impl_window->input_window->root_y - priv->abs_y;
-    }
-  else				/* GDK_MODE_WINDOW */
-    {
-      double x_resolution = gdkdev->axes[x_axis].resolution;
-      double y_resolution = gdkdev->axes[y_axis].resolution;
-      double device_aspect;
-      /*
-       * Some drivers incorrectly report the resolution of the device
-       * as zero (in partiular linuxwacom < 0.5.3 with usb tablets).
-       * This causes the device_aspect to become NaN and totally
-       * breaks windowed mode.  If this is the case, the best we can
-       * do is to assume the resolution is non-zero is equal in both
-       * directions (which is true for many devices).  The absolute
-       * value of the resolution doesn't matter since we only use the
-       * ratio.
-       */
-      if ((x_resolution == 0) || (y_resolution == 0))
-	{
-	  x_resolution = 1;
-	  y_resolution = 1;
-	}
-      device_aspect = (device_height*y_resolution) /
-	(device_width*x_resolution);
-      if (device_aspect * priv->width >= priv->height)
-	{
-	  /* device taller than window */
-	  x_scale = priv->width / device_width;
-	  y_scale = (x_scale * x_resolution) / y_resolution;
-
-	  x_offset = 0;
-	  y_offset = -(device_height * y_scale -  priv->height)/2;
-	}
-      else
-	{
-	  /* window taller than device */
-	  y_scale = priv->height / device_height;
-	  x_scale = (y_scale * y_resolution) / x_resolution;
-
-	  y_offset = 0;
-	  x_offset = - (device_width * x_scale - priv->width)/2;
-	}
-    }
-
-  for (i=0; i<gdkdev->info.num_axes; i++)
-    {
-      switch (gdkdev->info.axes[i].use)
-	{
-	case GDK_AXIS_X:
-	  axis_out[i] = x_offset + x_scale * (axis_data[x_axis] - x_min);
-	  if (x_out)
-	    *x_out = axis_out[i];
-	  break;
-	case GDK_AXIS_Y:
-	  axis_out[i] = y_offset + y_scale * (axis_data[y_axis] - y_min);
-	  if (y_out)
-	    *y_out = axis_out[i];
-	  break;
-	default:
-	  axis_out[i] =
-	    (gdkdev->info.axes[i].max * (axis_data[i] - gdkdev->axes[i].min_value) +
-	     gdkdev->info.axes[i].min * (gdkdev->axes[i].max_value - axis_data[i])) /
-	    (gdkdev->axes[i].max_value - gdkdev->axes[i].min_value);
-	  break;
-	}
-    }
-}
-
-/* combine the state of the core device and the device state
- * into one - for now we do this in a simple-minded manner -
- * we just take the keyboard portion of the core device and
- * the button portion (all of?) the device state.
- * Any button remapping should go on here.
- */
-static guint
-gdk_input_translate_state(guint state, guint device_state)
-{
-  return device_state | (state & 0xFF);
-}
-
-
-gboolean
-_gdk_input_common_other_event (GdkEvent         *event,
-			       XEvent           *xevent,
-			       GdkWindow        *window,
-			       GdkDevicePrivate *gdkdev)
-{
-  GdkWindowObject *priv, *impl_window;
-  GdkInputWindow *input_window;
-
-  priv = (GdkWindowObject *) window;
-  impl_window = (GdkWindowObject *)_gdk_window_get_impl_window (window);
-  input_window = impl_window->input_window;
-
-  if ((xevent->type == gdkdev->buttonpress_type) ||
-      (xevent->type == gdkdev->buttonrelease_type))
-    {
-      XDeviceButtonEvent *xdbe = (XDeviceButtonEvent *)(xevent);
-
-      g_return_val_if_fail (xdbe->button < 256, FALSE);
-      if (xdbe->type == gdkdev->buttonpress_type)
-	{
-	  event->button.type = GDK_BUTTON_PRESS;
-	  if (!(gdkdev->button_state[xdbe->button/8] & 1 << (xdbe->button%8)))
-	    {
-	      gdkdev->button_state[xdbe->button/8] |= 1 << (xdbe->button%8);
-	      gdkdev->button_count++;
-	    }
-	}
-      else
-	{
-	  event->button.type = GDK_BUTTON_RELEASE;
-	  if (gdkdev->button_state[xdbe->button/8] & 1 << (xdbe->button%8))
-	    {
-	      gdkdev->button_state[xdbe->button/8] &= ~(1 << (xdbe->button%8));
-	      gdkdev->button_count--;
-	    }
-	}
-      event->button.device = &gdkdev->info;
-      event->button.window = window;
-      event->button.time = xdbe->time;
-
-      event->button.axes = g_new (gdouble, gdkdev->info.num_axes);
-      gdk_input_update_axes (gdkdev, xdbe->axes_count, xdbe->first_axis,
-			     xdbe->axis_data);
-      gdk_input_translate_coordinates (gdkdev, window, gdkdev->axis_data,
-				       event->button.axes,
-				       &event->button.x, &event->button.y);
-      event->button.x_root = event->button.x + priv->abs_x + input_window->root_x;
-      event->button.y_root = event->button.y + priv->abs_y + input_window->root_y;
-      event->button.state = gdk_input_translate_state (xdbe->state,xdbe->device_state);
-      event->button.button = xdbe->button;
-
-      if (event->button.type == GDK_BUTTON_PRESS)
-	_gdk_event_button_generate (gdk_drawable_get_display (event->button.window),
-				    event);
-
-      GDK_NOTE (EVENTS,
-	g_print ("button %s:\t\twindow: %ld  device: %ld  x,y: %f %f  button: %d\n",
-		 (event->button.type == GDK_BUTTON_PRESS) ? "press" : "release",
-		 xdbe->window,
-		 xdbe->deviceid,
-		 event->button.x, event->button.y,
-		 xdbe->button));
-
-      /* Update the timestamp of the latest user interaction, if the event has
-       * a valid timestamp.
-       */
-      if (gdk_event_get_time (event) != GDK_CURRENT_TIME)
-	gdk_x11_window_set_user_time (gdk_window_get_toplevel (window),
-				      gdk_event_get_time (event));
-      return TRUE;
-  }
-
-  if ((xevent->type == gdkdev->keypress_type) ||
-      (xevent->type == gdkdev->keyrelease_type))
-    {
-      XDeviceKeyEvent *xdke = (XDeviceKeyEvent *)(xevent);
-
-      GDK_NOTE (EVENTS,
-	g_print ("device key %s:\twindow: %ld  device: %ld  keycode: %d\n",
-		 (event->key.type == GDK_KEY_PRESS) ? "press" : "release",
-		 xdke->window,
-		 xdke->deviceid,
-		 xdke->keycode));
-
-      if (xdke->keycode < gdkdev->min_keycode ||
-	  xdke->keycode >= gdkdev->min_keycode + gdkdev->info.num_keys)
-	{
-	  g_warning ("Invalid device key code received");
-	  return FALSE;
-	}
-
-      event->key.keyval = gdkdev->info.keys[xdke->keycode - gdkdev->min_keycode].keyval;
-
-      if (event->key.keyval == 0)
-	{
-	  GDK_NOTE (EVENTS,
-	    g_print ("\t\ttranslation - NONE\n"));
-
-	  return FALSE;
-	}
-
-      event->key.type = (xdke->type == gdkdev->keypress_type) ?
-	GDK_KEY_PRESS : GDK_KEY_RELEASE;
-
-      event->key.window = window;
-      event->key.time = xdke->time;
-
-      event->key.state = gdk_input_translate_state(xdke->state, xdke->device_state)
-	| gdkdev->info.keys[xdke->keycode - gdkdev->min_keycode].modifiers;
-
-      /* Add a string translation for the key event */
-      if ((event->key.keyval >= 0x20) && (event->key.keyval <= 0xFF))
-	{
-	  event->key.length = 1;
-	  event->key.string = g_new (gchar, 2);
-	  event->key.string[0] = (gchar)event->key.keyval;
-	  event->key.string[1] = 0;
-	}
-      else
-	{
-	  event->key.length = 0;
-	  event->key.string = g_new0 (gchar, 1);
-	}
-
-      GDK_NOTE (EVENTS,
-	g_print ("\t\ttranslation - keyval: %d modifiers: %#x\n",
-		 event->key.keyval,
-		 event->key.state));
-
-      /* Update the timestamp of the latest user interaction, if the event has
-       * a valid timestamp.
-       */
-      if (gdk_event_get_time (event) != GDK_CURRENT_TIME)
-	gdk_x11_window_set_user_time (gdk_window_get_toplevel (window),
-				      gdk_event_get_time (event));
-      return TRUE;
-    }
-
-  if (xevent->type == gdkdev->motionnotify_type)
-    {
-      XDeviceMotionEvent *xdme = (XDeviceMotionEvent *)(xevent);
-
-      event->motion.device = &gdkdev->info;
-
-      event->motion.axes = g_new (gdouble, gdkdev->info.num_axes);
-      gdk_input_update_axes (gdkdev, xdme->axes_count, xdme->first_axis, xdme->axis_data);
-      gdk_input_translate_coordinates(gdkdev, window, gdkdev->axis_data,
-				      event->motion.axes,
-				      &event->motion.x,&event->motion.y);
-      event->motion.x_root = event->motion.x + priv->abs_x + input_window->root_x;
-      event->motion.y_root = event->motion.y + priv->abs_y + input_window->root_y;
-
-      event->motion.type = GDK_MOTION_NOTIFY;
-      event->motion.window = window;
-      event->motion.time = xdme->time;
-      event->motion.state = gdk_input_translate_state(xdme->state,
-						      xdme->device_state);
-      event->motion.is_hint = xdme->is_hint;
-
-      GDK_NOTE (EVENTS,
-	g_print ("motion notify:\t\twindow: %ld  device: %ld  x,y: %f %f  state %#4x  hint: %s\n",
-		 xdme->window,
-		 xdme->deviceid,
-		 event->motion.x, event->motion.y,
-		 event->motion.state,
-		 (xdme->is_hint) ? "true" : "false"));
-
-
-      /* Update the timestamp of the latest user interaction, if the event has
-       * a valid timestamp.
-       */
-      if (gdk_event_get_time (event) != GDK_CURRENT_TIME)
-	gdk_x11_window_set_user_time (gdk_window_get_toplevel (window),
-				      gdk_event_get_time (event));
-      return TRUE;
-    }
-
-  if (xevent->type == gdkdev->devicestatenotify_type)
-    {
-      int i;
-      XDeviceStateNotifyEvent *xdse = (XDeviceStateNotifyEvent *)(xevent);
-      XInputClass *input_class = (XInputClass *)xdse->data;
-      for (i=0; i<xdse->num_classes; i++)
-	{
-	  if (input_class->class == ValuatorClass)
-	    gdk_input_update_axes (gdkdev, gdkdev->info.num_axes, 0,
-				   ((XValuatorState *)input_class)->valuators);
-	  input_class = (XInputClass *)(((char *)input_class)+input_class->length);
-	}
-
-      GDK_NOTE (EVENTS,
-	g_print ("device state notify:\t\twindow: %ld  device: %ld\n",
-		 xdse->window,
-		 xdse->deviceid));
-
-      return FALSE;
-    }
-  if (xevent->type == gdkdev->proximityin_type ||
-      xevent->type == gdkdev->proximityout_type)
-    {
-      XProximityNotifyEvent *xpne = (XProximityNotifyEvent *)(xevent);
-
-      event->proximity.device = &gdkdev->info;
-      event->proximity.type = (xevent->type == gdkdev->proximityin_type)?
-	GDK_PROXIMITY_IN:GDK_PROXIMITY_OUT;
-      event->proximity.window = window;
-      event->proximity.time = xpne->time;
-
-      /* Update the timestamp of the latest user interaction, if the event has
-       * a valid timestamp.
-       */
-      if (gdk_event_get_time (event) != GDK_CURRENT_TIME)
-	gdk_x11_window_set_user_time (gdk_window_get_toplevel (window),
-				      gdk_event_get_time (event));
-      return TRUE;
-  }
-
-  return FALSE;			/* wasn't one of our event types */
-}
-
-gboolean
-_gdk_input_common_event_selected (GdkEvent         *event,
-				  GdkWindow        *window,
-				  GdkDevicePrivate *gdkdev)
-{
-  GdkWindowObject *priv = (GdkWindowObject *) window;
-
-  switch (event->type) {
-    case GDK_BUTTON_PRESS:
-      return priv->extension_events & GDK_BUTTON_PRESS_MASK;
-
-    case GDK_BUTTON_RELEASE:
-      return priv->extension_events & GDK_BUTTON_RELEASE_MASK;
-
-    case GDK_KEY_PRESS:
-      return priv->extension_events & GDK_KEY_PRESS_MASK;
-
-    case GDK_KEY_RELEASE:
-      return priv->extension_events & GDK_KEY_RELEASE_MASK;
-
-    case GDK_MOTION_NOTIFY:
-      if (priv->extension_events & GDK_POINTER_MOTION_MASK)
-	return TRUE;
-      if (gdkdev->button_count && (priv->extension_events & GDK_BUTTON_MOTION_MASK))
-	return TRUE;
-
-      if ((gdkdev->button_state[0] & 1 << 1) && (priv->extension_events & GDK_BUTTON1_MOTION_MASK))
-	return TRUE;
-      if ((gdkdev->button_state[0] & 1 << 2) && (priv->extension_events & GDK_BUTTON2_MOTION_MASK))
-	return TRUE;
-      if ((gdkdev->button_state[0] & 1 << 3) && (priv->extension_events & GDK_BUTTON3_MOTION_MASK))
-	return TRUE;
-
-      return FALSE;
-
-    case GDK_PROXIMITY_IN:
-	  return priv->extension_events & GDK_PROXIMITY_IN_MASK;
-
-    case GDK_PROXIMITY_OUT:
-	  return priv->extension_events & GDK_PROXIMITY_OUT_MASK;
-
-    default:
-      return FALSE;
-  }
-
-
-}
-
-
-gboolean
-_gdk_device_get_history (GdkDevice         *device,
-			 GdkWindow         *window,
-			 guint32            start,
-			 guint32            stop,
-			 GdkTimeCoord    ***events,
-			 gint              *n_events)
-{
-  GdkTimeCoord **coords;
-  XDeviceTimeCoord *device_coords;
-  GdkWindow *impl_window;
-  GdkDevicePrivate *gdkdev;
-  gint mode_return;
-  gint axis_count_return;
-  gint i;
-
-  gdkdev = (GdkDevicePrivate *)device;
-
-  impl_window = _gdk_window_get_impl_window (window);
-
-  device_coords = XGetDeviceMotionEvents (GDK_WINDOW_XDISPLAY (impl_window),
-					  gdkdev->xdevice,
-					  start, stop,
-					  n_events, &mode_return,
-					  &axis_count_return);
-
-  if (device_coords)
-    {
-      coords = _gdk_device_allocate_history (device, *n_events);
-
-      for (i = 0; i < *n_events; i++)
-	{
-	  coords[i]->time = device_coords[i].time;
-
-	  gdk_input_translate_coordinates (gdkdev, window,
-					   device_coords[i].data,
-					   coords[i]->axes, NULL, NULL);
-	}
-
-      XFreeDeviceMotionEvents (device_coords);
-
-      *events = coords;
-
-      return TRUE;
-    }
-  else
-    return FALSE;
-}
-
-void
-gdk_device_get_state (GdkDevice       *device,
-		      GdkWindow       *window,
-		      gdouble         *axes,
-		      GdkModifierType *mask)
-{
-  gint i;
-
-  g_return_if_fail (device != NULL);
-  g_return_if_fail (GDK_IS_WINDOW (window));
-
-  if (GDK_IS_CORE (device))
-    {
-      gint x_int, y_int;
-
-      gdk_window_get_pointer (window, &x_int, &y_int, mask);
-
-      if (axes)
-	{
-	  axes[0] = x_int;
-	  axes[1] = y_int;
-	}
-    }
-  else
-    {
-      GdkDevicePrivate *gdkdev;
-      XDeviceState *state;
-      XInputClass *input_class;
-
-      if (mask)
-	gdk_window_get_pointer (window, NULL, NULL, mask);
-
-      gdkdev = (GdkDevicePrivate *)device;
-
-      state = XQueryDeviceState (GDK_WINDOW_XDISPLAY (window),
-				 gdkdev->xdevice);
-      input_class = state->data;
-      for (i=0; i<state->num_classes; i++)
-	{
-	  switch (input_class->class)
-	    {
-	    case ValuatorClass:
-	      if (axes)
-		gdk_input_translate_coordinates (gdkdev, window,
-						 ((XValuatorState *)input_class)->valuators,
-						 axes, NULL, NULL);
-	      break;
-
-	    case ButtonClass:
-	      if (mask)
-		{
-		  *mask &= 0xFF;
-		  if (((XButtonState *)input_class)->num_buttons > 0)
-		    *mask |= ((XButtonState *)input_class)->buttons[0] << 7;
-		  /* GDK_BUTTON1_MASK = 1 << 8, and button n is stored
-		   * in bit 1<<(n%8) in byte n/8. n = 1,2,... */
-		}
-	      break;
-	    }
-	  input_class = (XInputClass *)(((char *)input_class)+input_class->length);
-	}
-      XFreeDeviceState (state);
-    }
-}
-
-#define __GDK_INPUT_X11_C__
-#include "gdkaliasdef.c"
diff --git a/gdk/x11/gdkinput-xfree.c b/gdk/x11/gdkinput-xfree.c
deleted file mode 100644
index d0f217c..0000000
--- a/gdk/x11/gdkinput-xfree.c
+++ /dev/null
@@ -1,421 +0,0 @@
-/* GDK - The GIMP Drawing Kit
- * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * 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.
- */
-
-#include "config.h"
-#include "gdkinputprivate.h"
-#include "gdkdisplay-x11.h"
-#include "gdkalias.h"
-
-/*
- * Modified by the GTK+ Team and others 1997-2000.  See the AUTHORS
- * file for a list of people on the GTK+ Team.  See the ChangeLog
- * files for a list of changes.  These files are distributed with
- * GTK+ at ftp://ftp.gtk.org/pub/gtk/.
- */
-
-/* forward declarations */
-
-static void gdk_input_check_proximity (GdkDisplay *display);
-
-void
-_gdk_input_init(GdkDisplay *display)
-{
-  _gdk_init_input_core (display);
-  display->ignore_core_events = FALSE;
-  _gdk_input_common_init (display, FALSE);
-}
-
-gboolean
-gdk_device_set_mode (GdkDevice      *device,
-		     GdkInputMode    mode)
-{
-  GList *tmp_list;
-  GdkDevicePrivate *gdkdev;
-  GdkInputWindow *input_window;
-  GdkDisplayX11 *display_impl;
-
-  if (GDK_IS_CORE (device))
-    return FALSE;
-
-  gdkdev = (GdkDevicePrivate *)device;
-
-  if (device->mode == mode)
-    return TRUE;
-
-  device->mode = mode;
-
-  if (mode == GDK_MODE_WINDOW)
-    device->has_cursor = FALSE;
-  else if (mode == GDK_MODE_SCREEN)
-    device->has_cursor = TRUE;
-
-  display_impl = GDK_DISPLAY_X11 (gdkdev->display);
-  for (tmp_list = display_impl->input_windows; tmp_list; tmp_list = tmp_list->next)
-    {
-      input_window = (GdkInputWindow *)tmp_list->data;
-      _gdk_input_select_events (input_window->impl_window, gdkdev);
-    }
-
-  return TRUE;
-}
-
-static void
-gdk_input_check_proximity (GdkDisplay *display)
-{
-  GdkDisplayX11 *display_impl = GDK_DISPLAY_X11 (display);
-  GList *tmp_list = display_impl->input_devices;
-  gint new_proximity = 0;
-
-  while (tmp_list && !new_proximity)
-    {
-      GdkDevicePrivate *gdkdev = (GdkDevicePrivate *)(tmp_list->data);
-
-      if (gdkdev->info.mode != GDK_MODE_DISABLED
-	  && !GDK_IS_CORE (gdkdev)
-	  && gdkdev->xdevice)
-	{
-	  XDeviceState *state = XQueryDeviceState(display_impl->xdisplay,
-						  gdkdev->xdevice);
-	  XInputClass *xic;
-	  int i;
-
-	  xic = state->data;
-	  for (i=0; i<state->num_classes; i++)
-	    {
-	      if (xic->class == ValuatorClass)
-		{
-		  XValuatorState *xvs = (XValuatorState *)xic;
-		  if ((xvs->mode & ProximityState) == InProximity)
-		    {
-		      new_proximity = TRUE;
-		    }
-		  break;
-		}
-	      xic = (XInputClass *)((char *)xic + xic->length);
-	    }
-
-	  XFreeDeviceState (state);
-	}
-      tmp_list = tmp_list->next;
-    }
-
-  display->ignore_core_events = new_proximity;
-}
-
-void
-_gdk_input_configure_event (XConfigureEvent *xevent,
-			    GdkWindow       *window)
-{
-  GdkWindowObject *priv = (GdkWindowObject *)window;
-  GdkInputWindow *input_window;
-  gint root_x, root_y;
-
-  input_window = priv->input_window;
-  if (input_window != NULL)
-    {
-      _gdk_input_get_root_relative_geometry (window, &root_x, &root_y);
-      input_window->root_x = root_x;
-      input_window->root_y = root_y;
-    }
-}
-
-void
-_gdk_input_crossing_event (GdkWindow *window,
-			   gboolean enter)
-{
-  GdkDisplay *display = GDK_WINDOW_DISPLAY (window);
-  GdkDisplayX11 *display_impl = GDK_DISPLAY_X11 (display);
-  GdkWindowObject *priv = (GdkWindowObject *)window;
-  GdkInputWindow *input_window;
-  gint root_x, root_y;
-
-  if (enter)
-    {
-      gdk_input_check_proximity(display);
-
-      input_window = priv->input_window;
-      if (input_window != NULL)
-	{
-	  _gdk_input_get_root_relative_geometry (window, &root_x, &root_y);
-	  input_window->root_x = root_x;
-	  input_window->root_y = root_y;
-	}
-    }
-  else
-    display->ignore_core_events = FALSE;
-}
-
-static GdkEventType
-get_input_event_type (GdkDevicePrivate *gdkdev,
-		      XEvent *xevent,
-		      int *core_x, int *core_y)
-{
-  if (xevent->type == gdkdev->buttonpress_type)
-    {
-      XDeviceButtonEvent *xie = (XDeviceButtonEvent *)(xevent);
-      *core_x = xie->x;
-      *core_y = xie->y;
-      return GDK_BUTTON_PRESS;
-    }
-
-  if (xevent->type == gdkdev->buttonrelease_type)
-    {
-      XDeviceButtonEvent *xie = (XDeviceButtonEvent *)(xevent);
-      *core_x = xie->x;
-      *core_y = xie->y;
-      return GDK_BUTTON_RELEASE;
-    }
-
-  if (xevent->type == gdkdev->keypress_type)
-    {
-      XDeviceKeyEvent *xie = (XDeviceKeyEvent *)(xevent);
-      *core_x = xie->x;
-      *core_y = xie->y;
-      return GDK_KEY_PRESS;
-    }
-
-  if (xevent->type == gdkdev->keyrelease_type)
-    {
-      XDeviceKeyEvent *xie = (XDeviceKeyEvent *)(xevent);
-      *core_x = xie->x;
-      *core_y = xie->y;
-      return GDK_KEY_RELEASE;
-    }
-
-  if (xevent->type == gdkdev->motionnotify_type)
-    {
-      XDeviceMotionEvent *xie = (XDeviceMotionEvent *)(xevent);
-      *core_x = xie->x;
-      *core_y = xie->y;
-      return GDK_MOTION_NOTIFY;
-    }
-
-  if (xevent->type == gdkdev->proximityin_type)
-    {
-      XProximityNotifyEvent *xie = (XProximityNotifyEvent *)(xevent);
-      *core_x = xie->x;
-      *core_y = xie->y;
-      return GDK_PROXIMITY_IN;
-    }
-
-  if (xevent->type == gdkdev->proximityout_type)
-    {
-      XProximityNotifyEvent *xie = (XProximityNotifyEvent *)(xevent);
-      *core_x = xie->x;
-      *core_y = xie->y;
-      return GDK_PROXIMITY_OUT;
-    }
-
-  *core_x = 0;
-  *core_y = 0;
-  return GDK_NOTHING;
-}
-
-
-gboolean
-_gdk_input_other_event (GdkEvent *event,
-			XEvent *xevent,
-			GdkWindow *event_window)
-{
-  GdkWindow *window;
-  GdkWindowObject *priv;
-  GdkInputWindow *iw;
-  GdkDevicePrivate *gdkdev;
-  GdkEventType event_type;
-  int x, y;
-  GdkDisplay *display = GDK_WINDOW_DISPLAY (event_window);
-  GdkDisplayX11 *display_impl = GDK_DISPLAY_X11 (display);
-
-  /* This is a sort of a hack, as there isn't any XDeviceAnyEvent -
-     but it's potentially faster than scanning through the types of
-     every device. If we were deceived, then it won't match any of
-     the types for the device anyways */
-  gdkdev = _gdk_input_find_device (display,
-				   ((XDeviceButtonEvent *)xevent)->deviceid);
-  if (!gdkdev)
-    return FALSE;			/* we don't handle it - not an XInput event */
-
-  event_type = get_input_event_type (gdkdev, xevent, &x, &y);
-  if (event_type == GDK_NOTHING)
-    return FALSE;
-
-  /* If we're not getting any event window its likely because we're outside the
-     window and there is no grab. We should still report according to the
-     implicit grab though. */
-  iw = ((GdkWindowObject *)event_window)->input_window;
-
-  if (iw->button_down_window)
-    window = iw->button_down_window;
-  else
-    window = _gdk_window_get_input_window_for_event (event_window,
-                                                     event_type,
-                                                     x, y,
-                                                     xevent->xany.serial);
-  priv = (GdkWindowObject *)window;
-  if (window == NULL)
-    return FALSE;
-
-  if (gdkdev->info.mode == GDK_MODE_DISABLED ||
-      priv->extension_events == 0 ||
-      !(gdkdev->info.has_cursor || (priv->extension_events & GDK_ALL_DEVICES_MASK)))
-    return FALSE;
-
-  if (!display->ignore_core_events && priv->extension_events != 0)
-    gdk_input_check_proximity (GDK_WINDOW_DISPLAY (window));
-
-  if (!_gdk_input_common_other_event (event, xevent, window, gdkdev))
-    return FALSE;
-
-  if (event->type == GDK_BUTTON_PRESS)
-    iw->button_down_window = window;
-  if (event->type == GDK_BUTTON_RELEASE && !gdkdev->button_count)
-    iw->button_down_window = NULL;
-
-  if (event->type == GDK_PROXIMITY_OUT &&
-      display->ignore_core_events)
-    gdk_input_check_proximity (GDK_WINDOW_DISPLAY (window));
-
-  return _gdk_input_common_event_selected(event, window, gdkdev);
-}
-
-gint
-_gdk_input_grab_pointer (GdkWindow      *window,
-			 GdkWindow      *native_window, /* This is the toplevel */
-			 gint            owner_events,
-			 GdkEventMask    event_mask,
-			 GdkWindow *     confine_to,
-			 guint32         time)
-{
-  GdkInputWindow *input_window;
-  GdkWindowObject *priv, *impl_window;
-  gboolean need_ungrab;
-  GdkDevicePrivate *gdkdev;
-  GList *tmp_list;
-  XEventClass event_classes[GDK_MAX_DEVICE_CLASSES];
-  gint num_classes;
-  gint result;
-  GdkDisplayX11 *display_impl  = GDK_DISPLAY_X11 (GDK_WINDOW_DISPLAY (window));
-
-  tmp_list = display_impl->input_windows;
-  need_ungrab = FALSE;
-
-  while (tmp_list)
-    {
-      input_window = (GdkInputWindow *)tmp_list->data;
-
-      if (input_window->grabbed)
-	{
-	  input_window->grabbed = FALSE;
-	  need_ungrab = TRUE;
-	  break;
-	}
-      tmp_list = tmp_list->next;
-    }
-
-  priv = (GdkWindowObject *)window;
-  impl_window = (GdkWindowObject *)_gdk_window_get_impl_window (window);
-  input_window = impl_window->input_window;
-  if (priv->extension_events)
-    {
-      g_assert (input_window != NULL);
-      input_window->grabbed = TRUE;
-
-      tmp_list = display_impl->input_devices;
-      while (tmp_list)
-	{
-	  gdkdev = (GdkDevicePrivate *)tmp_list->data;
-	  if (!GDK_IS_CORE (gdkdev) && gdkdev->xdevice)
-	    {
-	      _gdk_input_common_find_events (gdkdev, event_mask,
-					     event_classes, &num_classes);
-#ifdef G_ENABLE_DEBUG
-	      if (_gdk_debug_flags & GDK_DEBUG_NOGRABS)
-		result = GrabSuccess;
-	      else
-#endif
-		result = XGrabDevice (display_impl->xdisplay, gdkdev->xdevice,
-				      GDK_WINDOW_XWINDOW (native_window),
-				      owner_events, num_classes, event_classes,
-				      GrabModeAsync, GrabModeAsync, time);
-
-	      /* FIXME: if failure occurs on something other than the first
-		 device, things will be badly inconsistent */
-	      if (result != Success)
-		return result;
-	    }
-	  tmp_list = tmp_list->next;
-	}
-    }
-  else
-    {
-      tmp_list = display_impl->input_devices;
-      while (tmp_list)
-	{
-	  gdkdev = (GdkDevicePrivate *)tmp_list->data;
-	  if (!GDK_IS_CORE (gdkdev) && gdkdev->xdevice &&
-	      ((gdkdev->button_count != 0) || need_ungrab))
-	    {
-	      XUngrabDevice (display_impl->xdisplay, gdkdev->xdevice, time);
-	      memset(gdkdev->button_state, 0, sizeof (gdkdev->button_state));
-	      gdkdev->button_count = 0;
-	    }
-
-	  tmp_list = tmp_list->next;
-	}
-    }
-
-  return Success;
-}
-
-void
-_gdk_input_ungrab_pointer (GdkDisplay *display,
-			   guint32 time)
-{
-  GdkInputWindow *input_window = NULL; /* Quiet GCC */
-  GdkDevicePrivate *gdkdev;
-  GList *tmp_list;
-  GdkDisplayX11 *display_impl = GDK_DISPLAY_X11 (display);
-
-  tmp_list = display_impl->input_windows;
-  while (tmp_list)
-    {
-      input_window = (GdkInputWindow *)tmp_list->data;
-      if (input_window->grabbed)
-	break;
-      tmp_list = tmp_list->next;
-    }
-
-  if (tmp_list)			/* we found a grabbed window */
-    {
-      input_window->grabbed = FALSE;
-
-      tmp_list = display_impl->input_devices;
-      while (tmp_list)
-	{
-	  gdkdev = (GdkDevicePrivate *)tmp_list->data;
-	  if (!GDK_IS_CORE (gdkdev) && gdkdev->xdevice)
-	    XUngrabDevice( display_impl->xdisplay, gdkdev->xdevice, time);
-
-	  tmp_list = tmp_list->next;
-	}
-    }
-}
-
-#define __GDK_INPUT_XFREE_C__
-#include "gdkaliasdef.c"
diff --git a/gdk/x11/gdkinput.c b/gdk/x11/gdkinput.c
index 25f93dc..cb6cd7d 100644
--- a/gdk/x11/gdkinput.c
+++ b/gdk/x11/gdkinput.c
@@ -27,115 +27,23 @@
 #include "config.h"
 
 #include <stdlib.h>
-#include <X11/Xlib.h>
-#include <X11/Xutil.h>
 
-#include "gdkx.h"
-#include "gdkinput.h"
-#include "gdkprivate.h"
-#include "gdkinputprivate.h"
 #include "gdkscreen-x11.h"
 #include "gdkdisplay-x11.h"
+#include "gdkwindow.h"
 #include "gdkalias.h"
 
-static GdkDeviceAxis gdk_input_core_axes[] = {
-  { GDK_AXIS_X, 0, 0 },
-  { GDK_AXIS_Y, 0, 0 }
-};
-
-void
-_gdk_init_input_core (GdkDisplay *display)
-{
-  GdkDevicePrivate *private;
-
-  display->core_pointer = g_object_new (GDK_TYPE_DEVICE, NULL);
-  private = (GdkDevicePrivate *)display->core_pointer;
-
-  display->core_pointer->name = "Core Pointer";
-  display->core_pointer->source = GDK_SOURCE_MOUSE;
-  display->core_pointer->mode = GDK_MODE_SCREEN;
-  display->core_pointer->has_cursor = TRUE;
-  display->core_pointer->num_axes = 2;
-  display->core_pointer->axes = gdk_input_core_axes;
-  display->core_pointer->num_keys = 0;
-  display->core_pointer->keys = NULL;
-
-  private->display = display;
-}
-
-static void gdk_device_class_init (GdkDeviceClass *klass);
-static void gdk_device_dispose    (GObject        *object);
-
-static gpointer gdk_device_parent_class = NULL;
-
-GType
-gdk_device_get_type (void)
-{
-  static GType object_type = 0;
-
-  if (!object_type)
-    {
-      static const GTypeInfo object_info =
-	{
-	  sizeof (GdkDeviceClass),
-	  (GBaseInitFunc) NULL,
-	  (GBaseFinalizeFunc) NULL,
-	  (GClassInitFunc) gdk_device_class_init,
-	  NULL,           /* class_finalize */
-	  NULL,           /* class_data */
-	  sizeof (GdkDevicePrivate),
-	  0,              /* n_preallocs */
-	  (GInstanceInitFunc) NULL,
-	};
-
-      object_type = g_type_register_static (G_TYPE_OBJECT,
-					    g_intern_static_string ("GdkDevice"),
-					    &object_info, 0);
-    }
+/* Addition used for extension_events mask */
+#define GDK_ALL_DEVICES_MASK (1<<30)
 
-  return object_type;
-}
-
-static void
-gdk_device_class_init (GdkDeviceClass *klass)
+struct _GdkInputWindow
 {
-  GObjectClass *object_class = G_OBJECT_CLASS (klass);
-
-  gdk_device_parent_class = g_type_class_peek_parent (klass);
+  GList *windows; /* GdkWindow:s with extension_events set */
 
-  object_class->dispose  = gdk_device_dispose;
-}
-
-static void
-gdk_device_dispose (GObject *object)
-{
-  GdkDevicePrivate *gdkdev = (GdkDevicePrivate *) object;
-
-  if (gdkdev->display && !GDK_IS_CORE (gdkdev))
-    {
-#ifndef XINPUT_NONE
-      if (gdkdev->xdevice)
-	{
-	  XCloseDevice (GDK_DISPLAY_XDISPLAY (gdkdev->display), gdkdev->xdevice);
-	  gdkdev->xdevice = NULL;
-	}
-      g_free (gdkdev->axes);
-      g_free (gdkdev->axis_data);
-      gdkdev->axes = NULL;
-      gdkdev->axis_data = NULL;
-#endif /* !XINPUT_NONE */
-
-      g_free (gdkdev->info.name);
-      g_free (gdkdev->info.keys);
-      g_free (gdkdev->info.axes);
-
-      gdkdev->info.name = NULL;
-      gdkdev->info.keys = NULL;
-      gdkdev->info.axes = NULL;
-    }
+  /* gdk window */
+  GdkWindow *impl_window; /* an impl window */
+};
 
-  G_OBJECT_CLASS (gdk_device_parent_class)->dispose (object);
-}
 
 /**
  * gdk_devices_list:
@@ -151,216 +59,46 @@ gdk_devices_list (void)
   return gdk_display_list_devices (gdk_display_get_default ());
 }
 
-/**
- * gdk_display_list_devices:
- * @display: a #GdkDisplay
- *
- * Returns the list of available input devices attached to @display.
- * The list is statically allocated and should not be freed.
- *
- * Return value: a list of #GdkDevice
- *
- * Since: 2.2
- **/
-GList *
-gdk_display_list_devices (GdkDisplay *display)
-{
-  g_return_val_if_fail (GDK_IS_DISPLAY (display), NULL);
-
-  return GDK_DISPLAY_X11 (display)->input_devices;
-}
-
-void
-gdk_device_set_source (GdkDevice      *device,
-		       GdkInputSource  source)
-{
-  g_return_if_fail (device != NULL);
-
-  device->source = source;
-}
-
-void
-gdk_device_set_key (GdkDevice      *device,
-		    guint           index,
-		    guint           keyval,
-		    GdkModifierType modifiers)
-{
-  g_return_if_fail (device != NULL);
-  g_return_if_fail (index < device->num_keys);
-
-  device->keys[index].keyval = keyval;
-  device->keys[index].modifiers = modifiers;
-}
-
-void
-gdk_device_set_axis_use (GdkDevice   *device,
-			 guint        index,
-			 GdkAxisUse   use)
+static void
+_gdk_input_select_device_events (GdkWindow *impl_window,
+                                 GdkDevice *device)
 {
-  g_return_if_fail (device != NULL);
-  g_return_if_fail (index < device->num_axes);
-
-  device->axes[index].use = use;
+  guint event_mask;
+  GdkWindowObject *w;
+  GdkInputWindow *iw;
+  GdkInputMode mode;
+  gboolean has_cursor;
+  GdkDeviceType type;
+  GList *l;
+
+  event_mask = 0;
+  iw = ((GdkWindowObject *)impl_window)->input_window;
+
+  g_object_get (device,
+                "type", &type,
+                "input-mode", &mode,
+                "has-cursor", &has_cursor,
+                NULL);
+
+  if (iw == NULL ||
+      mode == GDK_MODE_DISABLED ||
+      type == GDK_DEVICE_TYPE_MASTER)
+    return;
 
-  switch (use)
+  for (l = iw->windows; l != NULL; l = l->next)
     {
-    case GDK_AXIS_X:
-    case GDK_AXIS_Y:
-      device->axes[index].min = 0.;
-      device->axes[index].max = 0.;
-      break;
-    case GDK_AXIS_XTILT:
-    case GDK_AXIS_YTILT:
-      device->axes[index].min = -1.;
-      device->axes[index].max = 1;
-      break;
-    default:
-      device->axes[index].min = 0.;
-      device->axes[index].max = 1;
-      break;
-    }
-}
+      w = l->data;
 
-static gboolean
-impl_coord_in_window (GdkWindow *window,
-		      int impl_x,
-		      int impl_y)
-{
-  GdkWindowObject *priv = (GdkWindowObject *)window;
-
-  if (impl_x < priv->abs_x ||
-      impl_x > priv->abs_x + priv->width)
-    return FALSE;
-  if (impl_y < priv->abs_y ||
-      impl_y > priv->abs_y + priv->height)
-    return FALSE;
-  return TRUE;
-}
+      if (has_cursor || (w->extension_events & GDK_ALL_DEVICES_MASK))
+        {
+          event_mask = w->extension_events;
 
-/**
- * gdk_device_get_history:
- * @device: a #GdkDevice
- * @window: the window with respect to which which the event coordinates will be reported
- * @start: starting timestamp for range of events to return
- * @stop: ending timestamp for the range of events to return
- * @events: location to store a newly-allocated array of #GdkTimeCoord, or %NULL
- * @n_events: location to store the length of @events, or %NULL
- *
- * Obtains the motion history for a device; given a starting and
- * ending timestamp, return all events in the motion history for
- * the device in the given range of time. Some windowing systems
- * do not support motion history, in which case, %FALSE will
- * be returned. (This is not distinguishable from the case where
- * motion history is supported and no events were found.)
- *
- * Return value: %TRUE if the windowing system supports motion history and
- *  at least one event was found.
- **/
-gboolean
-gdk_device_get_history  (GdkDevice         *device,
-			 GdkWindow         *window,
-			 guint32            start,
-			 guint32            stop,
-			 GdkTimeCoord    ***events,
-			 gint              *n_events)
-{
-  GdkTimeCoord **coords = NULL;
-  GdkWindow *impl_window;
-  gboolean result = FALSE;
-  int tmp_n_events = 0;
-
-  g_return_val_if_fail (GDK_WINDOW_IS_X11 (window), FALSE);
-
-  impl_window = _gdk_window_get_impl_window (window);
-
-  if (GDK_WINDOW_DESTROYED (window))
-    /* Nothing */ ;
-  else if (GDK_IS_CORE (device))
-    {
-      XTimeCoord *xcoords;
+          if (event_mask)
+            event_mask |= GDK_PROXIMITY_OUT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK;
 
-      xcoords = XGetMotionEvents (GDK_DRAWABLE_XDISPLAY (window),
-				  GDK_DRAWABLE_XID (impl_window),
-				  start, stop, &tmp_n_events);
-      if (xcoords)
-	{
-	  GdkWindowObject *priv = (GdkWindowObject *)window;
-          int i, j;
-
-	  coords = _gdk_device_allocate_history (device, tmp_n_events);
-	  j = 0;
-
-	  for (i = 0; i < tmp_n_events; i++)
-	    {
-	      if (impl_coord_in_window (window, xcoords[i].x, xcoords[i].y))
-		{
-		  coords[j]->time = xcoords[i].time;
-		  coords[j]->axes[0] = xcoords[i].x - priv->abs_x;
-		  coords[j]->axes[1] = xcoords[i].y - priv->abs_y;
-		  j++;
-		}
-	    }
-
-	  XFree (xcoords);
-
-          /* free the events we allocated too much */
-          for (i = j; i < tmp_n_events; i++)
-            {
-              g_free (coords[i]);
-              coords[i] = NULL;
-            }
-
-          tmp_n_events = j;
-
-          if (tmp_n_events > 0)
-            {
-              result = TRUE;
-            }
-          else
-            {
-              gdk_device_free_history (coords, tmp_n_events);
-              coords = NULL;
-            }
-	}
+          gdk_window_set_device_events ((GdkWindow *) w, device, event_mask);
+        }
     }
-  else
-    result = _gdk_device_get_history (device, window, start, stop, &coords, &tmp_n_events);
-
-  if (n_events)
-    *n_events = tmp_n_events;
-
-  if (events)
-    *events = coords;
-  else if (coords)
-    gdk_device_free_history (coords, tmp_n_events);
-
-  return result;
-}
-
-GdkTimeCoord **
-_gdk_device_allocate_history (GdkDevice *device,
-			      gint       n_events)
-{
-  GdkTimeCoord **result = g_new (GdkTimeCoord *, n_events);
-  gint i;
-
-  for (i=0; i<n_events; i++)
-    result[i] = g_malloc (sizeof (GdkTimeCoord) -
-			  sizeof (double) * (GDK_MAX_TIMECOORD_AXES - device->num_axes));
-
-  return result;
-}
-
-void
-gdk_device_free_history (GdkTimeCoord **events,
-			 gint           n_events)
-{
-  gint i;
-
-  for (i=0; i<n_events; i++)
-    g_free (events[i]);
-
-  g_free (events);
 }
 
 static void
@@ -431,15 +169,8 @@ gdk_input_set_extension_events (GdkWindow *window, gint mask,
 	  iw->impl_window = (GdkWindow *)impl_window;
 
 	  iw->windows = NULL;
-	  iw->grabbed = FALSE;
 
 	  display_x11->input_windows = g_list_append (display_x11->input_windows, iw);
-
-#ifndef XINPUT_NONE
-	  /* we might not receive ConfigureNotify so get the root_relative_geometry
-	   * now, just in case */
-	  _gdk_input_get_root_relative_geometry (window, &iw->root_x, &iw->root_y);
-#endif /* !XINPUT_NONE */
 	  impl_window->input_window = iw;
 	}
 
@@ -454,10 +185,8 @@ gdk_input_set_extension_events (GdkWindow *window, gint mask,
 
   for (tmp_list = display_x11->input_devices; tmp_list; tmp_list = tmp_list->next)
     {
-      GdkDevicePrivate *gdkdev = tmp_list->data;
-
-      if (!GDK_IS_CORE (gdkdev))
-	_gdk_input_select_events ((GdkWindow *)impl_window, gdkdev);
+      GdkDevice *dev = tmp_list->data;
+      _gdk_input_select_device_events (GDK_WINDOW (impl_window), dev);
     }
 }
 
@@ -467,40 +196,20 @@ _gdk_input_window_destroy (GdkWindow *window)
   unset_extension_events (window);
 }
 
-/**
- * gdk_device_get_axis:
- * @device: a #GdkDevice
- * @axes: pointer to an array of axes
- * @use: the use to look for
- * @value: location to store the found value.
- *
- * Interprets an array of double as axis values for a given device,
- * and locates the value in the array for a given axis use.
- *
- * Return value: %TRUE if the given axis use was found, otherwise %FALSE
- **/
-gboolean
-gdk_device_get_axis (GdkDevice  *device,
-		     gdouble    *axes,
-		     GdkAxisUse  use,
-		     gdouble    *value)
+void
+_gdk_input_check_extension_events (GdkDevice *device)
 {
-  gint i;
-
-  g_return_val_if_fail (device != NULL, FALSE);
-
-  if (axes == NULL)
-    return FALSE;
+  GdkDisplayX11 *display_impl;
+  GdkInputWindow *input_window;
+  GList *tmp_list;
 
-  for (i=0; i<device->num_axes; i++)
-    if (device->axes[i].use == use)
-      {
-	if (value)
-	  *value = axes[i];
-	return TRUE;
-      }
+  display_impl = GDK_DISPLAY_X11 (gdk_device_get_display (device));
 
-  return FALSE;
+  for (tmp_list = display_impl->input_windows; tmp_list; tmp_list = tmp_list->next)
+    {
+      input_window = tmp_list->data;
+      _gdk_input_select_device_events (input_window->impl_window, device);
+    }
 }
 
 #define __GDK_INPUT_C__
diff --git a/gdk/x11/gdkinputprivate.h b/gdk/x11/gdkinputprivate.h
deleted file mode 100644
index 1b89230..0000000
--- a/gdk/x11/gdkinputprivate.h
+++ /dev/null
@@ -1,189 +0,0 @@
-/* GDK - The GIMP Drawing Kit
- * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * 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.
- */
-
-/*
- * Modified by the GTK+ Team and others 1997-2000.  See the AUTHORS
- * file for a list of people on the GTK+ Team.  See the ChangeLog
- * files for a list of changes.  These files are distributed with
- * GTK+ at ftp://ftp.gtk.org/pub/gtk/. 
- */
-
-#ifndef __GDK_INPUTPRIVATE_H__
-#define __GDK_INPUTPRIVATE_H__
-
-#include "config.h"
-#include "gdkinput.h"
-#include "gdkevents.h"
-#include "gdkx.h"
-
-#include <X11/Xlib.h>
-
-#ifndef XINPUT_NONE
-#include <X11/extensions/XInput.h>
-#endif
-
-
-typedef struct _GdkAxisInfo    GdkAxisInfo;
-typedef struct _GdkDevicePrivate GdkDevicePrivate;
-
-/* information about a device axis */
-struct _GdkAxisInfo
-{
-  /* reported x resolution */
-  gint xresolution;
-
-  /* reported x minimum/maximum values */
-  gint xmin_value, xmax_value;
-
-  /* calibrated resolution (for aspect ration) - only relative values
-     between axes used */
-  gint resolution;
-  
-  /* calibrated minimum/maximum values */
-  gint min_value, max_value;
-};
-
-#define GDK_INPUT_NUM_EVENTC 6
-
-struct _GdkDevicePrivate
-{
-  GdkDevice info;
-
-  guint32 deviceid;
-
-  GdkDisplay *display;
-  
-
-#ifndef XINPUT_NONE
-  /* information about the axes */
-  GdkAxisInfo *axes;
-  gint *axis_data;
-
-  /* Information about XInput device */
-  XDevice       *xdevice;
-
-  /* minimum key code for device */
-  gint min_keycode;	       
-
-  int buttonpress_type, buttonrelease_type, keypress_type,
-      keyrelease_type, motionnotify_type, proximityin_type, 
-      proximityout_type, changenotify_type, devicestatenotify_type;
-
-  /* true if we need to select a different set of events, but
-     can't because this is the core pointer */
-  gint needs_update;
-
-  /* Mask of buttons (used for button grabs) */
-  char button_state[32];
-  gint button_count;
-
-  /* true if we've claimed the device as active. (used only for XINPUT_GXI) */
-  gint claimed;
-#endif /* !XINPUT_NONE */
-};
-
-struct _GdkDeviceClass
-{
-  GObjectClass parent_class;
-};
-
-/* Addition used for extension_events mask */
-#define GDK_ALL_DEVICES_MASK (1<<30)
-
-struct _GdkInputWindow
-{
-  GList *windows; /* GdkWindow:s with extension_events set */
-
-  /* gdk window */
-  GdkWindow *impl_window; /* an impl window */
-  GdkWindow *button_down_window;
-
-  /* position relative to root window */
-  gint root_x;
-  gint root_y;
-
-  /* Is there a pointer grab for this window ? */
-  gint grabbed;
-};
-
-/* Global data */
-
-#define GDK_IS_CORE(d) (((GdkDevice *)(d)) == ((GdkDevicePrivate *)(d))->display->core_pointer)
-
-/* Function declarations */
-
-GdkInputWindow *_gdk_input_window_find       (GdkWindow *window);
-void            _gdk_input_window_destroy    (GdkWindow *window);
-GdkTimeCoord ** _gdk_device_allocate_history (GdkDevice *device,
-					      gint       n_events);
-void            _gdk_init_input_core         (GdkDisplay *display);
-
-/* The following functions are provided by each implementation
- * (xfree, gxi, and none)
- */
-void             _gdk_input_configure_event  (XConfigureEvent  *xevent,
-					      GdkWindow        *window);
-void             _gdk_input_crossing_event   (GdkWindow        *window,
-					      gboolean          enter);
-gboolean         _gdk_input_other_event      (GdkEvent         *event,
-					      XEvent           *xevent,
-					      GdkWindow        *window);
-gint             _gdk_input_grab_pointer     (GdkWindow        *window,
-					      GdkWindow        *native_window,
-					      gint              owner_events,
-					      GdkEventMask      event_mask,
-					      GdkWindow        *confine_to,
-					      guint32           time);
-void             _gdk_input_ungrab_pointer   (GdkDisplay       *display,
-					      guint32           time);
-gboolean         _gdk_device_get_history     (GdkDevice         *device,
-					      GdkWindow         *window,
-					      guint32            start,
-					      guint32            stop,
-					      GdkTimeCoord    ***events,
-					      gint              *n_events);
-
-#ifndef XINPUT_NONE
-
-#define GDK_MAX_DEVICE_CLASSES 13
-
-gint               _gdk_input_common_init               (GdkDisplay	  *display,
-							 gint              include_core);
-GdkDevicePrivate * _gdk_input_find_device               (GdkDisplay	  *display,
-							 guint32           id);
-void               _gdk_input_get_root_relative_geometry(GdkWindow        *window,
-							 int              *x_ret,
-							 int              *y_ret);
-void               _gdk_input_common_find_events        (GdkDevicePrivate *gdkdev,
-							 gint              mask,
-							 XEventClass      *classes,
-							 int              *num_classes);
-void               _gdk_input_select_events             (GdkWindow        *impl_window,
-							 GdkDevicePrivate *gdkdev);
-gint               _gdk_input_common_other_event        (GdkEvent         *event,
-							 XEvent           *xevent,
-							 GdkWindow        *window,
-							 GdkDevicePrivate *gdkdev);
-gboolean	   _gdk_input_common_event_selected     (GdkEvent         *event,
-							 GdkWindow        *window,
-							 GdkDevicePrivate *gdkdev);
-
-#endif /* !XINPUT_NONE */
-
-#endif /* __GDK_INPUTPRIVATE_H__ */
diff --git a/gdk/x11/gdkmain-x11.c b/gdk/x11/gdkmain-x11.c
index f12c709..7d1a667 100644
--- a/gdk/x11/gdkmain-x11.c
+++ b/gdk/x11/gdkmain-x11.c
@@ -47,11 +47,13 @@
 #include "gdkasync.h"
 #include "gdkdisplay-x11.h"
 #include "gdkinternals.h"
+#include "gdkprivate-x11.h"
 #include "gdkintl.h"
 #include "gdkregion-generic.h"
-#include "gdkinputprivate.h"
 #include "gdkalias.h"
 
+#include <gdk/gdkdeviceprivate.h>
+
 typedef struct _GdkPredicate  GdkPredicate;
 typedef struct _GdkErrorTrap  GdkErrorTrap;
 
@@ -116,7 +118,7 @@ gdk_get_use_xshm (void)
   return GDK_DISPLAY_X11 (gdk_display_get_default ())->use_xshm;
 }
 
-static GdkGrabStatus
+GdkGrabStatus
 gdk_x11_convert_grab_status (gint status)
 {
   switch (status)
@@ -143,171 +145,46 @@ has_pointer_grab_callback (GdkDisplay *display,
 			   gpointer data,
 			   gulong serial)
 {
-  _gdk_display_pointer_grab_update (display, serial);
-}
-
-GdkGrabStatus
-_gdk_windowing_pointer_grab (GdkWindow *window,
-			     GdkWindow *native,
-			     gboolean owner_events,
-			     GdkEventMask event_mask,
-			     GdkWindow *confine_to,
-			     GdkCursor *cursor,
-			     guint32 time)
-{
-  gint return_val;
-  GdkCursorPrivate *cursor_private;
-  GdkDisplayX11 *display_x11;
-  guint xevent_mask;
-  Window xwindow;
-  Window xconfine_to;
-  Cursor xcursor;
-  int i;
-
-  if (confine_to)
-    confine_to = _gdk_window_get_impl_window (confine_to);
-
-  display_x11 = GDK_DISPLAY_X11 (GDK_WINDOW_DISPLAY (native));
-
-  cursor_private = (GdkCursorPrivate*) cursor;
-
-  xwindow = GDK_WINDOW_XID (native);
-
-  if (!confine_to || GDK_WINDOW_DESTROYED (confine_to))
-    xconfine_to = None;
-  else
-    xconfine_to = GDK_WINDOW_XID (confine_to);
-
-  if (!cursor)
-    xcursor = None;
-  else
-    {
-      _gdk_x11_cursor_update_theme (cursor);
-      xcursor = cursor_private->xcursor;
-    }
-
-  xevent_mask = 0;
-  for (i = 0; i < _gdk_nenvent_masks; i++)
-    {
-      if (event_mask & (1 << (i + 1)))
-	xevent_mask |= _gdk_event_mask_table[i];
-    }
-
-  /* We don't want to set a native motion hint mask, as we're emulating motion
-   * hints. If we set a native one we just wouldn't get any events.
-   */
-  xevent_mask &= ~PointerMotionHintMask;
-
-  return_val = _gdk_input_grab_pointer (window,
-					native,
-					owner_events,
-					event_mask,
-					confine_to,
-					time);
+  GdkDevice *device = data;
 
-  if (return_val == GrabSuccess ||
-      G_UNLIKELY (!display_x11->trusted_client && return_val == AlreadyGrabbed))
-    {
-      if (!GDK_WINDOW_DESTROYED (native))
-	{
-#ifdef G_ENABLE_DEBUG
-	  if (_gdk_debug_flags & GDK_DEBUG_NOGRABS)
-	    return_val = GrabSuccess;
-	  else
-#endif
-	    return_val = XGrabPointer (GDK_WINDOW_XDISPLAY (native),
-				       xwindow,
-				       owner_events,
-				       xevent_mask,
-				       GrabModeAsync, GrabModeAsync,
-				       xconfine_to,
-				       xcursor,
-				       time);
-	}
-      else
-	return_val = AlreadyGrabbed;
-    }
-
-  if (return_val == GrabSuccess)
-    _gdk_x11_roundtrip_async (GDK_DISPLAY_OBJECT (display_x11),
-			      has_pointer_grab_callback,
-			      NULL);
-
-  return gdk_x11_convert_grab_status (return_val);
+  _gdk_display_device_grab_update (display, device, serial);
 }
 
-/*
- *--------------------------------------------------------------
- * gdk_keyboard_grab
- *
- *   Grabs the keyboard to a specific window
- *
- * Arguments:
- *   "window" is the window which will receive the grab
- *   "owner_events" specifies whether events will be reported as is,
- *     or relative to "window"
- *   "time" specifies the time
- *
- * Results:
- *
- * Side effects:
- *   requires a corresponding call to gdk_keyboard_ungrab
- *
- *--------------------------------------------------------------
- */
-
 GdkGrabStatus
-gdk_keyboard_grab (GdkWindow *	   window,
-		   gboolean	   owner_events,
-		   guint32	   time)
+_gdk_windowing_device_grab (GdkDevice    *device,
+                            GdkWindow    *window,
+                            GdkWindow    *native,
+                            gboolean      owner_events,
+                            GdkEventMask  event_mask,
+                            GdkWindow    *confine_to,
+                            GdkCursor    *cursor,
+                            guint32       time)
 {
-  gint return_val;
-  unsigned long serial;
   GdkDisplay *display;
-  GdkDisplayX11 *display_x11;
-  GdkWindow *native;
-
-  g_return_val_if_fail (window != NULL, 0);
-  g_return_val_if_fail (GDK_IS_WINDOW (window), 0);
-
-  native = gdk_window_get_toplevel (window);
-
-  /* TODO: What do we do for offscreens and  children? We need to proxy the grab somehow */
-  if (!GDK_IS_WINDOW_IMPL_X11 (GDK_WINDOW_OBJECT (native)->impl))
-    return GDK_GRAB_SUCCESS;
+  GdkGrabStatus status = GDK_GRAB_SUCCESS;
 
-  display = GDK_WINDOW_DISPLAY (native);
-  display_x11 = GDK_DISPLAY_X11 (display);
+  if (!window || GDK_WINDOW_DESTROYED (window))
+    return GDK_GRAB_NOT_VIEWABLE;
 
-  serial = NextRequest (GDK_WINDOW_XDISPLAY (native));
+  display = gdk_device_get_display (device);
 
-  if (!GDK_WINDOW_DESTROYED (native))
-    {
 #ifdef G_ENABLE_DEBUG
-      if (_gdk_debug_flags & GDK_DEBUG_NOGRABS)
-	return_val = GrabSuccess;
-      else
-#endif
-	return_val = XGrabKeyboard (GDK_WINDOW_XDISPLAY (native),
-				    GDK_WINDOW_XID (native),
-				    owner_events,
-				    GrabModeAsync, GrabModeAsync,
-				    time);
-	if (G_UNLIKELY (!display_x11->trusted_client && 
-			return_val == AlreadyGrabbed))
-	  /* we can't grab the keyboard, but we can do a GTK-local grab */
-	  return_val = GrabSuccess;
-    }
+  if (_gdk_debug_flags & GDK_DEBUG_NOGRABS)
+    status = GrabSuccess;
   else
-    return_val = AlreadyGrabbed;
-
-  if (return_val == GrabSuccess)
-    _gdk_display_set_has_keyboard_grab (display,
-					window,	native,
-					owner_events,
-					serial, time);
-
-  return gdk_x11_convert_grab_status (return_val);
+#endif
+    status = GDK_DEVICE_GET_CLASS (device)->grab (device,
+                                                  native,
+                                                  owner_events,
+                                                  event_mask,
+                                                  confine_to,
+                                                  cursor,
+                                                  time);
+  if (status == GDK_GRAB_SUCCESS)
+    _gdk_x11_roundtrip_async (display,
+			      has_pointer_grab_callback,
+                              device);
+  return status;
 }
 
 /**
@@ -325,21 +202,21 @@ _gdk_xgrab_check_unmap (GdkWindow *window,
 			gulong     serial)
 {
   GdkDisplay *display = gdk_drawable_get_display (window);
+  GdkDeviceManager *device_manager;
+  GList *devices, *d;
 
-  _gdk_display_end_pointer_grab (display, serial, window, TRUE);
+  device_manager = gdk_display_get_device_manager (display);
 
-  if (display->keyboard_grab.window &&
-      serial >= display->keyboard_grab.serial)
-    {
-      GdkWindowObject *private = GDK_WINDOW_OBJECT (window);
-      GdkWindowObject *tmp = GDK_WINDOW_OBJECT (display->keyboard_grab.window);
+  /* Get all devices */
+  devices = gdk_device_manager_get_devices (device_manager, GDK_DEVICE_TYPE_MASTER);
+  devices = g_list_concat (devices, gdk_device_manager_get_devices (device_manager, GDK_DEVICE_TYPE_SLAVE));
+  devices = g_list_concat (devices, gdk_device_manager_get_devices (device_manager, GDK_DEVICE_TYPE_FLOATING));
 
-      while (tmp && tmp != private)
-	tmp = tmp->parent;
+  /* End all grabs on the newly hidden window */
+  for (d = devices; d; d = d->next)
+    _gdk_display_end_device_grab (display, d->data, serial, window, TRUE);
 
-      if (tmp)
-	_gdk_display_unset_has_keyboard_grab (display, TRUE);
-    }
+  g_list_free (devices);
 }
 
 /**
@@ -353,25 +230,35 @@ void
 _gdk_xgrab_check_destroy (GdkWindow *window)
 {
   GdkDisplay *display = gdk_drawable_get_display (window);
-  GdkPointerGrabInfo *grab;
+  GdkDeviceManager *device_manager;
+  GdkDeviceGrabInfo *grab;
+  GList *devices, *d;
 
-  /* Make sure there is no lasting grab in this native
-     window */
-  grab = _gdk_display_get_last_pointer_grab (display);
-  if (grab && grab->native_window == window)
+  device_manager = gdk_display_get_device_manager (display);
+
+  /* Get all devices */
+  devices = gdk_device_manager_get_devices (device_manager, GDK_DEVICE_TYPE_MASTER);
+  devices = g_list_concat (devices, gdk_device_manager_get_devices (device_manager, GDK_DEVICE_TYPE_SLAVE));
+  devices = g_list_concat (devices, gdk_device_manager_get_devices (device_manager, GDK_DEVICE_TYPE_FLOATING));
+
+  for (d = devices; d; d = d->next)
     {
-      /* We don't know the actual serial to end, but it
-	 doesn't really matter as this only happens
-	 after we get told of the destroy from the
-	 server so we know its ended in the server,
-	 just make sure its ended. */
-      grab->serial_end = grab->serial_start;
-      grab->implicit_ungrab = TRUE;
+      /* Make sure there is no lasting grab in this native window */
+      grab = _gdk_display_get_last_device_grab (display, d->data);
+
+      if (grab && grab->native_window == window)
+        {
+          /* We don't know the actual serial to end, but it
+             doesn't really matter as this only happens
+             after we get told of the destroy from the
+             server so we know its ended in the server,
+             just make sure its ended. */
+          grab->serial_end = grab->serial_start;
+          grab->implicit_ungrab = TRUE;
+        }
     }
-  
-  if (window == display->keyboard_grab.native_window &&
-      display->keyboard_grab.window != NULL)
-    _gdk_display_unset_has_keyboard_grab (display, TRUE);
+
+  g_list_free (devices);
 }
 
 void
@@ -734,5 +621,16 @@ gdk_x11_get_default_xdisplay (void)
   return GDK_DISPLAY_XDISPLAY (gdk_display_get_default ());
 }
 
+void
+_gdk_windowing_event_data_copy (const GdkEvent *src,
+                                GdkEvent       *dst)
+{
+}
+
+void
+_gdk_windowing_event_data_free (GdkEvent *event)
+{
+}
+
 #define __GDK_MAIN_X11_C__
 #include "gdkaliasdef.c"
diff --git a/gdk/x11/gdkprivate-x11.h b/gdk/x11/gdkprivate-x11.h
index bfbf3d9..8bd23a6 100644
--- a/gdk/x11/gdkprivate-x11.h
+++ b/gdk/x11/gdkprivate-x11.h
@@ -173,8 +173,7 @@ void _gdk_x11_precache_atoms (GdkDisplay          *display,
 			      const gchar * const *atom_names,
 			      gint                 n_atoms);
 
-void _gdk_x11_events_init_screen   (GdkScreen *screen);
-void _gdk_x11_events_uninit_screen (GdkScreen *screen);
+void _gdk_screen_x11_events_init   (GdkScreen *screen);
 
 void _gdk_events_init           (GdkDisplay *display);
 void _gdk_events_uninit         (GdkDisplay *display);
@@ -182,7 +181,6 @@ void _gdk_windowing_window_init (GdkScreen *screen);
 void _gdk_visual_init           (GdkScreen *screen);
 void _gdk_dnd_init		(GdkDisplay *display);
 void _gdk_windowing_image_init  (GdkDisplay *display);
-void _gdk_input_init            (GdkDisplay *display);
 
 PangoRenderer *_gdk_x11_renderer_get (GdkDrawable *drawable,
 				      GdkGC       *gc);
@@ -194,6 +192,9 @@ gboolean _gdk_x11_get_xft_setting (GdkScreen   *screen,
 				   const gchar *name,
 				   GValue      *value);
 
+GdkGrabStatus gdk_x11_convert_grab_status (gint status);
+
+
 extern GdkDrawableClass  _gdk_x11_drawable_class;
 extern gboolean	         _gdk_use_xshm;
 extern const int         _gdk_nenvent_masks;
diff --git a/gdk/x11/gdkscreen-x11.c b/gdk/x11/gdkscreen-x11.c
index 1faa827..850fb59 100644
--- a/gdk/x11/gdkscreen-x11.c
+++ b/gdk/x11/gdkscreen-x11.c
@@ -51,6 +51,8 @@
 #include <X11/extensions/Xfixes.h>
 #endif
 
+#include "gdksettings.c"
+
 static void         gdk_screen_x11_dispose     (GObject		  *object);
 static void         gdk_screen_x11_finalize    (GObject		  *object);
 static void	    init_randr_support	       (GdkScreen	  *screen);
@@ -66,6 +68,14 @@ static guint signals[LAST_SIGNAL] = { 0 };
 
 G_DEFINE_TYPE (GdkScreenX11, _gdk_screen_x11, GDK_TYPE_SCREEN)
 
+typedef struct _NetWmSupportedAtoms NetWmSupportedAtoms;
+
+struct _NetWmSupportedAtoms
+{
+  Atom *atoms;
+  gulong n_atoms;
+};
+
 struct _GdkX11Monitor
 {
   GdkRectangle  geometry;
@@ -273,11 +283,23 @@ gdk_screen_set_default_colormap (GdkScreen   *screen,
 }
 
 static void
+_gdk_screen_x11_events_uninit (GdkScreen *screen)
+{
+  GdkScreenX11 *screen_x11 = GDK_SCREEN_X11 (screen);
+
+  if (screen_x11->xsettings_client)
+    {
+      xsettings_client_destroy (screen_x11->xsettings_client);
+      screen_x11->xsettings_client = NULL;
+    }
+}
+
+static void
 gdk_screen_x11_dispose (GObject *object)
 {
   GdkScreenX11 *screen_x11 = GDK_SCREEN_X11 (object);
 
-  _gdk_x11_events_uninit_screen (GDK_SCREEN (object));
+  _gdk_screen_x11_events_uninit (GDK_SCREEN (object));
 
   if (screen_x11->default_colormap)
     {
@@ -1360,5 +1382,637 @@ gdk_screen_get_window_stack (GdkScreen *screen)
   return ret;
 }
 
+/* Sends a ClientMessage to all toplevel client windows */
+static gboolean
+gdk_event_send_client_message_to_all_recurse (GdkDisplay *display,
+					      XEvent     *xev,
+					      guint32     xid,
+					      guint       level)
+{
+  Atom type = None;
+  int format;
+  unsigned long nitems, after;
+  unsigned char *data;
+  Window *ret_children, ret_root, ret_parent;
+  unsigned int ret_nchildren;
+  gboolean send = FALSE;
+  gboolean found = FALSE;
+  gboolean result = FALSE;
+  int i;
+
+  gdk_error_trap_push ();
+
+  if (XGetWindowProperty (GDK_DISPLAY_XDISPLAY (display), xid,
+			  gdk_x11_get_xatom_by_name_for_display (display, "WM_STATE"),
+			  0, 0, False, AnyPropertyType,
+			  &type, &format, &nitems, &after, &data) != Success)
+    goto out;
+
+  if (type)
+    {
+      send = TRUE;
+      XFree (data);
+    }
+  else
+    {
+      /* OK, we're all set, now let's find some windows to send this to */
+      if (!XQueryTree (GDK_DISPLAY_XDISPLAY (display), xid,
+		      &ret_root, &ret_parent,
+		      &ret_children, &ret_nchildren))
+	goto out;
+
+      for(i = 0; i < ret_nchildren; i++)
+	if (gdk_event_send_client_message_to_all_recurse (display, xev, ret_children[i], level + 1))
+	  found = TRUE;
+
+      XFree (ret_children);
+    }
+
+  if (send || (!found && (level == 1)))
+    {
+      xev->xclient.window = xid;
+      _gdk_send_xevent (display, xid, False, NoEventMask, xev);
+    }
+
+  result = send || found;
+
+ out:
+  gdk_error_trap_pop ();
+
+  return result;
+}
+
+/**
+ * gdk_screen_broadcast_client_message:
+ * @screen: the #GdkScreen where the event will be broadcasted.
+ * @event: the #GdkEvent.
+ *
+ * On X11, sends an X ClientMessage event to all toplevel windows on
+ * @screen.
+ *
+ * Toplevel windows are determined by checking for the WM_STATE property,
+ * as described in the Inter-Client Communication Conventions Manual (ICCCM).
+ * If no windows are found with the WM_STATE property set, the message is
+ * sent to all children of the root window.
+ *
+ * On Windows, broadcasts a message registered with the name
+ * GDK_WIN32_CLIENT_MESSAGE to all top-level windows. The amount of
+ * data is limited to one long, i.e. four bytes.
+ *
+ * Since: 2.2
+ */
+
+void
+gdk_screen_broadcast_client_message (GdkScreen *screen,
+				     GdkEvent  *event)
+{
+  XEvent sev;
+  GdkWindow *root_window;
+
+  g_return_if_fail (event != NULL);
+
+  root_window = gdk_screen_get_root_window (screen);
+
+  /* Set up our event to send, with the exception of its target window */
+  sev.xclient.type = ClientMessage;
+  sev.xclient.display = GDK_WINDOW_XDISPLAY (root_window);
+  sev.xclient.format = event->client.data_format;
+  memcpy(&sev.xclient.data, &event->client.data, sizeof (sev.xclient.data));
+  sev.xclient.message_type =
+    gdk_x11_atom_to_xatom_for_display (GDK_WINDOW_DISPLAY (root_window),
+				       event->client.message_type);
+
+  gdk_event_send_client_message_to_all_recurse (gdk_screen_get_display (screen),
+						&sev,
+						GDK_WINDOW_XID (root_window),
+						0);
+}
+
+static gboolean
+check_transform (const gchar *xsettings_name,
+		 GType        src_type,
+		 GType        dest_type)
+{
+  if (!g_value_type_transformable (src_type, dest_type))
+    {
+      g_warning ("Cannot transform xsetting %s of type %s to type %s\n",
+		 xsettings_name,
+		 g_type_name (src_type),
+		 g_type_name (dest_type));
+      return FALSE;
+    }
+  else
+    return TRUE;
+}
+
+/**
+ * gdk_screen_get_setting:
+ * @screen: the #GdkScreen where the setting is located
+ * @name: the name of the setting
+ * @value: location to store the value of the setting
+ *
+ * Retrieves a desktop-wide setting such as double-click time
+ * for the #GdkScreen @screen.
+ *
+ * FIXME needs a list of valid settings here, or a link to
+ * more information.
+ *
+ * Returns: %TRUE if the setting existed and a value was stored
+ *   in @value, %FALSE otherwise.
+ *
+ * Since: 2.2
+ **/
+gboolean
+gdk_screen_get_setting (GdkScreen   *screen,
+			const gchar *name,
+			GValue      *value)
+{
+
+  const char *xsettings_name = NULL;
+  XSettingsResult result;
+  XSettingsSetting *setting = NULL;
+  GdkScreenX11 *screen_x11;
+  gboolean success = FALSE;
+  gint i;
+  GValue tmp_val = { 0, };
+
+  g_return_val_if_fail (GDK_IS_SCREEN (screen), FALSE);
+
+  screen_x11 = GDK_SCREEN_X11 (screen);
+
+  for (i = 0; i < GDK_SETTINGS_N_ELEMENTS(); i++)
+    if (strcmp (GDK_SETTINGS_GDK_NAME (i), name) == 0)
+      {
+	xsettings_name = GDK_SETTINGS_X_NAME (i);
+	break;
+      }
+
+  if (!xsettings_name)
+    goto out;
+
+  result = xsettings_client_get_setting (screen_x11->xsettings_client,
+					 xsettings_name, &setting);
+  if (result != XSETTINGS_SUCCESS)
+    goto out;
+
+  switch (setting->type)
+    {
+    case XSETTINGS_TYPE_INT:
+      if (check_transform (xsettings_name, G_TYPE_INT, G_VALUE_TYPE (value)))
+	{
+	  g_value_init (&tmp_val, G_TYPE_INT);
+	  g_value_set_int (&tmp_val, setting->data.v_int);
+	  g_value_transform (&tmp_val, value);
+
+	  success = TRUE;
+	}
+      break;
+    case XSETTINGS_TYPE_STRING:
+      if (check_transform (xsettings_name, G_TYPE_STRING, G_VALUE_TYPE (value)))
+	{
+	  g_value_init (&tmp_val, G_TYPE_STRING);
+	  g_value_set_string (&tmp_val, setting->data.v_string);
+	  g_value_transform (&tmp_val, value);
+
+	  success = TRUE;
+	}
+      break;
+    case XSETTINGS_TYPE_COLOR:
+      if (!check_transform (xsettings_name, GDK_TYPE_COLOR, G_VALUE_TYPE (value)))
+	{
+	  GdkColor color;
+
+	  g_value_init (&tmp_val, GDK_TYPE_COLOR);
+
+	  color.pixel = 0;
+	  color.red = setting->data.v_color.red;
+	  color.green = setting->data.v_color.green;
+	  color.blue = setting->data.v_color.blue;
+
+	  g_value_set_boxed (&tmp_val, &color);
+
+	  g_value_transform (&tmp_val, value);
+
+	  success = TRUE;
+	}
+      break;
+    }
+
+  g_value_unset (&tmp_val);
+
+ out:
+  if (setting)
+    xsettings_setting_free (setting);
+
+  if (success)
+    return TRUE;
+  else
+    return _gdk_x11_get_xft_setting (screen, name, value);
+}
+
+static void
+cleanup_atoms(gpointer data)
+{
+  NetWmSupportedAtoms *supported_atoms = data;
+  if (supported_atoms->atoms)
+      XFree (supported_atoms->atoms);
+  g_free (supported_atoms);
+}
+
+static void
+fetch_net_wm_check_window (GdkScreen *screen)
+{
+  GdkScreenX11 *screen_x11;
+  GdkDisplay *display;
+  Atom type;
+  gint format;
+  gulong n_items;
+  gulong bytes_after;
+  guchar *data;
+  Window *xwindow;
+  GTimeVal tv;
+  gint error;
+
+  screen_x11 = GDK_SCREEN_X11 (screen);
+  display = screen_x11->display;
+
+  g_return_if_fail (GDK_DISPLAY_X11 (display)->trusted_client);
+  
+  g_get_current_time (&tv);
+
+  if (ABS  (tv.tv_sec - screen_x11->last_wmspec_check_time) < 15)
+    return; /* we've checked recently */
+
+  screen_x11->last_wmspec_check_time = tv.tv_sec;
+
+  data = NULL;
+  XGetWindowProperty (screen_x11->xdisplay, screen_x11->xroot_window,
+		      gdk_x11_get_xatom_by_name_for_display (display, "_NET_SUPPORTING_WM_CHECK"),
+		      0, G_MAXLONG, False, XA_WINDOW, &type, &format,
+		      &n_items, &bytes_after, &data);
+  
+  if (type != XA_WINDOW)
+    {
+      if (data)
+        XFree (data);
+      return;
+    }
+
+  xwindow = (Window *)data;
+
+  if (screen_x11->wmspec_check_window == *xwindow)
+    {
+      XFree (xwindow);
+      return;
+    }
+
+  gdk_error_trap_push ();
+
+  /* Find out if this WM goes away, so we can reset everything. */
+  XSelectInput (screen_x11->xdisplay, *xwindow, StructureNotifyMask);
+  gdk_display_sync (display);
+
+  error = gdk_error_trap_pop ();
+  if (!error)
+    {
+      screen_x11->wmspec_check_window = *xwindow;
+      screen_x11->need_refetch_net_supported = TRUE;
+      screen_x11->need_refetch_wm_name = TRUE;
+
+      /* Careful, reentrancy */
+      _gdk_x11_screen_window_manager_changed (GDK_SCREEN (screen_x11));
+    }
+  else if (error == BadWindow)
+    {
+      /* Leftover property, try again immediately, new wm may be starting up */
+      screen_x11->last_wmspec_check_time = 0;
+    }
+
+  XFree (xwindow);
+}
+
+/**
+ * gdk_x11_screen_supports_net_wm_hint:
+ * @screen: the relevant #GdkScreen.
+ * @property: a property atom.
+ *
+ * This function is specific to the X11 backend of GDK, and indicates
+ * whether the window manager supports a certain hint from the
+ * Extended Window Manager Hints Specification. You can find this
+ * specification on
+ * <ulink url="http://www.freedesktop.org">http://www.freedesktop.org</ulink>.
+ *
+ * When using this function, keep in mind that the window manager
+ * can change over time; so you shouldn't use this function in
+ * a way that impacts persistent application state. A common bug
+ * is that your application can start up before the window manager
+ * does when the user logs in, and before the window manager starts
+ * gdk_x11_screen_supports_net_wm_hint() will return %FALSE for every property.
+ * You can monitor the window_manager_changed signal on #GdkScreen to detect
+ * a window manager change.
+ *
+ * Return value: %TRUE if the window manager supports @property
+ *
+ * Since: 2.2
+ **/
+gboolean
+gdk_x11_screen_supports_net_wm_hint (GdkScreen *screen,
+				     GdkAtom    property)
+{
+  gulong i;
+  GdkScreenX11 *screen_x11;
+  NetWmSupportedAtoms *supported_atoms;
+  GdkDisplay *display;
+
+  g_return_val_if_fail (GDK_IS_SCREEN (screen), FALSE);
+
+  screen_x11 = GDK_SCREEN_X11 (screen);
+  display = screen_x11->display;
+
+  if (!G_LIKELY (GDK_DISPLAY_X11 (display)->trusted_client))
+    return FALSE;
+
+  supported_atoms = g_object_get_data (G_OBJECT (screen), "gdk-net-wm-supported-atoms");
+  if (!supported_atoms)
+    {
+      supported_atoms = g_new0 (NetWmSupportedAtoms, 1);
+      g_object_set_data_full (G_OBJECT (screen), "gdk-net-wm-supported-atoms", supported_atoms, cleanup_atoms);
+    }
+
+  fetch_net_wm_check_window (screen);
+
+  if (screen_x11->wmspec_check_window == None)
+    return FALSE;
+
+  if (screen_x11->need_refetch_net_supported)
+    {
+      /* WM has changed since we last got the supported list,
+       * refetch it.
+       */
+      Atom type;
+      gint format;
+      gulong bytes_after;
+
+      screen_x11->need_refetch_net_supported = FALSE;
+
+      if (supported_atoms->atoms)
+        XFree (supported_atoms->atoms);
+
+      supported_atoms->atoms = NULL;
+      supported_atoms->n_atoms = 0;
+
+      XGetWindowProperty (GDK_DISPLAY_XDISPLAY (display), screen_x11->xroot_window,
+                          gdk_x11_get_xatom_by_name_for_display (display, "_NET_SUPPORTED"),
+                          0, G_MAXLONG, False, XA_ATOM, &type, &format,
+                          &supported_atoms->n_atoms, &bytes_after,
+                          (guchar **)&supported_atoms->atoms);
+
+      if (type != XA_ATOM)
+        return FALSE;
+    }
+
+  if (supported_atoms->atoms == NULL)
+    return FALSE;
+
+  i = 0;
+  while (i < supported_atoms->n_atoms)
+    {
+      if (supported_atoms->atoms[i] == gdk_x11_atom_to_xatom_for_display (display, property))
+        return TRUE;
+
+      ++i;
+    }
+
+  return FALSE;
+}
+
+/**
+ * gdk_net_wm_supports:
+ * @property: a property atom.
+ *
+ * This function is specific to the X11 backend of GDK, and indicates
+ * whether the window manager for the default screen supports a certain
+ * hint from the Extended Window Manager Hints Specification. See
+ * gdk_x11_screen_supports_net_wm_hint() for complete details.
+ *
+ * Return value: %TRUE if the window manager supports @property
+ **/
+gboolean
+gdk_net_wm_supports (GdkAtom property)
+{
+  return gdk_x11_screen_supports_net_wm_hint (gdk_screen_get_default (), property);
+}
+
+static void
+refcounted_grab_server (Display *xdisplay)
+{
+  GdkDisplay *display = gdk_x11_lookup_xdisplay (xdisplay);
+
+  gdk_x11_display_grab (display);
+}
+
+static void
+refcounted_ungrab_server (Display *xdisplay)
+{
+  GdkDisplay *display = gdk_x11_lookup_xdisplay (xdisplay);
+
+  gdk_x11_display_ungrab (display);
+}
+
+static GdkFilterReturn
+gdk_xsettings_client_event_filter (GdkXEvent *xevent,
+				   GdkEvent  *event,
+				   gpointer   data)
+{
+  GdkScreenX11 *screen = data;
+
+  if (xsettings_client_process_event (screen->xsettings_client, (XEvent *)xevent))
+    return GDK_FILTER_REMOVE;
+  else
+    return GDK_FILTER_CONTINUE;
+}
+
+static Bool
+gdk_xsettings_watch_cb (Window   window,
+			Bool	 is_start,
+			long     mask,
+			void    *cb_data)
+{
+  GdkWindow *gdkwin;
+  GdkScreen *screen = cb_data;
+
+  gdkwin = gdk_window_lookup_for_display (gdk_screen_get_display (screen), window);
+
+  if (is_start)
+    {
+      if (gdkwin)
+	g_object_ref (gdkwin);
+      else
+	{
+	  gdkwin = gdk_window_foreign_new_for_display (gdk_screen_get_display (screen), window);
+	  
+	  /* gdk_window_foreign_new_for_display() can fail and return NULL if the
+	   * window has already been destroyed.
+	   */
+	  if (!gdkwin)
+	    return False;
+	}
+
+      gdk_window_add_filter (gdkwin, gdk_xsettings_client_event_filter, screen);
+    }
+  else
+    {
+      if (!gdkwin)
+	{
+	  /* gdkwin should not be NULL here, since if starting the watch succeeded
+	   * we have a reference on the window. It might mean that the caller didn't
+	   * remove the watch when it got a DestroyNotify event. Or maybe the
+	   * caller ignored the return value when starting the watch failed.
+	   */
+	  g_warning ("gdk_xsettings_watch_cb(): Couldn't find window to unwatch");
+	  return False;
+	}
+      
+      gdk_window_remove_filter (gdkwin, gdk_xsettings_client_event_filter, screen);
+      g_object_unref (gdkwin);
+    }
+
+  return True;
+}
+
+static void
+gdk_xsettings_notify_cb (const char       *name,
+			 XSettingsAction   action,
+			 XSettingsSetting *setting,
+			 void             *data)
+{
+  GdkEvent new_event;
+  GdkScreen *screen = data;
+  GdkScreenX11 *screen_x11 = data;
+  int i;
+
+  if (screen_x11->xsettings_in_init)
+    return;
+  
+  new_event.type = GDK_SETTING;
+  new_event.setting.window = gdk_screen_get_root_window (screen);
+  new_event.setting.send_event = FALSE;
+  new_event.setting.name = NULL;
+
+  for (i = 0; i < GDK_SETTINGS_N_ELEMENTS() ; i++)
+    if (strcmp (GDK_SETTINGS_X_NAME (i), name) == 0)
+      {
+	new_event.setting.name = (char*) GDK_SETTINGS_GDK_NAME (i);
+	break;
+      }
+  
+  if (!new_event.setting.name)
+    return;
+  
+  switch (action)
+    {
+    case XSETTINGS_ACTION_NEW:
+      new_event.setting.action = GDK_SETTING_ACTION_NEW;
+      break;
+    case XSETTINGS_ACTION_CHANGED:
+      new_event.setting.action = GDK_SETTING_ACTION_CHANGED;
+      break;
+    case XSETTINGS_ACTION_DELETED:
+      new_event.setting.action = GDK_SETTING_ACTION_DELETED;
+      break;
+    }
+
+  gdk_event_put (&new_event);
+}
+
+void
+_gdk_screen_x11_events_init (GdkScreen *screen)
+{
+  GdkScreenX11 *screen_x11 = GDK_SCREEN_X11 (screen);
+
+  /* Keep a flag to avoid extra notifies that we don't need
+   */
+  screen_x11->xsettings_in_init = TRUE;
+  screen_x11->xsettings_client = xsettings_client_new_with_grab_funcs (screen_x11->xdisplay,
+						                       screen_x11->screen_num,
+						                       gdk_xsettings_notify_cb,
+						                       gdk_xsettings_watch_cb,
+						                       screen,
+                                                                       refcounted_grab_server,
+                                                                       refcounted_ungrab_server);
+  screen_x11->xsettings_in_init = FALSE;
+}
+
+/**
+ * gdk_x11_screen_get_window_manager_name:
+ * @screen: a #GdkScreen
+ *
+ * Returns the name of the window manager for @screen.
+ *
+ * Return value: the name of the window manager screen @screen, or
+ * "unknown" if the window manager is unknown. The string is owned by GDK
+ * and should not be freed.
+ *
+ * Since: 2.2
+ **/
+const char*
+gdk_x11_screen_get_window_manager_name (GdkScreen *screen)
+{
+  GdkScreenX11 *screen_x11;
+
+  screen_x11 = GDK_SCREEN_X11 (screen);
+
+  if (!G_LIKELY (GDK_DISPLAY_X11 (screen_x11->display)->trusted_client))
+    return screen_x11->window_manager_name;
+
+  fetch_net_wm_check_window (screen);
+
+  if (screen_x11->need_refetch_wm_name)
+    {
+      /* Get the name of the window manager */
+      screen_x11->need_refetch_wm_name = FALSE;
+
+      g_free (screen_x11->window_manager_name);
+      screen_x11->window_manager_name = g_strdup ("unknown");
+
+      if (screen_x11->wmspec_check_window != None)
+        {
+          Atom type;
+          gint format;
+          gulong n_items;
+          gulong bytes_after;
+          gchar *name;
+
+          name = NULL;
+
+	  gdk_error_trap_push ();
+
+          XGetWindowProperty (GDK_DISPLAY_XDISPLAY (screen_x11->display),
+                              screen_x11->wmspec_check_window,
+                              gdk_x11_get_xatom_by_name_for_display (screen_x11->display,
+                                                                     "_NET_WM_NAME"),
+                              0, G_MAXLONG, False,
+                              gdk_x11_get_xatom_by_name_for_display (screen_x11->display,
+                                                                     "UTF8_STRING"),
+                              &type, &format,
+                              &n_items, &bytes_after,
+                              (guchar **)&name);
+
+          gdk_display_sync (screen_x11->display);
+
+          gdk_error_trap_pop ();
+
+          if (name != NULL)
+            {
+              g_free (screen_x11->window_manager_name);
+              screen_x11->window_manager_name = g_strdup (name);
+              XFree (name);
+            }
+        }
+    }
+
+  return GDK_SCREEN_X11 (screen)->window_manager_name;
+}
+
 #define __GDK_SCREEN_X11_C__
 #include "gdkaliasdef.c"
diff --git a/gdk/x11/gdkwindow-x11.c b/gdk/x11/gdkwindow-x11.c
index a8dd83b..40c957b 100644
--- a/gdk/x11/gdkwindow-x11.c
+++ b/gdk/x11/gdkwindow-x11.c
@@ -39,17 +39,19 @@
 #include <unistd.h>
 
 #include "gdk.h"
+#include "gdkx.h"
 
 #include "gdkwindow.h"
 #include "gdkwindowimpl.h"
 #include "gdkasync.h"
-#include "gdkinputprivate.h"
 #include "gdkdisplay-x11.h"
 #include "gdkprivate-x11.h"
 #include "gdkregion.h"
 #include "gdkinternals.h"
 #include "MwmUtil.h"
 #include "gdkwindow-x11.h"
+#include "gdkdeviceprivate.h"
+#include "gdkeventsource.h"
 #include "gdkalias.h"
 
 #include <stdlib.h>
@@ -146,6 +148,8 @@ static void
 gdk_window_impl_x11_init (GdkWindowImplX11 *impl)
 {  
   impl->toplevel_window_type = -1;
+  impl->device_cursor = g_hash_table_new_full (NULL, NULL, NULL,
+                                               (GDestroyNotify) gdk_cursor_unref);
 }
 
 GdkToplevelX11 *
@@ -210,6 +214,8 @@ gdk_window_impl_x11_finalize (GObject *object)
   if (window_impl->cursor)
     gdk_cursor_unref (window_impl->cursor);
 
+  g_hash_table_destroy (window_impl->device_cursor);
+
   G_OBJECT_CLASS (gdk_window_impl_x11_parent_class)->finalize (object);
 }
 
@@ -513,18 +519,31 @@ check_leader_window_title (GdkDisplay *display)
 }
 
 static Window
-create_focus_window (Display *xdisplay,
-		     XID      parent)
+create_focus_window (GdkDisplay *display,
+		     XID         parent)
 {
-  Window focus_window = XCreateSimpleWindow (xdisplay, parent,
-					     -1, -1, 1, 1, 0,
-					     0, 0);
-  
+  GdkDisplayX11 *display_x11;
+  GdkEventMask event_mask;
+  Display *xdisplay;
+  Window focus_window;
+
+  xdisplay = GDK_DISPLAY_XDISPLAY (display);
+  display_x11 = GDK_DISPLAY_X11 (display);
+
+  focus_window = XCreateSimpleWindow (xdisplay, parent,
+                                      -1, -1, 1, 1, 0,
+                                      0, 0);
+
   /* FIXME: probably better to actually track the requested event mask for the toplevel
    */
-  XSelectInput (xdisplay, focus_window,
-		KeyPressMask | KeyReleaseMask | FocusChangeMask);
-  
+  event_mask = (GDK_KEY_PRESS_MASK |
+                GDK_KEY_RELEASE_MASK |
+                GDK_FOCUS_CHANGE_MASK);
+
+  gdk_event_source_select_events ((GdkEventSource *) display_x11->event_source,
+                                  focus_window,
+                                  event_mask, 0);
+
   XMapWindow (xdisplay, focus_window);
 
   return focus_window;
@@ -573,6 +592,7 @@ setup_toplevel_window (GdkWindow *window,
 {
   GdkWindowObject *obj = (GdkWindowObject *)window;
   GdkToplevelX11 *toplevel = _gdk_x11_window_get_toplevel (window);
+  GdkDisplay *display = gdk_drawable_get_display (window);
   Display *xdisplay = GDK_WINDOW_XDISPLAY (window);
   XID xid = GDK_WINDOW_XID (window);
   XID xparent = GDK_WINDOW_XID (parent);
@@ -591,7 +611,7 @@ setup_toplevel_window (GdkWindow *window,
       /* The focus window is off the visible area, and serves to receive key
        * press events so they don't get sent to child windows.
        */
-      toplevel->focus_window = create_focus_window (xdisplay, xid);
+      toplevel->focus_window = create_focus_window (display, xid);
       _gdk_xid_table_insert (screen_x11->display, &toplevel->focus_window, window);
     }
   
@@ -652,6 +672,7 @@ _gdk_window_impl_new (GdkWindow     *window,
   GdkWindowImplX11 *impl;
   GdkDrawableImplX11 *draw_impl;
   GdkScreenX11 *screen_x11;
+  GdkDisplayX11 *display_x11;
   
   Window xparent;
   Visual *xvisual;
@@ -664,12 +685,12 @@ _gdk_window_impl_new (GdkWindow     *window,
   
   unsigned int class;
   const char *title;
-  int i;
   
   private = (GdkWindowObject *) window;
   
   screen_x11 = GDK_SCREEN_X11 (screen);
   xparent = GDK_WINDOW_XID (real_parent);
+  display_x11 = GDK_DISPLAY_X11 (GDK_SCREEN_DISPLAY (screen));
   
   impl = g_object_new (_gdk_window_impl_get_type (), NULL);
   private->impl = (GdkDrawable *)impl;
@@ -683,15 +704,6 @@ _gdk_window_impl_new (GdkWindow     *window,
 
   xvisual = ((GdkVisualPrivate*) visual)->xvisual;
   
-  xattributes.event_mask = StructureNotifyMask | PropertyChangeMask;
-  for (i = 0; i < _gdk_nenvent_masks; i++)
-    {
-      if (event_mask & (1 << (i + 1)))
-	xattributes.event_mask |= _gdk_event_mask_table[i];
-    }
-  if (xattributes.event_mask)
-    xattributes_mask |= CWEventMask;
-  
   if (attributes_mask & GDK_WA_NOREDIR)
     {
       xattributes.override_redirect =
@@ -837,6 +849,10 @@ _gdk_window_impl_new (GdkWindow     *window,
 
   if (attributes_mask & GDK_WA_TYPE_HINT)
     gdk_window_set_type_hint (window, attributes->type_hint);
+
+  gdk_event_source_select_events ((GdkEventSource *) display_x11->event_source,
+                                  GDK_WINDOW_XWINDOW (window), event_mask,
+                                  StructureNotifyMask | PropertyChangeMask);
 }
 
 static GdkEventMask
@@ -2713,41 +2729,30 @@ gdk_window_x11_set_back_pixmap (GdkWindow *window,
 }
 
 static void
-gdk_window_x11_set_cursor (GdkWindow *window,
-                           GdkCursor *cursor)
+gdk_window_x11_set_device_cursor (GdkWindow *window,
+                                  GdkDevice *device,
+                                  GdkCursor *cursor)
 {
   GdkWindowObject *private;
   GdkWindowImplX11 *impl;
-  GdkCursorPrivate *cursor_private;
-  Cursor xcursor;
-  
-  private = (GdkWindowObject *)window;
-  impl = GDK_WINDOW_IMPL_X11 (private->impl);
-  cursor_private = (GdkCursorPrivate*) cursor;
 
-  if (impl->cursor)
-    {
-      gdk_cursor_unref (impl->cursor);
-      impl->cursor = NULL;
-    }
+  g_return_if_fail (GDK_IS_WINDOW (window));
+  g_return_if_fail (GDK_IS_DEVICE (device));
+
+  private = (GdkWindowObject *) window;
+  impl = GDK_WINDOW_IMPL_X11 (private->impl);
 
   if (!cursor)
-    xcursor = None;
+    g_hash_table_remove (impl->device_cursor, device);
   else
     {
       _gdk_x11_cursor_update_theme (cursor);
-      xcursor = cursor_private->xcursor;
+      g_hash_table_replace (impl->device_cursor,
+                            device, gdk_cursor_ref (cursor));
     }
-  
+
   if (!GDK_WINDOW_DESTROYED (window))
-    {
-      XDefineCursor (GDK_WINDOW_XDISPLAY (window),
-		     GDK_WINDOW_XID (window),
-		     xcursor);
-      
-      if (cursor)
-	impl->cursor = gdk_cursor_ref (cursor);
-    }
+    GDK_DEVICE_GET_CLASS (device)->set_window_cursor (device, window, cursor);
 }
 
 GdkCursor *
@@ -3072,106 +3077,112 @@ gdk_window_get_frame_extents (GdkWindow    *window,
 }
 
 void
-_gdk_windowing_get_pointer (GdkDisplay       *display,
-			    GdkScreen       **screen,
-			    gint             *x,
-			    gint             *y,
-			    GdkModifierType  *mask)
+_gdk_windowing_get_device_state (GdkDisplay       *display,
+                                 GdkDevice        *device,
+                                 GdkScreen       **screen,
+                                 gint             *x,
+                                 gint             *y,
+                                 GdkModifierType  *mask)
 {
   GdkScreen *default_screen;
-  Display *xdisplay;
-  Window xwindow;
-  Window root = None;
-  Window child;
-  int rootx, rooty;
-  int winx;
-  int winy;
-  unsigned int xmask;
 
   if (display->closed)
     return;
 
   default_screen = gdk_display_get_default_screen (display);
 
-  xdisplay = GDK_SCREEN_XDISPLAY (default_screen);
-  xwindow = GDK_SCREEN_XROOTWIN (default_screen);
-  
-  if (G_LIKELY (GDK_DISPLAY_X11 (display)->trusted_client)) 
+
+  if (G_LIKELY (GDK_DISPLAY_X11 (display)->trusted_client))
     {
-      XQueryPointer (xdisplay, xwindow,
-		     &root, &child, &rootx, &rooty, &winx, &winy, &xmask);
-    } 
-  else 
+      GdkWindow *root;
+
+      GDK_DEVICE_GET_CLASS (device)->query_state (device,
+                                                  gdk_screen_get_root_window (default_screen),
+                                                  &root, NULL,
+                                                  x, y,
+                                                  NULL, NULL,
+                                                  mask);
+      *screen = gdk_drawable_get_screen (root);
+    }
+  else
     {
       XSetWindowAttributes attributes;
-      Window w;
-      
-      w = XCreateWindow (xdisplay, xwindow, 0, 0, 1, 1, 0, 
-			 CopyFromParent, InputOnly, CopyFromParent, 
+      Display *xdisplay;
+      Window xwindow, w, root, child;
+      int rootx, rooty, winx, winy;
+      unsigned int xmask;
+
+      /* FIXME: untrusted clients not multidevice-safe */
+
+      xdisplay = GDK_SCREEN_XDISPLAY (default_screen);
+      xwindow = GDK_SCREEN_XROOTWIN (default_screen);
+
+      w = XCreateWindow (xdisplay, xwindow, 0, 0, 1, 1, 0,
+			 CopyFromParent, InputOnly, CopyFromParent,
 			 0, &attributes);
-      XQueryPointer (xdisplay, w, 
+      XQueryPointer (xdisplay, w,
 		     &root, &child, &rootx, &rooty, &winx, &winy, &xmask);
       XDestroyWindow (xdisplay, w);
+
+      if (root != None)
+        {
+          GdkWindow *gdk_root = gdk_window_lookup_for_display (display, root);
+          *screen = gdk_drawable_get_screen (gdk_root);
+        }
+
+      *x = rootx;
+      *y = rooty;
+      *mask = xmask;
     }
-  
-  if (root != None)
-    {
-      GdkWindow *gdk_root = gdk_window_lookup_for_display (display, root);
-      *screen = gdk_drawable_get_screen (gdk_root);
-    }
-  
-  *x = rootx;
-  *y = rooty;
-  *mask = xmask;
 }
 
 static gboolean
-gdk_window_x11_get_pointer (GdkWindow       *window,
-			    gint            *x,
-			    gint            *y,
-			    GdkModifierType *mask)
+gdk_window_x11_get_device_state (GdkWindow       *window,
+                                 GdkDevice       *device,
+                                 gint            *x,
+                                 gint            *y,
+                                 GdkModifierType *mask)
 {
   GdkDisplay *display = GDK_WINDOW_DISPLAY (window);
   gboolean return_val;
-  Window root;
-  Window child;
-  int rootx, rooty;
-  int winx = 0;
-  int winy = 0;
-  unsigned int xmask = 0;
 
   g_return_val_if_fail (window == NULL || GDK_IS_WINDOW (window), FALSE);
 
-  
   return_val = TRUE;
+
   if (!GDK_WINDOW_DESTROYED (window))
     {
       if (G_LIKELY (GDK_DISPLAY_X11 (display)->trusted_client))
 	{
-	  if (XQueryPointer (GDK_WINDOW_XDISPLAY (window),
-			     GDK_WINDOW_XID (window),
-			     &root, &child, &rootx, &rooty, &winx, &winy, &xmask))
-	    {
-	      if (child)
-		return_val = gdk_window_lookup_for_display (GDK_WINDOW_DISPLAY (window), child) != NULL;
-	    }
+          GdkWindow *child;
+
+          GDK_DEVICE_GET_CLASS (device)->query_state (device, window,
+                                                      NULL, &child,
+                                                      NULL, NULL,
+                                                      x, y, mask);
+          return_val = (child != NULL);
 	}
       else
 	{
 	  GdkScreen *screen;
 	  int originx, originy;
-	  _gdk_windowing_get_pointer (gdk_drawable_get_display (window), &screen,
-				      &rootx, &rooty, &xmask);
+          int rootx, rooty;
+          int winx = 0;
+          int winy = 0;
+          unsigned int xmask = 0;
+
+          _gdk_windowing_get_device_state (gdk_drawable_get_display (window), device,
+                                           &screen, &rootx, &rooty, &xmask);
 	  gdk_window_get_origin (window, &originx, &originy);
 	  winx = rootx - originx;
 	  winy = rooty - originy;
+
+          *x = winx;
+          *y = winy;
+          *mask = xmask;
 	}
     }
 
-  *x = winx;
-  *y = winy;
-  *mask = xmask;
-
   return return_val;
 }
 
@@ -3202,37 +3213,62 @@ gdk_display_warp_pointer (GdkDisplay *display,
 			  gint        x,
 			  gint        y)
 {
-  Display *xdisplay;
-  Window dest;
+  GdkDevice *device;
 
-  xdisplay = GDK_DISPLAY_XDISPLAY (display);
-  dest = GDK_WINDOW_XWINDOW (gdk_screen_get_root_window (screen));
+  g_return_if_fail (GDK_IS_DISPLAY (display));
+  g_return_if_fail (GDK_IS_SCREEN (screen));
 
-  XWarpPointer (xdisplay, None, dest, 0, 0, 0, 0, x, y);  
+  device = display->core_pointer;
+  GDK_DEVICE_GET_CLASS (device)->warp (device, screen, x, y);
+}
+
+/**
+ * gdk_display_warp_device:
+ * @display: a #GdkDisplay.
+ * @device: a #GdkDevice.
+ * @screen: the screen of @display to warp @device to.
+ * @x: the X coordinate of the destination.
+ * @y: the Y coordinate of the destination.
+ *
+ * Warps @device in @display to the point @x,@y on
+ * the screen @screen, unless the device is confined
+ * to a window by a grab, in which case it will be moved
+ * as far as allowed by the grab. Warping the pointer
+ * creates events as if the user had moved the mouse
+ * instantaneously to the destination.
+ *
+ * Note that the pointer should normally be under the
+ * control of the user. This function was added to cover
+ * some rare use cases like keyboard navigation support
+ * for the color picker in the #GtkColorSelectionDialog.
+ **/
+void
+gdk_display_warp_device (GdkDisplay *display,
+                         GdkDevice  *device,
+                         GdkScreen  *screen,
+                         gint        x,
+                         gint        y)
+{
+  g_return_if_fail (GDK_IS_DISPLAY (display));
+  g_return_if_fail (GDK_IS_DEVICE (device));
+  g_return_if_fail (GDK_IS_SCREEN (screen));
+  g_return_if_fail (display == gdk_device_get_display (device));
+
+  GDK_DEVICE_GET_CLASS (device)->warp (device, screen, x, y);
 }
 
 GdkWindow*
-_gdk_windowing_window_at_pointer (GdkDisplay *display,
-                                  gint       *win_x,
-				  gint       *win_y,
-				  GdkModifierType *mask,
-				  gboolean   get_toplevel)
+_gdk_windowing_window_at_device_position (GdkDisplay      *display,
+                                          GdkDevice       *device,
+                                          gint            *win_x,
+                                          gint            *win_y,
+                                          GdkModifierType *mask,
+                                          gboolean         get_toplevel)
 {
   GdkWindow *window;
   GdkScreen *screen;
-  Window root;
-  Window xwindow;
-  Window child;
-  Window xwindow_last = 0;
-  Display *xdisplay;
-  int rootx = -1, rooty = -1;
-  int winx, winy;
-  unsigned int xmask;
 
   screen = gdk_display_get_default_screen (display);
-  
-  xwindow = GDK_SCREEN_XROOTWIN (screen);
-  xdisplay = GDK_SCREEN_XDISPLAY (screen);
 
   /* This function really only works if the mouse pointer is held still
    * during its operation. If it moves from one leaf window to another
@@ -3240,35 +3276,24 @@ _gdk_windowing_window_at_pointer (GdkDisplay *display,
    * and the result.
    */
   gdk_x11_display_grab (display);
-  if (G_LIKELY (GDK_DISPLAY_X11 (display)->trusted_client)) 
-    {
-      XQueryPointer (xdisplay, xwindow,
-		     &root, &child, &rootx, &rooty, &winx, &winy, &xmask);
-      if (root == xwindow)
-	xwindow = child;
-      else
-	xwindow = root;
-      
-      while (xwindow)
-	{
-	  xwindow_last = xwindow;
-	  XQueryPointer (xdisplay, xwindow,
-			 &root, &xwindow, &rootx, &rooty, &winx, &winy, &xmask);
-	  if (get_toplevel &&
-	      (window = gdk_window_lookup_for_display (display, xwindow_last)) != NULL &&
-	      GDK_WINDOW_TYPE (window) != GDK_WINDOW_FOREIGN)
-	    {
-	      xwindow = xwindow_last;
-	      break;
-	    }
-	}
-    } 
-  else 
+  if (G_LIKELY (GDK_DISPLAY_X11 (display)->trusted_client))
+    window = GDK_DEVICE_GET_CLASS (device)->window_at_position (device, win_x, win_y, mask, get_toplevel);
+  else
     {
       gint i, screens, width, height;
       GList *toplevels, *list;
-      Window pointer_window;
-      
+      Window pointer_window, root, xwindow, child;
+      Window xwindow_last = 0;
+      Display *xdisplay;
+      int rootx = -1, rooty = -1;
+      int winx, winy;
+      unsigned int xmask;
+
+      /* FIXME: untrusted clients case not multidevice-safe */
+
+      xwindow = GDK_SCREEN_XROOTWIN (screen);
+      xdisplay = GDK_SCREEN_XDISPLAY (screen);
+
       pointer_window = None;
       screens = gdk_display_get_n_screens (display);
       for (i = 0; i < screens; ++i) {
@@ -3329,16 +3354,17 @@ _gdk_windowing_window_at_pointer (GdkDisplay *display,
 	      GDK_WINDOW_TYPE (window) != GDK_WINDOW_FOREIGN)
 	    break;
 	}
+
+      window = gdk_window_lookup_for_display (display, xwindow_last);
+
+      *win_x = window ? winx : -1;
+      *win_y = window ? winy : -1;
+      if (mask)
+        *mask = xmask;
     }
-  
+
   gdk_x11_display_ungrab (display);
 
-  window = gdk_window_lookup_for_display (display, xwindow_last);
-  *win_x = window ? winx : -1;
-  *win_y = window ? winy : -1;
-  if (mask)
-    *mask = xmask;
-  
   return window;
 }
 
@@ -3369,21 +3395,18 @@ gdk_window_x11_set_events (GdkWindow    *window,
                            GdkEventMask  event_mask)
 {
   long xevent_mask = 0;
-  int i;
   
   if (!GDK_WINDOW_DESTROYED (window))
     {
+      GdkDisplayX11 *display_x11;
+
       if (GDK_WINDOW_XID (window) != GDK_WINDOW_XROOTWIN (window))
         xevent_mask = StructureNotifyMask | PropertyChangeMask;
-      for (i = 0; i < _gdk_nenvent_masks; i++)
-	{
-	  if (event_mask & (1 << (i + 1)))
-	    xevent_mask |= _gdk_event_mask_table[i];
-	}
-      
-      XSelectInput (GDK_WINDOW_XDISPLAY (window),
-		    GDK_WINDOW_XID (window),
-		    xevent_mask);
+
+      display_x11 = GDK_DISPLAY_X11 (gdk_drawable_get_display (window));
+      gdk_event_source_select_events ((GdkEventSource *) display_x11->event_source,
+                                      GDK_WINDOW_XWINDOW (window), event_mask,
+                                      xevent_mask);
     }
 }
 
@@ -5593,10 +5616,10 @@ gdk_window_impl_iface_init (GdkWindowImplIface *iface)
   iface->set_back_pixmap = gdk_window_x11_set_back_pixmap;
   iface->reparent = gdk_window_x11_reparent;
   iface->clear_region = gdk_window_x11_clear_region;
-  iface->set_cursor = gdk_window_x11_set_cursor;
+  iface->set_device_cursor = gdk_window_x11_set_device_cursor;
   iface->get_geometry = gdk_window_x11_get_geometry;
   iface->get_root_coords = gdk_window_x11_get_root_coords;
-  iface->get_pointer = gdk_window_x11_get_pointer;
+  iface->get_device_state = gdk_window_x11_get_device_state;
   iface->get_deskrelative_origin = gdk_window_x11_get_deskrelative_origin;
   iface->shape_combine_region = gdk_window_x11_shape_combine_region;
   iface->input_shape_combine_region = gdk_window_x11_input_shape_combine_region;
@@ -5604,8 +5627,61 @@ gdk_window_impl_iface_init (GdkWindowImplIface *iface)
   iface->queue_antiexpose = _gdk_x11_window_queue_antiexpose;
   iface->queue_translation = _gdk_x11_window_queue_translation;
   iface->destroy = _gdk_x11_window_destroy;
-  iface->input_window_destroy = _gdk_input_window_destroy;
-  iface->input_window_crossing = _gdk_input_crossing_event;
+}
+
+static Bool
+timestamp_predicate (Display *display,
+		     XEvent  *xevent,
+		     XPointer arg)
+{
+  Window xwindow = GPOINTER_TO_UINT (arg);
+  GdkDisplay *gdk_display = gdk_x11_lookup_xdisplay (display);
+
+  if (xevent->type == PropertyNotify &&
+      xevent->xproperty.window == xwindow &&
+      xevent->xproperty.atom == gdk_x11_get_xatom_by_name_for_display (gdk_display,
+								       "GDK_TIMESTAMP_PROP"))
+    return True;
+
+  return False;
+}
+
+/**
+ * gdk_x11_get_server_time:
+ * @window: a #GdkWindow, used for communication with the server.
+ *          The window must have GDK_PROPERTY_CHANGE_MASK in its
+ *          events mask or a hang will result.
+ *
+ * Routine to get the current X server time stamp.
+ *
+ * Return value: the time stamp.
+ **/
+guint32
+gdk_x11_get_server_time (GdkWindow *window)
+{
+  Display *xdisplay;
+  Window   xwindow;
+  guchar c = 'a';
+  XEvent xevent;
+  Atom timestamp_prop_atom;
+
+  g_return_val_if_fail (GDK_IS_WINDOW (window), 0);
+  g_return_val_if_fail (!GDK_WINDOW_DESTROYED (window), 0);
+
+  xdisplay = GDK_WINDOW_XDISPLAY (window);
+  xwindow = GDK_WINDOW_XWINDOW (window);
+  timestamp_prop_atom =
+    gdk_x11_get_xatom_by_name_for_display (GDK_WINDOW_DISPLAY (window),
+					   "GDK_TIMESTAMP_PROP");
+
+  XChangeProperty (xdisplay, xwindow, timestamp_prop_atom,
+		   timestamp_prop_atom,
+		   8, PropModeReplace, &c, 1);
+
+  XIfEvent (xdisplay, &xevent,
+	    timestamp_predicate, GUINT_TO_POINTER(xwindow));
+
+  return xevent.xproperty.time;
 }
 
 #define __GDK_WINDOW_X11_C__
diff --git a/gdk/x11/gdkwindow-x11.h b/gdk/x11/gdkwindow-x11.h
index 9a7f2ae..762f104 100644
--- a/gdk/x11/gdkwindow-x11.h
+++ b/gdk/x11/gdkwindow-x11.h
@@ -60,6 +60,8 @@ struct _GdkWindowImplX11
 
   GdkToplevelX11 *toplevel;	/* Toplevel-specific information */
   GdkCursor *cursor;
+  GHashTable *device_cursor;
+
   gint8 toplevel_window_type;
   guint no_bg : 1;	        /* Set when the window background is temporarily
 				 * unset during resizing and scaling */
diff --git a/gtk/Makefile.am b/gtk/Makefile.am
index d35170a..4629174 100644
--- a/gtk/Makefile.am
+++ b/gtk/Makefile.am
@@ -193,6 +193,7 @@ gtk_public_h_sources =          \
 	gtkcontainer.h		\
 	gtkcurve.h		\
 	gtkdebug.h              \
+	gtkdevicegroup.h	\
 	gtkdialog.h		\
 	gtkdnd.h		\
 	gtkdrawingarea.h	\
@@ -447,6 +448,7 @@ gtk_base_c_sources =            \
 	gtkcomboboxentry.c	\
 	gtkcontainer.c		\
 	gtkcurve.c		\
+	gtkdevicegroup.c	\
 	gtkdialog.c		\
 	gtkdrawingarea.c	\
 	gtkeditable.c           \
diff --git a/gtk/gtk.symbols b/gtk/gtk.symbols
index 6a276db..4d99fcd 100644
--- a/gtk/gtk.symbols
+++ b/gtk/gtk.symbols
@@ -2372,6 +2372,7 @@ gtk_list_store_swap
 #if IN_FILE(__GTK_MAIN_C__)
 gtk_get_option_group
 gtk_get_current_event
+gtk_get_current_event_device
 gtk_get_current_event_state
 gtk_get_current_event_time
 #ifndef GTK_DISABLE_DEPRECATED
diff --git a/gtk/gtkdevicegroup.c b/gtk/gtkdevicegroup.c
new file mode 100644
index 0000000..0f4b531
--- /dev/null
+++ b/gtk/gtkdevicegroup.c
@@ -0,0 +1,149 @@
+/* GTK - The GIMP Toolkit
+ * Copyright (C) 2009 Carlos Garnacho  <carlosg@gnome.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.	 See the GNU
+ * 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.
+ */
+
+#include "config.h"
+#include "gtkintl.h"
+#include "gtkdevicegroup.h"
+
+typedef struct GtkDeviceGroupPrivate GtkDeviceGroupPrivate;
+
+struct GtkDeviceGroupPrivate
+{
+  GList *devices;
+};
+
+#define GTK_DEVICE_GROUP_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GTK_TYPE_DEVICE_GROUP, GtkDeviceGroupPrivate))
+
+static void gtk_device_group_class_init (GtkDeviceGroupClass *klass);
+static void gtk_device_group_init       (GtkDeviceGroup      *group);
+
+static void gtk_device_group_finalize   (GObject             *object);
+
+enum {
+  DEVICE_ADDED,
+  DEVICE_REMOVED,
+  LAST_SIGNAL
+};
+
+static guint signals[LAST_SIGNAL] = { 0 };
+
+G_DEFINE_TYPE (GtkDeviceGroup, gtk_device_group, G_TYPE_OBJECT)
+
+
+static void
+gtk_device_group_class_init (GtkDeviceGroupClass *klass)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+  object_class->finalize = gtk_device_group_finalize;
+
+  signals[DEVICE_ADDED] =
+    g_signal_new (I_("device-added"),
+                  G_TYPE_FROM_CLASS (object_class),
+                  G_SIGNAL_RUN_LAST,
+                  G_STRUCT_OFFSET (GtkDeviceGroupClass, device_added),
+                  NULL, NULL,
+                  g_cclosure_marshal_VOID__OBJECT,
+                  G_TYPE_NONE, 1, GDK_TYPE_DEVICE);
+  signals[DEVICE_REMOVED] =
+    g_signal_new (I_("device-removed"),
+                  G_TYPE_FROM_CLASS (object_class),
+                  G_SIGNAL_RUN_LAST,
+                  G_STRUCT_OFFSET (GtkDeviceGroupClass, device_removed),
+                  NULL, NULL,
+                  g_cclosure_marshal_VOID__OBJECT,
+                  G_TYPE_NONE, 1, GDK_TYPE_DEVICE);
+
+  g_type_class_add_private (object_class, sizeof (GtkDeviceGroupPrivate));
+}
+
+static void
+gtk_device_group_init (GtkDeviceGroup *group)
+{
+}
+
+static void
+gtk_device_group_finalize (GObject *object)
+{
+  GtkDeviceGroupPrivate *priv;
+
+  priv = GTK_DEVICE_GROUP_GET_PRIVATE (object);
+
+  g_list_foreach (priv->devices, (GFunc) g_object_unref, NULL);
+  g_list_free (priv->devices);
+
+  G_OBJECT_CLASS (gtk_device_group_parent_class)->finalize (object);
+}
+
+void
+gtk_device_group_add_device (GtkDeviceGroup *group,
+                             GdkDevice      *device)
+{
+  GtkDeviceGroupPrivate *priv;
+
+  g_return_if_fail (GTK_IS_DEVICE_GROUP (group));
+  g_return_if_fail (GDK_IS_DEVICE (device));
+
+  priv = GTK_DEVICE_GROUP_GET_PRIVATE (group);
+
+  if (g_list_find (priv->devices, device))
+    return;
+
+  priv->devices = g_list_prepend (priv->devices,
+                                  g_object_ref (device));
+
+  g_signal_emit (group, signals[DEVICE_ADDED], 0, device);
+}
+
+void
+gtk_device_group_remove_device (GtkDeviceGroup *group,
+                                GdkDevice      *device)
+{
+  GtkDeviceGroupPrivate *priv;
+  GList *dev;
+
+  g_return_if_fail (GTK_IS_DEVICE_GROUP (group));
+  g_return_if_fail (GDK_IS_DEVICE (device));
+
+  priv = GTK_DEVICE_GROUP_GET_PRIVATE (group);
+
+  dev = g_list_find (priv->devices, device);
+
+  if (!dev)
+    return;
+
+  priv->devices = g_list_remove_link (priv->devices, dev);
+
+  g_signal_emit (group, signals[DEVICE_REMOVED], 0, device);
+
+  g_object_unref (dev->data);
+  g_list_free_1 (dev);
+}
+
+GList *
+gtk_device_group_get_devices (GtkDeviceGroup *group)
+{
+  GtkDeviceGroupPrivate *priv;
+
+  g_return_val_if_fail (GTK_IS_DEVICE_GROUP (group), NULL);
+
+  priv = GTK_DEVICE_GROUP_GET_PRIVATE (group);
+
+  return priv->devices;
+}
diff --git a/gtk/gtkdevicegroup.h b/gtk/gtkdevicegroup.h
new file mode 100644
index 0000000..1ccbcee
--- /dev/null
+++ b/gtk/gtkdevicegroup.h
@@ -0,0 +1,71 @@
+/* GTK - The GIMP Toolkit
+ * Copyright (C) 2009 Carlos Garnacho  <carlosg@gnome.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.	 See the GNU
+ * 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.
+ */
+
+#if defined(GTK_DISABLE_SINGLE_INCLUDES) && !defined (__GTK_H_INSIDE__) && !defined (GTK_COMPILATION)
+#error "Only <gtk/gtk.h> can be included directly."
+#endif
+
+#ifndef __GTK_DEVICE_GROUP_H__
+#define __GTK_DEVICE_GROUP_H__
+
+#include <gdk/gdk.h>
+
+G_BEGIN_DECLS
+
+typedef struct GtkDeviceGroup GtkPointerGroup;
+
+#define GTK_TYPE_DEVICE_GROUP         (gtk_device_group_get_type ())
+#define GTK_DEVICE_GROUP(o)           (G_TYPE_CHECK_INSTANCE_CAST ((o), GTK_TYPE_DEVICE_GROUP, GtkDeviceGroup))
+#define GTK_DEVICE_GROUP_CLASS(c)     (G_TYPE_CHECK_CLASS_CAST    ((c), GTK_TYPE_DEVICE_GROUP, GtkDeviceGroupClass))
+#define GTK_IS_DEVICE_GROUP(o)        (G_TYPE_CHECK_INSTANCE_TYPE ((o), GTK_TYPE_DEVICE_GROUP))
+#define GTK_IS_DEVICE_GROUP_CLASS(c)  (G_TYPE_CHECK_CLASS_TYPE    ((c), GTK_TYPE_DEVICE_GROUP))
+#define GTK_DEVICE_GROUP_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS  ((o), GTK_TYPE_DEVICE_GROUP, GtkDeviceGroupClass))
+
+typedef struct GtkDeviceGroup GtkDeviceGroup;
+typedef struct GtkDeviceGroupClass GtkDeviceGroupClass;
+
+struct GtkDeviceGroup
+{
+  GObject parent_instance;
+};
+
+struct GtkDeviceGroupClass
+{
+  GObjectClass parent_class;
+
+  void (* device_added)   (GtkDeviceGroup *group,
+                           GdkDevice      *device);
+  void (* device_removed) (GtkDeviceGroup *group,
+                           GdkDevice      *device);
+};
+
+GType   gtk_device_group_get_type       (void) G_GNUC_CONST;
+
+void    gtk_device_group_add_device     (GtkDeviceGroup  *group,
+                                         GdkDevice       *device);
+
+void    gtk_device_group_remove_device  (GtkDeviceGroup  *group,
+                                         GdkDevice       *device);
+
+GList * gtk_device_group_get_devices    (GtkDeviceGroup  *group);
+
+
+G_END_DECLS
+
+#endif /* __GTK_POINTER_GROUP_H__ */
diff --git a/gtk/gtkmain.c b/gtk/gtkmain.c
index 645da6f..63ac0ec 100644
--- a/gtk/gtkmain.c
+++ b/gtk/gtkmain.c
@@ -1327,12 +1327,10 @@ gtk_main_iteration_do (gboolean blocking)
 
 /* private libgtk to libgdk interfaces
  */
-gboolean gdk_pointer_grab_info_libgtk_only  (GdkDisplay *display,
-					     GdkWindow **grab_window,
-					     gboolean   *owner_events);
-gboolean gdk_keyboard_grab_info_libgtk_only (GdkDisplay *display,
-					     GdkWindow **grab_window,
-					     gboolean   *owner_events);
+gboolean gdk_device_grab_info_libgtk_only (GdkDisplay  *display,
+                                           GdkDevice   *device,
+                                           GdkWindow  **grab_window,
+                                           gboolean    *owner_events);
 
 static void
 rewrite_events_translate (GdkWindow *old_window,
@@ -1407,6 +1405,7 @@ rewrite_event_for_grabs (GdkEvent *event)
   gpointer grab_widget_ptr;
   gboolean owner_events;
   GdkDisplay *display;
+  GdkDevice *device;
 
   switch (event->type)
     {
@@ -1418,20 +1417,15 @@ rewrite_event_for_grabs (GdkEvent *event)
     case GDK_MOTION_NOTIFY:
     case GDK_PROXIMITY_IN:
     case GDK_PROXIMITY_OUT:
-      display = gdk_drawable_get_display (event->proximity.window);
-      if (!gdk_pointer_grab_info_libgtk_only (display, &grab_window, &owner_events) ||
-	  !owner_events)
-	return NULL;
-      break;
-
     case GDK_KEY_PRESS:
     case GDK_KEY_RELEASE:
-      display = gdk_drawable_get_display (event->key.window);
-      if (!gdk_keyboard_grab_info_libgtk_only (display, &grab_window, &owner_events) ||
+      display = gdk_drawable_get_display (event->any.window);
+      device = gdk_event_get_device (event);
+
+      if (!gdk_device_grab_info_libgtk_only (display, device, &grab_window, &owner_events) ||
 	  !owner_events)
-	return NULL;
+        return NULL;
       break;
-
     default:
       return NULL;
     }
@@ -2281,6 +2275,23 @@ gtk_get_current_event_state (GdkModifierType *state)
 }
 
 /**
+ * gtk_get_current_event_device:
+ *
+ * If there is a current event and it has a device, return that
+ * device, otherwise return %NULL.
+ *
+ * Returns: a #GdkDevice, or %NULL
+ **/
+GdkDevice *
+gtk_get_current_event_device (void)
+{
+  if (current_events)
+    return gdk_event_get_device (current_events->data);
+  else
+    return NULL;
+}
+
+/**
  * gtk_get_event_widget:
  * @event: a #GdkEvent
  *
diff --git a/gtk/gtkmain.h b/gtk/gtkmain.h
index 975057e..63bd030 100644
--- a/gtk/gtkmain.h
+++ b/gtk/gtkmain.h
@@ -209,6 +209,7 @@ void	   gtk_key_snooper_remove  (guint	    snooper_handler_id);
 GdkEvent*       gtk_get_current_event       (void);
 guint32         gtk_get_current_event_time  (void);
 gboolean        gtk_get_current_event_state (GdkModifierType *state);
+GdkDevice *     gtk_get_current_event_device (void);
 
 GtkWidget* gtk_get_event_widget	   (GdkEvent	   *event);
 
diff --git a/gtk/gtkmarshalers.list b/gtk/gtkmarshalers.list
index 77873cb..22af46d 100644
--- a/gtk/gtkmarshalers.list
+++ b/gtk/gtkmarshalers.list
@@ -83,6 +83,7 @@ VOID:OBJECT,INT,OBJECT
 VOID:OBJECT,INT,INT
 VOID:OBJECT,INT,INT,BOXED,UINT,UINT
 VOID:OBJECT,OBJECT
+VOID:OBJECT,POINTER
 VOID:OBJECT,STRING
 VOID:OBJECT,STRING,STRING
 VOID:OBJECT,UINT
diff --git a/gtk/gtkwidget.c b/gtk/gtkwidget.c
index 5677fd7..a34ef25 100644
--- a/gtk/gtkwidget.c
+++ b/gtk/gtkwidget.c
@@ -128,6 +128,7 @@ enum {
   KEYNAV_FAILED,
   DRAG_FAILED,
   DAMAGE_EVENT,
+  MULTIDEVICE_EVENT,
   LAST_SIGNAL
 };
 
@@ -159,6 +160,7 @@ enum {
 };
 
 typedef	struct	_GtkStateData	 GtkStateData;
+typedef struct  _GtkMultiDeviceData GtkMultiDeviceData;
 
 struct _GtkStateData
 {
@@ -168,6 +170,12 @@ struct _GtkStateData
   guint		use_forall : 1;
 };
 
+struct _GtkMultiDeviceData
+{
+  GList *groups;
+  GHashTable *by_dev;
+};
+
 /* --- prototypes --- */
 static void	gtk_widget_class_init		(GtkWidgetClass     *klass);
 static void	gtk_widget_base_class_finalize	(GtkWidgetClass     *klass);
@@ -311,6 +319,8 @@ static GQuark		quark_mnemonic_labels = 0;
 static GQuark		quark_tooltip_markup = 0;
 static GQuark		quark_has_tooltip = 0;
 static GQuark		quark_tooltip_window = 0;
+static GQuark           quark_multidevice_events = 0;
+static GQuark           quark_multidevice_data = 0;
 GParamSpecPool         *_gtk_widget_child_property_pool = NULL;
 GObjectNotifyContext   *_gtk_widget_child_property_notify_context = NULL;
 
@@ -399,6 +409,8 @@ gtk_widget_class_init (GtkWidgetClass *klass)
   quark_tooltip_markup = g_quark_from_static_string ("gtk-tooltip-markup");
   quark_has_tooltip = g_quark_from_static_string ("gtk-has-tooltip");
   quark_tooltip_window = g_quark_from_static_string ("gtk-tooltip-window");
+  quark_multidevice_events = g_quark_from_static_string ("gtk-multidevice-events");
+  quark_multidevice_data = g_quark_from_static_string ("gtk-multidevice-data");
 
   style_property_spec_pool = g_param_spec_pool_new (FALSE);
   _gtk_widget_child_property_pool = g_param_spec_pool_new (TRUE);
@@ -2201,6 +2213,15 @@ gtk_widget_class_init (GtkWidgetClass *klass)
 		  _gtk_marshal_BOOLEAN__UINT,
                   G_TYPE_BOOLEAN, 1, G_TYPE_UINT);
 
+  widget_signals[MULTIDEVICE_EVENT] =
+    g_signal_new (I_("multidevice-event"),
+                  G_TYPE_FROM_CLASS (gobject_class),
+                  G_SIGNAL_RUN_LAST,
+                  0, NULL, NULL,
+                  _gtk_marshal_VOID__OBJECT_POINTER,
+                  G_TYPE_NONE, 2,
+                  GTK_TYPE_DEVICE_GROUP, G_TYPE_POINTER);
+
   binding_set = gtk_binding_set_by_class (klass);
   gtk_binding_entry_add_signal (binding_set, GDK_F10, GDK_SHIFT_MASK,
                                 "popup-menu", 0);
@@ -4643,6 +4664,84 @@ event_window_is_still_viewable (GdkEvent *event)
     }
 }
 
+static void
+compose_multidevice_event (GtkWidget               *widget,
+                           GdkDevice               *device,
+                           GdkEventMotion          *new_event)
+{
+  GHashTable *multidevice_events;
+  GtkMultiDeviceEventType type;
+  GtkDeviceGroup *group;
+  GtkMultiDeviceData *data;
+  GtkMultiDeviceEvent event;
+  GdkEvent *updated_event;
+  GList *devices;
+  gint i = 0;
+
+  data = g_object_get_qdata ((GObject *) widget,
+                             quark_multidevice_data);
+  if (!data)
+    return;
+
+  group = g_hash_table_lookup (data->by_dev, device);
+
+  if (!group)
+    return;
+
+  multidevice_events = g_object_get_qdata ((GObject *) widget,
+                                           quark_multidevice_events);
+
+  if (G_UNLIKELY (!multidevice_events))
+    {
+      multidevice_events = g_hash_table_new_full (g_direct_hash,
+                                                   g_direct_equal,
+                                                   (GDestroyNotify) g_object_unref,
+                                                   (GDestroyNotify) gdk_event_free);
+      g_object_set_qdata_full ((GObject *) widget,
+                               quark_multidevice_events,
+			       multidevice_events,
+                               (GDestroyNotify) g_hash_table_destroy);
+    }
+
+  if (!new_event)
+    {
+      g_hash_table_remove (multidevice_events, device);
+      type = GTK_EVENT_DEVICE_REMOVED;
+      updated_event = NULL;
+    }
+  else
+    {
+      if (g_hash_table_lookup (multidevice_events, device) == NULL)
+        type = GTK_EVENT_DEVICE_ADDED;
+      else
+        type = GTK_EVENT_DEVICE_UPDATED;
+
+      updated_event = gdk_event_copy ((GdkEvent *) new_event);
+
+      g_hash_table_insert (multidevice_events,
+                           g_object_ref (device),
+                           updated_event);
+    }
+
+  devices = gtk_device_group_get_devices (group);
+
+  /* Compose event */
+  event.type = type;
+  event.n_events = g_list_length (devices);
+  event.events = g_new0 (GdkEventMotion *, event.n_events);
+  event.updated_event = (GdkEventMotion *) updated_event;
+
+  while (devices)
+    {
+      event.events[i] = g_hash_table_lookup (multidevice_events, devices->data);
+      devices = devices->next;
+      i++;
+    }
+
+  g_signal_emit (widget, widget_signals[MULTIDEVICE_EVENT], 0, group, &event);
+  g_free (event.events);
+}
+
 static gint
 gtk_widget_event_internal (GtkWidget *widget,
 			   GdkEvent  *event)
@@ -4765,6 +4864,15 @@ gtk_widget_event_internal (GtkWidget *widget,
 	}
       if (signal_num != -1)
 	g_signal_emit (widget, widget_signals[signal_num], 0, event, &return_val);
+
+      if (event->type == GDK_MOTION_NOTIFY &&
+          (GTK_WIDGET_FLAGS (widget) & GTK_MULTIDEVICE) != 0)
+        {
+          GdkEventMotion *event_motion;
+
+          event_motion = (GdkEventMotion *) event;
+          compose_multidevice_event (widget, event_motion->device, event_motion);
+        }
     }
   if (WIDGET_REALIZED_FOR_EVENT (widget, event))
     g_signal_emit (widget, widget_signals[EVENT_AFTER], 0, event);
@@ -10922,5 +11030,242 @@ gtk_widget_get_window (GtkWidget *widget)
   return widget->window;
 }
 
+void
+gtk_widget_set_support_multidevice (GtkWidget *widget,
+                                    gboolean   support_multidevice)
+{
+  g_return_if_fail (GTK_IS_WIDGET (widget));
+
+  if (support_multidevice)
+    {
+      GTK_WIDGET_SET_FLAGS (widget, GTK_MULTIDEVICE);
+      gtk_widget_set_extension_events (widget, GDK_EXTENSION_EVENTS_ALL);
+    }
+  else
+    {
+      GTK_WIDGET_UNSET_FLAGS (widget, GTK_MULTIDEVICE);
+      gtk_widget_set_extension_events (widget, GDK_EXTENSION_EVENTS_NONE);
+    }
+}
+
+static GdkEventMotion *
+convert_event_to_motion (GdkEvent *event)
+{
+  GdkEventMotion *new_event;
+
+  new_event = gdk_event_new (GDK_MOTION_NOTIFY);
+
+  switch (event->type)
+    {
+    case GDK_BUTTON_PRESS:
+    case GDK_2BUTTON_PRESS:
+    case GDK_3BUTTON_PRESS:
+    case GDK_BUTTON_RELEASE:
+      new_event->window = g_object_ref (event->button.window);
+      new_event->send_event = TRUE;
+      new_event->time = event->button.time;
+      new_event->x = event->button.x;
+      new_event->y = event->button.y;
+
+      if (event->button.axes)
+	new_event->axes = g_memdup (event->button.axes,
+                                    sizeof (gdouble) * event->button.device->num_axes);
+
+      new_event->state = 0; /* FIXME */
+      new_event->is_hint = FALSE;
+      new_event->device = g_object_ref (event->button.device);
+      new_event->x_root = event->button.x_root;
+      new_event->y_root = event->button.y_root;
+      break;
+    case GDK_ENTER_NOTIFY:
+    case GDK_LEAVE_NOTIFY:
+      new_event->window = g_object_ref (event->crossing.window);
+      new_event->send_event = TRUE;
+      new_event->time = event->crossing.time;
+      new_event->x = event->crossing.x;
+      new_event->y = event->crossing.y;
+      new_event->axes = NULL; /* FIXME: not ideal for non-mice */
+      new_event->state = 0; /* FIXME */
+      new_event->is_hint = FALSE;
+      new_event->device = g_object_ref (event->crossing.device);
+      new_event->x_root = event->crossing.x_root;
+      new_event->y_root = event->crossing.y_root;
+      break;
+    default:
+      g_warning ("Event with type %d can not be transformed to GdkEventMotion", event->type);
+      gdk_event_free ((GdkEvent *) new_event);
+      new_event = NULL;
+    }
+
+  return new_event;
+}
+
+static void
+device_group_device_added (GtkDeviceGroup *group,
+                           GdkDevice      *device,
+                           GtkWidget      *widget)
+{
+  GtkMultiDeviceData *data;
+  GtkDeviceGroup *old_group;
+  GdkEventMotion *new_event = NULL;
+  GtkWidget *event_widget;
+  GdkEvent *event;
+
+  data = g_object_get_qdata (G_OBJECT (widget), quark_multidevice_data);
+
+  if (G_UNLIKELY (!data))
+    return;
+
+  /* Remove device from old group, if any */
+  old_group = g_hash_table_lookup (data->by_dev, device);
+
+  if (old_group)
+    gtk_device_group_remove_device (old_group, device);
+
+  g_hash_table_insert (data->by_dev,
+                       g_object_ref (device),
+                       g_object_ref (group));
+
+  event = gtk_get_current_event ();
+
+  if (!event)
+    return;
+
+  if (event->type == GDK_MOTION_NOTIFY)
+    new_event = (GdkEventMotion *) event;
+  else
+    {
+      event_widget = gtk_get_event_widget (event);
+
+      if (widget == event_widget)
+        new_event = convert_event_to_motion (event);
+
+      gdk_event_free (event);
+    }
+
+  if (new_event)
+    {
+      gtk_widget_event_internal (widget, (GdkEvent *) new_event);
+      gdk_event_free ((GdkEvent *) new_event);
+    }
+}
+
+static void
+device_group_device_removed (GtkDeviceGroup *group,
+                             GdkDevice      *device,
+                             GtkWidget      *widget)
+{
+  GtkMultiDeviceData *data;
+
+  data = g_object_get_qdata (G_OBJECT (widget), quark_multidevice_data);
+
+  g_assert (data != NULL);
+
+  compose_multidevice_event (widget, device, NULL);
+  g_hash_table_remove (data->by_dev, device);
+}
+
+static void
+free_multidevice_data (GtkMultiDeviceData *data)
+{
+  g_list_foreach (data->groups, (GFunc) g_object_unref, NULL);
+  g_list_free (data->groups);
+
+  g_hash_table_destroy (data->by_dev);
+
+  g_slice_free (GtkMultiDeviceData, data);
+}
+
+GtkDeviceGroup *
+gtk_widget_create_device_group (GtkWidget *widget)
+{
+  GtkMultiDeviceData *data;
+  GtkDeviceGroup *group;
+
+  g_return_val_if_fail (GTK_IS_WIDGET (widget), NULL);
+
+  group = g_object_new (GTK_TYPE_DEVICE_GROUP, NULL);
+
+  g_signal_connect (group, "device-added",
+                    G_CALLBACK (device_group_device_added), widget);
+  g_signal_connect (group, "device-removed",
+                    G_CALLBACK (device_group_device_removed), widget);
+
+  data = g_object_get_qdata (G_OBJECT (widget), quark_multidevice_data);
+
+  if (G_UNLIKELY (!data))
+    {
+      data = g_slice_new0 (GtkMultiDeviceData);
+      data->by_dev = g_hash_table_new_full (g_direct_hash,
+                                            g_direct_equal,
+                                            (GDestroyNotify) g_object_unref,
+                                            (GDestroyNotify) g_object_unref);
+
+      g_object_set_qdata_full (G_OBJECT (widget),
+                               quark_multidevice_data,
+                               data,
+                               (GDestroyNotify) free_multidevice_data);
+    }
+
+  data->groups = g_list_prepend (data->groups, group);
+
+  return group;
+}
+
+void
+gtk_widget_remove_device_group (GtkWidget      *widget,
+                                GtkDeviceGroup *group)
+{
+  GtkMultiDeviceData *data;
+  GList *devices, *g;
+
+  g_return_if_fail (GTK_IS_WIDGET (widget));
+  g_return_if_fail (GTK_IS_DEVICE_GROUP (group));
+
+  data = g_object_get_qdata (G_OBJECT (widget), quark_multidevice_data);
+
+  if (G_UNLIKELY (!data))
+    return;
+
+  g = g_list_find (data->groups, group);
+
+  if (G_UNLIKELY (!g))
+    return;
+
+  devices = gtk_device_group_get_devices (group);
+
+  /* Free per-device data */
+  while (devices)
+    {
+      g_hash_table_remove (data->by_dev, devices->data);
+      devices = devices->next;
+    }
+
+  /* Free group */
+  data->groups = g_list_remove_link (data->groups, g);
+  g_object_unref (g->data);
+  g_list_free_1 (g);
+}
+
+GtkDeviceGroup *
+gtk_widget_get_group_for_device (GtkWidget *widget,
+                                 GdkDevice *device)
+{
+  GtkMultiDeviceData *data;
+  GtkDeviceGroup *group = NULL;
+
+  g_return_val_if_fail (GTK_IS_WIDGET (widget), NULL);
+  g_return_val_if_fail (GDK_IS_DEVICE (device), NULL);
+
+  data = g_object_get_qdata (G_OBJECT (widget), quark_multidevice_data);
+
+  if (!data)
+    return NULL;
+
+  group = g_hash_table_lookup (data->by_dev, device);
+
+  return group;
+}
+
 #define __GTK_WIDGET_C__
 #include "gtkaliasdef.c"
diff --git a/gtk/gtkwidget.h b/gtk/gtkwidget.h
index ed28663..daa20ae 100644
--- a/gtk/gtkwidget.h
+++ b/gtk/gtkwidget.h
@@ -37,6 +37,7 @@
 #include <gtk/gtkadjustment.h>
 #include <gtk/gtkstyle.h>
 #include <gtk/gtksettings.h>
+#include <gtk/gtkdevicegroup.h>
 #include <atk/atk.h>
 
 G_BEGIN_DECLS
@@ -78,7 +79,8 @@ typedef enum
   GTK_RECEIVES_DEFAULT = 1 << 20,
 
   GTK_DOUBLE_BUFFERED  = 1 << 21,
-  GTK_NO_SHOW_ALL      = 1 << 22
+  GTK_NO_SHOW_ALL      = 1 << 22,
+  GTK_MULTIDEVICE      = 1 << 23
 } GtkWidgetFlags;
 
 /* Kinds of widget-specific help */
@@ -146,6 +148,7 @@ typedef struct _GtkWidgetShapeInfo GtkWidgetShapeInfo;
 typedef struct _GtkClipboard	   GtkClipboard;
 typedef struct _GtkTooltip         GtkTooltip;
 typedef struct _GtkWindow          GtkWindow;
+typedef struct _GtkMultiDeviceEvent GtkMultiDeviceEvent;
 typedef void     (*GtkCallback)        (GtkWidget        *widget,
 					gpointer	  data);
 
@@ -431,6 +434,21 @@ struct _GtkWidgetClass
   void (*_gtk_reserved7) (void);
 };
 
+typedef enum
+{
+  GTK_EVENT_DEVICE_ADDED,
+  GTK_EVENT_DEVICE_REMOVED,
+  GTK_EVENT_DEVICE_UPDATED
+} GtkMultiDeviceEventType;
+
+struct _GtkMultiDeviceEvent
+{
+  GtkMultiDeviceEventType type;
+  guint n_events;
+  GdkEventMotion **events;
+  GdkEventMotion *updated_event;
+};
+
 struct _GtkWidgetAuxInfo
 {
   gint x;
@@ -668,6 +686,16 @@ GtkClipboard *gtk_widget_get_clipboard   (GtkWidget *widget,
 GdkPixmap *   gtk_widget_get_snapshot    (GtkWidget    *widget,
                                           GdkRectangle *clip_rect);
 
+/* Multidevice support */
+void             gtk_widget_set_support_multidevice (GtkWidget      *widget,
+                                                     gboolean        support_multidevice);
+GtkDeviceGroup * gtk_widget_get_group_for_device    (GtkWidget      *widget,
+                                                     GdkDevice      *device);
+GtkDeviceGroup * gtk_widget_create_device_group     (GtkWidget      *widget);
+void             gtk_widget_remove_device_group     (GtkWidget      *widget,
+                                                     GtkDeviceGroup *group);
+
+
 #ifndef GTK_DISABLE_DEPRECATED
 #define gtk_widget_set_visual(widget,visual)  ((void) 0)
 #define gtk_widget_push_visual(visual)        ((void) 0)
diff --git a/tests/Makefile.am b/tests/Makefile.am
index 8956c65..42df89c 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -1,6 +1,8 @@
 ## Makefile.am for gtk+/tests
 include $(top_srcdir)/Makefile.decl
 
+SUBDIRS = multidevice
+
 INCLUDES =				\
 	-I$(top_srcdir)			\
 	-I$(top_builddir)/gdk		\
diff --git a/tests/multidevice/Makefile.am b/tests/multidevice/Makefile.am
new file mode 100644
index 0000000..a26d276
--- /dev/null
+++ b/tests/multidevice/Makefile.am
@@ -0,0 +1,47 @@
+## Makefile.am for gtk+/tests/mpx
+include $(top_srcdir)/Makefile.decl
+
+INCLUDES =				\
+	-I$(top_srcdir)			\
+	-I$(top_builddir)/gdk		\
+	-I$(top_srcdir)/gdk		\
+	-DGDK_DISABLE_DEPRECATED	\
+	-DGTK_DISABLE_DEPRECATED	\
+	$(GTK_DEBUG_FLAGS)		\
+	$(GTK_DEP_CFLAGS)
+
+DEPS =									\
+	$(top_builddir)/gdk-pixbuf/libgdk_pixbuf-$(GTK_API_VERSION).la	\
+	$(top_builddir)/gdk/$(gdktargetlib)				\
+	$(top_builddir)/gtk/$(gtktargetlib)
+
+LDADDS =								\
+	$(top_builddir)/gdk-pixbuf/libgdk_pixbuf-$(GTK_API_VERSION).la	\
+	$(top_builddir)/gdk/$(gdktargetlib)				\
+	$(top_builddir)/gtk/$(gtktargetlib)
+
+noinst_PROGRAMS = 			\
+	testangle			\
+	testcoordinates			\
+	testphotoalbum
+
+testangle_DEPENDENCIES = $(TEST_DEPS)
+testcoordinates_DEPENDENCIES = $(TEST_DEPS)
+testphotoalbum_DEPENDENCIES = $(TEST_DEPS)
+
+testangle_LDADD = $(LDADDS)
+testcoordinates_LDADD = $(LDADDS)
+testphotoalbum_LDADD = $(LDADDS)
+
+testangle_SOURCES =		\
+	testangle.c
+
+testcoordinates_SOURCES = 	\
+	testcoordinatewidget.c	\
+	testcoordinatewidget.h	\
+	testcoordinates.c
+
+testphotoalbum_SOURCES =	\
+	testphotoalbumwidget.c	\
+	testphotoalbumwidget.h	\
+	testphotoalbum.c
diff --git a/tests/multidevice/testangle.c b/tests/multidevice/testangle.c
new file mode 100644
index 0000000..97b4844
--- /dev/null
+++ b/tests/multidevice/testangle.c
@@ -0,0 +1,188 @@
+/*
+ * Copyright (C) 2009 Carlos Garnacho  <carlosg@gnome.org>
+ *
+ * This work is provided "as is"; redistribution and modification
+ * in whole or in part, in any medium, physical or electronic is
+ * permitted without restriction.
+ *
+ * This work is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * In no event shall the authors or contributors be liable for any
+ * direct, indirect, incidental, special, exemplary, or consequential
+ * damages (including, but not limited to, procurement of substitute
+ * goods or services; loss of use, data, or profits; or business
+ * interruption) however caused and on any theory of liability, whether
+ * in contract, strict liability, or tort (including negligence or
+ * otherwise) arising in any way out of the use of this software, even
+ * if advised of the possibility of such damage.
+ */
+
+#include <gtk/gtk.h>
+
+typedef struct DeviceData DeviceData;
+typedef struct Data Data;
+
+struct Data
+{
+  GtkDeviceGroup *group;
+
+  gdouble x;
+  gdouble y;
+  gdouble angle;
+  gdouble distance;
+};
+
+static gboolean
+enter_notify_cb (GtkWidget        *widget,
+                 GdkEventCrossing *event,
+                 gpointer          user_data)
+{
+  Data *data;
+  GList *devices;
+
+  data = (Data *) user_data;
+  devices = gtk_device_group_get_devices (data->group);
+
+  if (g_list_length (devices) < 2)
+    gtk_device_group_add_device (data->group, event->device);
+}
+
+static gboolean
+leave_notify_cb (GtkWidget        *widget,
+                 GdkEventCrossing *event,
+                 gpointer          user_data)
+{
+  Data *data;
+
+  data = (Data *) user_data;
+
+  gtk_device_group_remove_device (data->group, event->device);
+}
+
+static void
+multidevice_cb (GtkWidget           *widget,
+                GtkDeviceGroup      *group,
+                GtkMultiDeviceEvent *event,
+                gpointer             user_data)
+{
+  gint i;
+  Data *data;
+
+  data = user_data;
+
+  if (event->n_events > 0)
+    {
+      data->x = event->events[0]->x;
+      data->y = event->events[0]->y;
+    }
+
+  if (event->n_events > 1)
+    {
+      gdk_events_get_distance ((GdkEvent *) event->events[0],
+                               (GdkEvent *) event->events[1],
+                               &data->distance);
+      gdk_events_get_angle ((GdkEvent *) event->events[0],
+                            (GdkEvent *) event->events[1],
+                            &data->angle);
+    }
+  else
+    {
+      data->distance = 0;
+      data->angle = 0;
+    }
+
+  gtk_widget_queue_draw (widget);
+}
+
+static gboolean
+expose_cb (GtkWidget      *widget,
+           GdkEventExpose *event,
+           gpointer        user_data)
+{
+  Data *data;
+  cairo_t *cr;
+
+  data = (Data *) user_data;
+  cr = gdk_cairo_create (widget->window);
+
+  cairo_translate (cr, data->x, data->y);
+
+  cairo_save (cr);
+
+  cairo_set_source_rgb (cr, 0., 0., 0.);
+  cairo_move_to (cr, 0, 0);
+  cairo_rel_line_to (cr, 1000, 0);
+  cairo_stroke (cr);
+
+  cairo_restore (cr);
+
+  if (data->angle > 0)
+    {
+      cairo_save (cr);
+
+      cairo_set_source_rgb (cr, 0, 0, 1);
+      cairo_move_to (cr, 0, 0);
+
+      cairo_arc (cr,
+                 0.,
+                 0.,
+                 MAX (10., data->distance),
+                 0.,
+                 data->angle);
+
+      cairo_close_path (cr);
+
+      cairo_fill (cr);
+
+      cairo_restore (cr);
+
+      cairo_set_source_rgb (cr, 1., 0., 0.);
+      cairo_rotate (cr, data->angle);
+      cairo_move_to (cr, 0, 0);
+      cairo_rel_line_to (cr, data->distance, 0);
+      cairo_stroke (cr);
+    }
+
+  cairo_destroy (cr);
+
+  return TRUE;
+}
+
+int
+main (int argc, gchar *argv[])
+{
+  GtkWidget *window;
+  GList *devices;
+  Data data = { 0 };
+
+  gtk_init (&argc, &argv);
+
+  window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+  gtk_widget_set_app_paintable (window, TRUE);
+
+  data.group = gtk_widget_create_device_group (window);
+
+  gtk_widget_add_events (window,
+                         (GDK_POINTER_MOTION_MASK |
+                          GDK_ENTER_NOTIFY_MASK |
+                          GDK_LEAVE_NOTIFY_MASK));
+
+  gtk_widget_set_support_multidevice (window, TRUE);
+
+  g_signal_connect (window, "enter-notify-event",
+                    G_CALLBACK (enter_notify_cb), &data);
+  g_signal_connect (window, "leave-notify-event",
+                    G_CALLBACK (leave_notify_cb), &data);
+  g_signal_connect (window, "multidevice-event",
+                    G_CALLBACK (multidevice_cb), &data);
+  g_signal_connect (window, "expose-event",
+                    G_CALLBACK (expose_cb), &data);
+
+  gtk_widget_show (window);
+
+  gtk_main ();
+
+  return 0;
+}
diff --git a/tests/multidevice/testcoordinates.c b/tests/multidevice/testcoordinates.c
new file mode 100644
index 0000000..73918f7
--- /dev/null
+++ b/tests/multidevice/testcoordinates.c
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2009 Carlos Garnacho  <carlosg@gnome.org>
+ *
+ * This work is provided "as is"; redistribution and modification
+ * in whole or in part, in any medium, physical or electronic is
+ * permitted without restriction.
+ *
+ * This work is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * In no event shall the authors or contributors be liable for any
+ * direct, indirect, incidental, special, exemplary, or consequential
+ * damages (including, but not limited to, procurement of substitute
+ * goods or services; loss of use, data, or profits; or business
+ * interruption) however caused and on any theory of liability, whether
+ * in contract, strict liability, or tort (including negligence or
+ * otherwise) arising in any way out of the use of this software, even
+ * if advised of the possibility of such damage.
+ */
+
+#include <gtk/gtk.h>
+#include "testcoordinatewidget.h"
+
+int
+main (int argc, char *argv[])
+{
+  GtkWidget *window, *widget;
+  GList *devices;
+
+  gtk_init (&argc, &argv);
+
+  window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+
+  widget = test_coordinate_widget_new ();
+  gtk_container_add (GTK_CONTAINER (window), widget);
+
+  g_signal_connect (window, "delete-event",
+                    G_CALLBACK (gtk_main_quit), NULL);
+
+  gtk_widget_show_all (window);
+
+  gtk_main ();
+
+  return 0;
+}
diff --git a/tests/multidevice/testcoordinatewidget.c b/tests/multidevice/testcoordinatewidget.c
new file mode 100644
index 0000000..91ba1fa
--- /dev/null
+++ b/tests/multidevice/testcoordinatewidget.c
@@ -0,0 +1,249 @@
+/*
+ * Copyright (C) 2009 Carlos Garnacho  <carlosg@gnome.org>
+ *
+ * This work is provided "as is"; redistribution and modification
+ * in whole or in part, in any medium, physical or electronic is
+ * permitted without restriction.
+ *
+ * This work is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * In no event shall the authors or contributors be liable for any
+ * direct, indirect, incidental, special, exemplary, or consequential
+ * damages (including, but not limited to, procurement of substitute
+ * goods or services; loss of use, data, or profits; or business
+ * interruption) however caused and on any theory of liability, whether
+ * in contract, strict liability, or tort (including negligence or
+ * otherwise) arising in any way out of the use of this software, even
+ * if advised of the possibility of such damage.
+ */
+
+#include "testcoordinatewidget.h"
+
+#define TEST_COORDINATE_WIDGET_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), TEST_TYPE_COORDINATE_WIDGET, TestCoordinateWidgetPrivate))
+
+typedef struct TestCoordinateWidgetPrivate TestCoordinateWidgetPrivate;
+
+struct TestCoordinateWidgetPrivate
+{
+  GtkDeviceGroup *group;
+  GHashTable *coordinates;
+};
+
+static void test_coordinate_widget_class_init        (TestCoordinateWidgetClass *klass);
+static void test_coordinate_widget_init              (TestCoordinateWidget      *coordinate);
+
+static void test_coordinate_widget_destroy           (GtkObject          *object);
+
+static gboolean test_coordinate_widget_enter_notify  (GtkWidget          *widget,
+                                                      GdkEventCrossing   *event);
+static gboolean test_coordinate_widget_leave_notify  (GtkWidget          *widget,
+                                                      GdkEventCrossing   *event);
+static gboolean test_coordinate_widget_expose        (GtkWidget          *widget,
+                                                      GdkEventExpose     *event);
+
+static void     test_coordinate_widget_multidevice_event (GtkWidget           *widget,
+                                                          GtkDeviceGroup      *group,
+                                                          GtkMultiDeviceEvent *event);
+
+G_DEFINE_TYPE (TestCoordinateWidget, test_coordinate_widget, GTK_TYPE_DRAWING_AREA)
+
+
+static void
+test_coordinate_widget_class_init (TestCoordinateWidgetClass *klass)
+{
+  GtkObjectClass *object_class = GTK_OBJECT_CLASS (klass);
+  GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
+
+  object_class->destroy = test_coordinate_widget_destroy;
+
+  widget_class->enter_notify_event = test_coordinate_widget_enter_notify;
+  widget_class->leave_notify_event = test_coordinate_widget_leave_notify;
+  widget_class->expose_event = test_coordinate_widget_expose;
+
+  g_type_class_add_private (klass, sizeof (TestCoordinateWidgetPrivate));
+}
+
+static void
+test_coordinate_widget_init (TestCoordinateWidget *coord)
+{
+  TestCoordinateWidgetPrivate *priv;
+  GtkWidget *widget;
+
+  priv = TEST_COORDINATE_WIDGET_GET_PRIVATE (coord);
+  widget = GTK_WIDGET (coord);
+
+  priv->coordinates = g_hash_table_new_full (g_direct_hash,
+                                             g_direct_equal,
+                                             (GDestroyNotify) g_object_unref,
+                                             (GDestroyNotify) g_free);
+
+  gtk_widget_add_events (widget,
+                         (GDK_POINTER_MOTION_MASK |
+                          GDK_ENTER_NOTIFY_MASK |
+                          GDK_LEAVE_NOTIFY_MASK));
+
+  priv->group = gtk_widget_create_device_group (GTK_WIDGET (coord));
+  gtk_widget_set_support_multidevice (widget, TRUE);
+
+  /* Multidevice events are not exposed through GtkWidgetClass */
+  g_signal_connect (coord, "multidevice-event",
+                    G_CALLBACK (test_coordinate_widget_multidevice_event), NULL);
+}
+
+static void
+test_coordinate_widget_destroy (GtkObject *object)
+{
+  TestCoordinateWidgetPrivate *priv;
+
+  priv = TEST_COORDINATE_WIDGET_GET_PRIVATE (object);
+
+  if (priv->coordinates)
+    {
+      g_hash_table_destroy (priv->coordinates);
+      priv->coordinates = NULL;
+    }
+
+  if (priv->group)
+    {
+      gtk_widget_remove_device_group (GTK_WIDGET (object), priv->group);
+      priv->group = NULL;
+    }
+
+  GTK_OBJECT_CLASS (test_coordinate_widget_parent_class)->destroy (object);
+}
+
+static gboolean
+test_coordinate_widget_enter_notify (GtkWidget        *widget,
+                                     GdkEventCrossing *event)
+{
+  TestCoordinateWidgetPrivate *priv;
+  GdkPoint *point;
+
+  priv = TEST_COORDINATE_WIDGET_GET_PRIVATE (widget);
+
+  gtk_device_group_add_device (priv->group, event->device);
+
+  point = g_new (GdkPoint, 1);
+  point->x = event->x;
+  point->y = event->y;
+
+  g_hash_table_insert (priv->coordinates,
+                       g_object_ref (event->device),
+                       point);
+
+  gtk_widget_queue_draw (widget);
+
+  return FALSE;
+}
+
+static gboolean
+test_coordinate_widget_leave_notify (GtkWidget        *widget,
+                                     GdkEventCrossing *event)
+{
+  TestCoordinateWidgetPrivate *priv;
+
+  priv = TEST_COORDINATE_WIDGET_GET_PRIVATE (widget);
+
+  gtk_device_group_add_device (priv->group, event->device);
+  g_hash_table_remove (priv->coordinates, event->device);
+
+  gtk_widget_queue_draw (widget);
+
+  return FALSE;
+}
+
+static void
+test_coordinate_widget_multidevice_event (GtkWidget           *widget,
+                                          GtkDeviceGroup      *group,
+                                          GtkMultiDeviceEvent *event)
+{
+  TestCoordinateWidgetPrivate *priv;
+  GdkEventMotion *ev;
+  GdkPoint *point;
+
+  priv = TEST_COORDINATE_WIDGET_GET_PRIVATE (widget);
+  ev = event->updated_event;
+
+  point = g_hash_table_lookup (priv->coordinates, ev->device);
+
+  if (G_UNLIKELY (!point))
+    return;
+
+  point->x = ev->x;
+  point->y = ev->y;
+
+  gtk_widget_queue_draw (widget);
+}
+
+static gboolean
+test_coordinate_widget_expose (GtkWidget      *widget,
+                               GdkEventExpose *event)
+{
+  TestCoordinateWidgetPrivate *priv;
+  GList *coords, *c;
+  cairo_t *cr;
+
+  priv = TEST_COORDINATE_WIDGET_GET_PRIVATE (widget);
+  cr = gdk_cairo_create (widget->window);
+
+  coords = g_hash_table_get_values (priv->coordinates);
+
+  cairo_set_source_rgb (cr, 1., 1., 1.);
+  cairo_rectangle (cr,
+                   widget->allocation.x,
+                   widget->allocation.y,
+                   widget->allocation.width,
+                   widget->allocation.height);
+  cairo_fill (cr);
+
+  cairo_set_source_rgb (cr, 0.8, 0.8, 0.8);
+
+  for (c = coords; c; c = c->next)
+    {
+      GdkPoint *point;
+
+      point = c->data;
+
+      cairo_move_to (cr, point->x, 0);
+      cairo_rel_line_to (cr, 0, widget->allocation.height);
+
+      cairo_move_to (cr, 0, point->y);
+      cairo_rel_line_to (cr, widget->allocation.width, 0);
+    }
+
+  cairo_stroke (cr);
+
+  cairo_set_source_rgba (cr, 0.5, 0., 0., 0.5);
+
+  if (g_list_length (coords) > 1)
+    {
+      for (c = coords; c; c = c->next)
+        {
+          GdkPoint *point;
+
+          point = c->data;
+
+          cairo_line_to (cr, point->x, point->y);
+        }
+
+      cairo_close_path (cr);
+      cairo_fill_preserve (cr);
+
+      cairo_set_source_rgba (cr, 0., 0., 0.5, 0.5);
+      cairo_stroke (cr);
+    }
+
+  cairo_destroy (cr);
+
+  g_list_free (coords);
+
+  return TRUE;
+}
+
+GtkWidget *
+test_coordinate_widget_new (void)
+{
+  return g_object_new (TEST_TYPE_COORDINATE_WIDGET, NULL);
+}
diff --git a/tests/multidevice/testcoordinatewidget.h b/tests/multidevice/testcoordinatewidget.h
new file mode 100644
index 0000000..bfa764d
--- /dev/null
+++ b/tests/multidevice/testcoordinatewidget.h
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2009 Carlos Garnacho  <carlosg@gnome.org>
+ *
+ * This work is provided "as is"; redistribution and modification
+ * in whole or in part, in any medium, physical or electronic is
+ * permitted without restriction.
+ *
+ * This work is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * In no event shall the authors or contributors be liable for any
+ * direct, indirect, incidental, special, exemplary, or consequential
+ * damages (including, but not limited to, procurement of substitute
+ * goods or services; loss of use, data, or profits; or business
+ * interruption) however caused and on any theory of liability, whether
+ * in contract, strict liability, or tort (including negligence or
+ * otherwise) arising in any way out of the use of this software, even
+ * if advised of the possibility of such damage.
+ */
+
+#ifndef __TEST_COORDINATE_WIDGET_H__
+#define __TEST_COORDINATE_WIDGET_H__
+
+#include <gtk/gtk.h>
+
+G_BEGIN_DECLS
+
+#define TEST_TYPE_COORDINATE_WIDGET         (test_coordinate_widget_get_type ())
+#define TEST_COORDINATE_WIDGET(o)           (G_TYPE_CHECK_INSTANCE_CAST ((o), TEST_TYPE_COORDINATE_WIDGET, TestCoordinateWidget))
+#define TEST_COORDINATE_WIDGET_CLASS(c)     (G_TYPE_CHECK_CLASS_CAST    ((c), TEST_TYPE_COORDINATE_WIDGET, TestCoordinateWidgetClass))
+#define TEST_IS_COORDINATE_WIDGET(o)        (G_TYPE_CHECK_INSTANCE_TYPE ((o), TEST_TYPE_COORDINATE_WIDGET))
+#define TEST_IS_COORDINATE_WIDGET_CLASS(c)  (G_TYPE_CHECK_CLASS_TYPE    ((c), TEST_TYPE_COORDINATE_WIDGET))
+#define TEST_COORDINATE_WIDGET_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS  ((o), TEST_TYPE_COORDINATE_WIDGET, TestCoordinateWidgetClass))
+
+typedef struct TestCoordinateWidget TestCoordinateWidget;
+typedef struct TestCoordinateWidgetClass TestCoordinateWidgetClass;
+
+struct TestCoordinateWidget
+{
+  GtkDrawingArea parent_instance;
+};
+
+struct TestCoordinateWidgetClass
+{
+  GtkDrawingAreaClass parent_class;
+};
+
+
+GType       test_coordinate_widget_get_type (void) G_GNUC_CONST;
+
+GtkWidget * test_coordinate_widget_new      (void);
+
+
+G_END_DECLS
+
+#endif /* __TEST_COORDINATE_WIDGET_H__ */
diff --git a/tests/multidevice/testphotoalbum.c b/tests/multidevice/testphotoalbum.c
new file mode 100644
index 0000000..ab0625f
--- /dev/null
+++ b/tests/multidevice/testphotoalbum.c
@@ -0,0 +1,112 @@
+/*
+ * Copyright (C) 2009 Carlos Garnacho  <carlosg@gnome.org>
+ *
+ * This work is provided "as is"; redistribution and modification
+ * in whole or in part, in any medium, physical or electronic is
+ * permitted without restriction.
+ *
+ * This work is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * In no event shall the authors or contributors be liable for any
+ * direct, indirect, incidental, special, exemplary, or consequential
+ * damages (including, but not limited to, procurement of substitute
+ * goods or services; loss of use, data, or profits; or business
+ * interruption) however caused and on any theory of liability, whether
+ * in contract, strict liability, or tort (including negligence or
+ * otherwise) arising in any way out of the use of this software, even
+ * if advised of the possibility of such damage.
+ */
+
+#include <gtk/gtk.h>
+#include "testphotoalbumwidget.h"
+
+static void
+add_image (TestPhotoAlbumWidget *album,
+           const gchar          *image_path)
+{
+  GdkPixbuf *pixbuf;
+
+  pixbuf = gdk_pixbuf_new_from_file_at_size (image_path,
+                                             200, -1,
+                                             NULL);
+  if (pixbuf)
+    {
+      test_photo_album_widget_add_image (album, pixbuf);
+      g_object_unref (pixbuf);
+    }
+}
+
+static void
+read_directory (GFile                *directory,
+                TestPhotoAlbumWidget *album)
+{
+  GFileEnumerator *enumerator;
+  GError *error = NULL;
+  GFileInfo *info;
+
+  enumerator = g_file_enumerate_children (directory,
+                                          G_FILE_ATTRIBUTE_STANDARD_NAME,
+                                          G_FILE_QUERY_INFO_NONE,
+                                          NULL,
+                                          &error);
+
+  if (error)
+    {
+      g_warning ("%s\n", error->message);
+      g_error_free (error);
+      return;
+    }
+
+  while ((info = g_file_enumerator_next_file (enumerator, NULL, NULL)) != NULL)
+    {
+      GFile *file;
+      gchar *path;
+
+      file = g_file_get_child (directory, g_file_info_get_name (info));
+
+      path = g_file_get_path (file);
+      add_image (album, path);
+      g_free (path);
+
+      g_object_unref (file);
+    }
+
+  g_file_enumerator_close (enumerator, NULL, NULL);
+  g_object_unref (enumerator);
+}
+
+int
+main (int argc, char *argv[])
+{
+  GtkWidget *window, *widget;
+  GList *devices;
+  GFile *dir;
+
+  if (argc != 2)
+    {
+      g_print ("USAGE: %s <path-to-directory-with-images>\n", argv[0]);
+      return -1;
+    }
+
+  gtk_init (&argc, &argv);
+
+  dir = g_file_new_for_commandline_arg (argv[1]);
+
+  window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+
+  widget = test_photo_album_widget_new ();
+  gtk_container_add (GTK_CONTAINER (window), widget);
+
+  g_signal_connect (window, "delete-event",
+                    G_CALLBACK (gtk_main_quit), NULL);
+
+  read_directory (dir, TEST_PHOTO_ALBUM_WIDGET (widget));
+
+  gtk_widget_show_all (window);
+
+  gtk_main ();
+
+  return 0;
+}
diff --git a/tests/multidevice/testphotoalbumwidget.c b/tests/multidevice/testphotoalbumwidget.c
new file mode 100644
index 0000000..e7c526b
--- /dev/null
+++ b/tests/multidevice/testphotoalbumwidget.c
@@ -0,0 +1,507 @@
+/*
+ * Copyright (C) 2009 Carlos Garnacho  <carlosg@gnome.org>
+ *
+ * This work is provided "as is"; redistribution and modification
+ * in whole or in part, in any medium, physical or electronic is
+ * permitted without restriction.
+ *
+ * This work is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * In no event shall the authors or contributors be liable for any
+ * direct, indirect, incidental, special, exemplary, or consequential
+ * damages (including, but not limited to, procurement of substitute
+ * goods or services; loss of use, data, or profits; or business
+ * interruption) however caused and on any theory of liability, whether
+ * in contract, strict liability, or tort (including negligence or
+ * otherwise) arising in any way out of the use of this software, even
+ * if advised of the possibility of such damage.
+ */
+
+#include "testphotoalbumwidget.h"
+#include <math.h>
+
+#define TEST_PHOTO_ALBUM_WIDGET_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), TEST_TYPE_PHOTO_ALBUM_WIDGET, TestPhotoAlbumWidgetPrivate))
+
+typedef struct TestPhotoAlbumWidgetPrivate TestPhotoAlbumWidgetPrivate;
+typedef struct TestPhoto TestPhoto;
+
+struct TestPhoto
+{
+  GdkPixbuf *pixbuf;
+  GtkDeviceGroup *group;
+  gdouble center_x;
+  gdouble center_y;
+  gdouble x;
+  gdouble y;
+  gdouble angle;
+  gdouble zoom;
+
+  GdkRegion *region;
+
+  gdouble base_zoom;
+  gdouble base_angle;
+  gdouble initial_distance;
+  gdouble initial_angle;
+};
+
+struct TestPhotoAlbumWidgetPrivate
+{
+  GPtrArray *photos;
+};
+
+static GQuark quark_group_photo = 0;
+
+
+static void test_photo_album_widget_class_init (TestPhotoAlbumWidgetClass *klass);
+static void test_photo_album_widget_init       (TestPhotoAlbumWidget      *album);
+
+static void test_photo_album_widget_destroy    (GtkObject          *object);
+
+static gboolean test_photo_album_widget_button_press      (GtkWidget           *widget,
+                                                           GdkEventButton      *event);
+static gboolean test_photo_album_widget_button_release    (GtkWidget           *widget,
+                                                           GdkEventButton      *event);
+static void     test_photo_album_widget_multidevice_event (GtkWidget           *widget,
+                                                           GtkDeviceGroup      *group,
+                                                           GtkMultiDeviceEvent *event);
+static gboolean test_photo_album_widget_expose            (GtkWidget           *widget,
+                                                           GdkEventExpose      *event);
+
+G_DEFINE_TYPE (TestPhotoAlbumWidget, test_photo_album_widget, GTK_TYPE_DRAWING_AREA)
+
+
+static void
+test_photo_album_widget_class_init (TestPhotoAlbumWidgetClass *klass)
+{
+  GtkObjectClass *object_class = GTK_OBJECT_CLASS (klass);
+  GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
+
+  object_class->destroy = test_photo_album_widget_destroy;
+
+  widget_class->button_press_event = test_photo_album_widget_button_press;
+  widget_class->button_release_event = test_photo_album_widget_button_release;
+  widget_class->expose_event = test_photo_album_widget_expose;
+
+  g_type_class_add_private (klass, sizeof (TestPhotoAlbumWidgetPrivate));
+
+  quark_group_photo = g_quark_from_static_string ("group-photo");
+}
+
+static void
+test_photo_album_widget_init (TestPhotoAlbumWidget *album)
+{
+  TestPhotoAlbumWidgetPrivate *priv;
+  GtkWidget *widget;
+
+  priv = TEST_PHOTO_ALBUM_WIDGET_GET_PRIVATE (album);
+  widget = GTK_WIDGET (album);
+
+  priv->photos = g_ptr_array_new ();
+
+  gtk_widget_add_events (widget,
+                         (GDK_POINTER_MOTION_MASK |
+                          GDK_BUTTON_MOTION_MASK |
+                          GDK_BUTTON_PRESS_MASK |
+                          GDK_BUTTON_RELEASE_MASK));
+
+  gtk_widget_set_support_multidevice (widget, TRUE);
+
+  /* Multidevice events are not exposed through GtkWidgetClass */
+  g_signal_connect (album, "multidevice-event",
+                    G_CALLBACK (test_photo_album_widget_multidevice_event), NULL);
+}
+
+static void
+calculate_rotated_point (gdouble  angle,
+                         gdouble  zoom,
+                         gdouble  center_x,
+                         gdouble  center_y,
+                         gdouble  point_x,
+                         gdouble  point_y,
+                         gdouble *ret_x,
+                         gdouble *ret_y)
+{
+  gdouble distance, xd, yd, ang;
+
+  xd = center_x - point_x;
+  yd = center_y - point_y;
+
+  if (xd == 0 && yd == 0)
+    {
+      *ret_x = center_x;
+      *ret_y = center_y;
+      return;
+    }
+
+  distance = sqrt ((xd * xd) + (yd * yd));
+  distance *= zoom;
+
+  ang = atan2 (xd, yd);
+
+  /* Invert angle */
+  ang = (2 * G_PI) - ang;
+
+  /* Shift it 270° */
+  ang += 3 * (G_PI / 2);
+
+  /* And constraint it to 0°-360° */
+  ang = fmod (ang, 2 * G_PI);
+  ang += angle;
+
+  *ret_x = center_x + (distance * cos (ang));
+  *ret_y = center_y + (distance * sin (ang));
+}
+
+static void
+allocate_photo_region (TestPhoto *photo)
+{
+  GdkRegion *region;
+  GdkPoint points[4];
+  gint width, height, i;
+
+  width = gdk_pixbuf_get_width (photo->pixbuf);
+  height = gdk_pixbuf_get_height (photo->pixbuf);
+
+  /* Top/left */
+  points[0].x = photo->x - photo->center_x;
+  points[0].y = photo->y - photo->center_y;
+
+  /* Top/right */
+  points[1].x = photo->x - photo->center_x + width;
+  points[1].y = photo->y - photo->center_y;
+
+  /* Bottom/right */
+  points[2].x = photo->x - photo->center_x + width;
+  points[2].y = photo->y - photo->center_y + height;
+
+  /* Bottom/left */
+  points[3].x = photo->x - photo->center_x;
+  points[3].y = photo->y - photo->center_y + height;
+
+  for (i = 0; i < 4; i++)
+    {
+      gdouble ret_x, ret_y;
+
+      calculate_rotated_point (photo->angle,
+                               photo->zoom,
+                               photo->x,
+                               photo->y,
+                               (gdouble) points[i].x,
+                               (gdouble) points[i].y,
+                               &ret_x,
+                               &ret_y);
+
+      points[i].x = (gint) ret_x;
+      points[i].y = (gint) ret_y;
+    }
+
+  if (photo->region)
+    gdk_region_destroy (photo->region);
+
+  photo->region = gdk_region_polygon (points, 4, GDK_WINDING_RULE);
+}
+
+static TestPhoto *
+test_photo_new (TestPhotoAlbumWidget *album,
+                GdkPixbuf            *pixbuf)
+{
+  TestPhoto *photo;
+  static gdouble angle = 0;
+
+  photo = g_slice_new0 (TestPhoto);
+  photo->pixbuf = g_object_ref (pixbuf);
+  photo->group = gtk_widget_create_device_group (GTK_WIDGET (album));
+  g_object_set_qdata (G_OBJECT (photo->group), quark_group_photo, photo);
+
+  photo->center_x = 0;
+  photo->center_y = 0;
+  photo->x = 0;
+  photo->y = 0;
+  photo->angle = angle;
+  photo->zoom = 1.0;
+
+  angle += 0.1;
+
+  allocate_photo_region (photo);
+
+  return photo;
+}
+
+static void
+test_photo_free (TestPhoto            *photo,
+                 TestPhotoAlbumWidget *album)
+{
+  g_object_unref (photo->pixbuf);
+  gtk_widget_remove_device_group (GTK_WIDGET (album), photo->group);
+
+  g_slice_free (TestPhoto, photo);
+}
+
+static void
+test_photo_raise (TestPhoto            *photo,
+                  TestPhotoAlbumWidget *album)
+{
+  TestPhotoAlbumWidgetPrivate *priv;
+
+  priv = TEST_PHOTO_ALBUM_WIDGET_GET_PRIVATE (album);
+  g_ptr_array_remove (priv->photos, photo);
+  g_ptr_array_add (priv->photos, photo);
+}
+
+static void
+test_photo_album_widget_destroy (GtkObject *object)
+{
+  TestPhotoAlbumWidgetPrivate *priv;
+
+  priv = TEST_PHOTO_ALBUM_WIDGET_GET_PRIVATE (object);
+
+  if (priv->photos)
+    {
+      g_ptr_array_foreach (priv->photos, (GFunc) test_photo_free, object);
+      g_ptr_array_free (priv->photos, TRUE);
+      priv->photos = NULL;
+    }
+
+  GTK_OBJECT_CLASS (test_photo_album_widget_parent_class)->destroy (object);
+}
+
+static TestPhoto *
+find_photo_at_position (TestPhotoAlbumWidget *album,
+                        gdouble               x,
+                        gdouble               y)
+{
+  TestPhotoAlbumWidgetPrivate *priv;
+  TestPhoto *photo;
+  gint i;
+
+  priv = TEST_PHOTO_ALBUM_WIDGET_GET_PRIVATE (album);
+
+  for (i = priv->photos->len - 1; i >= 0; i--)
+    {
+      photo = g_ptr_array_index (priv->photos, i);
+
+      if (gdk_region_point_in (photo->region, (gint) x, (gint) y))
+        return photo;
+    }
+
+  return NULL;
+}
+
+static gboolean
+test_photo_album_widget_button_press (GtkWidget      *widget,
+                                      GdkEventButton *event)
+{
+  TestPhoto *photo;
+
+  photo = find_photo_at_position (TEST_PHOTO_ALBUM_WIDGET (widget), event->x, event->y);
+
+  if (!photo)
+    return FALSE;
+
+  test_photo_raise (photo, TEST_PHOTO_ALBUM_WIDGET (widget));
+  gtk_device_group_add_device (photo->group, event->device);
+
+  return TRUE;
+}
+
+static gboolean
+test_photo_album_widget_button_release (GtkWidget      *widget,
+                                        GdkEventButton *event)
+{
+  GtkDeviceGroup *group;
+
+  group = gtk_widget_get_group_for_device (widget, event->device);
+
+  if (group)
+    gtk_device_group_remove_device (group, event->device);
+}
+
+static void
+test_photo_album_widget_multidevice_event (GtkWidget           *widget,
+                                           GtkDeviceGroup      *group,
+                                           GtkMultiDeviceEvent *event)
+{
+  TestPhotoAlbumWidgetPrivate *priv;
+  GdkRegion *region;
+  TestPhoto *photo;
+  gboolean new_center = FALSE;
+  gboolean new_position = FALSE;
+  gdouble event_x, event_y;
+
+  priv = TEST_PHOTO_ALBUM_WIDGET_GET_PRIVATE (widget);
+  photo = g_object_get_qdata (G_OBJECT (group), quark_group_photo);
+
+  region = gdk_region_copy (photo->region);
+
+  if (event->n_events == 1)
+    {
+      if (event->type == GTK_EVENT_DEVICE_REMOVED)
+        {
+          /* Device was just removed, unset zoom/angle info */
+          photo->base_zoom = 0;
+          photo->base_angle = 0;
+          photo->initial_angle = 0;
+          photo->initial_distance = 0;
+          new_center = TRUE;
+        }
+      else if (event->type == GTK_EVENT_DEVICE_ADDED)
+        new_center = TRUE;
+
+      event_x = event->events[0]->x;
+      event_y = event->events[0]->y;
+      new_position = TRUE;
+    }
+  else if (event->n_events == 2)
+    {
+      gdouble distance, angle;
+
+      gdk_events_get_center ((GdkEvent *) event->events[0],
+                             (GdkEvent *) event->events[1],
+                             &event_x, &event_y);
+
+      gdk_events_get_distance ((GdkEvent *) event->events[0],
+                               (GdkEvent *) event->events[1],
+                               &distance);
+
+      gdk_events_get_angle ((GdkEvent *) event->events[0],
+                            (GdkEvent *) event->events[1],
+                            &angle);
+
+      if (event->type == GTK_EVENT_DEVICE_ADDED)
+        {
+          photo->base_zoom = photo->zoom;
+          photo->base_angle = photo->angle;
+          photo->initial_angle = angle;
+          photo->initial_distance = distance;
+          new_center = TRUE;
+        }
+
+      photo->zoom = photo->base_zoom * (distance / photo->initial_distance);
+      photo->angle = photo->base_angle + (angle - photo->initial_angle);
+      new_position = TRUE;
+    }
+
+  if (new_center)
+    {
+      gdouble origin_x, origin_y;
+
+      origin_x = photo->x - photo->center_x;
+      origin_y = photo->y - photo->center_y;
+
+      calculate_rotated_point (- photo->angle,
+                               1 / photo->zoom,
+                               photo->x - origin_x,
+                               photo->y - origin_y,
+                               event_x - origin_x,
+                               event_y - origin_y,
+                               &photo->center_x,
+                               &photo->center_y);
+    }
+
+  if (new_position)
+    {
+      photo->x = event_x;
+      photo->y = event_y;
+    }
+
+  allocate_photo_region (photo);
+
+  gdk_region_union (region, photo->region);
+  gdk_region_shrink (region, -4, -4);
+
+  gdk_window_invalidate_region (widget->window, region, FALSE);
+}
+
+static gboolean
+test_photo_album_widget_expose (GtkWidget      *widget,
+                                GdkEventExpose *event)
+{
+  TestPhotoAlbumWidgetPrivate *priv;
+  cairo_t *cr;
+  gint i;
+
+  priv = TEST_PHOTO_ALBUM_WIDGET_GET_PRIVATE (widget);
+  cr = gdk_cairo_create (widget->window);
+
+  for (i = 0; i < priv->photos->len; i++)
+    {
+      TestPhoto *photo = g_ptr_array_index (priv->photos, i);
+      GdkRegion *region;
+      gint width, height;
+
+      region = gdk_region_copy (photo->region);
+      gdk_region_shrink (region, -4, -4);
+
+      gdk_region_intersect (region, event->region);
+
+      if (gdk_region_empty (region))
+        {
+          gdk_region_destroy (region);
+          continue;
+        }
+
+      width = gdk_pixbuf_get_width (photo->pixbuf);
+      height = gdk_pixbuf_get_height (photo->pixbuf);
+
+      cairo_save (cr);
+
+      gdk_cairo_region (cr, region);
+      cairo_clip (cr);
+
+      cairo_translate (cr, photo->x, photo->y);
+
+      cairo_scale (cr, photo->zoom, photo->zoom);
+      cairo_rotate (cr, photo->angle);
+      gdk_cairo_set_source_pixbuf (cr,
+                                   photo->pixbuf,
+                                   - photo->center_x,
+                                   - photo->center_y);
+
+      cairo_rectangle (cr,
+                       - photo->center_x,
+                       - photo->center_y,
+                       width, height);
+      cairo_fill_preserve (cr);
+
+      cairo_set_source_rgb (cr, 0., 0., 0.);
+      cairo_stroke (cr);
+
+      cairo_restore (cr);
+
+      gdk_region_destroy (region);
+    }
+
+  cairo_destroy (cr);
+
+  return TRUE;
+}
+
+GtkWidget *
+test_photo_album_widget_new (void)
+{
+  return g_object_new (TEST_TYPE_PHOTO_ALBUM_WIDGET, NULL);
+}
+
+void
+test_photo_album_widget_add_image (TestPhotoAlbumWidget *album,
+                                   GdkPixbuf            *pixbuf)
+{
+  TestPhotoAlbumWidgetPrivate *priv;
+  TestPhoto *photo;
+
+  g_return_if_fail (TEST_IS_PHOTO_ALBUM_WIDGET (album));
+  g_return_if_fail (GDK_IS_PIXBUF (pixbuf));
+
+  priv = TEST_PHOTO_ALBUM_WIDGET_GET_PRIVATE (album);
+
+  photo = test_photo_new (album, pixbuf);
+  g_ptr_array_add (priv->photos, photo);
+
+  if (GTK_WIDGET_REALIZED (album) &&
+      GTK_WIDGET_DRAWABLE (album))
+    gdk_window_invalidate_region (GTK_WIDGET (album)->window,
+                                  photo->region,
+                                  FALSE);
+}
diff --git a/tests/multidevice/testphotoalbumwidget.h b/tests/multidevice/testphotoalbumwidget.h
new file mode 100644
index 0000000..0a36378
--- /dev/null
+++ b/tests/multidevice/testphotoalbumwidget.h
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2009 Carlos Garnacho  <carlosg@gnome.org>
+ *
+ * This work is provided "as is"; redistribution and modification
+ * in whole or in part, in any medium, physical or electronic is
+ * permitted without restriction.
+ *
+ * This work is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * In no event shall the authors or contributors be liable for any
+ * direct, indirect, incidental, special, exemplary, or consequential
+ * damages (including, but not limited to, procurement of substitute
+ * goods or services; loss of use, data, or profits; or business
+ * interruption) however caused and on any theory of liability, whether
+ * in contract, strict liability, or tort (including negligence or
+ * otherwise) arising in any way out of the use of this software, even
+ * if advised of the possibility of such damage.
+ */
+
+#ifndef __TEST_PHOTO_ALBUM_WIDGET_H__
+#define __TEST_PHOTO_ALBUM_WIDGET_H__
+
+#include <gtk/gtk.h>
+
+G_BEGIN_DECLS
+
+#define TEST_TYPE_PHOTO_ALBUM_WIDGET         (test_photo_album_widget_get_type ())
+#define TEST_PHOTO_ALBUM_WIDGET(o)           (G_TYPE_CHECK_INSTANCE_CAST ((o), TEST_TYPE_PHOTO_ALBUM_WIDGET, TestPhotoAlbumWidget))
+#define TEST_PHOTO_ALBUM_WIDGET_CLASS(c)     (G_TYPE_CHECK_CLASS_CAST    ((c), TEST_TYPE_PHOTO_ALBUM_WIDGET, TestPhotoAlbumWidgetClass))
+#define TEST_IS_PHOTO_ALBUM_WIDGET(o)        (G_TYPE_CHECK_INSTANCE_TYPE ((o), TEST_TYPE_PHOTO_ALBUM_WIDGET))
+#define TEST_IS_PHOTO_ALBUM_WIDGET_CLASS(c)  (G_TYPE_CHECK_CLASS_TYPE    ((c), TEST_TYPE_PHOTO_ALBUM_WIDGET))
+#define TEST_PHOTO_ALBUM_WIDGET_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS  ((o), TEST_TYPE_PHOTO_ALBUM_WIDGET, TestPhotoAlbumWidgetClass))
+
+typedef struct TestPhotoAlbumWidget TestPhotoAlbumWidget;
+typedef struct TestPhotoAlbumWidgetClass TestPhotoAlbumWidgetClass;
+
+struct TestPhotoAlbumWidget
+{
+  GtkDrawingArea parent_instance;
+};
+
+struct TestPhotoAlbumWidgetClass
+{
+  GtkDrawingAreaClass parent_class;
+};
+
+
+GType       test_photo_album_widget_get_type  (void) G_GNUC_CONST;
+
+GtkWidget * test_photo_album_widget_new       (void);
+
+void        test_photo_album_widget_add_image (TestPhotoAlbumWidget *album,
+                                               GdkPixbuf            *pixbuf);
+
+
+G_END_DECLS
+
+#endif /* __TEST_PHOTO_ALBUM_WIDGET_H__ */
