[gtk+] Merge the xi2-for-master branch



commit bd4609b14042a91646cd9057764eecfbc6faf42b
Author: Matthias Clasen <mclasen redhat com>
Date:   Tue May 25 18:38:44 2010 -0400

    Merge the xi2-for-master branch

 configure.ac                               |   17 +-
 docs/reference/gdk/gdk-docs.sgml           |    2 +-
 docs/reference/gdk/gdk3-sections.txt       |   68 +-
 docs/reference/gdk/gdk3.types              |    2 +
 docs/reference/gdk/tmpl/input_devices.sgml |  309 ---
 docs/reference/gtk/compiling.sgml          |    7 +
 docs/reference/gtk/gtk-docs.sgml           |    1 +
 docs/reference/gtk/gtk3-sections.txt       |   28 +-
 gdk/Makefile.am                            |    5 +
 gdk/gdk.c                                  |   23 +
 gdk/gdk.h                                  |   15 +-
 gdk/gdk.symbols                            |  210 ++-
 gdk/gdkdevice.c                            | 1399 +++++++++++++
 gdk/gdkdevice.h                            |  292 +++
 gdk/gdkdevicemanager.c                     |  302 +++
 gdk/gdkdevicemanager.h                     |   76 +
 gdk/gdkdeviceprivate.h                     |  135 ++
 gdk/gdkdisplay.c                           | 1099 ++++++++---
 gdk/gdkdisplay.h                           |  108 +-
 gdk/gdkdnd.h                               |    5 +
 gdk/gdkevents.c                            |  317 +++-
 gdk/gdkevents.h                            |   46 +-
 gdk/gdkglobals.c                           |    1 +
 gdk/gdkinput.h                             |  148 +--
 gdk/gdkinternals.h                         |  106 +-
 gdk/gdkoffscreenwindow.c                   |   35 +-
 gdk/gdktypes.h                             |   44 +
 gdk/gdkwindow.c                            | 1116 ++++++++---
 gdk/gdkwindow.h                            |   32 +-
 gdk/gdkwindowimpl.h                        |   14 +-
 gdk/quartz/Makefile.am                     |    2 +
 gdk/quartz/gdkdevice-core.c                |  356 ++++
 gdk/quartz/gdkdevice-core.h                |   51 +
 gdk/quartz/gdkdevicemanager-core.c         |  130 ++
 gdk/quartz/gdkdevicemanager-core.h         |   54 +
 gdk/quartz/gdkdisplay-quartz.c             |   11 +
 gdk/quartz/gdkevents-quartz.c              |  460 +++--
 gdk/quartz/gdkinput.c                      |  367 +---
 gdk/quartz/gdkinputprivate.h               |    5 -
 gdk/quartz/gdkprivate-quartz.h             |    3 +-
 gdk/quartz/gdkwindow-quartz.c              |  132 +-
 gdk/win32/Makefile.am                      |    8 +-
 gdk/win32/gdkdevice-win32.c                |  396 ++++
 gdk/win32/gdkdevice-win32.h                |   51 +
 gdk/win32/gdkdevice-wintab.c               |  386 ++++
 gdk/win32/gdkdevice-wintab.h               |   79 +
 gdk/win32/gdkdevicemanager-win32.c         | 1089 ++++++++++
 gdk/win32/gdkdevicemanager-win32.h         |   59 +
 gdk/win32/gdkdnd-win32.c                   |  222 ++-
 gdk/win32/gdkevents-win32.c                |  266 ++--
 gdk/win32/gdkinput-win32.c                 | 1392 -------------
 gdk/win32/gdkinput-win32.h                 |  147 --
 gdk/win32/gdkinput.c                       |  441 +----
 gdk/win32/gdkmain-win32.c                  |    3 +-
 gdk/win32/gdkwindow-win32.c                |  246 +--
 gdk/x11/Makefile.am                        |   18 +-
 gdk/x11/gdkdevice-core.c                   |  501 +++++
 gdk/x11/gdkdevice-core.h                   |   52 +
 gdk/x11/gdkdevice-xi.c                     |  624 ++++++
 gdk/x11/gdkdevice-xi.h                     |   88 +
 gdk/x11/gdkdevice-xi2.c                    |  621 ++++++
 gdk/x11/gdkdevice-xi2.h                    |   64 +
 gdk/x11/gdkdevicemanager-core.c            |  904 ++++++++
 gdk/x11/gdkdevicemanager-core.h            |   54 +
 gdk/x11/gdkdevicemanager-x11.c             |   76 +
 gdk/x11/gdkdevicemanager-xi.c              |  650 ++++++
 gdk/x11/gdkdevicemanager-xi.h              |   58 +
 gdk/x11/gdkdevicemanager-xi2.c             | 1146 +++++++++++
 gdk/x11/gdkdevicemanager-xi2.h             |   61 +
 gdk/x11/gdkdisplay-x11.c                   | 1303 +++++++++++-
 gdk/x11/gdkdnd-x11.c                       |  127 +-
 gdk/x11/gdkevents-x11.c                    | 3088 ----------------------------
 gdk/x11/gdkeventsource.c                   |  427 ++++
 gdk/x11/gdkeventsource.h                   |   46 +
 gdk/x11/gdkeventtranslator.c               |  100 +
 gdk/x11/gdkeventtranslator.h               |   65 +
 gdk/x11/gdkinput-none.c                    |  126 --
 gdk/x11/gdkinput-x11.c                     |  937 ---------
 gdk/x11/gdkinput-xfree.c                   |  422 ----
 gdk/x11/gdkinput.c                         |  547 +-----
 gdk/x11/gdkinputprivate.h                  |  189 --
 gdk/x11/gdkmain-x11.c                      |  266 +--
 gdk/x11/gdkprivate-x11.h                   |    7 +-
 gdk/x11/gdkscreen-x11.c                    |  656 ++++++-
 gdk/x11/gdkwindow-x11.c                    |  426 +++--
 gdk/x11/gdkwindow-x11.h                    |    2 +
 gtk/gtk.symbols                            |   12 +
 gtk/gtkaboutdialog.c                       |   42 +-
 gtk/gtkbutton.c                            |   34 +-
 gtk/gtkcellrendereraccel.c                 |   89 +-
 gtk/gtkcolorsel.c                          |   98 +-
 gtk/gtkcombobox.c                          |  120 +-
 gtk/gtkcombobox.h                          |    2 +
 gtk/gtkdnd.c                               |  155 +-
 gtk/gtkentry.c                             |   15 +-
 gtk/gtkentrycompletion.c                   |   34 +-
 gtk/gtkentryprivate.h                      |    5 +-
 gtk/gtkhandlebox.c                         |   38 +-
 gtk/gtkiconview.c                          |   21 +-
 gtk/gtklabel.c                             |    9 +-
 gtk/gtkmain.c                              |  258 ++-
 gtk/gtkmain.h                              |    7 +
 gtk/gtkmarshalers.list                     |    1 +
 gtk/gtkmenu.c                              |  246 ++-
 gtk/gtkmenu.h                              |    9 +
 gtk/gtkmenushell.c                         |   51 +-
 gtk/gtkmenushell.h                         |    8 +
 gtk/gtknotebook.c                          |   13 +-
 gtk/gtkpaned.c                             |   31 +-
 gtk/gtkplug-x11.c                          |   72 +-
 gtk/gtkprivate.h                           |    1 -
 gtk/gtkrange.c                             |   77 +-
 gtk/gtkscalebutton.c                       |  144 +-
 gtk/gtksocket.c                            |    2 +-
 gtk/gtkspinbutton.c                        |    6 +-
 gtk/gtktextview.c                          |   56 +-
 gtk/gtktooltip.c                           |   14 +-
 gtk/gtktreeprivate.h                       |    3 +-
 gtk/gtktreeview.c                          |   86 +-
 gtk/gtktreeviewcolumn.c                    |    3 +-
 gtk/gtkwidget.c                            |  516 ++++-
 gtk/gtkwidget.h                            |   30 +-
 gtk/gtkwindow.c                            |  232 ++-
 gtk/gtkwindow.h                            |   14 +
 124 files changed, 18196 insertions(+), 10327 deletions(-)
---
diff --git a/configure.ac b/configure.ac
index a99e7d2..e4fa242 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1560,22 +1560,22 @@ if test "x$gdktarget" = "xx11"; then
   fi
 
   # set up things for XInput
-
-  if test "x$with_xinput" = "xxfree" || test "x$with_xinput" = "xyes"; then
+  if test "x$with_xinput" != "xno" && $PKG_CONFIG --exists "xi" ; 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"
-    else
-      GTK_ADD_LIB(x_extra_libs, Xi)
-    fi
+    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
     AC_DEFINE(XINPUT_NONE, 1,
               [Define to 1 if no XInput should be used])
   fi
 
-  AM_CONDITIONAL(XINPUT_XFREE, test x$with_xinput = xxfree || test x$with_xinput = xyes)
+  AM_CONDITIONAL(XINPUT_NONE,  test "x$with_xinput" = "xno")
+  AM_CONDITIONAL(XINPUT_XFREE, test "x$with_xinput" != "xno")
+  AM_CONDITIONAL(XINPUT_2,     test "x$have_xinput2" = "xyes")
 
   # Check for the RANDR extension
   if $PKG_CONFIG --exists "xrandr >= 1.2.99" ; then
@@ -1633,6 +1633,7 @@ else
   XPACKAGES=
 
   AM_CONDITIONAL(XINPUT_XFREE, false)
+  AM_CONDITIONAL(XINPUT_2, false)
   AM_CONDITIONAL(USE_X11, false)
   AM_CONDITIONAL(HAVE_X11R6, false)
 fi
diff --git a/docs/reference/gdk/gdk-docs.sgml b/docs/reference/gdk/gdk-docs.sgml
index 81fd772..a0a295d 100644
--- a/docs/reference/gdk/gdk-docs.sgml
+++ b/docs/reference/gdk/gdk-docs.sgml
@@ -53,7 +53,7 @@
     <xi:include href="xml/threads.xml" />
 
     <xi:include href="xml/input.xml" />
-    <xi:include href="xml/input_devices.xml" />
+    <xi:include href="xml/gdkdevicemanager.xml" />
 
     <xi:include href="xml/pango_interaction.xml" />
     <xi:include href="xml/cairo_interaction.xml" />
diff --git a/docs/reference/gdk/gdk3-sections.txt b/docs/reference/gdk/gdk3-sections.txt
index e9be5c5..903e9b8 100644
--- a/docs/reference/gdk/gdk3-sections.txt
+++ b/docs/reference/gdk/gdk3-sections.txt
@@ -81,6 +81,7 @@ gdk_filter_return_get_type
 gdk_font_type_get_type
 gdk_function_get_type
 gdk_gc_values_mask_get_type
+gdk_grab_ownership_get_type
 gdk_grab_status_get_type
 gdk_gravity_get_type
 gdk_image_type_get_type
@@ -110,8 +111,7 @@ gdk_window_type_get_type
 gdk_window_type_hint_get_type
 gdk_wm_decoration_get_type
 gdk_wm_function_get_type
-gdk_pointer_grab_info_libgtk_only
-gdk_keyboard_grab_info_libgtk_only
+gdk_device_grab_info_libgtk_only
 gdk_add_option_entries_libgtk_only
 gdk_pre_parse_libgtk_only
 </SECTION>
@@ -126,9 +126,11 @@ gdk_display_get_name
 gdk_display_get_n_screens
 gdk_display_get_screen
 gdk_display_get_default_screen
+gdk_display_get_device_manager
 gdk_display_pointer_ungrab
 gdk_display_keyboard_ungrab
 gdk_display_pointer_is_grabbed
+gdk_display_device_is_grabbed
 gdk_display_beep
 gdk_display_sync
 gdk_display_flush
@@ -141,10 +143,15 @@ gdk_display_add_client_message_filter
 gdk_display_set_double_click_time
 gdk_display_set_double_click_distance
 gdk_display_get_pointer
+gdk_display_get_device_state
 gdk_display_get_window_at_pointer
+gdk_display_get_window_at_device_position
 GdkDisplayPointerHooks
 gdk_display_set_pointer_hooks
+GdkDisplayDeviceHooks
+gdk_display_set_device_hooks
 gdk_display_warp_pointer
+gdk_display_warp_device
 gdk_display_supports_cursor_color
 gdk_display_supports_cursor_alpha
 gdk_display_get_default_cursor_size
@@ -741,6 +748,7 @@ gdk_window_get_origin
 gdk_window_get_deskrelative_origin
 gdk_window_get_root_coords
 gdk_window_get_pointer
+gdk_window_get_device_position
 GdkModifierType
 gdk_window_get_parent
 gdk_window_get_toplevel
@@ -763,6 +771,14 @@ GdkWMFunction
 gdk_get_default_root_window
 
 <SUBSECTION>
+gdk_window_get_support_multidevice
+gdk_window_set_support_multidevice
+gdk_window_get_device_cursor
+gdk_window_set_device_cursor
+gdk_window_get_device_events
+gdk_window_set_device_events
+
+<SUBSECTION>
 GdkPointerHooks
 gdk_set_pointer_hooks
 
@@ -1027,14 +1043,22 @@ gdk_keymap_get_type
 </SECTION>
 
 <SECTION>
-<TITLE>Input Devices</TITLE>
-<FILE>input_devices</FILE>
+<TITLE>GdkDeviceManager</TITLE>
+<FILE>gdkdevicemanager</FILE>
+GdkDeviceManager
 GdkDevice
+GdkDeviceType
 GdkInputSource
 GdkInputMode
 GdkDeviceKey
 GdkDeviceAxis
 GdkAxisUse
+GdkGrabOwnership
+gdk_enable_multidevice
+gdk_device_manager_get_display
+gdk_device_manager_list_devices
+
+<SUBSECTION>
 gdk_devices_list
 gdk_device_get_name
 gdk_device_set_source
@@ -1045,8 +1069,16 @@ gdk_device_set_key
 gdk_device_get_key
 gdk_device_set_axis_use
 gdk_device_get_axis_use
-gdk_device_get_core_pointer
+gdk_device_get_associated_device
+gdk_device_get_device_type
+gdk_device_get_display
 gdk_device_get_has_cursor
+gdk_device_get_n_axes
+gdk_device_get_core_pointer
+
+<SUBSECTION>
+gdk_device_grab
+gdk_device_ungrab
 
 <SUBSECTION>
 gdk_device_get_state
@@ -1054,16 +1086,30 @@ gdk_device_get_history
 gdk_device_free_history
 GdkTimeCoord
 gdk_device_get_axis
+gdk_device_list_axes
+gdk_device_get_axis_value
 
 <SUBSECTION>
 gdk_input_set_extension_events
 GdkExtensionMode
 
+<SUBSECTION>
+gdk_devices_list
+gdk_device_get_core_pointer
+
 <SUBSECTION Standard>
 GDK_TYPE_AXIS_USE
 GDK_TYPE_EXTENSION_MODE
 GDK_TYPE_INPUT_MODE
 GDK_TYPE_INPUT_SOURCE
+GDK_TYPE_DEVICE_TYPE
+GDK_TYPE_GRAB_OWNERSHIP
+GDK_DEVICE_MANAGER
+GDK_DEVICE_MANAGER_CLASS
+GDK_DEVICE_MANAGER_GET_CLASS
+GDK_IS_DEVICE_MANAGER
+GDK_IS_DEVICE_MANAGER_CLASS
+GDK_TYPE_DEVICE_MANAGER
 GDK_DEVICE
 GDK_DEVICE_CLASS
 GDK_DEVICE_GET_CLASS
@@ -1073,7 +1119,12 @@ GDK_TYPE_DEVICE
 
 <SUBSECTION Private>
 GdkDeviceClass
+GdkDevicePrivate
+GdkDeviceManagerClass
+GdkDeviceManagerPrivate
 gdk_device_get_type
+gdk_device_manager_get_type
+gdk_device_type_get_type
 GDK_MAX_TIMECOORD_AXES
 </SECTION>
 
@@ -1101,6 +1152,9 @@ gdk_event_get_axis
 gdk_event_get_coords
 gdk_event_get_root_coords
 gdk_event_request_motions
+gdk_events_get_angle
+gdk_events_get_center
+gdk_events_get_distance
 
 <SUBSECTION>
 gdk_event_handler_set
@@ -1117,6 +1171,8 @@ gdk_get_show_events
 gdk_set_show_events
 gdk_event_set_screen
 gdk_event_get_screen
+gdk_event_get_device
+gdk_event_set_device
 
 <SUBSECTION>
 gdk_setting_get
@@ -1240,6 +1296,8 @@ gdk_drag_context_get_action
 gdk_drag_context_get_actions
 gdk_drag_context_get_suggested_action
 gdk_drag_context_list_targets
+gdk_drag_context_get_device
+gdk_drag_context_set_device
 
 <SUBSECTION Standard>
 GDK_DRAG_CONTEXT
diff --git a/docs/reference/gdk/gdk3.types b/docs/reference/gdk/gdk3.types
index 12b0f8d..8b200f5 100644
--- a/docs/reference/gdk/gdk3.types
+++ b/docs/reference/gdk/gdk3.types
@@ -9,3 +9,5 @@ gdk_pango_renderer_get_type
 gdk_pixmap_get_type
 gdk_gc_get_type
 gdk_keymap_get_type
+gdk_device_get_type
+gdk_device_manager_get_type
diff --git a/docs/reference/gtk/compiling.sgml b/docs/reference/gtk/compiling.sgml
index c5e58b7..d126bd4 100644
--- a/docs/reference/gtk/compiling.sgml
+++ b/docs/reference/gtk/compiling.sgml
@@ -63,6 +63,13 @@ symbol GDK_MULTIHEAD_SAFE by using the command line option
 </para>
 
 <para>
+Similarly, if you want to make sure that your program doesn't use any
+functions which may be problematic in a multidevice setting, you can
+define the preprocessor symbol GDK_MULTIDEVICE_SAFE by using the command
+line option <literal>-DGTK_MULTIDEVICE_SAFE=1</literal>.
+</para>
+
+<para>
 The recommended way of using GTK+ has always been to only include the
 toplevel headers <filename>gtk.h</filename>, <filename>gdk.h</filename>, 
 <filename>gdk-pixbuf.h</filename>.
diff --git a/docs/reference/gtk/gtk-docs.sgml b/docs/reference/gtk/gtk-docs.sgml
index 468587e..9f35339 100644
--- a/docs/reference/gtk/gtk-docs.sgml
+++ b/docs/reference/gtk/gtk-docs.sgml
@@ -336,6 +336,7 @@ that is, GUI components such as #GtkButton or #GtkTextView.
       <xi:include href="xml/gtktooltip.xml" />
       <xi:include href="xml/gtkviewport.xml" />
       <xi:include href="xml/gtkaccessible.xml" />
+      <xi:include href="xml/gtkdevicegroup.xml" />
     </chapter>
 
     <chapter id="AbstractObjects">
diff --git a/docs/reference/gtk/gtk3-sections.txt b/docs/reference/gtk/gtk3-sections.txt
index f1545fc..40c8e39 100644
--- a/docs/reference/gtk/gtk3-sections.txt
+++ b/docs/reference/gtk/gtk3-sections.txt
@@ -79,11 +79,6 @@ gtk_accelerator_get_label
 gtk_accelerator_set_default_mod_mask
 gtk_accelerator_get_default_mod_mask
 
-<SUBSECTION Private>
-GtkAccelGroupEntry
-GtkAccelGroupPrivate
-gtk_accel_group_get_type
-
 <SUBSECTION Standard>
 GtkAccelGroupClass
 GTK_TYPE_ACCEL_GROUP
@@ -92,6 +87,11 @@ GTK_IS_ACCEL_GROUP
 GTK_ACCEL_GROUP_CLASS
 GTK_IS_ACCEL_GROUP_CLASS
 GTK_ACCEL_GROUP_GET_CLASS
+
+<SUBSECTION Private>
+GtkAccelGroupPrivate
+GtkAccelGroupEntry
+gtk_accel_group_get_type
 </SECTION>
 
 <SECTION>
@@ -263,10 +263,10 @@ gtk_action_group_add_action_with_accel
 gtk_action_group_remove_action
 GtkActionEntry
 gtk_action_group_add_actions
-gtk_action_group_add_actions_full 
+gtk_action_group_add_actions_full
 GtkToggleActionEntry
 gtk_action_group_add_toggle_actions
-gtk_action_group_add_toggle_actions_full 
+gtk_action_group_add_toggle_actions_full
 GtkRadioActionEntry
 gtk_action_group_add_radio_actions
 gtk_action_group_add_radio_actions_full
@@ -782,6 +782,7 @@ gtk_combo_box_insert_text
 gtk_combo_box_prepend_text
 gtk_combo_box_remove_text
 gtk_combo_box_get_active_text
+gtk_combo_box_popup_for_device
 gtk_combo_box_popup
 gtk_combo_box_popdown
 gtk_combo_box_get_popup_accessible
@@ -1961,6 +1962,7 @@ gtk_menu_new
 gtk_menu_set_screen
 gtk_menu_reorder_child
 gtk_menu_attach
+gtk_menu_popup_for_device
 gtk_menu_popup
 gtk_menu_set_accel_group
 gtk_menu_get_accel_group
@@ -4812,15 +4814,18 @@ gtk_widget_set_parent
 gtk_widget_set_parent_window
 gtk_widget_get_parent_window
 gtk_widget_set_events
+gtk_widget_get_events
 gtk_widget_add_events
 gtk_widget_set_extension_events
 gtk_widget_get_extension_events
+gtk_widget_set_device_events
+gtk_widget_get_device_events
+gtk_widget_add_device_events
 gtk_widget_get_toplevel
 gtk_widget_get_ancestor
 gtk_widget_get_colormap
 gtk_widget_set_colormap
 gtk_widget_get_visual
-gtk_widget_get_events
 gtk_widget_get_pointer
 gtk_widget_is_ancestor
 gtk_widget_translate_coordinates
@@ -4938,11 +4943,14 @@ gtk_widget_is_toplevel
 gtk_widget_set_window
 gtk_widget_set_receives_default
 gtk_widget_get_receives_default
+gtk_widget_set_support_multidevice
+gtk_widget_get_support_multidevice
 gtk_widget_set_realized
 gtk_widget_get_realized
 gtk_widget_set_mapped
 gtk_widget_get_mapped
 gtk_widget_get_requisition
+gtk_widget_device_is_shadowed
 
 <SUBSECTION>
 gtk_requisition_copy
@@ -5091,6 +5099,7 @@ gtk_window_group_new
 gtk_window_group_add_window
 gtk_window_group_remove_window
 gtk_window_group_list_windows
+gtk_window_group_get_current_device_grab
 <SUBSECTION Standard>
 GTK_IS_WINDOW_GROUP
 GTK_IS_WINDOW_GROUP_CLASS
@@ -5132,6 +5141,8 @@ gtk_false
 gtk_grab_add
 gtk_grab_get_current
 gtk_grab_remove
+gtk_device_grab_add
+gtk_device_grab_remove
 
 <SUBSECTION>
 gtk_init_add
@@ -5153,6 +5164,7 @@ gtk_key_snooper_remove
 gtk_get_current_event
 gtk_get_current_event_time
 gtk_get_current_event_state
+gtk_get_current_event_device
 gtk_get_event_widget
 gtk_propagate_event
 
diff --git a/gdk/Makefile.am b/gdk/Makefile.am
index ec244a8..21b296c 100644
--- a/gdk/Makefile.am
+++ b/gdk/Makefile.am
@@ -77,6 +77,9 @@ gdk_public_h_sources =				\
 	gdkcairo.h				\
 	gdkcolor.h				\
 	gdkcursor.h				\
+	gdkdevice.h				\
+	gdkdevicemanager.h			\
+	gdkdeviceprivate.h			\
 	gdkdisplay.h				\
 	gdkdisplaymanager.h			\
 	gdkdnd.h				\
@@ -122,6 +125,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.c b/gdk/gdk.c
index 8fe3c45..89a4e2f 100644
--- a/gdk/gdk.c
+++ b/gdk/gdk.c
@@ -787,5 +787,28 @@ gdk_set_program_class (const char *program_class)
   gdk_progclass = g_strdup (program_class);
 }
 
+/**
+ * gdk_enable_multidevice:
+ *
+ * Enables multidevice support in GDK. This call must happen prior
+ * to gdk_display_open(), gtk_init(), gtk_init_with_args() or
+ * gtk_init_check() in order to take effect.
+ *
+ * Note that individual #GdkWindow<!-- -->s still need to explicitly
+ * enable multidevice awareness through gdk_window_set_support_multidevice().
+ *
+ * This function must be called before initializing GDK.
+ *
+ * Since: 3.0
+ **/
+void
+gdk_enable_multidevice (void)
+{
+  if (gdk_initialized)
+    return;
+
+  _gdk_enable_multidevice = TRUE;
+}
+
 #define __GDK_C__
 #include "gdkaliasdef.c"
diff --git a/gdk/gdk.h b/gdk/gdk.h
index 219298e..e910a8b 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>
@@ -66,6 +68,7 @@ G_BEGIN_DECLS
 /* Initialization, exit and events
  */
 #define	  GDK_PRIORITY_EVENTS		(G_PRIORITY_DEFAULT)
+void      gdk_enable_multidevice        (void);
 void 	  gdk_parse_args	   	(gint	   	*argc,
 					 gchar        ***argv);
 void 	  gdk_init		   	(gint	   	*argc,
@@ -109,6 +112,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,
@@ -118,18 +122,15 @@ GdkGrabStatus gdk_pointer_grab       (GdkWindow    *window,
 GdkGrabStatus gdk_keyboard_grab      (GdkWindow    *window,
 				      gboolean      owner_events,
 				      guint32       time_);
-
-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);
+#endif /* GDK_MULTIDEVICE_SAFE */
 
 #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 36b111f..9ee4082 100644
--- a/gdk/gdk.symbols
+++ b/gdk/gdk.symbols
@@ -19,13 +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
-gdk_events_pending
-#endif
-#endif
-
 #if IN_HEADER(__GDK_TEST_UTILS_H__)
 #if IN_FILE(__GDK_TEST_UTILS_X11_C__)
 gdk_test_simulate_button
@@ -41,6 +34,7 @@ gdk_event_free
 gdk_event_get
 gdk_event_get_axis
 gdk_event_get_coords
+gdk_event_get_device
 gdk_event_get_root_coords
 gdk_event_get_screen
 gdk_event_get_state
@@ -51,13 +45,56 @@ gdk_event_new
 gdk_event_peek
 gdk_event_put
 gdk_event_request_motions
+gdk_event_set_device
 gdk_event_set_screen
+gdk_events_get_distance
+gdk_events_get_angle
+gdk_events_get_center
 gdk_get_show_events
 gdk_set_show_events
 gdk_setting_get
 #endif
 #endif
 
+#if IN_HEADER(__GDK_EVENTS_H__)
+#if IN_FILE(__GDK_DISPLAY_X11_C__)
+gdk_add_client_message_filter
+#endif
+#endif
+
+#if IN_HEADER(__GDK_EVENTS_H__)
+#if IN_FILE(__GDK_EVENT_SOURCE_C__)
+gdk_events_pending
+#endif
+#endif
+
+#if IN_HEADER(__GDK_EVENT_TRANSLATOR_H__)
+#if IN_FILE(__GDK_EVENT_TRANSLATOR_C__)
+#ifdef GDK_WINDOWING_X11
+gdk_event_translator_get_type G_GNUC_CONST
+gdk_event_translator_translate
+gdk_event_translator_get_handled_events
+gdk_event_translator_select_window_events
+#endif
+#endif
+#endif
+
+#if IN_HEADER(__GDK_DEVICE_MANAGER_CORE_H__)
+#if IN_FILE(__GDK_DEVICE_MANAGER_CORE_C__)
+#ifdef GDK_WINDOWING_X11
+gdk_device_manager_core_get_type
+#endif
+#endif
+#endif
+
+#if IN_HEADER(__GDK_DEVICE_MANAGER_XI2_H__)
+#if IN_FILE(__GDK_DEVICE_MANAGER_XI2_C__)
+#ifdef GDK_WINDOWING_X11
+gdk_device_manager_xi2_get_type
+#endif
+#endif
+#endif
+
 #if IN_HEADER(__GDK_H__)
 #if IN_FILE(__GDK_MAIN_X11_C__)
 gdk_error_trap_pop
@@ -67,7 +104,6 @@ gdk_get_display
 gdk_get_use_xshm
 gdk_set_use_xshm
 #endif
-gdk_keyboard_grab
 #endif
 #endif
 
@@ -75,17 +111,24 @@ 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
+#ifndef GDK_MULTIDEVICE_SAFE
 gdk_display_pointer_is_grabbed
 #endif
 #endif
+#endif
 
 #if IN_HEADER(__GDK_H__)
 #if IN_FILE(__GDK_IM_X11_C__)
@@ -98,14 +141,12 @@ gdk_set_locale
 #endif
 
 #if IN_HEADER(__GDK_H__)
-#if IN_FILE(__GDK_EVENTS_X11_C__)
+#if IN_FILE(__GDK_DISPLAY_X11_C__)
+#ifndef GDK_DISABLE_DEPRECATED
+gdk_display_list_devices
+#endif
 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
@@ -132,6 +173,7 @@ gdk_get_display_arg_name
 gdk_get_program_class
 gdk_init
 gdk_init_check
+gdk_enable_multidevice
 gdk_pre_parse_libgtk_only
 gdk_parse_args
 gdk_set_program_class
@@ -167,6 +209,14 @@ 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_PROPERTY_H__)
 #if IN_FILE(__GDK_SELECTION_C__)
 gdk_string_to_compound_text
@@ -210,6 +260,7 @@ gdk_axis_use_get_type G_GNUC_CONST
 gdk_byte_order_get_type G_GNUC_CONST
 gdk_cap_style_get_type G_GNUC_CONST
 gdk_crossing_mode_get_type G_GNUC_CONST
+gdk_device_type_get_type G_GNUC_CONST
 gdk_extension_mode_get_type G_GNUC_CONST
 gdk_event_mask_get_type G_GNUC_CONST
 gdk_event_type_get_type G_GNUC_CONST
@@ -217,6 +268,7 @@ gdk_fill_get_type G_GNUC_CONST
 gdk_fill_rule_get_type G_GNUC_CONST
 gdk_filter_return_get_type G_GNUC_CONST
 gdk_function_get_type G_GNUC_CONST
+gdk_grab_ownership_get_type G_GNUC_CONST
 gdk_grab_status_get_type G_GNUC_CONST
 gdk_gravity_get_type G_GNUC_CONST
 gdk_join_style_get_type G_GNUC_CONST
@@ -367,76 +419,99 @@ 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
+gdk_device_manager_list_devices
+#endif
+#endif
+
+#if IN_HEADER(__GDK_DEVICE_H__)
+#if IN_FILE(__GDK_DEVICE_C__)
 gdk_device_free_history
+gdk_device_get_associated_device
 gdk_device_get_axis
 gdk_device_get_axis_use
+gdk_device_get_axis_value
+gdk_device_get_device_type
+gdk_device_get_display
 gdk_device_get_has_cursor
 gdk_device_get_history
 gdk_device_get_key
 gdk_device_get_mode
+gdk_device_get_n_axes
 gdk_device_get_name
 gdk_device_get_source
 gdk_device_get_type G_GNUC_CONST
+gdk_device_get_state
+gdk_device_list_axes
 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
+gdk_device_grab
 #endif
 #endif
 
-#if IN_HEADER(__GDK_INPUT_H__)
-#if IN_FILE(__GDK_DISPLAY_C__)
-gdk_device_get_core_pointer
+#if IN_HEADER(__GDK_DEVICE_H__)
+#if IN_FILE(__GDK_DISPLAY_X11_C__)
+gdk_device_ungrab
 #endif
 #endif
 
-#if IN_HEADER(__GDK_INPUT_H__)
-#if IN_FILE(__GDK_INPUT_X11_C__)
-gdk_device_get_state
+#if IN_HEADER(__GDK_DEVICE_H__)
+#if IN_FILE(__GDK_DISPLAY_C__)
+gdk_device_grab_info_libgtk_only
 #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_device_state
 gdk_display_get_type G_GNUC_CONST
-gdk_display_get_window_at_pointer
+gdk_display_get_window_at_device_position
 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
+gdk_display_set_device_hooks
+gdk_display_device_is_grabbed
+#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__)
+gdk_display_warp_device
+#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 +530,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 +539,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 +551,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
@@ -511,6 +579,8 @@ gdk_drag_abort
 gdk_drag_begin
 gdk_drag_context_get_type G_GNUC_CONST
 gdk_drag_context_new
+gdk_drag_context_get_device
+gdk_drag_context_set_device
 gdk_drag_drop
 gdk_drag_drop_succeeded
 gdk_drag_find_window_for_screen
@@ -659,12 +729,14 @@ gdk_window_get_back_pixmap
 gdk_window_get_background
 gdk_window_get_composited
 gdk_window_get_cursor
-gdk_window_get_deskrelative_origin
 gdk_window_get_focus_on_map
 gdk_window_get_geometry
 gdk_window_get_modal_hint
 gdk_window_get_origin
 gdk_window_get_root_coords
+gdk_window_get_deskrelative_origin
+gdk_window_set_support_multidevice
+gdk_window_get_support_multidevice
 gdk_window_set_background
 gdk_window_set_back_pixmap
 gdk_window_set_cursor
@@ -679,7 +751,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
@@ -698,7 +772,9 @@ gdk_window_get_children
 gdk_window_get_internal_paint_info
 gdk_window_get_parent
 gdk_window_get_effective_parent
+#ifndef GDK_MULTIDEVICE_SAFE
 gdk_window_get_pointer
+#endif
 gdk_window_get_position
 gdk_window_get_state
 gdk_window_get_toplevel
@@ -726,10 +802,17 @@ 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
+gdk_window_set_device_events
+gdk_window_get_device_events
+gdk_window_set_device_cursor
+gdk_window_get_device_cursor
+gdk_window_get_device_position
 #endif
 #endif
 
@@ -908,12 +991,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
@@ -1048,13 +1125,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
@@ -1069,6 +1139,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
@@ -1077,6 +1148,7 @@ gdk_screen_get_height_mm
 gdk_screen_get_number
 gdk_screen_get_primary_monitor
 gdk_screen_get_root_window
+gdk_screen_get_setting
 gdk_screen_get_default_colormap
 gdk_screen_set_default_colormap
 gdk_screen_get_n_monitors
@@ -1219,6 +1291,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__)
@@ -1249,22 +1322,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__)
@@ -1274,6 +1343,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..6ee68eb
--- /dev/null
+++ b/gdk/gdkdevice.c
@@ -0,0 +1,1399 @@
+/* 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 "gdkdevice.h"
+#include "gdkdeviceprivate.h"
+#include "gdkintl.h"
+#include "gdkinternals.h"
+#include "gdkalias.h"
+
+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 *associated;
+  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_ASSOCIATED_DEVICE,
+  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;
+
+  /**
+   * GdkDevice:display:
+   *
+   * The #GdkDisplay the #GdkDevice pertains to.
+   *
+   * Since: 3.0
+   */
+  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_PARAM_STATIC_STRINGS));
+  /**
+   * GdkDevice:device-manager:
+   *
+   * The #GdkDeviceManager the #GdkDevice pertains to.
+   *
+   * Since: 3.0
+   */
+  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_PARAM_STATIC_STRINGS));
+  /**
+   * GdkDevice:name:
+   *
+   * The device name.
+   *
+   * Since: 3.0
+   */
+  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_PARAM_STATIC_STRINGS));
+  /**
+   * GdkDevice:type:
+   *
+   * Device role in the device manager.
+   *
+   * Since: 3.0
+   */
+  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_PARAM_STATIC_STRINGS));
+  /**
+   * GdkDevice:associated-device:
+   *
+   * Associated pointer or keyboard to this device, if any. Devices of type #GDK_DEVICE_TYPE_MASTER
+   * always come in keyboard/pointer pairs. Other device types will have a %NULL associated device.
+   *
+   * Since: 3.0
+   */
+  g_object_class_install_property (object_class,
+				   PROP_ASSOCIATED_DEVICE,
+				   g_param_spec_object ("associated-device",
+                                                        P_("Associated device"),
+                                                        P_("Associated pointer or keyboard to this device"),
+                                                        GDK_TYPE_DEVICE,
+                                                        G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
+  /**
+   * GdkDevice:input-source:
+   *
+   * Source type for the device.
+   *
+   * Since: 3.0
+   */
+  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_PARAM_STATIC_STRINGS));
+  /**
+   * GdkDevice:input-mode:
+   *
+   * Input mode for the device.
+   *
+   * Since: 3.0
+   */
+  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_PARAM_STATIC_STRINGS));
+  /**
+   * GdkDevice:has-cursor:
+   *
+   * Whether the device is represented by a cursor on the screen. Devices of type
+   * %GDK_DEVICE_TYPE_MASTER will have %TRUE here.
+   *
+   * Since: 3.0
+   */
+  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_PARAM_STATIC_STRINGS));
+  /**
+   * GdkDevice:n-axes:
+   *
+   * Number of axes in the device.
+   *
+   * Since: 3.0
+   */
+  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_PARAM_STATIC_STRINGS));
+
+  g_type_class_add_private (object_class, sizeof (GdkDevicePrivate));
+}
+
+static void
+gdk_device_init (GdkDevice *device)
+{
+  GdkDevicePrivate *priv;
+
+  device->priv = priv = G_TYPE_INSTANCE_GET_PRIVATE (device,
+                                                     GDK_TYPE_DEVICE,
+                                                     GdkDevicePrivate);
+
+  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 = device->priv;
+
+  if (priv->associated)
+    {
+      _gdk_device_set_associated_device (priv->associated, NULL);
+      g_object_unref (priv->associated);
+      priv->associated = 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 = device->priv;
+
+  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 = device->priv;
+
+  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_ASSOCIATED_DEVICE:
+      g_value_set_object (value, priv->associated);
+      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;
+    }
+}
+
+/**
+ * gdk_device_get_state:
+ * @device: a #GdkDevice.
+ * @window: a #GdkWindow.
+ * @axes: an array of doubles to store the values of the axes of @device in,
+ * or %NULL.
+ * @mask: location to store the modifiers, or %NULL.
+ *
+ * Gets the current state of a device relative to @window.
+ */
+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: (array length=n_events) (out) (transfer none): 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 (n_events)
+    *n_events = 0;
+
+  if (events)
+    *events = NULL;
+
+  if (GDK_WINDOW_DESTROYED (window))
+    return 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;
+}
+
+/**
+ * gdk_device_free_history:
+ * @events: (inout) (transfer none): an array of #GdkTimeCoord.
+ * @n_events: the length of the array.
+ *
+ * Frees an array of #GdkTimeCoord that was returned by gdk_device_get_history().
+ */
+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);
+}
+
+/**
+ * gdk_device_get_name:
+ * @device: a #GdkDevice
+ *
+ * Determines the name of the device.
+ *
+ * Return value: a name
+ *
+ * Since: 2.20
+ **/
+const gchar *
+gdk_device_get_name (GdkDevice *device)
+{
+  g_return_val_if_fail (GDK_IS_DEVICE (device), NULL);
+
+  return device->name;
+}
+
+/**
+ * gdk_device_get_has_cursor:
+ * @device: a #GdkDevice
+ *
+ * Determines whether the pointer follows device motion.
+ *
+ * Return value: %TRUE if the pointer follows device motion
+ *
+ * Since: 2.20
+ **/
+gboolean
+gdk_device_get_has_cursor (GdkDevice *device)
+{
+  g_return_val_if_fail (GDK_IS_DEVICE (device), FALSE);
+
+  return device->has_cursor;
+}
+
+/**
+ * gdk_device_get_source:
+ * @device: a #GdkDevice
+ *
+ * Determines the type of the device.
+ *
+ * Return value: a #GdkInputSource
+ *
+ * Since: 2.20
+ **/
+GdkInputSource
+gdk_device_get_source (GdkDevice *device)
+{
+  g_return_val_if_fail (GDK_IS_DEVICE (device), 0);
+
+  return device->source;
+}
+
+/**
+ * gdk_device_set_source:
+ * @device: a #GdkDevice.
+ * @source: the source type.
+ *
+ * Sets the source type for an input device.
+ **/
+void
+gdk_device_set_source (GdkDevice      *device,
+		       GdkInputSource  source)
+{
+  g_return_if_fail (GDK_IS_DEVICE (device));
+
+  device->source = source;
+}
+
+/**
+ * gdk_device_get_mode:
+ * @device: a #GdkDevice
+ *
+ * Determines the mode of the device.
+ *
+ * Return value: a #GdkInputSource
+ *
+ * Since: 2.20
+ **/
+GdkInputMode
+gdk_device_get_mode (GdkDevice *device)
+{
+  g_return_val_if_fail (GDK_IS_DEVICE (device), 0);
+
+  return device->mode;
+}
+
+/**
+ * gdk_device_set_mode:
+ * @device: a #GdkDevice.
+ * @mode: the input mode.
+ *
+ * Sets a the mode of an input device. The mode controls if the
+ * device is active and whether the device's range is mapped to the
+ * entire screen or to a single window.
+ *
+ * Returns: %TRUE if the mode was successfully changed.
+ **/
+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;
+}
+
+/**
+ * gdk_device_get_key:
+ * @device: a #GdkDevice.
+ * @index_: the index of the macro button to get.
+ * @keyval: return value for the keyval.
+ * @modifiers: return value for modifiers.
+ *
+ * If @index_ has a valid keyval, this function will return %TRUE
+ * and fill in @keyval and @modifiers with the keyval settings.
+ *
+ * Returns: %TRUE if keyval is set for @index.
+ *
+ * Since: 2.20
+ **/
+gboolean
+gdk_device_get_key (GdkDevice       *device,
+                    guint            index_,
+                    guint           *keyval,
+                    GdkModifierType *modifiers)
+{
+  g_return_val_if_fail (GDK_IS_DEVICE (device), FALSE);
+  g_return_val_if_fail (index_ < device->num_keys, FALSE);
+
+  if (!device->keys[index_].keyval &&
+      !device->keys[index_].modifiers)
+    return FALSE;
+
+  if (keyval)
+    *keyval = device->keys[index_].keyval;
+
+  if (modifiers)
+    *modifiers = device->keys[index_].modifiers;
+
+  return TRUE;
+}
+
+/**
+ * gdk_device_set_key:
+ * @device: a #GdkDevice
+ * @index_: the index of the macro button to set
+ * @keyval: the keyval to generate
+ * @modifiers: the modifiers to set
+ *
+ * Specifies the X key event to generate when a macro button of a device
+ * is pressed.
+ **/
+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;
+}
+
+/**
+ * gdk_device_get_axis_use:
+ * @device: a #GdkDevice.
+ * @index_: the index of the axis.
+ *
+ * Returns the axis use for @index_.
+ *
+ * Returns: a #GdkAxisUse specifying how the axis is used.
+ *
+ * Since: 2.20
+ **/
+GdkAxisUse
+gdk_device_get_axis_use (GdkDevice *device,
+                         guint      index_)
+{
+  g_return_val_if_fail (GDK_IS_DEVICE (device), GDK_AXIS_IGNORE);
+  g_return_val_if_fail (index_ < device->num_axes, GDK_AXIS_IGNORE);
+
+  return device->axes[index_].use;
+}
+
+/**
+ * gdk_device_set_axis_use:
+ * @device: a #GdkDevice
+ * @index_: the index of the axis
+ * @use: specifies how the axis is used
+ *
+ * Specifies how an axis of a device is used.
+ **/
+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 = device->priv;
+  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_axis = 0;
+      device->axes[index_].max = info->max_axis = 0;
+      break;
+    case GDK_AXIS_XTILT:
+    case GDK_AXIS_YTILT:
+      device->axes[index_].min = info->min_axis = -1;
+      device->axes[index_].max = info->max_axis = 1;
+      break;
+    default:
+      device->axes[index_].min = info->min_axis = 0;
+      device->axes[index_].max = info->max_axis = 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.
+ *
+ * Since: 3.0
+ **/
+GdkDisplay *
+gdk_device_get_display (GdkDevice *device)
+{
+  GdkDevicePrivate *priv;
+
+  g_return_val_if_fail (GDK_IS_DEVICE (device), NULL);
+
+  priv = device->priv;
+
+  return priv->display;
+}
+
+/**
+ * gdk_device_get_associated_device:
+ * @device: a #GdkDevice
+ *
+ * Returns the associated device to @device, if @device is of type
+ * %GDK_DEVICE_TYPE_MASTER, it will return the paired pointer or
+ * keyboard.
+ *
+ * If @device is of type %GDK_DEVICE_TYPE_SLAVE, it will return
+ * the master device to which @device is attached to.
+ *
+ * If @device is of type %GDK_DEVICE_TYPE_FLOATING, %NULL will be
+ * returned, as there is no associated device.
+ *
+ * Returns: The associated device, or %NULL
+ *
+ * Since: 3.0
+ **/
+GdkDevice *
+gdk_device_get_associated_device (GdkDevice *device)
+{
+  GdkDevicePrivate *priv;
+
+  g_return_val_if_fail (GDK_IS_DEVICE (device), NULL);
+
+  priv = device->priv;
+
+  return priv->associated;
+}
+
+void
+_gdk_device_set_associated_device (GdkDevice *device,
+                                   GdkDevice *associated)
+{
+  GdkDevicePrivate *priv;
+
+  g_return_if_fail (GDK_IS_DEVICE (device));
+  g_return_if_fail (GDK_IS_DEVICE (associated));
+
+  priv = device->priv;
+
+  if (priv->associated == associated)
+    return;
+
+  if (priv->associated)
+    {
+      g_object_unref (priv->associated);
+      priv->associated = NULL;
+    }
+
+  if (associated)
+    priv->associated = g_object_ref (associated);
+}
+
+/**
+ * gdk_device_get_device_type:
+ * @device: a #GdkDevice
+ *
+ * Returns the device type for @device.
+ *
+ * Returns: the #GdkDeviceType for @device.
+ *
+ * Since: 3.0
+ **/
+GdkDeviceType
+gdk_device_get_device_type (GdkDevice *device)
+{
+  GdkDevicePrivate *priv;
+
+  g_return_val_if_fail (GDK_IS_DEVICE (device), GDK_DEVICE_TYPE_MASTER);
+
+  priv = device->priv;
+
+  return priv->type;
+}
+
+/**
+ * gdk_device_get_n_axes:
+ * @device: a #GdkDevice
+ *
+ * Returns the number of axes the device currently has.
+ *
+ * Returns: the number of axes.
+ *
+ * Since: 3.0
+ **/
+guint
+gdk_device_get_n_axes (GdkDevice *device)
+{
+  g_return_val_if_fail (GDK_IS_DEVICE (device), 0);
+
+  return device->num_axes;
+}
+
+/**
+ * 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().
+ *
+ * Since: 3.0
+ **/
+GList *
+gdk_device_list_axes (GdkDevice *device)
+{
+  GdkDevicePrivate *priv;
+  GList *axes = NULL;
+  gint i;
+
+  priv = device->priv;
+
+  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);
+}
+
+/**
+ * gdk_device_get_axis_value:
+ * @device: a #GdkDevice.
+ * @axes: pointer to an array of axes
+ * @axis_label: #GdkAtom with the axis label.
+ * @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 label, as returned
+ * by gdk_device_list_axes()
+ *
+ * Returns: %TRUE if the given axis use was found, otherwise %FALSE.
+ *
+ * Since: 3.0
+ **/
+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 = device->priv;
+
+  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 = device->priv;
+
+  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;
+}
+
+static GdkEventMask
+get_native_grab_event_mask (GdkEventMask grab_mask)
+{
+  /* Similar to the above but for pointer events only */
+  return
+    GDK_POINTER_MOTION_MASK |
+    GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
+    GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK |
+    GDK_SCROLL_MASK |
+    (grab_mask &
+     ~(GDK_POINTER_MOTION_HINT_MASK |
+       GDK_BUTTON_MOTION_MASK |
+       GDK_BUTTON1_MOTION_MASK |
+       GDK_BUTTON2_MOTION_MASK |
+       GDK_BUTTON3_MOTION_MASK));
+}
+
+/**
+ * gdk_device_grab:
+ * @device: a #GdkDevice
+ * @window: the #GdkWindow which will own the grab (the grab window)
+ * @grab_ownership: specifies the 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.
+ *
+ * Since: 3.0
+ **/
+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,
+                                    get_native_grab_event_mask (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,
+                                    grab_ownership,
+                                    owner_events,
+                                    event_mask,
+                                    serial,
+                                    time_,
+                                    FALSE);
+    }
+
+  return res;
+}
+
+/* Private API */
+void
+_gdk_device_reset_axes (GdkDevice *device)
+{
+  GdkDevicePrivate *priv;
+  gint i;
+
+  priv = device->priv;
+
+  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 = device->priv;
+
+  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);
+}
+
+static 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 = device->priv;
+
+  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 = device->priv;
+
+  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 = device->priv;
+
+  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)
+    {
+      if (axis_width > 0)
+        scale = gdk_screen_get_width (gdk_drawable_get_screen (window)) / axis_width;
+      else
+        scale = 1;
+
+      offset = - window_root_x - window_private->abs_x;
+    }
+  else
+    {
+      if (axis_width > 0)
+        scale = gdk_screen_get_height (gdk_drawable_get_screen (window)) / axis_width;
+      else
+        scale = 1;
+
+      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 = device->priv;
+
+  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..dfbf48a
--- /dev/null
+++ b/gdk/gdkdevice.h
@@ -0,0 +1,292 @@
+/* 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 _GdkDevicePrivate GdkDevicePrivate;
+
+typedef struct _GdkDeviceKey GdkDeviceKey;
+typedef struct _GdkDeviceAxis GdkDeviceAxis;
+typedef struct _GdkTimeCoord GdkTimeCoord;
+
+/**
+ * GdkExtensionMode:
+ * @GDK_EXTENSION_EVENTS_NONE: no extension events are desired.
+ * @GDK_EXTENSION_EVENTS_ALL: all extension events are desired.
+ * @GDK_EXTENSION_EVENTS_CURSOR: extension events are desired only if a cursor
+ *                               will be displayed for the device.
+ *
+ * An enumeration used to specify which extension events
+ * are desired for a particular widget.
+ */
+typedef enum
+{
+  GDK_EXTENSION_EVENTS_NONE,
+  GDK_EXTENSION_EVENTS_ALL,
+  GDK_EXTENSION_EVENTS_CURSOR
+} GdkExtensionMode;
+
+/**
+ * GdkInputSource:
+ * @GDK_SOURCE_MOUSE: the device is a mouse. (This will be reported for the core
+ *                    pointer, even if it is something else, such as a trackball.)
+ * @GDK_SOURCE_PEN: the device is a stylus of a graphics tablet or similar device.
+ * @GDK_SOURCE_ERASER: the device is an eraser. Typically, this would be the other end
+ *                     of a stylus on a graphics tablet.
+ * @GDK_SOURCE_CURSOR: the device is a graphics tablet "puck" or similar device.
+ * @GDK_SOURCE_KEYBOARD: the device is a keyboard.
+ *
+ * An enumeration describing the type of an input device in general terms.
+ */
+typedef enum
+{
+  GDK_SOURCE_MOUSE,
+  GDK_SOURCE_PEN,
+  GDK_SOURCE_ERASER,
+  GDK_SOURCE_CURSOR,
+  GDK_SOURCE_KEYBOARD
+} GdkInputSource;
+
+/**
+ * GdkInputMode:
+ * @GDK_MODE_DISABLED: the device is disabled and will not report any events.
+ * @GDK_MODE_SCREEN: the device is enabled. The device's coordinate space
+ *                   maps to the entire screen.
+ * @GDK_MODE_WINDOW: the device is enabled. The device's coordinate space
+ *                   is mapped to a single window. The manner in which this window
+ *                   is chosen is undefined, but it will typically be the same
+ *                   way in which the focus window for key events is determined.
+ *
+ * An enumeration that describes the mode of an input device.
+ */
+typedef enum
+{
+  GDK_MODE_DISABLED,
+  GDK_MODE_SCREEN,
+  GDK_MODE_WINDOW
+} GdkInputMode;
+
+/**
+ * GdkAxisUse:
+ * @GDK_AXIS_IGNORE: the axis is ignored.
+ * @GDK_AXIS_X: the axis is used as the x axis.
+ * @GDK_AXIS_Y: the axis is used as the y axis.
+ * @GDK_AXIS_PRESSURE: the axis is used for pressure information.
+ * @GDK_AXIS_XTILT: the axis is used for x tilt information.
+ * @GDK_AXIS_YTILT: the axis is used for x tilt information.
+ * @GDK_AXIS_WHEEL: the axis is used for wheel information.
+ * @GDK_AXIS_LAST: a constant equal to the numerically highest axis value.
+ *
+ * An enumeration describing the way in which a device
+ * axis (valuator) maps onto the predefined valuator
+ * types that GTK+ understands.
+ */
+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;
+
+/**
+ * GdkDeviceType:
+ * @GDK_DEVICE_TYPE_MASTER: Device is a master (or virtual) device. There will
+ *                          be an associated focus indicator on the screen.
+ * @GDK_DEVICE_TYPE_SLAVE: Device is a slave (or physical) device.
+ * @GDK_DEVICE_TYPE_FLOATING: Device is a physical device, currently not attached to
+ *                            any virtual device.
+ *
+ * Indicates the device type. See <link linkend="GdkDeviceManager.description">above</link>
+ * for more information about the meaning of these device types.
+ */
+typedef enum {
+  GDK_DEVICE_TYPE_MASTER,
+  GDK_DEVICE_TYPE_SLAVE,
+  GDK_DEVICE_TYPE_FLOATING
+} GdkDeviceType;
+
+/**
+ * GdkDeviceKey:
+ * @keyval: the keyval to generate when the macro button is pressed.
+ *          If this is 0, no keypress will be generated.
+ * @modifiers: the modifiers set for the generated key event.
+ *
+ * The <structname>GdkDeviceKey</structname> structure contains information
+ * about the mapping of one device macro button onto a normal X key event.
+ */
+struct _GdkDeviceKey
+{
+  guint keyval;
+  GdkModifierType modifiers;
+};
+
+/**
+ * GdkDeviceAxis:
+ * @use: specifies how the axis is used.
+ * @min: the minimal value that will be reported by this axis.
+ * @max: the maximal value that will be reported by this axis.
+ *
+ * The <structname>GdkDeviceAxis</structname> structure contains information
+ * about the range and mapping of a device axis.
+ */
+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
+
+/**
+ * GdkTimeCoord:
+ * @time: The timestamp for this event.
+ * @axes: the values of the device's axes.
+ *
+ * The #GdkTimeCoord structure stores a single event in a motion history.
+ */
+struct _GdkTimeCoord
+{
+  guint32 time;
+  gdouble axes[GDK_MAX_TIMECOORD_AXES];
+};
+
+struct _GdkDevice
+{
+  GObject parent_instance;
+
+  /* All fields are read-only */
+  gchar *GSEAL (name);
+  GdkInputSource GSEAL (source);
+  GdkInputMode GSEAL (mode);
+  gboolean GSEAL (has_cursor);	     /* TRUE if a X pointer follows device motion */
+
+  gint GSEAL (num_axes);
+  GdkDeviceAxis *GSEAL (axes);
+
+  gint GSEAL (num_keys);
+  GdkDeviceKey *GSEAL (keys);
+
+  /*< private >*/
+  GdkDevicePrivate *priv;
+};
+
+GType gdk_device_get_type (void) G_GNUC_CONST;
+
+G_CONST_RETURN gchar *gdk_device_get_name       (GdkDevice *device);
+gboolean              gdk_device_get_has_cursor (GdkDevice *device);
+
+/* Functions to configure a device */
+GdkInputSource gdk_device_get_source    (GdkDevice      *device);
+void           gdk_device_set_source    (GdkDevice      *device,
+					 GdkInputSource  source);
+
+GdkInputMode   gdk_device_get_mode      (GdkDevice      *device);
+gboolean       gdk_device_set_mode      (GdkDevice      *device,
+					 GdkInputMode    mode);
+
+gboolean       gdk_device_get_key       (GdkDevice       *device,
+                                         guint            index_,
+                                         guint           *keyval,
+                                         GdkModifierType *modifiers);
+void           gdk_device_set_key       (GdkDevice      *device,
+					 guint           index_,
+					 guint           keyval,
+					 GdkModifierType modifiers);
+
+GdkAxisUse     gdk_device_get_axis_use  (GdkDevice         *device,
+                                         guint              index_);
+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);
+
+guint    gdk_device_get_n_axes     (GdkDevice       *device);
+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_associated_device (GdkDevice     *device);
+
+GdkDeviceType gdk_device_get_device_type (GdkDevice *device);
+
+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);
+
+
+G_END_DECLS
+
+#endif /* __GDK_DEVICE_H__ */
diff --git a/gdk/gdkdevicemanager.c b/gdk/gdkdevicemanager.c
new file mode 100644
index 0000000..5bce0fc
--- /dev/null
+++ b/gdk/gdkdevicemanager.c
@@ -0,0 +1,302 @@
+/* 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 "gdkdevicemanager.h"
+#include "gdkintl.h"
+#include "gdkinternals.h"
+#include "gdkalias.h"
+
+/**
+ * SECTION:gdkdevicemanager
+ * @Short_description: Functions for handling input devices
+ * @Long_description: In addition to a single pointer and keyboard for user interface input, GDK
+ *                    contains support for a variety of input devices, including graphics tablets,
+ *                    touchscreens and multiple pointers/keyboards interacting simultaneously with
+ *                    the user interface. Under X, the support for multiple input devices is done
+ *                    through the <firstterm>XInput 2</firstterm> extension, which also supports
+ *                    additional features such as sub-pixel positioning information and additional
+ *                    device-dependent information.
+ * @Title: GdkDeviceManager
+ * @See_also: #GdkDevice, #GdkEvent, gdk_enable_multidevice()
+ *
+ * By default, GDK supports the traditional single keyboard/pointer input scheme (Plus additional
+ * special input devices such as tablets. In short, backwards compatible with 2.X). Since version 3.0,
+ * if gdk_enable_multidevice() is called before gdk_display_open() and the platform supports it, GDK
+ * will be aware of multiple keyboard/pointer pairs interacting simultaneously with the user interface.
+ *
+ * Conceptually, in multidevice mode there are 2 device types, virtual devices (or master devices)
+ * are represented by the pointer cursors and keyboard foci that are seen on the screen. physical
+ * devices (or slave devices) represent the hardware that is controlling the virtual devices, and
+ * thus has no visible cursor on the screen.
+ *
+ * Virtual devices are always paired, there is a keyboard device for every pointer device,
+ * associations between devices may be inspected through gdk_device_get_associated_device().
+ *
+ * There may be several virtual devices, and several physical devices could be controlling each of
+ * these virtual devices. Physical devices may also be "floating", which means they are not attached
+ * to any virtual device.
+ *
+ * By default, GDK will automatically listen for events coming from all master devices, setting the
+ * #GdkDevice for all events coming from input devices
+ * <footnote>
+ *   Events containing device information are #GDK_MOTION_NOTIFY, #GDK_BUTTON_PRESS, #GDK_2BUTTON_PRESS,
+ *   #GDK_3BUTTON_PRESS, #GDK_BUTTON_RELEASE, #GDK_SCROLL, #GDK_KEY_PRESS, #GDK_KEY_RELEASE,
+ *   #GDK_ENTER_NOTIFY, #GDK_LEAVE_NOTIFY, #GDK_FOCUS_CHANGE, #GDK_PROXIMITY_IN, #GDK_PROXIMITY_OUT,
+ *   #GDK_DRAG_ENTER, #GDK_DRAG_LEAVE, #GDK_DRAG_MOTION, #GDK_DRAG_STATUS, #GDK_DROP_START,
+ *   #GDK_DROP_FINISHED and #GDK_GRAB_BROKEN.
+ * </footnote>
+ * , although gdk_window_set_support_multidevice() has to be called on #GdkWindow<!-- --> in order to
+ * support additional features of multiple pointer interaction, such as multiple, per-device enter/leave
+ * events. The default setting will emit just one enter/leave event pair for all devices on the window.
+ * See gdk_window_set_support_multidevice() documentation for more information.
+ *
+ * In order to listen for events coming from other than a virtual device, gdk_window_set_device_events()
+ * must be called. Generally, this function can be used to modify the event mask for any given device.
+ *
+ * Input devices may also provide additional information besides X/Y. For example, graphics tablets may
+ * also provide pressure and X/Y tilt information. This information is device-dependent, and may be
+ * queried through gdk_device_get_axis(). In multidevice mode, virtual devices will change axes in order
+ * to always represent the physical device that is routing events through it. Whenever the physical device
+ * changes, the #GdkDevice:n-axes property will be notified, and gdk_device_list_axes() will return the
+ * new device axes.
+ *
+ * Devices may also have associated <firstterm>keys</firstterm> or macro buttons. Such keys can be
+ * globally set to map into normal X keyboard events. The mapping is set using gdk_device_set_key().
+ *
+ * In order to query the device hierarchy and be aware of changes in the device hierarchy (such as
+ * virtual devices being created or removed, or physical devices being plugged or unplugged), GDK
+ * provides #GdkDeviceManager. On X11, multidevice support is implemented through XInput 2. If
+ * gdk_enable_multidevice() is called, the XInput 2.x #GdkDeviceManager implementation will be used
+ * as input source, else either the core or XInput 1.x implementations will be used.
+ */
+
+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 };
+
+
+struct _GdkDeviceManagerPrivate
+{
+  GdkDisplay *display;
+};
+
+
+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 |
+                                                        G_PARAM_STATIC_STRINGS));
+
+  /**
+   * 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-removed 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 *device_manager)
+{
+  GdkDeviceManagerPrivate *priv;
+
+  device_manager->priv = priv = G_TYPE_INSTANCE_GET_PRIVATE (device_manager,
+                                                             GDK_TYPE_DEVICE_MANAGER,
+                                                             GdkDeviceManagerPrivate);
+}
+
+static void
+gdk_device_manager_set_property (GObject      *object,
+                                 guint         prop_id,
+                                 const GValue *value,
+                                 GParamSpec   *pspec)
+{
+  GdkDeviceManagerPrivate *priv;
+
+  priv = GDK_DEVICE_MANAGER (object)->priv;
+
+  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 (object)->priv;
+
+  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.
+ *
+ * Since: 3.0
+ **/
+GdkDisplay *
+gdk_device_manager_get_display (GdkDeviceManager *device_manager)
+{
+  GdkDeviceManagerPrivate *priv;
+
+  g_return_val_if_fail (GDK_IS_DEVICE_MANAGER (device_manager), NULL);
+
+  priv = device_manager->priv;
+
+  return priv->display;
+}
+
+/**
+ * gdk_device_manager_list_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 (). The list elements are owned by
+ *          GTK+ and must not be freed or unreffed.
+ *
+ * Since: 3.0
+ **/
+GList *
+gdk_device_manager_list_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)->list_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..32ac376
--- /dev/null
+++ b/gdk/gdkdevicemanager.h
@@ -0,0 +1,76 @@
+/* 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 (__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 _GdkDeviceManagerPrivate GdkDeviceManagerPrivate;
+typedef struct _GdkDeviceManagerClass GdkDeviceManagerClass;
+
+struct _GdkDeviceManager
+{
+  GObject parent_instance;
+
+  /*< private >*/
+  GdkDeviceManagerPrivate *priv;
+};
+
+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 * (* list_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_list_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..15f0546
--- /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_associated_device (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 ac49a5b..d643294 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,87 @@ 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->motion_hint_info = g_hash_table_new_full (NULL, NULL, NULL,
+                                                     (GDestroyNotify) g_free);
+
+  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 +286,28 @@ gdk_display_dispose (GObject *object)
                                                  NULL);
     }
 
+  if (device_manager)
+    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,12 +410,55 @@ 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
+ *
+ * Deprecated: 3.0. Use gdk_device_ungrab(), together with gdk_device_grab()
+ *             instead.
+ */
+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_list_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.
  *
  * Ungrabs the pointer on the default display, if it is grabbed by this 
  * application.
+ *
+ * Deprecated: 3.0. Use gdk_device_ungrab(), together with gdk_device_grab()
+ *             instead.
  **/
 void
 gdk_pointer_ungrab (guint32 time)
@@ -294,8 +474,10 @@ gdk_pointer_ungrab (guint32 time)
  *
  * Note that this does not take the inmplicit pointer grab on button
  * presses into account.
-
- * Return value: %TRUE if the pointer is currently grabbed by this application.* 
+ *
+ * Return value: %TRUE if the pointer is currently grabbed by this application.
+ *
+ * Deprecated: 3.0. Use gdk_display_device_is_grabbed() instead.
  **/
 gboolean
 gdk_pointer_is_grabbed (void)
@@ -304,12 +486,55 @@ 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
+ *
+ * Deprecated: 3.0. Use gdk_device_ungrab(), together with gdk_device_grab()
+ *             instead.
+ */
+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_list_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.
  * 
  * Ungrabs the keyboard on the default display, if it is grabbed by this 
  * application.
+ *
+ * Deprecated: 3.0. Use gdk_device_ungrab(), together with gdk_device_grab()
+ *             instead.
  **/
 void
 gdk_keyboard_ungrab (guint32 time)
@@ -377,6 +602,10 @@ gdk_event_send_clientmessage_toall (GdkEvent *event)
  * 
  * Return value: the core pointer device; this is owned by the
  *   display and should not be freed.
+ *
+ * Deprecated: 3.0. Use gdk_display_get_device_manager() instead, or
+ *             gdk_event_get_device() if a #GdkEvent with pointer device
+ *             information is available.
  **/
 GdkDevice *
 gdk_device_get_core_pointer (void)
@@ -394,6 +623,10 @@ gdk_device_get_core_pointer (void)
  *   display and should not be freed.
  *
  * Since: 2.2
+ *
+ * Deprecated: 3.0. Use gdk_display_get_device_manager() instead, or
+ *             gdk_event_get_device() if a #GdkEvent with device
+ *             information is available.
  **/
 GdkDevice *
 gdk_display_get_core_pointer (GdkDisplay *display)
@@ -445,11 +678,21 @@ _gdk_get_sm_client_id (void)
 }
 
 void
-_gdk_display_enable_motion_hints (GdkDisplay *display)
+_gdk_display_enable_motion_hints (GdkDisplay *display,
+                                  GdkDevice  *device)
 {
-  gulong serial;
-  
-  if (display->pointer_info.motion_hint_serial != 0)
+  gulong *device_serial, serial;
+
+  device_serial = g_hash_table_lookup (display->motion_hint_info, device);
+
+  if (!device_serial)
+    {
+      device_serial = g_new0 (gulong, 1);
+      *device_serial = G_MAXULONG;
+      g_hash_table_insert (display->motion_hint_info, device, device_serial);
+    }
+
+  if (*device_serial != 0)
     {
       serial = _gdk_windowing_window_get_next_serial (display);
       /* We might not actually generate the next request, so
@@ -458,12 +701,119 @@ _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 < *device_serial)
+	*device_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.
+ *
+ * Since: 3.0
+ **/
+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.
+ *
+ * Since: 3.0
+ **/
+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.
+ *
+ * Since: 3.0
+ **/
+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: (allow-none): location to store the screen that the
@@ -476,6 +826,8 @@ _gdk_display_enable_motion_hints (GdkDisplay *display)
  * mask for a given display.
  *
  * Since: 2.2
+ *
+ * Deprecated: 3.0. Use gdk_display_get_device_state() instead.
  **/
 void
 gdk_display_get_pointer (GdkDisplay      *display,
@@ -484,33 +836,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 +872,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 +886,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;
@@ -580,25 +922,82 @@ gdk_window_real_window_get_pointer (GdkDisplay       *display,
  * Returns: (transfer none): the window under the mouse pointer, or %NULL
  *
  * Since: 2.2
+ *
+ * Deprecated: 3.0. Use gdk_display_get_window_at_device_position() instead.
  **/
 GdkWindow *
 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);
 }
 
 /**
@@ -617,20 +1016,23 @@ gdk_display_get_window_at_pointer (GdkDisplay *display,
  * Return value: the previous pointer hook table
  *
  * Since: 2.2
+ *
+ * Deprecated: 3.0. Use gdk_display_set_device_hooks() instead.
  **/
 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 +1079,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 +1093,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);
 }
 
 /**
@@ -706,6 +1118,8 @@ singlehead_default_window_at_pointer  (GdkScreen       *screen,
  * see gdk_display_set_pointer_hooks().
  * 
  * Return value: the previous pointer hook table
+ *
+ * Deprecated: 3.0. Use gdk_display_set_device_hooks() instead.
  **/
 GdkPointerHooks *
 gdk_set_pointer_hooks (const GdkPointerHooks *new_hooks)
@@ -725,7 +1139,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)
 {
@@ -733,45 +1147,54 @@ generate_grab_broken_event (GdkWindow *window,
 
   if (!GDK_WINDOW_DESTROYED (window))
     {
-      GdkEvent event;
-      event.type = GDK_GRAB_BROKEN;
-      event.grab_broken.window = window;
-      event.grab_broken.send_event = 0;
-      event.grab_broken.keyboard = keyboard;
-      event.grab_broken.implicit = implicit;
-      event.grab_broken.grab_window = grab_window;
-      gdk_event_put (&event);
+      GdkEvent *event;
+
+      event = gdk_event_new (GDK_GRAB_BROKEN);
+      event->grab_broken.window = g_object_ref (window);
+      event->grab_broken.send_event = FALSE;
+      event->grab_broken.implicit = implicit;
+      event->grab_broken.grab_window = grab_window;
+      gdk_event_set_device (event, device);
+      event->grab_broken.keyboard = (device->source == GDK_SOURCE_KEYBOARD) ? TRUE : FALSE;
+
+      gdk_event_put (event);
+      gdk_event_free (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 +1204,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 +1227,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 +1237,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 +1283,7 @@ synthesize_crossing_events (GdkDisplay *display,
       _gdk_synthesize_crossing_events (display,
 				       src_window,
 				       dest_window,
+                                       device,
 				       crossing_mode,
 				       x, y, state,
 				       time,
@@ -873,13 +1295,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 +1312,7 @@ synthesize_crossing_events (GdkDisplay *display,
       _gdk_synthesize_crossing_events (display,
 				       src_window,
 				       NULL,
+                                       device,
 				       crossing_mode,
 				       x, y, state,
 				       time,
@@ -899,6 +1323,7 @@ synthesize_crossing_events (GdkDisplay *display,
       _gdk_synthesize_crossing_events (display,
 				       NULL,
 				       dest_window,
+                                       device,
 				       crossing_mode,
 				       x, y, state,
 				       time,
@@ -908,15 +1333,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 +1358,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 +1392,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 +1418,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 +1444,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 +1504,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 +1569,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 +1596,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 +1731,6 @@ gdk_pointer_grab_info_libgtk_only (GdkDisplay *display,
     return FALSE;
 }
 
-
 /**
  * gdk_display_pointer_is_grabbed:
  * @display: a #GdkDisplay
@@ -1263,21 +1740,77 @@ gdk_pointer_grab_info_libgtk_only (GdkDisplay *display,
  * Returns: %TRUE if an active X pointer grab is in effect
  *
  * Since: 2.2
+ *
+ * Deprecated: 3.0. Use gdk_display_device_is_grabbed() instead.
  */
 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_list_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;
+}
+
+/**
+ * gdk_display_device_is_grabbed:
+ * @display: a #GdkDisplay
+ * @device: a #GdkDevice
+ *
+ * Returns %TRUE if there is an ongoing grab on @device for @display.
+ *
+ * Returns: %TRUE if there is a grab in effect for @device.
+ **/
+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.
+ *
+ * Since: 3.0
+ **/
+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 5de93ef..55fcf38 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,29 @@ struct _GdkDisplay
   /* Information for determining if the latest button click
    * is part of a double-click or triple-click
    */
-  guint32 GSEAL (button_click_time[2]);	/* The last 2 button click times. */
-  GdkWindow *GSEAL (button_window[2]);  /* The last 2 windows to receive button presses. */
-  gint GSEAL (button_number[2]);        /* The last 2 buttons to be pressed. */
+  GHashTable *GSEAL (multiple_click_info);
 
   guint GSEAL (double_click_time);	/* Maximum time between clicks in msecs */
   GdkDevice *GSEAL (core_pointer);	/* Core pointer device */
 
-  const GdkDisplayPointerHooks *GSEAL (pointer_hooks); /* Current hooks for querying pointer */
+  const GdkDisplayDeviceHooks *GSEAL (device_hooks); /* Current hooks for querying pointer */
   
   guint GSEAL (closed) : 1;		/* Whether this display has been closed */
   guint GSEAL (ignore_core_events) : 1; /* Don't send core motion and button event */
 
   guint GSEAL (double_click_distance);	/* Maximum distance between clicks in pixels */
-  gint GSEAL (button_x[2]);             /* The last 2 button click positions. */
-  gint GSEAL (button_y[2]);
 
-  GList *GSEAL (pointer_grabs);
-  GdkKeyboardGrabInfo GSEAL (keyboard_grab);
-  GdkPointerWindowInfo GSEAL (pointer_info);
+  GHashTable *GSEAL (device_grabs);
+  GHashTable *GSEAL (motion_hint_info);
+
+  /* Hashtable containing a GdkPointerWindowInfo for each device */
+  GHashTable *GSEAL (pointers_info);
 
   /* Last reported event time from server */
   guint32 GSEAL (last_event_time);
+
+  /* Device manager associated to the display */
+  GdkDeviceManager *GSEAL (device_manager);
 };
 
 struct _GdkDisplayClass
@@ -138,6 +149,44 @@ struct _GdkDisplayPointerHooks
 				    gint            *win_y);
 };
 
+/**
+ * GdkDisplayDeviceHooks:
+ * @get_device_state: Obtains the current position and modifier state for
+ * @device. The position is given in coordinates relative to the window
+ * containing the pointer, which is returned in @window.
+ * @window_get_device_position: Obtains the window underneath the device
+ * position. Current device position and modifier state are returned in
+ * @x, @y and @mask. The position is given in coordinates relative to
+ * @window.
+ * @window_at_device_position: Obtains the window underneath the device
+ * position, returning the location of that window in @win_x, @win_y.
+ * Returns %NULL if the window under the mouse pointer is not known to
+ * GDK (for example, belongs to another application).
+ *
+ * A table of pointers to functions for getting quantities related to
+ * the current device position. Each #GdkDisplay has a table of this type,
+ * which can be set using gdk_display_set_device_hooks().
+ */
+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,18 +196,26 @@ 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);
 
 void	    gdk_display_close		   (GdkDisplay  *display);
 
+#ifndef GDK_DISABLE_DEPRECATED
 GList *     gdk_display_list_devices       (GdkDisplay  *display);
+#endif /* GDK_DISABLE_DEPRECATED */
 
 GdkEvent* gdk_display_get_event  (GdkDisplay     *display);
 GdkEvent* gdk_display_peek_event (GdkDisplay     *display);
@@ -177,7 +234,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 +252,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 +304,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/gdkdnd.h b/gdk/gdkdnd.h
index 7fa98a5..2b6ceb0 100644
--- a/gdk/gdkdnd.h
+++ b/gdk/gdkdnd.h
@@ -32,6 +32,7 @@
 #define __GDK_DND_H__
 
 #include <gdk/gdktypes.h>
+#include <gdk/gdkdevice.h>
 
 G_BEGIN_DECLS
 
@@ -106,6 +107,10 @@ struct _GdkDragContextClass {
 GType            gdk_drag_context_get_type   (void) G_GNUC_CONST;
 GdkDragContext * gdk_drag_context_new        (void);
 
+void             gdk_drag_context_set_device           (GdkDragContext *context,
+                                                        GdkDevice      *device);
+GdkDevice *      gdk_drag_context_get_device           (GdkDragContext *context);
+
 GList           *gdk_drag_context_list_targets         (GdkDragContext *context);
 GdkDragAction    gdk_drag_context_get_actions          (GdkDragContext *context);
 GdkDragAction    gdk_drag_context_get_suggested_action (GdkDragContext *context);
diff --git a/gdk/gdkevents.c b/gdk/gdkevents.c
index 6152202..151ce86 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"
@@ -446,6 +447,7 @@ gdk_event_copy (const GdkEvent *event)
       GdkEventPrivate *private = (GdkEventPrivate *)event;
 
       new_private->screen = private->screen;
+      new_private->device = private->device;
     }
   
   switch (event->any.type)
@@ -920,6 +922,95 @@ gdk_event_get_axis (const GdkEvent *event,
 }
 
 /**
+ * gdk_event_set_device:
+ * @event: a #GdkEvent
+ * @device: a #GdkDevice
+ *
+ * Sets the device for @event to @device. The event must
+ * have been allocated by GTK+, for instance, by
+ * gdk_event_copy().
+ *
+ * Since: 3.0
+ **/
+void
+gdk_event_set_device (GdkEvent  *event,
+                      GdkDevice *device)
+{
+  GdkEventPrivate *private;
+
+  g_return_if_fail (gdk_event_is_allocated (event));
+
+  private = (GdkEventPrivate *) event;
+
+  private->device = device;
+
+  switch (event->type)
+    {
+    case GDK_MOTION_NOTIFY:
+      event->motion.device = device;
+      break;
+    case GDK_BUTTON_PRESS:
+    case GDK_2BUTTON_PRESS:
+    case GDK_3BUTTON_PRESS:
+    case GDK_BUTTON_RELEASE:
+      event->button.device = device;
+      break;
+    case GDK_SCROLL:
+      event->scroll.device = device;
+      break;
+    case GDK_PROXIMITY_IN:
+    case GDK_PROXIMITY_OUT:
+      event->proximity.device = device;
+      break;
+    default:
+      break;
+    }
+}
+
+/**
+ * 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.
+ *
+ * Since: 3.0
+ **/
+GdkDevice *
+gdk_event_get_device (const GdkEvent *event)
+{
+  g_return_val_if_fail (event != NULL, NULL);
+
+  if (gdk_event_is_allocated (event))
+    {
+      GdkEventPrivate *private = (GdkEventPrivate *) event;
+
+      if (private->device)
+        return private->device;
+    }
+
+  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_SCROLL:
+      return event->scroll.device;
+    case GDK_PROXIMITY_IN:
+    case GDK_PROXIMITY_OUT:
+      return event->proximity.device;
+    default:
+      return NULL;
+    }
+}
+
+/**
  * gdk_event_request_motions:
  * @event: a valid #GdkEvent
  *
@@ -954,8 +1045,139 @@ gdk_event_request_motions (const GdkEventMotion *event)
       gdk_device_get_state (event->device, event->window, NULL, NULL);
       
       display = gdk_drawable_get_display (event->window);
-      _gdk_display_enable_motion_hints (display);
+      _gdk_display_enable_motion_hints (display, event->device);
+    }
+}
+
+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;
+}
+
+/**
+ * gdk_events_get_distance:
+ * @event1: first #GdkEvent
+ * @event2: second #GdkEvent
+ * @distance: return location for the distance
+ *
+ * If both events have X/Y information, the distance between both coordinates
+ * (as in a straight line going from @event1 to @event2) will be returned.
+ *
+ * Returns: %TRUE if the distance could be calculated.
+ *
+ * Since: 3.0
+ **/
+gboolean
+gdk_events_get_distance (GdkEvent *event1,
+                         GdkEvent *event2,
+                         gdouble  *distance)
+{
+  return gdk_events_get_axis_distances (event1, event2,
+                                        NULL, NULL,
+                                        distance);
+}
+
+/**
+ * gdk_events_get_angle:
+ * @event1: first #GdkEvent
+ * @event2: second #GdkEvent
+ * @angle: return location for the relative angle between both events
+ *
+ * If both events contain X/Y information, this function will return %TRUE
+ * and return in @angle the relative angle from @event1 to @event2. The rotation
+ * direction for positive angles is from the positive X axis towards the positive
+ * Y axis.
+ *
+ * Returns: %TRUE if the angle could be calculated.
+ *
+ * Since: 3.0
+ **/
+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;
+}
+
+/**
+ * gdk_events_get_center:
+ * @event1: first #GdkEvent
+ * @event2: second #GdkEvent
+ * @x: return location for the X coordinate of the center
+ * @y: return location for the Y coordinate of the center
+ *
+ * If both events contain X/Y information, the center of both coordinates
+ * will be returned in @x and @y.
+ *
+ * Returns: %TRUE if the center could be calculated.
+ *
+ * Since: 3.0
+ **/
+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;
 }
 
 /**
@@ -1186,54 +1408,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 2f7a09b..f8f6d1a 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
 
@@ -187,35 +187,6 @@ typedef enum
   GDK_EVENT_LAST        /* helper variable for decls */
 } GdkEventType;
 
-/* Event masks. (Used to select what types of events a window
- *  will receive).
- */
-typedef enum
-{
-  GDK_EXPOSURE_MASK		= 1 << 1,
-  GDK_POINTER_MOTION_MASK	= 1 << 2,
-  GDK_POINTER_MOTION_HINT_MASK	= 1 << 3,
-  GDK_BUTTON_MOTION_MASK	= 1 << 4,
-  GDK_BUTTON1_MOTION_MASK	= 1 << 5,
-  GDK_BUTTON2_MOTION_MASK	= 1 << 6,
-  GDK_BUTTON3_MOTION_MASK	= 1 << 7,
-  GDK_BUTTON_PRESS_MASK		= 1 << 8,
-  GDK_BUTTON_RELEASE_MASK	= 1 << 9,
-  GDK_KEY_PRESS_MASK		= 1 << 10,
-  GDK_KEY_RELEASE_MASK		= 1 << 11,
-  GDK_ENTER_NOTIFY_MASK		= 1 << 12,
-  GDK_LEAVE_NOTIFY_MASK		= 1 << 13,
-  GDK_FOCUS_CHANGE_MASK		= 1 << 14,
-  GDK_STRUCTURE_MASK		= 1 << 15,
-  GDK_PROPERTY_CHANGE_MASK	= 1 << 16,
-  GDK_VISIBILITY_NOTIFY_MASK	= 1 << 17,
-  GDK_PROXIMITY_IN_MASK		= 1 << 18,
-  GDK_PROXIMITY_OUT_MASK	= 1 << 19,
-  GDK_SUBSTRUCTURE_MASK		= 1 << 20,
-  GDK_SCROLL_MASK               = 1 << 21,
-  GDK_ALL_EVENTS_MASK		= 0x3FFFFE
-} GdkEventMask;
-
 typedef enum
 {
   GDK_VISIBILITY_UNOBSCURED,
@@ -569,7 +540,22 @@ gboolean  gdk_event_get_root_coords	(const GdkEvent  *event,
 gboolean  gdk_event_get_axis            (const GdkEvent  *event,
                                          GdkAxisUse       axis_use,
                                          gdouble         *value);
+void       gdk_event_set_device         (GdkEvent        *event,
+                                         GdkDevice       *device);
+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/gdkglobals.c b/gdk/gdkglobals.c
index 4c3ad80..a805bcc 100644
--- a/gdk/gdkglobals.c
+++ b/gdk/gdkglobals.c
@@ -40,6 +40,7 @@ gchar              *_gdk_display_name = NULL;
 gint                _gdk_screen_number = -1;
 gchar              *_gdk_display_arg_name = NULL;
 gboolean            _gdk_native_windows = FALSE;
+gboolean            _gdk_enable_multidevice = FALSE;
 
 GSList             *_gdk_displays = NULL;
 
diff --git a/gdk/gdkinput.h b/gdk/gdkinput.h
index ad84395..bc30cd2 100644
--- a/gdk/gdkinput.h
+++ b/gdk/gdkinput.h
@@ -32,159 +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 *GSEAL (name);
-  GdkInputSource GSEAL (source);
-  GdkInputMode GSEAL (mode);
-  gboolean GSEAL (has_cursor);   /* TRUE if the X pointer follows device motion */
-	  
-  gint GSEAL (num_axes);
-  GdkDeviceAxis *GSEAL (axes);
-	  
-  gint GSEAL (num_keys);
-  GdkDeviceKey *GSEAL (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 */
-
-G_CONST_RETURN gchar *gdk_device_get_name       (GdkDevice *device);
-GdkInputSource        gdk_device_get_source     (GdkDevice *device);
-GdkInputMode          gdk_device_get_mode       (GdkDevice *device);
-gboolean              gdk_device_get_has_cursor (GdkDevice *device);
-
-void                  gdk_device_get_key        (GdkDevice       *device,
-                                                 guint            index,
-                                                 guint           *keyval,
-                                                 GdkModifierType *modifiers);
-GdkAxisUse            gdk_device_get_axis_use   (GdkDevice       *device,
-                                                 guint            index);
-
-/* 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);
+/* Returns a list of GdkDevice * */
+GList *    gdk_devices_list            (void);
 
-gboolean gdk_device_get_history  (GdkDevice         *device,
-				  GdkWindow         *window,
-				  guint32            start,
-				  guint32            stop,
-				  GdkTimeCoord    ***events,
-				  gint              *n_events);
+GdkDevice *gdk_device_get_core_pointer (void);
 
-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 822335f..b154c57 100644
--- a/gdk/gdkinternals.h
+++ b/gdk/gdkinternals.h
@@ -169,6 +169,7 @@ struct _GdkEventPrivate
   guint      flags;
   GdkScreen *screen;
   gpointer   windowing_data;
+  GdkDevice *device;
 };
 
 /* Tracks information about the pointer grab on this display */
@@ -182,13 +183,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 +243,7 @@ struct _GdkWindowObject
   guint accept_focus : 1;
   guint focus_on_map : 1;
   guint shaped : 1;
+  guint support_multidevice : 1;
   
   GdkEventMask event_mask;
 
@@ -255,6 +263,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;
@@ -274,6 +283,9 @@ struct _GdkWindowObject
   
   cairo_surface_t *cairo_surface;
   guint outstanding_surfaces; /* only set on impl window */
+
+  GList *devices_inside;
+  GHashTable *device_events;
 };
 
 #define GDK_WINDOW_TYPE(d) (((GdkWindowObject*)(GDK_WINDOW (d)))->window_type)
@@ -287,6 +299,7 @@ extern GSList    *_gdk_displays;
 extern gchar     *_gdk_display_name;
 extern gint       _gdk_screen_number;
 extern gchar     *_gdk_display_arg_name;
+extern gboolean   _gdk_enable_multidevice;
 
 void      _gdk_events_queue  (GdkDisplay *display);
 GdkEvent* _gdk_event_unqueue (GdkDisplay *display);
@@ -315,6 +328,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
 
@@ -453,17 +469,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,
@@ -580,34 +599,41 @@ 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_enable_motion_hints     (GdkDisplay *display);
-
+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,
+                                           GdkDevice  *device);
+
+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);
@@ -639,6 +665,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,
@@ -648,7 +675,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 23d92af..2c4113b 100644
--- a/gdk/gdkoffscreenwindow.c
+++ b/gdk/gdkoffscreenwindow.c
@@ -811,10 +811,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;
@@ -829,7 +830,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);
@@ -1110,27 +1111,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,
@@ -1289,7 +1269,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;
@@ -1298,7 +1277,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 78748dc..f2a1ff4 100644
--- a/gdk/gdktypes.h
+++ b/gdk/gdktypes.h
@@ -236,6 +236,50 @@ typedef enum
   GDK_GRAB_FROZEN          = 4
 } GdkGrabStatus;
 
+/**
+ * GdkGrabOwnership:
+ * @GDK_OWNERSHIP_NONE: All other devices' events are allowed.
+ * @GDK_OWNERSHIP_WINDOW: Other devices' events are blocked for the grab window.
+ * @GDK_OWNERSHIP_APPLICATION: Other devices' events are blocked for the whole application.
+ *
+ * Defines how device grabs interact with other devices.
+ */
+typedef enum
+{
+  GDK_OWNERSHIP_NONE,
+  GDK_OWNERSHIP_WINDOW,
+  GDK_OWNERSHIP_APPLICATION
+} GdkGrabOwnership;
+
+/* Event masks. (Used to select what types of events a window
+ *  *  will receive).
+ *   */
+typedef enum
+{
+  GDK_EXPOSURE_MASK             = 1 << 1,
+  GDK_POINTER_MOTION_MASK       = 1 << 2,
+  GDK_POINTER_MOTION_HINT_MASK  = 1 << 3,
+  GDK_BUTTON_MOTION_MASK        = 1 << 4,
+  GDK_BUTTON1_MOTION_MASK       = 1 << 5,
+  GDK_BUTTON2_MOTION_MASK       = 1 << 6,
+  GDK_BUTTON3_MOTION_MASK       = 1 << 7,
+  GDK_BUTTON_PRESS_MASK         = 1 << 8,
+  GDK_BUTTON_RELEASE_MASK       = 1 << 9,
+  GDK_KEY_PRESS_MASK            = 1 << 10,
+  GDK_KEY_RELEASE_MASK          = 1 << 11,
+  GDK_ENTER_NOTIFY_MASK         = 1 << 12,
+  GDK_LEAVE_NOTIFY_MASK         = 1 << 13,
+  GDK_FOCUS_CHANGE_MASK         = 1 << 14,
+  GDK_STRUCTURE_MASK            = 1 << 15,
+  GDK_PROPERTY_CHANGE_MASK      = 1 << 16,
+  GDK_VISIBILITY_NOTIFY_MASK    = 1 << 17,
+  GDK_PROXIMITY_IN_MASK         = 1 << 18,
+  GDK_PROXIMITY_OUT_MASK        = 1 << 19,
+  GDK_SUBSTRUCTURE_MASK         = 1 << 20,
+  GDK_SCROLL_MASK               = 1 << 21,
+  GDK_ALL_EVENTS_MASK           = 0x3FFFFE
+} GdkEventMask;
+
 typedef void (*GdkInputFunction) (gpointer	    data,
 				  gint		    source,
 				  GdkInputCondition condition);
diff --git a/gdk/gdkwindow.c b/gdk/gdkwindow.c
index 10b27d3..17d6e20 100644
--- a/gdk/gdkwindow.c
+++ b/gdk/gdkwindow.c
@@ -37,6 +37,8 @@
 #include "gdk.h"                /* For gdk_rectangle_union() */
 #include "gdkinternals.h"
 #include "gdkintl.h"
+#include "gdkscreen.h"
+#include "gdkdeviceprivate.h"
 #include "gdkdrawable.h"
 #include "gdkmarshalers.h"
 #include "gdkpixmap.h"
@@ -224,7 +226,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,
@@ -402,7 +403,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);
@@ -650,10 +652,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_drawable_get_display (GDK_DRAWABLE (window)));
+  g_signal_handlers_disconnect_by_func (device_manager, device_removed_cb, window);
 
   if (!GDK_WINDOW_DESTROYED (window))
     {
@@ -690,6 +712,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);
 }
 
@@ -1246,12 +1277,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;
@@ -1313,6 +1352,12 @@ get_native_grab_event_mask (GdkEventMask grab_mask)
      ~GDK_POINTER_MOTION_HINT_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.
@@ -1365,6 +1410,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);
 
@@ -1541,6 +1587,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_drawable_get_display (GDK_DRAWABLE (parent)));
+  g_signal_connect (device_manager, "device-removed",
+                    G_CALLBACK (device_removed_cb), window);
+
   return window;
 }
 
@@ -2003,6 +2056,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
@@ -2139,9 +2216,6 @@ _gdk_window_destroy_hierarchy (GdkWindow *window,
 
 	  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);
@@ -2165,11 +2239,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)
 	    {
@@ -6377,6 +6447,8 @@ gdk_window_constrain_size (GdkGeometry *geometry,
  * Return value: (transfer none): the window containing the pointer (as with
  * gdk_window_at_pointer()), or %NULL if the window containing the
  * pointer isn't known to GDK
+ *
+ * Deprecated: 3.0. Use gdk_window_get_device_position() instead.
  **/
 GdkWindow*
 gdk_window_get_pointer (GdkWindow	  *window,
@@ -6385,29 +6457,50 @@ 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.
+ *
+ * Since: 3.0
+ **/
+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;
@@ -6416,7 +6509,7 @@ gdk_window_get_pointer (GdkWindow	  *window,
   if (mask)
     *mask = tmp_mask;
 
-  _gdk_display_enable_motion_hints (display);
+  _gdk_display_enable_motion_hints (display, device);
 
   return child;
 }
@@ -6436,6 +6529,8 @@ gdk_window_get_pointer (GdkWindow	  *window,
  * gdk_display_get_window_at_pointer() instead.
  *
  * Return value: (transfer none): window under the mouse pointer
+ *
+ * Deprecated: 3.0. Use gdk_display_get_window_at_device_position() instead.
  **/
 GdkWindow*
 gdk_window_at_pointer (gint *win_x,
@@ -7086,30 +7181,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_list_devices (device_manager, GDK_DEVICE_TYPE_MASTER);
+      devices = g_list_concat (devices, gdk_device_manager_list_devices (device_manager, GDK_DEVICE_TYPE_SLAVE));
+      devices = g_list_concat (devices, gdk_device_manager_list_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);
@@ -7191,9 +7287,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,
@@ -7213,7 +7310,15 @@ gdk_window_set_events (GdkWindow       *window,
   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);
+    {
+      GList *devices = private->devices_inside;
+
+      while (devices)
+        {
+          _gdk_display_enable_motion_hints (display, (GdkDevice *) devices->data);
+          devices = devices->next;
+        }
+    }
 
   private->event_mask = event_mask;
 
@@ -7230,7 +7335,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
  **/
@@ -7248,6 +7354,115 @@ 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.
+ *
+ * Since: 3.0
+ **/
+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, device);
+
+  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
+ *
+ * Since: 3.0
+ **/
+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,
@@ -8023,6 +8238,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
@@ -8055,7 +8287,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
@@ -8066,7 +8298,6 @@ gdk_window_set_cursor (GdkWindow *window,
 		       GdkCursor *cursor)
 {
   GdkWindowObject *private;
-  GdkWindowImplIface *impl_iface;
   GdkDisplay *display;
 
   g_return_if_fail (GDK_IS_WINDOW (window));
@@ -8085,21 +8316,90 @@ 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
+ *
+ * Since: 3.0
+ **/
+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.
+ *
+ * Since: 3.0
+ **/
+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)
@@ -9394,27 +9694,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 */
@@ -9425,9 +9732,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 gboolean
@@ -9659,6 +9967,61 @@ 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 multiple,
+ * per device enter/leave events, device grabs and grab ownerships.
+ *
+ * Since: 3.0
+ **/
+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.
+ *
+ * Since: 3.0
+ **/
+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  */
@@ -9888,6 +10251,7 @@ send_crossing_event (GdkDisplay                 *display,
 		     GdkCrossingMode             mode,
 		     GdkNotifyType               notify_type,
 		     GdkWindow                  *subwindow,
+                     GdkDevice                  *device,
 		     gint                        toplevel_x,
 		     gint                        toplevel_y,
 		     GdkModifierType             mask,
@@ -9897,10 +10261,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)
@@ -9914,20 +10278,39 @@ 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);
+      gdk_event_set_device (event, device);
       event->crossing.time = time_;
       event->crossing.subwindow = subwindow;
       if (subwindow)
@@ -9954,6 +10337,7 @@ void
 _gdk_synthesize_crossing_events (GdkDisplay                 *display,
 				 GdkWindow                  *src,
 				 GdkWindow                  *dest,
+                                 GdkDevice                  *device,
 				 GdkCrossingMode             mode,
 				 gint                        toplevel_x,
 				 gint                        toplevel_y,
@@ -9978,6 +10362,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);
@@ -9997,7 +10393,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,
@@ -10019,6 +10415,7 @@ _gdk_synthesize_crossing_events (GdkDisplay                 *display,
 				   mode,
 				   notify_type,
 				   (GdkWindow *)last,
+                                   device,
 				   toplevel_x, toplevel_y,
 				   mask, time_,
 				   event_in_queue,
@@ -10065,6 +10462,7 @@ _gdk_synthesize_crossing_events (GdkDisplay                 *display,
 				   mode,
 				   notify_type,
 				   (GdkWindow *)next,
+                                   device,
 				   toplevel_x, toplevel_y,
 				   mask, time_,
 				   event_in_queue,
@@ -10086,6 +10484,7 @@ _gdk_synthesize_crossing_events (GdkDisplay                 *display,
 			   mode,
 			   notify_type,
 			   NULL,
+                           device,
 			   toplevel_x, toplevel_y,
 			   mask, time_,
 			   event_in_queue,
@@ -10100,14 +10499,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;
+
+  pointer_info = _gdk_display_get_pointer_info (display, device);
 
-  if (event_window == display->pointer_info.toplevel_under_pointer)
+  if (event_window == pointer_info->toplevel_under_pointer)
     pointer_window =
       _gdk_window_find_descendant_at (event_window,
 				      toplevel_x, toplevel_y,
@@ -10115,7 +10518,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)
@@ -10126,47 +10529,80 @@ 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);
+  _gdk_display_enable_motion_hints (display, device);
 }
 
-/*
- *--------------------------------------------------------------
- * gdk_pointer_grab
- *
- *   Grabs the pointer 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"
- *   "event_mask" masks only interesting events
- *   "confine_to" limits the cursor movement to the specified window
- *   "cursor" changes the cursor for the duration of the grab
- *   "time" specifies the time
- *
- * Results:
- *
- * Side effects:
- *   requires a corresponding call to gdk_pointer_ungrab
- *
- *--------------------------------------------------------------
- */
+/**
+ * gdk_pointer_grab:
+ * @window: the #GdkWindow which will own the grab (the grab window).
+ * @owner_events: if %FALSE then all pointer 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. Note that only pointer events (i.e. button and motion events)
+ *              may be selected.
+ * @confine_to: If non-%NULL, the pointer will be confined to this
+ *              window during the grab. If the pointer is outside @confine_to, it will
+ *              automatically be moved to the closest edge of @confine_to and enter
+ *              and leave events will be generated as necessary.
+ * @cursor: the cursor to display while the grab is active. If this is %NULL then
+ *          the normal cursors are used for @window and its descendants, and the cursor
+ *          for @window is used for all other windows.
+ * @time_: the timestamp of the event which led to this pointer grab. This usually
+ *         comes from a #GdkEventButton struct, though %GDK_CURRENT_TIME can be used if
+ *         the time isn't known.
+ *
+ * Grabs the pointer (usually a mouse) so that all events are passed to this
+ * application until the pointer is ungrabbed with gdk_pointer_ungrab(), or
+ * the grab window becomes unviewable.
+ * This overrides any previous pointer grab by this client.
+ *
+ * Pointer grabs are used for operations which need complete control over mouse
+ * events, even if the mouse leaves the application.
+ * For example in GTK+ it is used for Drag and Drop, for dragging the handle in
+ * the #GtkHPaned and #GtkVPaned widgets, and for resizing columns in #GtkCList
+ * widgets.
+ *
+ * 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.
+ *
+ * Deprecated: 3.0. Use gdk_device_grab() instead.
+ **/
 GdkGrabStatus
 gdk_pointer_grab (GdkWindow *	  window,
 		  gboolean	  owner_events,
@@ -10177,8 +10613,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);
@@ -10218,24 +10657,147 @@ 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_list_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;
+}
+
+/**
+ * gdk_keyboard_grab:
+ * @window: the #GdkWindow which will own the grab (the grab window).
+ * @owner_events: if %FALSE then all keyboard events are reported with respect to
+ *                @window. If %TRUE then keyboard events for this application are
+ *                reported as normal, but keyboard events outside this application
+ *                are reported with respect to @window. Both key press and key
+ *                release events are always reported, independant of the event mask
+ *                set by the application.
+ * @time: a timestamp from a #GdkEvent, or %GDK_CURRENT_TIME if no timestamp is
+available.
+ *
+ * Grabs the keyboard so that all events are passed to this
+ * application until the keyboard is ungrabbed with gdk_keyboard_ungrab().
+ * This overrides any previous keyboard grab by this client.
+ *
+ * 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.
+ *
+ * Deprecated: 3.0. Use gdk_device_grab() instead.
+ **/
+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_list_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;
 }
@@ -10262,7 +10824,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;
@@ -10275,30 +10838,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;
@@ -10317,12 +10889,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),
@@ -10333,6 +10905,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,
@@ -10342,9 +10915,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)
     {
@@ -10405,6 +10978,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;
@@ -10415,6 +10990,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);
@@ -10445,8 +11022,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_,
@@ -10462,16 +11040,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 &&
@@ -10491,7 +11070,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);
@@ -10500,30 +11080,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)
     {
@@ -10532,24 +11114,35 @@ 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)
+          gulong *device_serial;
+
+          device_serial = g_hash_table_lookup (display->motion_hint_info, device);
+
+          if (!device_serial ||
+              (*device_serial != 0 &&
+               serial < *device_serial))
 	    event_win = NULL; /* Ignore event */
 	  else
 	    {
 	      is_hint = TRUE;
-	      display->pointer_info.motion_hint_serial = G_MAXULONG;
+              *device_serial = G_MAXULONG;
 	    }
 	}
 
@@ -10561,11 +11154,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);
 	}
     }
 
@@ -10595,12 +11189,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,
@@ -10608,7 +11204,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,
@@ -10627,22 +11223,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);
@@ -10650,6 +11249,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)
@@ -10664,6 +11267,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);
@@ -10762,22 +11367,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,
@@ -10789,19 +11378,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
@@ -10818,31 +11427,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);
 	    }
 	}
 
@@ -10860,9 +11470,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)
@@ -10874,7 +11481,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,
@@ -10900,9 +11507,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;
@@ -10915,36 +11522,37 @@ _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)
-    _gdk_display_enable_motion_hints (display);
+  if (device &&
+      (pointer_info->state != old_state ||
+       pointer_info->button != old_button))
+    _gdk_display_enable_motion_hints (display, device);
 
   unlink_event = FALSE;
   if (is_motion_type (event->type))
@@ -10959,14 +11567,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);
 	}
     }
 
@@ -10989,9 +11597,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)
     {
@@ -11050,7 +11659,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 0bd254a..640f6af 100644
--- a/gdk/gdkwindow.h
+++ b/gdk/gdkwindow.h
@@ -543,6 +543,7 @@ struct _GdkWindowObject
   guint GSEAL (accept_focus) : 1;
   guint GSEAL (focus_on_map) : 1;
   guint GSEAL (shaped) : 1;
+  guint GSEAL (support_multidevice) : 1;
   
   GdkEventMask GSEAL (event_mask);
 
@@ -567,8 +568,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);
@@ -760,6 +764,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,
@@ -802,10 +811,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);
 
@@ -817,6 +834,11 @@ 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);
+GdkEventMask  gdk_window_get_device_events (GdkWindow    *window,
+                                            GdkDevice    *device);
 
 void          gdk_window_set_icon_list   (GdkWindow       *window,
 					  GList           *pixbufs);
@@ -906,9 +928,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);
 
@@ -929,6 +951,10 @@ 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);
 
 G_END_DECLS
 
diff --git a/gdk/gdkwindowimpl.h b/gdk/gdkwindowimpl.h
index 3a5029b..dc0ca75 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);
   gboolean     supports_native_bg;
 };
 
diff --git a/gdk/quartz/Makefile.am b/gdk/quartz/Makefile.am
index a5721b1..ac6c2c7 100644
--- a/gdk/quartz/Makefile.am
+++ b/gdk/quartz/Makefile.am
@@ -24,6 +24,8 @@ libgdk_quartz_la_SOURCES =    	\
 	gdkapplaunchcontext-quartz.c \
 	gdkcolor-quartz.c	\
 	gdkcursor-quartz.c	\
+	gdkdevice-core.c	\
+	gdkdevicemanager-core.c	\
 	gdkdisplay-quartz.c	\
 	gdkdnd-quartz.c		\
 	gdkdrawable-quartz.c	\
diff --git a/gdk/quartz/gdkdevice-core.c b/gdk/quartz/gdkdevice-core.c
new file mode 100644
index 0000000..81cd6a3
--- /dev/null
+++ b/gdk/quartz/gdkdevice-core.c
@@ -0,0 +1,356 @@
+/* GDK - The GIMP Drawing Kit
+ * Copyright (C) 2009 Carlos Garnacho <carlosg gnome org>
+ * Copyright (C) 2010 Kristian Rietveld <kris gtk 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"
+
+#import "GdkQuartzView.h"
+#include "gdkwindow-quartz.h"
+#include "gdkprivate-quartz.h"
+#include "gdkdevice-core.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
+gdk_device_core_get_history (GdkDevice      *device,
+                             GdkWindow      *window,
+                             guint32         start,
+                             guint32         stop,
+                             GdkTimeCoord ***events,
+                             guint          *n_events)
+{
+  return FALSE;
+}
+
+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
+translate_coords_to_child_coords (GdkWindow *parent,
+                                  GdkWindow *child,
+                                  gint      *x,
+                                  gint      *y)
+{
+  GdkWindow *current = child;
+
+  if (child == parent)
+    return;
+
+  while (current != parent)
+    {
+      gint tmp_x, tmp_y;
+
+      gdk_window_get_origin (current, &tmp_x, &tmp_y);
+
+      *x -= tmp_x;
+      *y -= tmp_y;
+
+      current = gdk_window_get_parent (current);
+    }
+}
+
+static void
+gdk_device_core_set_window_cursor (GdkDevice *device,
+                                   GdkWindow *window,
+                                   GdkCursor *cursor)
+{
+  GdkCursorPrivate *cursor_private;
+  NSCursor *nscursor;
+
+  cursor_private = (GdkCursorPrivate*) cursor;
+
+  if (GDK_WINDOW_DESTROYED (window))
+    return;
+
+  if (!cursor)
+    nscursor = [NSCursor arrowCursor];
+  else
+    nscursor = cursor_private->nscursor;
+
+  [nscursor set];
+}
+
+static void
+gdk_device_core_warp (GdkDevice *device,
+                      GdkScreen *screen,
+                      gint       x,
+                      gint       y)
+{
+  CGDisplayMoveCursorToPoint (CGMainDisplayID (), CGPointMake (x, y));
+}
+
+static GdkWindow *
+gdk_device_core_query_state_helper (GdkWindow       *window,
+                                    GdkDevice       *device,
+                                    gint            *x,
+                                    gint            *y,
+                                    GdkModifierType *mask)
+{
+  GdkWindowObject *toplevel;
+  GdkWindowObject *private;
+  NSPoint point;
+  gint x_tmp, y_tmp;
+  GdkWindow *found_window;
+
+  g_return_val_if_fail (window == NULL || GDK_IS_WINDOW (window), NULL);
+
+  if (GDK_WINDOW_DESTROYED (window))
+    {
+      *x = 0;
+      *y = 0;
+      *mask = 0;
+      return NULL;
+    }
+
+  toplevel = GDK_WINDOW_OBJECT (gdk_window_get_toplevel (window));
+
+  *mask = _gdk_quartz_events_get_current_event_mask ();
+
+  /* Get the y coordinate, needs to be flipped. */
+  if (window == _gdk_root)
+    {
+      point = [NSEvent mouseLocation];
+      _gdk_quartz_window_nspoint_to_gdk_xy (point, &x_tmp, &y_tmp);
+    }
+  else
+    {
+      GdkWindowImplQuartz *impl;
+      NSWindow *nswindow;
+
+      impl = GDK_WINDOW_IMPL_QUARTZ (toplevel->impl);
+      private = GDK_WINDOW_OBJECT (toplevel);
+      nswindow = impl->toplevel;
+
+      point = [nswindow mouseLocationOutsideOfEventStream];
+
+      x_tmp = point.x;
+      y_tmp = private->height - point.y;
+
+      window = (GdkWindow *)toplevel;
+    }
+
+  found_window = _gdk_quartz_window_find_child (window, x_tmp, y_tmp,
+                                                FALSE);
+
+  if (found_window == _gdk_root)
+    found_window = NULL;
+  else if (found_window)
+    translate_coords_to_child_coords (window, found_window,
+                                      &x_tmp, &y_tmp);
+
+  *x = x_tmp;
+  *y = y_tmp;
+
+  return found_window;
+}
+
+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;
+  GdkWindow *found_window;
+  NSPoint point;
+  gint x_tmp, y_tmp;
+
+  found_window = gdk_device_core_query_state_helper (window, device,
+                                                     win_x, win_y,
+                                                     mask);
+  if (!found_window)
+    return FALSE;
+
+  display = gdk_drawable_get_display (window);
+
+  if (root_window)
+    *root_window = _gdk_root;
+
+  if (child_window)
+    *child_window = found_window;
+
+  point = [NSEvent mouseLocation];
+  _gdk_quartz_window_nspoint_to_gdk_xy (point, &x_tmp, &y_tmp);
+
+  if (root_x)
+    *root_x = x_tmp;
+
+  if (root_y)
+    *root_y = y_tmp;
+
+  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_)
+{
+  /* Should remain empty */
+  return GDK_GRAB_SUCCESS;
+}
+
+static void
+gdk_device_core_ungrab (GdkDevice *device,
+                        guint32    time_)
+{
+  /* Should remain empty */
+}
+
+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;
+  GdkWindow *found_window;
+  NSPoint point;
+  gint x_tmp, y_tmp;
+
+  display = gdk_device_get_display (device);
+  screen = gdk_display_get_default_screen (display);
+
+  /* Get mouse coordinates, find window under the mouse pointer */
+  point = [NSEvent mouseLocation];
+  _gdk_quartz_window_nspoint_to_gdk_xy (point, &x_tmp, &y_tmp);
+
+  found_window = _gdk_quartz_window_find_child (_gdk_root, x_tmp, y_tmp,
+                                                get_toplevel);
+
+  if (found_window)
+    translate_coords_to_child_coords (_gdk_root, found_window,
+                                      &x_tmp, &y_tmp);
+
+  if (win_x)
+    *win_x = found_window ? x_tmp : -1;
+
+  if (win_y)
+    *win_y = found_window ? y_tmp : -1;
+
+  if (mask)
+    *mask = _gdk_quartz_events_get_current_event_mask ();
+
+  return found_window;
+}
+
+static void
+gdk_device_core_select_window_events (GdkDevice    *device,
+                                      GdkWindow    *window,
+                                      GdkEventMask  event_mask)
+{
+  /* The mask is set in the common code. */
+}
diff --git a/gdk/quartz/gdkdevice-core.h b/gdk/quartz/gdkdevice-core.h
new file mode 100644
index 0000000..8f53d6f
--- /dev/null
+++ b/gdk/quartz/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/quartz/gdkdevicemanager-core.c b/gdk/quartz/gdkdevicemanager-core.c
new file mode 100644
index 0000000..d4765c1
--- /dev/null
+++ b/gdk/quartz/gdkdevicemanager-core.c
@@ -0,0 +1,130 @@
+/* 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 <gdk/gdktypes.h>
+#include <gdk/gdkdevicemanager.h>
+#include "gdkdevicemanager-core.h"
+#include "gdkdevice-core.h"
+#include "gdkkeysyms.h"
+
+
+#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_list_devices (GdkDeviceManager *device_manager,
+                                                     GdkDeviceType     type);
+
+
+G_DEFINE_TYPE (GdkDeviceManagerCore, gdk_device_manager_core, GDK_TYPE_DEVICE_MANAGER)
+
+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->list_devices = gdk_device_manager_core_list_devices;
+}
+
+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_associated_device (device_manager->core_pointer, device_manager->core_keyboard);
+  _gdk_device_set_associated_device (device_manager->core_keyboard, device_manager->core_pointer);
+}
+
+static GList *
+gdk_device_manager_core_list_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/quartz/gdkdevicemanager-core.h b/gdk/quartz/gdkdevicemanager-core.h
new file mode 100644
index 0000000..0a337fc
--- /dev/null
+++ b/gdk/quartz/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/quartz/gdkdisplay-quartz.c b/gdk/quartz/gdkdisplay-quartz.c
index 5de6519..556f84e 100644
--- a/gdk/quartz/gdkdisplay-quartz.c
+++ b/gdk/quartz/gdkdisplay-quartz.c
@@ -23,6 +23,7 @@
 #include "gdk.h"
 #include "gdkprivate-quartz.h"
 #include "gdkscreen-quartz.h"
+#include "gdkdevicemanager-core.h"
 
 GdkWindow *
 gdk_display_get_default_group (GdkDisplay *display)
@@ -40,6 +41,14 @@ _gdk_windowing_set_default_display (GdkDisplay *display)
   g_assert (display == NULL || _gdk_display == display);
 }
 
+GdkDeviceManager *
+_gdk_device_manager_new (GdkDisplay *display)
+{
+  return g_object_new (GDK_TYPE_DEVICE_MANAGER_CORE,
+                       "display", display,
+                       NULL);
+}
+
 GdkDisplay *
 gdk_display_open (const gchar *display_name)
 {
@@ -50,6 +59,7 @@ gdk_display_open (const gchar *display_name)
   [NSApplication sharedApplication];
 
   _gdk_display = g_object_new (GDK_TYPE_DISPLAY, NULL);
+  _gdk_display->device_manager = _gdk_device_manager_new (_gdk_display);
 
   _gdk_visual_init ();
 
@@ -58,6 +68,7 @@ gdk_display_open (const gchar *display_name)
   _gdk_windowing_window_init ();
 
   _gdk_events_init ();
+
   _gdk_input_init ();
 
 #if 0
diff --git a/gdk/quartz/gdkevents-quartz.c b/gdk/quartz/gdkevents-quartz.c
index 24b9dd5..5a7180d 100644
--- a/gdk/quartz/gdkevents-quartz.c
+++ b/gdk/quartz/gdkevents-quartz.c
@@ -32,6 +32,7 @@
 #include "gdkscreen.h"
 #include "gdkkeysyms.h"
 #include "gdkprivate-quartz.h"
+#include "gdkdevicemanager-core.h"
 
 #define GRIP_WIDTH 15
 #define GRIP_HEIGHT 15
@@ -68,70 +69,52 @@ gdk_events_pending (void)
 	  (_gdk_quartz_event_loop_check_pending ()));
 }
 
-GdkGrabStatus
-gdk_keyboard_grab (GdkWindow  *window,
-		   gint        owner_events,
-		   guint32     time)
+GdkEvent*
+gdk_event_get_graphics_expose (GdkWindow *window)
 {
-  GdkDisplay *display;
-  GdkWindow  *toplevel;
-
-  g_return_val_if_fail (window != NULL, 0);
-  g_return_val_if_fail (GDK_IS_WINDOW (window), 0);
-
-  display = gdk_drawable_get_display (window);
-  toplevel = gdk_window_get_toplevel (window);
-
-  _gdk_display_set_has_keyboard_grab (display,
-                                      window,
-                                      toplevel,
-                                      owner_events,
-                                      0,
-                                      time);
-
-  return GDK_GRAB_SUCCESS;
-}
-
-void
-gdk_display_keyboard_ungrab (GdkDisplay *display,
-			     guint32     time)
-{
-  _gdk_display_unset_has_keyboard_grab (display, FALSE);
+  /* FIXME: Implement */
+  return NULL;
 }
 
 void
-gdk_display_pointer_ungrab (GdkDisplay *display,
-			    guint32     time)
+gdk_device_ungrab (GdkDevice *device,
+                   guint32    time_)
 {
-  GdkPointerGrabInfo *grab;
+  GdkDeviceGrabInfo *grab;
 
-  grab = _gdk_display_get_last_pointer_grab (display);
+  grab = _gdk_display_get_last_device_grab (_gdk_display, device);
   if (grab)
     grab->serial_end = 0;
 
-  _gdk_display_pointer_grab_update (display, 0);
+  _gdk_display_device_grab_update (_gdk_display, device, 0);
 }
 
 GdkGrabStatus
-_gdk_windowing_pointer_grab (GdkWindow    *window,
-                             GdkWindow    *native,
-                             gboolean	   owner_events,
-                             GdkEventMask  event_mask,
-                             GdkWindow    *confine_to,
-                             GdkCursor    *cursor,
-                             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)
 {
   g_return_val_if_fail (GDK_IS_WINDOW (window), 0);
   g_return_val_if_fail (confine_to == NULL || GDK_IS_WINDOW (confine_to), 0);
 
-  _gdk_display_add_pointer_grab (_gdk_display,
-                                 window,
-                                 native,
-                                 owner_events,
-                                 event_mask,
-                                 0,
-                                 time,
-                                 FALSE);
+  if (!window || GDK_WINDOW_DESTROYED (window))
+    return GDK_GRAB_NOT_VIEWABLE;
+
+  _gdk_display_add_device_grab (_gdk_display,
+                                device,
+                                window,
+                                native,
+                                GDK_OWNERSHIP_NONE,
+                                owner_events,
+                                event_mask,
+                                0,
+                                time,
+                                FALSE);
 
   return GDK_GRAB_SUCCESS;
 }
@@ -139,19 +122,27 @@ _gdk_windowing_pointer_grab (GdkWindow    *window,
 static void
 break_all_grabs (guint32 time)
 {
-  GdkPointerGrabInfo *grab;
+  GList *list, *l;
+  GdkDeviceManager *device_manager;
 
-  if (_gdk_display->keyboard_grab.window)
-    _gdk_display_unset_has_keyboard_grab (_gdk_display, FALSE);
-
-  grab = _gdk_display_get_last_pointer_grab (_gdk_display);
-  if (grab)
+  device_manager = gdk_display_get_device_manager (_gdk_display);
+  list = gdk_device_manager_list_devices (device_manager,
+                                          GDK_DEVICE_TYPE_MASTER);
+  for (l = list; l; l = l->next)
     {
-      grab->serial_end = 0;
-      grab->implicit_ungrab = TRUE;
+      GdkDeviceGrabInfo *grab;
+
+      grab = _gdk_display_get_last_device_grab (_gdk_display, l->data);
+      if (grab)
+        {
+          grab->serial_end = 0;
+          grab->implicit_ungrab = TRUE;
+        }
+
+      _gdk_display_device_grab_update (_gdk_display, l->data, 0);
     }
 
-  _gdk_display_pointer_grab_update (_gdk_display, 0);
+  g_list_free (list);
 }
 
 static void
@@ -344,11 +335,15 @@ create_focus_event (GdkWindow *window,
 		    gboolean   in)
 {
   GdkEvent *event;
+  GdkDeviceManagerCore *device_manager;
 
   event = gdk_event_new (GDK_FOCUS_CHANGE);
   event->focus_change.window = window;
   event->focus_change.in = in;
 
+  device_manager = GDK_DEVICE_MANAGER_CORE (_gdk_display->device_manager);
+  gdk_event_set_device (event, device_manager->core_keyboard);
+
   return event;
 }
 
@@ -481,6 +476,8 @@ _gdk_quartz_events_send_enter_notify_event (GdkWindow *window)
   event->crossing.detail = GDK_NOTIFY_ANCESTOR;
   event->crossing.state = 0;
 
+  gdk_event_set_device (event, _gdk_display->core_pointer);
+
   append_event (event, TRUE);
 }
 
@@ -511,8 +508,10 @@ find_toplevel_under_pointer (GdkDisplay *display,
                              gint       *y)
 {
   GdkWindow *toplevel;
+  GdkPointerWindowInfo *info;
 
-  toplevel = display->pointer_info.toplevel_under_pointer;
+  info = _gdk_display_get_pointer_info (display, display->core_pointer);
+  toplevel = info->toplevel_under_pointer;
   if (toplevel)
     {
       GdkWindowObject *private;
@@ -531,6 +530,197 @@ find_toplevel_under_pointer (GdkDisplay *display,
   return toplevel;
 }
 
+static GdkWindow *
+find_toplevel_for_keyboard_event (NSEvent *nsevent)
+{
+  GList *list, *l;
+  GdkWindow *window;
+  GdkDisplay *display;
+  GdkQuartzView *view;
+  GdkDeviceManager *device_manager;
+
+  view = (GdkQuartzView *)[[nsevent window] contentView];
+  window = [view gdkWindow];
+
+  display = gdk_drawable_get_display (GDK_DRAWABLE (window));
+
+  device_manager = gdk_display_get_device_manager (display);
+  list = gdk_device_manager_list_devices (device_manager,
+                                          GDK_DEVICE_TYPE_MASTER);
+  for (l = list; l; l = l->next)
+    {
+      GdkDeviceGrabInfo *grab;
+      GdkDevice *device = l->data;
+
+      if (device->source != GDK_SOURCE_KEYBOARD)
+        continue;
+
+      grab = _gdk_display_get_last_device_grab (display, device);
+      if (grab && grab->window && !grab->owner_events)
+        {
+          window = gdk_window_get_toplevel (grab->window);
+          break;
+        }
+    }
+
+  g_list_free (list);
+
+  return window;
+}
+
+static GdkWindow *
+find_toplevel_for_mouse_event (NSEvent    *nsevent,
+                               gint       *x,
+                               gint       *y)
+{
+  NSPoint point;
+  NSPoint screen_point;
+  NSEventType event_type;
+  GdkWindow *toplevel;
+  GdkQuartzView *view;
+  GdkDisplay *display;
+  GdkDeviceGrabInfo *grab;
+  GdkWindowObject *private;
+
+  view = (GdkQuartzView *)[[nsevent window] contentView];
+  toplevel = [view gdkWindow];
+
+  display = gdk_drawable_get_display (toplevel);
+  private = GDK_WINDOW_OBJECT (toplevel);
+
+  event_type = [nsevent type];
+  point = [nsevent locationInWindow];
+  screen_point = [[nsevent window] convertBaseToScreen:point];
+
+  /* From the docs for XGrabPointer:
+   *
+   * If owner_events is True and if a generated pointer event
+   * would normally be reported to this client, it is reported
+   * as usual. Otherwise, the event is reported with respect to
+   * the grab_window and is reported only if selected by
+   * event_mask. For either value of owner_events, unreported
+   * events are discarded.
+   */
+  grab = _gdk_display_get_last_device_grab (display,
+                                            display->core_pointer);
+  if (grab)
+    {
+      /* Implicit grabs do not go through XGrabPointer and thus the
+       * event mask should not be checked.
+       */
+      if (!grab->implicit
+          && (grab->event_mask & get_event_mask_from_ns_event (nsevent)) == 0)
+        return NULL;
+
+      if (grab->owner_events)
+        {
+          /* For owner events, we need to use the toplevel under the
+           * pointer, not the window from the NSEvent, since that is
+           * reported with respect to the key window, which could be
+           * wrong.
+           */
+          GdkWindow *toplevel_under_pointer;
+          gint x_tmp, y_tmp;
+
+          toplevel_under_pointer = find_toplevel_under_pointer (display,
+                                                                screen_point,
+                                                                &x_tmp, &y_tmp);
+          if (toplevel_under_pointer)
+            {
+              toplevel = toplevel_under_pointer;
+              *x = x_tmp;
+              *y = y_tmp;
+            }
+
+          return toplevel;
+        }
+      else
+        {
+          /* Finally check the grab window. */
+          GdkWindow *grab_toplevel;
+          GdkWindowObject *grab_private;
+          NSWindow *grab_nswindow;
+
+          grab_toplevel = gdk_window_get_toplevel (grab->window);
+          grab_private = (GdkWindowObject *)grab_toplevel;
+
+          grab_nswindow = ((GdkWindowImplQuartz *)grab_private->impl)->toplevel;
+          point = [grab_nswindow convertScreenToBase:screen_point];
+
+          /* Note: x_root and y_root are already right. */
+          *x = point.x;
+          *y = grab_private->height - point.y;
+
+          return grab_toplevel;
+        }
+
+      return NULL;
+    }
+  else 
+    {
+      /* The non-grabbed case. */
+      GdkWindow *toplevel_under_pointer;
+      gint x_tmp, y_tmp;
+
+      /* Ignore all events but mouse moved that might be on the title
+       * bar (above the content view). The reason is that otherwise
+       * gdk gets confused about getting e.g. button presses with no
+       * window (the title bar is not known to it).
+       */
+      if (event_type != NSMouseMoved)
+        if (*y < 0)
+          return NULL;
+
+      /* As for owner events, we need to use the toplevel under the
+       * pointer, not the window from the NSEvent.
+       */
+      toplevel_under_pointer = find_toplevel_under_pointer (display,
+                                                            screen_point,
+                                                            &x_tmp, &y_tmp);
+      if (toplevel_under_pointer)
+        {
+          GdkWindowObject *toplevel_private;
+          GdkWindowImplQuartz *toplevel_impl;
+
+          toplevel = toplevel_under_pointer;
+
+          toplevel_private = (GdkWindowObject *)toplevel;
+          toplevel_impl = (GdkWindowImplQuartz *)toplevel_private->impl;
+
+          if ([toplevel_impl->toplevel showsResizeIndicator])
+            {
+              NSRect frame;
+
+              /* If the resize indicator is visible and the event
+               * is in the lower right 15x15 corner, we leave these
+               * events to Cocoa as to be handled as resize events.
+               * Applications may have widgets in this area.  These
+               * will most likely be larger than 15x15 and for
+               * scroll bars there are also other means to move
+               * the scroll bar.  Since the resize indicator is
+               * the only way of resizing windows on Mac OS, it
+               * is too important to not make functional.
+               */
+              frame = [toplevel_impl->view bounds];
+              if (x_tmp > frame.size.width - GRIP_WIDTH
+                  && x_tmp < frame.size.width
+                  && y_tmp > frame.size.height - GRIP_HEIGHT
+                  && y_tmp < frame.size.height)
+                {
+                  return NULL;
+                }
+            }
+
+          *x = x_tmp;
+          *y = y_tmp;
+        }
+
+      return toplevel;
+    }
+
+  return NULL;
+}
+
 /* This function finds the correct window to send an event to, taking
  * into account grabs, event propagation, and event masks.
  */
@@ -542,14 +732,13 @@ find_window_for_ns_event (NSEvent *nsevent,
                           gint    *y_root)
 {
   GdkQuartzView *view;
-  GdkWindow *toplevel;
-  GdkWindowObject *private;
   NSPoint point;
   NSPoint screen_point;
   NSEventType event_type;
+  GdkWindow *toplevel;
+  GdkWindowObject *private;
 
   view = (GdkQuartzView *)[[nsevent window] contentView];
-
   toplevel = [view gdkWindow];
   private = GDK_WINDOW_OBJECT (toplevel);
 
@@ -576,138 +765,7 @@ find_window_for_ns_event (NSEvent *nsevent,
     case NSLeftMouseDragged:
     case NSRightMouseDragged:
     case NSOtherMouseDragged:
-      {
-	GdkDisplay *display;
-        GdkPointerGrabInfo *grab;
-
-        display = gdk_drawable_get_display (toplevel);
-
-	/* From the docs for XGrabPointer:
-	 *
-	 * If owner_events is True and if a generated pointer event
-	 * would normally be reported to this client, it is reported
-	 * as usual. Otherwise, the event is reported with respect to
-	 * the grab_window and is reported only if selected by
-	 * event_mask. For either value of owner_events, unreported
-	 * events are discarded.
-	 */
-        grab = _gdk_display_get_last_pointer_grab (display);
-	if (grab)
-	  {
-            /* Implicit grabs do not go through XGrabPointer and thus the
-             * event mask should not be checked.
-             */
-	    if (!grab->implicit
-                && (grab->event_mask & get_event_mask_from_ns_event (nsevent)) == 0)
-              return NULL;
-
-            if (grab->owner_events)
-              {
-                /* For owner events, we need to use the toplevel under the
-                 * pointer, not the window from the NSEvent, since that is
-                 * reported with respect to the key window, which could be
-                 * wrong.
-                 */
-                GdkWindow *toplevel_under_pointer;
-                gint x_tmp, y_tmp;
-
-                toplevel_under_pointer = find_toplevel_under_pointer (display,
-                                                                      screen_point,
-                                                                      &x_tmp, &y_tmp);
-                if (toplevel_under_pointer)
-                  {
-                    toplevel = toplevel_under_pointer;
-                    *x = x_tmp;
-                    *y = y_tmp;
-                  }
-
-                return toplevel;
-              }
-            else
-              {
-                /* Finally check the grab window. */
-		GdkWindow *grab_toplevel;
-                GdkWindowObject *grab_private;
-                NSWindow *grab_nswindow;
-
-		grab_toplevel = gdk_window_get_toplevel (grab->window);
-                grab_private = (GdkWindowObject *)grab_toplevel;
-
-                grab_nswindow = ((GdkWindowImplQuartz *)grab_private->impl)->toplevel;
-                point = [grab_nswindow convertScreenToBase:screen_point];
-
-                /* Note: x_root and y_root are already right. */
-                *x = point.x;
-                *y = grab_private->height - point.y;
-
-		return grab_toplevel;
-	      }
-
-	    return NULL;
-	  }
-	else 
-	  {
-	    /* The non-grabbed case. */
-            GdkWindow *toplevel_under_pointer;
-            gint x_tmp, y_tmp;
-
-            /* Ignore all events but mouse moved that might be on the title
-             * bar (above the content view). The reason is that otherwise
-             * gdk gets confused about getting e.g. button presses with no
-             * window (the title bar is not known to it).
-             */
-            if (event_type != NSMouseMoved)
-              if (*y < 0)
-                return NULL;
-
-            /* As for owner events, we need to use the toplevel under the
-             * pointer, not the window from the NSEvent.
-             */
-            toplevel_under_pointer = find_toplevel_under_pointer (display,
-                                                                  screen_point,
-                                                                  &x_tmp, &y_tmp);
-            if (toplevel_under_pointer)
-              {
-                GdkWindowObject *toplevel_private;
-                GdkWindowImplQuartz *toplevel_impl;
-
-                toplevel = toplevel_under_pointer;
-
-                toplevel_private = (GdkWindowObject *)toplevel;
-                toplevel_impl = (GdkWindowImplQuartz *)toplevel_private->impl;
-
-                if ([toplevel_impl->toplevel showsResizeIndicator])
-                  {
-                    NSRect frame;
-
-                    /* If the resize indicator is visible and the event
-                     * is in the lower right 15x15 corner, we leave these
-                     * events to Cocoa as to be handled as resize events.
-                     * Applications may have widgets in this area.  These
-                     * will most likely be larger than 15x15 and for
-                     * scroll bars there are also other means to move
-                     * the scroll bar.  Since the resize indicator is
-                     * the only way of resizing windows on Mac OS, it
-                     * is too important to not make functional.
-                     */
-                    frame = [toplevel_impl->view bounds];
-                    if (x_tmp > frame.size.width - GRIP_WIDTH
-                        && x_tmp < frame.size.width
-                        && y_tmp > frame.size.height - GRIP_HEIGHT
-                        && y_tmp < frame.size.height)
-                      {
-                        return NULL;
-                      }
-                  }
-
-                *x = x_tmp;
-                *y = y_tmp;
-              }
-
-            return toplevel;
-	  }
-      }
-      break;
+      return find_toplevel_for_mouse_event (nsevent, x, y);
       
     case NSMouseEntered:
     case NSMouseExited:
@@ -722,10 +780,7 @@ find_window_for_ns_event (NSEvent *nsevent,
     case NSKeyDown:
     case NSKeyUp:
     case NSFlagsChanged:
-      if (_gdk_display->keyboard_grab.window && !_gdk_display->keyboard_grab.owner_events)
-        return gdk_window_get_toplevel (_gdk_display->keyboard_grab.window);
-
-      return toplevel;
+      return find_toplevel_for_keyboard_event (nsevent);
 
     default:
       /* Ignore everything else. */
@@ -759,6 +814,8 @@ fill_crossing_event (GdkWindow       *toplevel,
   event->crossing.detail = detail;
   event->crossing.state = get_keyboard_modifiers_from_ns_event (nsevent);
 
+  gdk_event_set_device (event, _gdk_display->core_pointer);
+
   /* FIXME: Focus and button state? */
 }
 
@@ -883,6 +940,7 @@ fill_key_event (GdkWindow    *window,
                 GdkEventType  type)
 {
   GdkEventPrivate *priv;
+  GdkDeviceManagerCore *device_manager;
   gchar buf[7];
   gunichar c = 0;
 
@@ -896,6 +954,9 @@ fill_key_event (GdkWindow    *window,
   event->key.hardware_keycode = [nsevent keyCode];
   event->key.group = ([nsevent modifierFlags] & NSAlternateKeyMask) ? 1 : 0;
   event->key.keyval = GDK_VoidSymbol;
+
+  device_manager = GDK_DEVICE_MANAGER_CORE (_gdk_display->device_manager);
+  gdk_event_set_device (event, device_manager->core_keyboard);
   
   gdk_keymap_translate_keyboard_state (NULL,
 				       event->key.hardware_keycode,
@@ -1177,9 +1238,10 @@ gdk_event_translate (GdkEvent *event,
         }
       else if (![impl->toplevel isKeyWindow])
         {
-          GdkPointerGrabInfo *grab;
+          GdkDeviceGrabInfo *grab;
 
-          grab = _gdk_display_get_last_pointer_grab (_gdk_display);
+          grab = _gdk_display_get_last_device_grab (_gdk_display,
+                                                    _gdk_display->core_pointer);
           if (!grab)
             [impl->toplevel makeKeyWindow];
         }
diff --git a/gdk/quartz/gdkinput.c b/gdk/quartz/gdkinput.c
index 9407b08..c431fc4 100644
--- a/gdk/quartz/gdkinput.c
+++ b/gdk/quartz/gdkinput.c
@@ -31,11 +31,11 @@
 #include "gdkinput.h"
 #include "gdkprivate.h"
 #include "gdkinputprivate.h"
+#include <gdkdevice.h>
+#include <gdkdeviceprivate.h>
 
-static GdkDeviceAxis gdk_input_core_axes[] = {
-  { GDK_AXIS_X, 0, 0 },
-  { GDK_AXIS_Y, 0, 0 }
-};
+/* Addition used for extension_events mask */
+#define GDK_ALL_DEVICES_MASK (1<<30)
 
 GdkDevice *_gdk_core_pointer = NULL;
 
@@ -47,62 +47,6 @@ gint              _gdk_input_ignore_core;
 GList            *_gdk_input_windows;
 GList            *_gdk_input_devices;
 
-void
-_gdk_init_input_core (void)
-{
-  _gdk_core_pointer = g_object_new (GDK_TYPE_DEVICE, NULL);
-  
-  _gdk_core_pointer->name = "Core Pointer";
-  _gdk_core_pointer->source = GDK_SOURCE_MOUSE;
-  _gdk_core_pointer->mode = GDK_MODE_SCREEN;
-  _gdk_core_pointer->has_cursor = TRUE;
-  _gdk_core_pointer->num_axes = 2;
-  _gdk_core_pointer->axes = gdk_input_core_axes;
-  _gdk_core_pointer->num_keys = 0;
-  _gdk_core_pointer->keys = NULL;
-
-  _gdk_display->core_pointer = _gdk_core_pointer;
-}
-
-static void
-gdk_device_finalize (GObject *object)
-{
-  g_error ("A GdkDevice object was finalized. This should not happen");
-}
-
-static void
-gdk_device_class_init (GObjectClass *class)
-{
-  class->finalize = gdk_device_finalize;
-}
-
-GType
-gdk_device_get_type (void)
-{
-  static GType object_type = 0;
-
-  if (!object_type)
-    {
-      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,
-                                            "GdkDevice",
-                                            &object_info, 0);
-    }
-  
-  return object_type;
-}
 
 GList *
 gdk_devices_list (void)
@@ -116,173 +60,50 @@ gdk_display_list_devices (GdkDisplay *dpy)
   return _gdk_input_devices;
 }
 
-G_CONST_RETURN gchar *
-gdk_device_get_name (GdkDevice *device)
-{
-  g_return_val_if_fail (GDK_IS_DEVICE (device), NULL);
-
-  return device->name;
-}
-
-GdkInputSource
-gdk_device_get_source (GdkDevice *device)
-{
-  g_return_val_if_fail (GDK_IS_DEVICE (device), 0);
-
-  return device->source;
-}
-
-GdkInputMode
-gdk_device_get_mode (GdkDevice *device)
-{
-  g_return_val_if_fail (GDK_IS_DEVICE (device), 0);
-
-  return device->mode;
-}
-
-gboolean
-gdk_device_get_has_cursor (GdkDevice *device)
-{
-  g_return_val_if_fail (GDK_IS_DEVICE (device), FALSE);
-
-  return device->has_cursor;
-}
-
-void
-gdk_device_set_source (GdkDevice *device,
-		       GdkInputSource source)
-{
-  device->source = source;
-}
-
-void
-gdk_device_get_key (GdkDevice       *device,
-                    guint            index,
-                    guint           *keyval,
-                    GdkModifierType *modifiers)
+static void
+_gdk_input_select_device_events (GdkWindow *impl_window,
+                                 GdkDevice *device)
 {
-  g_return_if_fail (GDK_IS_DEVICE (device));
-  g_return_if_fail (index < device->num_keys);
-
-  if (!device->keys[index].keyval &&
-      !device->keys[index].modifiers)
+  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;
 
-  if (keyval)
-    *keyval = device->keys[index].keyval;
-
-  if (modifiers)
-    *modifiers = device->keys[index].modifiers;
-}
-
-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;
-}
-
-GdkAxisUse
-gdk_device_get_axis_use (GdkDevice *device,
-                         guint      index)
-{
-  g_return_val_if_fail (GDK_IS_DEVICE (device), GDK_AXIS_IGNORE);
-  g_return_val_if_fail (index < device->num_axes, GDK_AXIS_IGNORE);
-
-  return device->axes[index].use;
-}
-
-void
-gdk_device_set_axis_use (GdkDevice   *device,
-			 guint        index,
-			 GdkAxisUse   use)
-{
-  g_return_if_fail (device != NULL);
-  g_return_if_fail (index < device->num_axes);
-
-  device->axes[index].use = use;
-
-  switch (use)
+  for (l = _gdk_input_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;
 
-void 
-gdk_device_get_state (GdkDevice       *device,
-                      GdkWindow       *window,
-                      gdouble         *axes,
-                      GdkModifierType *mask)
-{
-  gint x_int, y_int;
+      if (has_cursor || (w->extension_events & GDK_ALL_DEVICES_MASK))
+        {
+          event_mask = w->extension_events;
 
-  g_assert (device == _gdk_core_pointer);
-      
-  gdk_window_get_pointer (window, &x_int, &y_int, mask);
+          if (event_mask)
+            event_mask |= GDK_PROXIMITY_OUT_MASK
+                | GDK_BUTTON_PRESS_MASK
+                | GDK_BUTTON_RELEASE_MASK;
 
-  if (axes)
-    {
-      axes[0] = x_int;
-      axes[1] = y_int;
+          gdk_window_set_device_events ((GdkWindow *) w, device, event_mask);
+        }
     }
 }
 
-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);
-}
-
-gboolean
-gdk_device_get_history  (GdkDevice         *device,
-			 GdkWindow         *window,
-			 guint32            start,
-			 guint32            stop,
-			 GdkTimeCoord    ***events,
-			 gint              *n_events)
-{
-  g_return_val_if_fail (window != NULL, FALSE);
-  g_return_val_if_fail (GDK_WINDOW_IS_QUARTZ (window), FALSE);
-  g_return_val_if_fail (events != NULL, FALSE);
-  g_return_val_if_fail (n_events != NULL, FALSE);
-
-  *n_events = 0;
-  *events = NULL;
-  return FALSE;
-}
-
-gboolean
-gdk_device_set_mode (GdkDevice   *device,
-                     GdkInputMode mode)
-{
-  return FALSE;
-}
-
 gint
 _gdk_input_enable_window (GdkWindow *window, GdkDevicePrivate *gdkdev)
 {
@@ -315,10 +136,12 @@ _gdk_input_window_find(GdkWindow *window)
    cases */
 
 void
-gdk_input_set_extension_events (GdkWindow *window, gint mask,
-				GdkExtensionMode mode)
+gdk_input_set_extension_events (GdkWindow        *window,
+                                gint              mask,
+                                GdkExtensionMode  mode)
 {
   GdkWindowObject *window_private;
+  GdkWindowObject *impl_window;
   GList *tmp_list;
   GdkInputWindow *iw;
 
@@ -326,13 +149,14 @@ gdk_input_set_extension_events (GdkWindow *window, gint mask,
   g_return_if_fail (GDK_WINDOW_IS_QUARTZ (window));
 
   window_private = (GdkWindowObject*) window;
+  impl_window = (GdkWindowObject *)_gdk_window_get_impl_window (window);
 
   if (mode == GDK_EXTENSION_EVENTS_NONE)
     mask = 0;
 
   if (mask != 0)
     {
-      iw = g_new(GdkInputWindow,1);
+      iw = g_new (GdkInputWindow, 1);
 
       iw->window = window;
       iw->mode = mode;
@@ -341,39 +165,32 @@ gdk_input_set_extension_events (GdkWindow *window, gint mask,
       iw->num_obscuring = 0;
       iw->grabbed = FALSE;
 
-      _gdk_input_windows = g_list_append (_gdk_input_windows,iw);
+      _gdk_input_windows = g_list_append (_gdk_input_windows, iw);
       window_private->extension_events = mask;
 
       /* Add enter window events to the event mask */
       /* FIXME, this is not needed for XINPUT_NONE */
       gdk_window_set_events (window,
-			     gdk_window_get_events (window) | 
-			     GDK_ENTER_NOTIFY_MASK);
+                             gdk_window_get_events (window) | 
+                             GDK_ENTER_NOTIFY_MASK);
     }
   else
     {
       iw = _gdk_input_window_find (window);
       if (iw)
-	{
-	  _gdk_input_windows = g_list_remove (_gdk_input_windows,iw);
-	  g_free (iw);
-	}
+        {
+          _gdk_input_windows = g_list_remove (_gdk_input_windows,iw);
+          g_free (iw);
+        }
 
       window_private->extension_events = 0;
     }
 
   for (tmp_list = _gdk_input_devices; tmp_list; tmp_list = tmp_list->next)
     {
-      GdkDevicePrivate *gdkdev = (GdkDevicePrivate *)(tmp_list->data);
+      GdkDevice *dev = tmp_list->data;
 
-      if (gdkdev != (GdkDevicePrivate *)_gdk_core_pointer)
-	{
-	  if (mask != 0 && gdkdev->info.mode != GDK_MODE_DISABLED
-	      && (gdkdev->info.has_cursor || mode == GDK_EXTENSION_EVENTS_ALL))
-	    _gdk_input_enable_window (window,gdkdev);
-	  else
-	    _gdk_input_disable_window (window,gdkdev);
-	}
+      _gdk_input_select_device_events (GDK_WINDOW (impl_window), dev);
     }
 }
 
@@ -390,10 +207,57 @@ _gdk_input_window_destroy (GdkWindow *window)
 }
 
 void
+_gdk_input_check_extension_events (GdkDevice *device)
+{
+}
+
+void
 _gdk_input_init (void)
 {
-  _gdk_init_input_core ();
-  _gdk_input_devices = g_list_append (NULL, _gdk_core_pointer);
+  GdkDeviceManager *device_manager;
+  GList *list, *l;
+
+  device_manager = gdk_display_get_device_manager (_gdk_display);
+
+  /* For backward compatibility, just add floating devices that are
+   * not keyboards.
+   */
+  list = gdk_device_manager_list_devices (device_manager,
+                                          GDK_DEVICE_TYPE_FLOATING);
+  for (l = list; l; l = l->next)
+    {
+      GdkDevice *device = l->data;
+
+      if (device->source == GDK_SOURCE_KEYBOARD)
+        continue;
+
+      _gdk_input_devices = g_list_prepend (_gdk_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_list_devices (device_manager,
+                                          GDK_DEVICE_TYPE_MASTER);
+
+  for (l = list; l; l = l->next)
+    {
+      GdkDevice *device = list->data;
+
+      if (device->source != GDK_SOURCE_MOUSE)
+        continue;
+
+      _gdk_display->core_pointer = device;
+      break;
+    }
+
+  g_list_free (list);
+
+  /* Add the core pointer to the devices list */
+  _gdk_input_devices = g_list_prepend (_gdk_input_devices,
+                                       _gdk_display->core_pointer);
+
   _gdk_input_ignore_core = FALSE;
 }
 
@@ -425,30 +289,3 @@ _gdk_input_exit (void)
     }
   g_list_free (_gdk_input_windows);
 }
-
-gboolean
-gdk_device_get_axis (GdkDevice *device, gdouble *axes, GdkAxisUse use, gdouble *value)
-{
-  gint i;
-  
-  g_return_val_if_fail (device != NULL, FALSE);
-
-  if (axes == NULL)
-    return FALSE;
-  
-  for (i = 0; i < device->num_axes; i++)
-    if (device->axes[i].use == use)
-      {
-	if (value)
-	  *value = axes[i];
-	return TRUE;
-      }
-  
-  return FALSE;
-}
-
-void
-_gdk_input_window_crossing (GdkWindow *window,
-                            gboolean   enter)
-{
-}
diff --git a/gdk/quartz/gdkinputprivate.h b/gdk/quartz/gdkinputprivate.h
index b8f13ac..0868f33 100644
--- a/gdk/quartz/gdkinputprivate.h
+++ b/gdk/quartz/gdkinputprivate.h
@@ -97,11 +97,6 @@ struct _GdkDevicePrivate {
   GdkDevice  info;
 };
 
-struct _GdkDeviceClass
-{
-  GObjectClass parent_class;
-};
-
 struct _GdkInputWindow
 {
   /* gdk window */
diff --git a/gdk/quartz/gdkprivate-quartz.h b/gdk/quartz/gdkprivate-quartz.h
index 49aafb4..0809dcf 100644
--- a/gdk/quartz/gdkprivate-quartz.h
+++ b/gdk/quartz/gdkprivate-quartz.h
@@ -152,7 +152,8 @@ void       _gdk_quartz_window_nspoint_to_gdk_xy     (NSPoint    point,
                                                      gint      *y);
 GdkWindow *_gdk_quartz_window_find_child            (GdkWindow *window,
 						     gint       x,
-						     gint       y);
+						     gint       y,
+                                                     gboolean   get_toplevel);
 void       _gdk_quartz_window_attach_to_parent      (GdkWindow *window);
 void       _gdk_quartz_window_detach_from_parent    (GdkWindow *window);
 void       _gdk_quartz_window_did_become_main       (GdkWindow *window);
diff --git a/gdk/quartz/gdkwindow-quartz.c b/gdk/quartz/gdkwindow-quartz.c
index 93b68c2..efc0672 100644
--- a/gdk/quartz/gdkwindow-quartz.c
+++ b/gdk/quartz/gdkwindow-quartz.c
@@ -23,6 +23,7 @@
 #include <Carbon/Carbon.h>
 
 #include "gdk.h"
+#include "gdkdeviceprivate.h"
 #include "gdkwindowimpl.h"
 #include "gdkprivate-quartz.h"
 #include "gdkscreen-quartz.h"
@@ -143,41 +144,47 @@ gdk_window_impl_quartz_get_context (GdkDrawable *drawable,
 static void
 check_grab_unmap (GdkWindow *window)
 {
+  GList *list, *l;
   GdkDisplay *display = gdk_drawable_get_display (window);
+  GdkDeviceManager *device_manager;
 
-  _gdk_display_end_pointer_grab (display, 0, window, TRUE);
-
-  if (display->keyboard_grab.window)
+  device_manager = gdk_display_get_device_manager (display);
+  list = gdk_device_manager_list_devices (device_manager,
+                                          GDK_DEVICE_TYPE_FLOATING);
+  for (l = list; l; l = l->next)
     {
-      GdkWindowObject *private = GDK_WINDOW_OBJECT (window);
-      GdkWindowObject *tmp = GDK_WINDOW_OBJECT (display->keyboard_grab.window);
-
-      while (tmp && tmp != private)
-	tmp = tmp->parent;
-
-      if (tmp)
-	_gdk_display_unset_has_keyboard_grab (display, TRUE);
+      _gdk_display_end_device_grab (display, l->data, 0, window, TRUE);
     }
+
+  g_list_free (list);
 }
 
 static void
 check_grab_destroy (GdkWindow *window)
 {
+  GList *list, *l;
   GdkDisplay *display = gdk_drawable_get_display (window);
-  GdkPointerGrabInfo *grab;
+  GdkDeviceManager *device_manager;
 
   /* 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);
+  list = gdk_device_manager_list_devices (device_manager,
+                                          GDK_DEVICE_TYPE_MASTER);
+
+  for (l = list; l; l = l->next)
     {
-      /* Serials are always 0 in quartz, but for clarity: */
-      grab->serial_end = grab->serial_start;
-      grab->implicit_ungrab = TRUE;
+      GdkDeviceGrabInfo *grab;
+
+      grab = _gdk_display_get_last_device_grab (display, l->data);
+      if (grab && grab->native_window == window)
+        {
+          /* Serials are always 0 in quartz, but for clarity: */
+          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 (list);
 }
 
 static void
@@ -698,7 +705,8 @@ find_child_window_helper (GdkWindow *window,
 			  gint       x,
 			  gint       y,
 			  gint       x_offset,
-			  gint       y_offset)
+			  gint       y_offset,
+                          gboolean   get_toplevel)
 {
   GdkWindowImplQuartz *impl;
   GList *l;
@@ -751,13 +759,15 @@ find_child_window_helper (GdkWindow *window,
             }
         }
 
-      if (x >= temp_x && y >= temp_y &&
+      if ((!get_toplevel || (get_toplevel && window == _gdk_root)) &&
+          x >= temp_x && y >= temp_y &&
 	  x < temp_x + child_private->width && y < temp_y + child_private->height)
 	{
 	  /* Look for child windows. */
 	  return find_child_window_helper (l->data,
 					   x, y,
-					   temp_x, temp_y);
+					   temp_x, temp_y,
+                                           get_toplevel);
 	}
     }
   
@@ -771,12 +781,13 @@ find_child_window_helper (GdkWindow *window,
 GdkWindow *
 _gdk_quartz_window_find_child (GdkWindow *window,
 			       gint       x,
-			       gint       y)
+			       gint       y,
+                               gboolean   get_toplevel)
 {
   GdkWindowObject *private = GDK_WINDOW_OBJECT (window);
 
   if (x >= 0 && y >= 0 && x < private->width && y < private->height)
-    return find_child_window_helper (window, x, y, 0, 0);
+    return find_child_window_helper (window, x, y, 0, 0, get_toplevel);
 
   return NULL;
 }
@@ -1701,8 +1712,9 @@ gdk_window_quartz_set_back_pixmap (GdkWindow *window,
 }
 
 static void
-gdk_window_quartz_set_cursor (GdkWindow *window,
-                              GdkCursor *cursor)
+gdk_window_quartz_set_device_cursor (GdkWindow *window,
+                                     GdkDevice *device,
+                                     GdkCursor *cursor)
 {
   GdkCursorPrivate *cursor_private;
   NSCursor *nscursor;
@@ -1893,10 +1905,11 @@ gdk_window_get_root_origin (GdkWindow *window,
 
 /* Returns coordinates relative to the passed in window. */
 static GdkWindow *
-gdk_window_quartz_get_pointer_helper (GdkWindow       *window,
-                                      gint            *x,
-                                      gint            *y,
-                                      GdkModifierType *mask)
+gdk_window_quartz_get_device_state_helper (GdkWindow       *window,
+                                           GdkDevice       *device,
+                                           gint            *x,
+                                           gint            *y,
+                                           GdkModifierType *mask)
 {
   GdkWindowObject *toplevel;
   GdkWindowObject *private;
@@ -1941,7 +1954,8 @@ gdk_window_quartz_get_pointer_helper (GdkWindow       *window,
       window = (GdkWindow *)toplevel;
     }
 
-  found_window = _gdk_quartz_window_find_child (window, x_tmp, y_tmp);
+  found_window = _gdk_quartz_window_find_child (window, x_tmp, y_tmp,
+                                                FALSE);
 
   /* We never return the root window. */
   if (found_window == _gdk_root)
@@ -1954,26 +1968,30 @@ gdk_window_quartz_get_pointer_helper (GdkWindow       *window,
 }
 
 static gboolean
-gdk_window_quartz_get_pointer (GdkWindow       *window,
-                               gint            *x,
-                               gint            *y,
-                               GdkModifierType *mask)
+gdk_window_quartz_get_device_state (GdkWindow       *window,
+                                    GdkDevice       *device,
+                                    gint            *x,
+                                    gint            *y,
+                                    GdkModifierType *mask)
 {
-  return gdk_window_quartz_get_pointer_helper (window, x, y, mask) != NULL;
+  return gdk_window_quartz_get_device_state_helper (window,
+                                                    device,
+                                                    x, y, mask) != NULL;
 }
 
 /* Returns coordinates relative to the root. */
 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)
 {
   g_return_if_fail (display == _gdk_display);
   
   *screen = _gdk_screen;
-  gdk_window_quartz_get_pointer_helper (_gdk_root, x, y, mask);
+  gdk_window_quartz_get_device_state_helper (_gdk_root, device, x, y, mask);
 }
 
 void
@@ -1997,9 +2015,10 @@ _gdk_windowing_window_at_pointer (GdkDisplay      *display,
   gint x, y;
   GdkModifierType tmp_mask = 0;
 
-  found_window = gdk_window_quartz_get_pointer_helper (_gdk_root,
-                                                       &x, &y,
-                                                       &tmp_mask);
+  found_window = gdk_window_quartz_get_device_state_helper (_gdk_root,
+                                                            display->core_pointer,
+                                                            &x, &y,
+                                                            &tmp_mask);
   if (found_window)
     {
       GdkWindowObject *private;
@@ -2052,6 +2071,21 @@ _gdk_windowing_window_at_pointer (GdkDisplay      *display,
   return found_window;
 }
 
+GdkWindow*
+_gdk_windowing_window_at_device_position (GdkDisplay      *display,
+                                          GdkDevice       *device,
+                                          gint            *win_x,
+                                          gint            *win_y,
+                                          GdkModifierType *mask,
+                                          gboolean         get_toplevel)
+{
+  return GDK_DEVICE_GET_CLASS (device)->window_at_position (device,
+                                                            win_x, win_y,
+                                                            mask,
+                                                            get_toplevel);
+}
+
+
 static GdkEventMask  
 gdk_window_quartz_get_events (GdkWindow *window)
 {
@@ -3065,10 +3099,10 @@ gdk_window_impl_iface_init (GdkWindowImplIface *iface)
   iface->set_background = gdk_window_quartz_set_background;
   iface->set_back_pixmap = gdk_window_quartz_set_back_pixmap;
   iface->reparent = gdk_window_quartz_reparent;
-  iface->set_cursor = gdk_window_quartz_set_cursor;
+  iface->set_device_cursor = gdk_window_quartz_set_device_cursor;
   iface->get_geometry = gdk_window_quartz_get_geometry;
   iface->get_root_coords = gdk_window_quartz_get_root_coords;
-  iface->get_pointer = gdk_window_quartz_get_pointer;
+  iface->get_device_state = gdk_window_quartz_get_device_state;
   iface->get_deskrelative_origin = gdk_window_quartz_get_deskrelative_origin;
   iface->shape_combine_region = gdk_window_quartz_shape_combine_region;
   iface->input_shape_combine_region = gdk_window_quartz_input_shape_combine_region;
@@ -3076,6 +3110,4 @@ gdk_window_impl_iface_init (GdkWindowImplIface *iface)
   iface->queue_antiexpose = _gdk_quartz_window_queue_antiexpose;
   iface->queue_translation = _gdk_quartz_window_queue_translation;
   iface->destroy = _gdk_quartz_window_destroy;
-  iface->input_window_destroy = _gdk_input_window_destroy;
-  iface->input_window_crossing = _gdk_input_window_crossing;
 }
diff --git a/gdk/win32/Makefile.am b/gdk/win32/Makefile.am
index 17d048e..e6c5b2d 100644
--- a/gdk/win32/Makefile.am
+++ b/gdk/win32/Makefile.am
@@ -29,6 +29,12 @@ libgdk_win32_la_SOURCES = \
 	gdkapplaunchcontext-win32.c \
 	gdkcolor-win32.c \
 	gdkcursor-win32.c \
+	gdkdevicemanager-win32.c \
+	gdkdevicemanager-win32.h \
+	gdkdevice-win32.c \
+	gdkdevice-win32.h \
+	gdkdevice-wintab.c \
+	gdkdevice-wintab.h \
 	gdkdisplay-win32.c \
 	gdkdnd-win32.c \
 	gdkdrawable-win32.c \
@@ -41,8 +47,6 @@ libgdk_win32_la_SOURCES = \
 	gdkim-win32.c \
 	gdkimage-win32.c \
 	gdkinput.c \
-	gdkinput-win32.c \
-	gdkinput-win32.h \
 	gdkkeys-win32.c \
 	gdkmain-win32.c \
 	gdkpixmap-win32.c \
diff --git a/gdk/win32/gdkdevice-win32.c b/gdk/win32/gdkdevice-win32.c
new file mode 100644
index 0000000..9e4eb81
--- /dev/null
+++ b/gdk/win32/gdkdevice-win32.c
@@ -0,0 +1,396 @@
+/* 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 <gdk/gdkwindow.h>
+
+#include <windowsx.h>
+#include <objbase.h>
+
+#include "gdkdevice-win32.h"
+#include "gdkwin32.h"
+
+static gboolean gdk_device_win32_get_history (GdkDevice      *device,
+                                              GdkWindow      *window,
+                                              guint32         start,
+                                              guint32         stop,
+                                              GdkTimeCoord ***events,
+                                              guint          *n_events);
+static void gdk_device_win32_get_state (GdkDevice       *device,
+                                        GdkWindow       *window,
+                                        gdouble         *axes,
+                                        GdkModifierType *mask);
+static void gdk_device_win32_set_window_cursor (GdkDevice *device,
+                                                GdkWindow *window,
+                                                GdkCursor *cursor);
+static void gdk_device_win32_warp (GdkDevice *device,
+                                   GdkScreen *screen,
+                                   gint       x,
+                                   gint       y);
+static gboolean gdk_device_win32_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_win32_grab   (GdkDevice     *device,
+                                              GdkWindow     *window,
+                                              gboolean       owner_events,
+                                              GdkEventMask   event_mask,
+                                              GdkWindow     *confine_to,
+                                              GdkCursor     *cursor,
+                                              guint32        time_);
+static void          gdk_device_win32_ungrab (GdkDevice     *device,
+                                              guint32        time_);
+static GdkWindow * gdk_device_win32_window_at_position (GdkDevice       *device,
+                                                        gint            *win_x,
+                                                        gint            *win_y,
+                                                        GdkModifierType *mask,
+                                                        gboolean         get_toplevel);
+static void      gdk_device_win32_select_window_events (GdkDevice       *device,
+                                                        GdkWindow       *window,
+                                                        GdkEventMask     event_mask);
+
+
+G_DEFINE_TYPE (GdkDeviceWin32, gdk_device_win32, GDK_TYPE_DEVICE)
+
+static void
+gdk_device_win32_class_init (GdkDeviceWin32Class *klass)
+{
+  GdkDeviceClass *device_class = GDK_DEVICE_CLASS (klass);
+
+  device_class->get_history = gdk_device_win32_get_history;
+  device_class->get_state = gdk_device_win32_get_state;
+  device_class->set_window_cursor = gdk_device_win32_set_window_cursor;
+  device_class->warp = gdk_device_win32_warp;
+  device_class->query_state = gdk_device_win32_query_state;
+  device_class->grab = gdk_device_win32_grab;
+  device_class->ungrab = gdk_device_win32_ungrab;
+  device_class->window_at_position = gdk_device_win32_window_at_position;
+  device_class->select_window_events = gdk_device_win32_select_window_events;
+}
+
+static void
+gdk_device_win32_init (GdkDeviceWin32 *device_win32)
+{
+  GdkDevice *device;
+
+  device = GDK_DEVICE (device_win32);
+
+  _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
+gdk_device_win32_get_history (GdkDevice      *device,
+                              GdkWindow      *window,
+                              guint32         start,
+                              guint32         stop,
+                              GdkTimeCoord ***events,
+                              guint          *n_events)
+{
+  return FALSE;
+}
+
+static void
+gdk_device_win32_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_win32_set_window_cursor (GdkDevice *device,
+                                    GdkWindow *window,
+                                    GdkCursor *cursor)
+{
+  GdkCursorPrivate *cursor_private;
+  GdkWindowObject *parent_window;
+  GdkWindowImplWin32 *impl;
+  HCURSOR hcursor;
+  HCURSOR hprevcursor;
+
+  impl = GDK_WINDOW_IMPL_WIN32 (GDK_WINDOW_OBJECT (window)->impl);
+  cursor_private = (GdkCursorPrivate*) cursor;
+
+  hprevcursor = impl->hcursor;
+
+  if (!cursor)
+    hcursor = NULL;
+  else
+    hcursor = cursor_private->hcursor;
+
+  if (hcursor != NULL)
+    {
+      /* If the pointer is over our window, set new cursor */
+      GdkWindow *curr_window = gdk_window_get_pointer (window, NULL, NULL, NULL);
+
+      if (curr_window == window ||
+          (curr_window && window == gdk_window_get_toplevel (curr_window)))
+        SetCursor (hcursor);
+      else
+        {
+          /* Climb up the tree and find whether our window is the
+           * first ancestor that has cursor defined, and if so, set
+           * new cursor.
+           */
+          GdkWindowObject *curr_window_obj = GDK_WINDOW_OBJECT (curr_window);
+
+          while (curr_window_obj &&
+                 !GDK_WINDOW_IMPL_WIN32 (curr_window_obj->impl)->hcursor)
+            {
+              curr_window_obj = curr_window_obj->parent;
+              if (curr_window_obj == GDK_WINDOW_OBJECT (window))
+                {
+                  SetCursor (hcursor);
+                  break;
+                }
+            }
+        }
+    }
+
+  /* Unset the previous cursor: Need to make sure it's no longer in
+   * use before we destroy it, in case we're not over our window but
+   * the cursor is still set to our old one.
+   */
+  if (hprevcursor != NULL &&
+      GetCursor () == hprevcursor)
+    {
+      /* Look for a suitable cursor to use instead */
+      hcursor = NULL;
+      parent_window = GDK_WINDOW_OBJECT (window)->parent;
+
+      while (hcursor == NULL)
+        {
+          if (parent_window)
+            {
+              impl = GDK_WINDOW_IMPL_WIN32 (parent_window->impl);
+              hcursor = impl->hcursor;
+              parent_window = parent_window->parent;
+            }
+          else
+            hcursor = LoadCursor (NULL, IDC_ARROW);
+        }
+
+      SetCursor (hcursor);
+    }
+}
+
+static void
+gdk_device_win32_warp (GdkDevice *device,
+                       GdkScreen *screen,
+                       gint       x,
+                       gint       y)
+{
+  SetCursorPos (x - _gdk_offset_x, y - _gdk_offset_y);
+}
+
+static GdkModifierType
+get_current_mask (void)
+{
+  GdkModifierType mask;
+  BYTE kbd[256];
+
+  GetKeyboardState (kbd);
+  mask = 0;
+  if (kbd[VK_SHIFT] & 0x80)
+    mask |= GDK_SHIFT_MASK;
+  if (kbd[VK_CAPITAL] & 0x80)
+    mask |= GDK_LOCK_MASK;
+  if (kbd[VK_CONTROL] & 0x80)
+    mask |= GDK_CONTROL_MASK;
+  if (kbd[VK_MENU] & 0x80)
+    mask |= GDK_MOD1_MASK;
+  if (kbd[VK_LBUTTON] & 0x80)
+    mask |= GDK_BUTTON1_MASK;
+  if (kbd[VK_MBUTTON] & 0x80)
+    mask |= GDK_BUTTON2_MASK;
+  if (kbd[VK_RBUTTON] & 0x80)
+    mask |= GDK_BUTTON3_MASK;
+
+  return mask;
+}
+
+static gboolean
+gdk_device_win32_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)
+{
+  gboolean return_val;
+  POINT point;
+  HWND hwnd, hwndc;
+
+  g_return_val_if_fail (window == NULL || GDK_IS_WINDOW (window), FALSE);
+  
+  return_val = TRUE;
+
+  hwnd = GDK_WINDOW_HWND (window);
+  GetCursorPos (&point);
+
+  if (root_x)
+    *root_x = point.x;
+
+  if (root_y)
+    *root_y = point.y;
+
+  ScreenToClient (hwnd, &point);
+
+  if (win_x)
+    *win_x = point.x;
+
+  if (win_y)
+    *win_y = point.y;
+
+  if (window == _gdk_root)
+    {
+      if (win_x)
+        *win_x += _gdk_offset_x;
+
+      if (win_y)
+        *win_y += _gdk_offset_y;
+    }
+
+  if (child_window)
+    {
+      hwndc = ChildWindowFromPoint (hwnd, point);
+
+      if (hwndc && hwndc != hwnd)
+        *child_window = gdk_win32_handle_table_lookup ((GdkNativeWindow) hwndc);
+      else
+        *child_window = NULL; /* Direct child unknown to gdk */
+    }
+
+  if (root_window)
+    {
+      GdkScreen *screen;
+
+      screen = gdk_drawable_get_screen (GDK_DRAWABLE (window));
+      *root_window = gdk_screen_get_root_window (screen);
+    }
+
+  if (mask)
+    *mask = get_current_mask ();
+
+  return TRUE;
+}
+
+static GdkGrabStatus
+gdk_device_win32_grab (GdkDevice    *device,
+                       GdkWindow    *window,
+                       gboolean      owner_events,
+                       GdkEventMask  event_mask,
+                       GdkWindow    *confine_to,
+                       GdkCursor    *cursor,
+                       guint32       time_)
+{
+  if (device->source != GDK_SOURCE_KEYBOARD)
+    SetCapture (GDK_WINDOW_HWND (window));
+
+  return GDK_GRAB_SUCCESS;
+}
+
+static void
+gdk_device_win32_ungrab (GdkDevice *device,
+                         guint32    time_)
+{
+  GdkDisplay *display;
+
+  display = gdk_device_get_display (device);
+
+  if (device->source != GDK_SOURCE_KEYBOARD)
+    ReleaseCapture ();
+}
+
+static GdkWindow *
+gdk_device_win32_window_at_position (GdkDevice       *device,
+                                     gint            *win_x,
+                                     gint            *win_y,
+                                     GdkModifierType *mask,
+                                     gboolean         get_toplevel)
+{
+  GdkWindow *window;
+  POINT point, pointc;
+  HWND hwnd, hwndc;
+  RECT rect;
+
+  GetCursorPos (&pointc);
+  point = pointc;
+  hwnd = WindowFromPoint (point);
+
+  if (hwnd == NULL)
+    {
+      window = _gdk_root;
+      *win_x = pointc.x + _gdk_offset_x;
+      *win_y = pointc.y + _gdk_offset_y;
+      return window;
+    }
+
+  ScreenToClient (hwnd, &point);
+
+  do
+    {
+      if (get_toplevel &&
+          (window = gdk_win32_handle_table_lookup ((GdkNativeWindow) hwnd)) != NULL &&
+          GDK_WINDOW_TYPE (window) != GDK_WINDOW_FOREIGN)
+        break;
+
+      hwndc = ChildWindowFromPoint (hwnd, point);
+      ClientToScreen (hwnd, &point);
+      ScreenToClient (hwndc, &point);
+    }
+  while (hwndc != hwnd && (hwnd = hwndc, 1));
+
+  window = gdk_win32_handle_table_lookup ((GdkNativeWindow) hwnd);
+
+  if (window && (win_x || win_y))
+    {
+      GetClientRect (hwnd, &rect);
+      *win_x = point.x - rect.left;
+      *win_y = point.y - rect.top;
+    }
+
+  return window;
+}
+
+static void
+gdk_device_win32_select_window_events (GdkDevice    *device,
+                                       GdkWindow    *window,
+                                       GdkEventMask  event_mask)
+{
+}
diff --git a/gdk/win32/gdkdevice-win32.h b/gdk/win32/gdkdevice-win32.h
new file mode 100644
index 0000000..ef9c322
--- /dev/null
+++ b/gdk/win32/gdkdevice-win32.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_WIN32_H__
+#define __GDK_DEVICE_WIN32_H__
+
+#include <gdk/gdkdeviceprivate.h>
+
+G_BEGIN_DECLS
+
+#define GDK_TYPE_DEVICE_WIN32         (gdk_device_win32_get_type ())
+#define GDK_DEVICE_WIN32(o)           (G_TYPE_CHECK_INSTANCE_CAST ((o), GDK_TYPE_DEVICE_WIN32, GdkDeviceWin32))
+#define GDK_DEVICE_WIN32_CLASS(c)     (G_TYPE_CHECK_CLASS_CAST ((c), GDK_TYPE_DEVICE_WIN32, GdkDeviceWin32Class))
+#define GDK_IS_DEVICE_WIN32(o)        (G_TYPE_CHECK_INSTANCE_TYPE ((o), GDK_TYPE_DEVICE_WIN32))
+#define GDK_IS_DEVICE_WIN32_CLASS(c)  (G_TYPE_CHECK_CLASS_TYPE ((c), GDK_TYPE_DEVICE_WIN32))
+#define GDK_DEVICE_WIN32_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GDK_TYPE_DEVICE_WIN32, GdkDeviceWin32Class))
+
+typedef struct _GdkDeviceWin32 GdkDeviceWin32;
+typedef struct _GdkDeviceWin32Class GdkDeviceWin32Class;
+
+struct _GdkDeviceWin32
+{
+  GdkDevice parent_instance;
+};
+
+struct _GdkDeviceWin32Class
+{
+  GdkDeviceClass parent_class;
+};
+
+GType gdk_device_win32_get_type (void) G_GNUC_CONST;
+
+G_END_DECLS
+
+#endif /* __GDK_DEVICE_WIN32_H__ */
diff --git a/gdk/win32/gdkdevice-wintab.c b/gdk/win32/gdkdevice-wintab.c
new file mode 100644
index 0000000..392e10b
--- /dev/null
+++ b/gdk/win32/gdkdevice-wintab.c
@@ -0,0 +1,386 @@
+/* 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 <gdk/gdkwindow.h>
+
+#include <windowsx.h>
+#include <objbase.h>
+
+#include "gdkwin32.h"
+#include "gdkdevice-wintab.h"
+
+static GQuark quark_window_input_info = 0;
+static GSList *input_windows = NULL;
+
+typedef struct
+{
+  gdouble root_x;
+  gdouble root_y;
+  GHashTable *device_events;
+} GdkWindowInputInfo;
+
+static gboolean gdk_device_wintab_get_history (GdkDevice      *device,
+                                               GdkWindow      *window,
+                                               guint32         start,
+                                               guint32         stop,
+                                               GdkTimeCoord ***events,
+                                               guint          *n_events);
+static void gdk_device_wintab_get_state (GdkDevice       *device,
+                                         GdkWindow       *window,
+                                         gdouble         *axes,
+                                         GdkModifierType *mask);
+static void gdk_device_wintab_set_window_cursor (GdkDevice *device,
+                                                 GdkWindow *window,
+                                                 GdkCursor *cursor);
+static void gdk_device_wintab_warp (GdkDevice *device,
+                                    GdkScreen *screen,
+                                    gint       x,
+                                    gint       y);
+static gboolean gdk_device_wintab_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_wintab_grab   (GdkDevice     *device,
+                                               GdkWindow     *window,
+                                               gboolean       owner_events,
+                                               GdkEventMask   event_mask,
+                                               GdkWindow     *confine_to,
+                                               GdkCursor     *cursor,
+                                               guint32        time_);
+static void          gdk_device_wintab_ungrab (GdkDevice     *device,
+                                               guint32        time_);
+static GdkWindow * gdk_device_wintab_window_at_position (GdkDevice       *device,
+                                                         gint            *win_x,
+                                                         gint            *win_y,
+                                                         GdkModifierType *mask,
+                                                         gboolean         get_toplevel);
+static void      gdk_device_wintab_select_window_events (GdkDevice       *device,
+                                                         GdkWindow       *window,
+                                                         GdkEventMask     event_mask);
+
+
+G_DEFINE_TYPE (GdkDeviceWintab, gdk_device_wintab, GDK_TYPE_DEVICE)
+
+static void
+gdk_device_wintab_class_init (GdkDeviceWintabClass *klass)
+{
+  GdkDeviceClass *device_class = GDK_DEVICE_CLASS (klass);
+
+  device_class->get_history = gdk_device_wintab_get_history;
+  device_class->get_state = gdk_device_wintab_get_state;
+  device_class->set_window_cursor = gdk_device_wintab_set_window_cursor;
+  device_class->warp = gdk_device_wintab_warp;
+  device_class->query_state = gdk_device_wintab_query_state;
+  device_class->grab = gdk_device_wintab_grab;
+  device_class->ungrab = gdk_device_wintab_ungrab;
+  device_class->window_at_position = gdk_device_wintab_window_at_position;
+  device_class->select_window_events = gdk_device_wintab_select_window_events;
+
+  quark_window_input_info = g_quark_from_static_string ("gdk-window-input-info");
+}
+
+static void
+gdk_device_wintab_init (GdkDeviceWintab *device_wintab)
+{
+  GdkDevice *device;
+
+  device = GDK_DEVICE (device_wintab);
+}
+
+static gboolean
+gdk_device_wintab_get_history (GdkDevice      *device,
+                               GdkWindow      *window,
+                               guint32         start,
+                               guint32         stop,
+                               GdkTimeCoord ***events,
+                               guint          *n_events)
+{
+  return FALSE;
+}
+
+static void
+gdk_device_wintab_get_state (GdkDevice       *device,
+                             GdkWindow       *window,
+                             gdouble         *axes,
+                             GdkModifierType *mask)
+{
+  GdkDeviceWintab *device_wintab;
+
+  device_wintab = GDK_DEVICE_WINTAB (device);
+
+  /* For now just use the last known button and axis state of the device.
+   * Since graphical tablets send an insane amount of motion events each
+   * second, the info should be fairly up to date */
+  if (mask)
+    {
+      gdk_window_get_pointer (window, NULL, NULL, mask);
+      *mask &= 0xFF; /* Mask away core pointer buttons */
+      *mask |= ((device_wintab->button_state << 8)
+                & (GDK_BUTTON1_MASK | GDK_BUTTON2_MASK
+                   | GDK_BUTTON3_MASK | GDK_BUTTON4_MASK
+                   | GDK_BUTTON5_MASK));
+    }
+
+  if (device_wintab->last_axis_data)
+    _gdk_device_wintab_translate_axes (device, window, axes, NULL, NULL);
+}
+
+static void
+gdk_device_wintab_set_window_cursor (GdkDevice *device,
+                                     GdkWindow *window,
+                                     GdkCursor *cursor)
+{
+}
+
+static void
+gdk_device_wintab_warp (GdkDevice *device,
+                        GdkScreen *screen,
+                        gint       x,
+                        gint       y)
+{
+}
+
+static gboolean
+gdk_device_wintab_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_wintab_grab (GdkDevice    *device,
+                        GdkWindow    *window,
+                        gboolean      owner_events,
+                        GdkEventMask  event_mask,
+                        GdkWindow    *confine_to,
+                        GdkCursor    *cursor,
+                        guint32       time_)
+{
+  return GDK_GRAB_SUCCESS;
+}
+
+static void
+gdk_device_wintab_ungrab (GdkDevice *device,
+                          guint32    time_)
+{
+}
+
+static GdkWindow *
+gdk_device_wintab_window_at_position (GdkDevice       *device,
+                                      gint            *win_x,
+                                      gint            *win_y,
+                                      GdkModifierType *mask,
+                                      gboolean         get_toplevel)
+{
+  return NULL;
+}
+
+static void
+input_info_free (GdkWindowInputInfo *info)
+{
+  g_hash_table_destroy (info->device_events);
+  g_free (info);
+}
+
+static void
+gdk_device_wintab_select_window_events (GdkDevice    *device,
+                                        GdkWindow    *window,
+                                        GdkEventMask  event_mask)
+{
+  GdkWindowInputInfo *info;
+
+  info = g_object_get_qdata (G_OBJECT (window),
+                             quark_window_input_info);
+  if (event_mask)
+    {
+      if (!info)
+        {
+          info = g_new0 (GdkWindowInputInfo, 1);
+          info->device_events = g_hash_table_new (NULL, NULL);
+
+          g_object_set_qdata_full (G_OBJECT (window),
+                                   quark_window_input_info,
+                                   info,
+                                   (GDestroyNotify) input_info_free);
+          input_windows = g_slist_prepend (input_windows, window);
+        }
+
+      g_hash_table_insert (info->device_events, device,
+                           GUINT_TO_POINTER (event_mask));
+    }
+  else if (info)
+    {
+      g_hash_table_remove (info->device_events, device);
+
+      if (g_hash_table_size (info->device_events) == 0)
+        {
+          g_object_set_qdata (G_OBJECT (window),
+                              quark_window_input_info,
+                              NULL);
+          input_windows = g_slist_remove (input_windows, window);
+        }
+    }
+}
+
+GdkEventMask
+_gdk_device_wintab_get_events (GdkDeviceWintab *device,
+                               GdkWindow       *window)
+{
+  GdkWindowInputInfo *info;
+
+  info = g_object_get_qdata (G_OBJECT (window),
+                             quark_window_input_info);
+
+  if (!info)
+    return 0;
+
+  return GPOINTER_TO_UINT (g_hash_table_lookup (info->device_events, device));
+}
+
+gboolean
+_gdk_device_wintab_get_window_coords (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_wintab_update_window_coords (GdkWindow *window)
+{
+  GdkWindowInputInfo *info;
+  gint root_x, root_y;
+
+  info = g_object_get_qdata (G_OBJECT (window),
+                             quark_window_input_info);
+
+  g_return_if_fail (info != NULL);
+
+  gdk_window_get_origin (window, &root_x, &root_y);
+  info->root_x = (gdouble) root_x;
+  info->root_y = (gdouble) root_y;
+}
+
+void
+_gdk_device_wintab_translate_axes (GdkDeviceWintab *device_wintab,
+                                   GdkWindow       *window,
+                                   gdouble         *axes,
+                                   gdouble         *x,
+                                   gdouble         *y)
+{
+  GdkDevice *device;
+  GdkWindow *impl_window;
+  gdouble root_x, root_y;
+  gdouble temp_x, temp_y;
+  gint i;
+
+  device = GDK_DEVICE (device_wintab);
+  impl_window = _gdk_window_get_impl_window (window);
+  temp_x = temp_y = 0;
+
+  if (!_gdk_device_wintab_get_window_coords (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 (gdk_device_get_mode (device) == GDK_MODE_WINDOW)
+            _gdk_device_translate_window_coord (device, window, i,
+                                                device_wintab->last_axis_data[i],
+                                                &axes[i]);
+          else
+            _gdk_device_translate_screen_coord (device, window,
+                                                root_x, root_y, i,
+                                                device_wintab->last_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,
+                                      device_wintab->last_axis_data[i],
+                                      &axes[i]);
+          break;
+        }
+    }
+
+  if (x)
+    *x = temp_x;
+
+  if (y)
+    *y = temp_y;
+}
+
+void
+_gdk_input_check_extension_events (GdkDevice *device)
+{
+  GSList *l;
+
+  if (!GDK_IS_DEVICE_WINTAB (device))
+    return;
+
+  for (l = input_windows; l; l = l->next)
+    {
+      GdkWindowObject *window_private;
+      GdkEventMask event_mask = 0;
+
+      window_private = l->data;
+
+      if (gdk_device_get_mode (device) != GDK_MODE_DISABLED)
+        event_mask = window_private->extension_events;
+
+      gdk_window_set_device_events (GDK_WINDOW (window_private),
+                                    device, event_mask);
+    }
+}
diff --git a/gdk/win32/gdkdevice-wintab.h b/gdk/win32/gdkdevice-wintab.h
new file mode 100644
index 0000000..79fae8a
--- /dev/null
+++ b/gdk/win32/gdkdevice-wintab.h
@@ -0,0 +1,79 @@
+/* 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_WINTAB_H__
+#define __GDK_DEVICE_WINTAB_H__
+
+#include <gdk/gdkdeviceprivate.h>
+
+#include <windows.h>
+#include <wintab.h>
+
+G_BEGIN_DECLS
+
+#define GDK_TYPE_DEVICE_WINTAB         (gdk_device_wintab_get_type ())
+#define GDK_DEVICE_WINTAB(o)           (G_TYPE_CHECK_INSTANCE_CAST ((o), GDK_TYPE_DEVICE_WINTAB, GdkDeviceWintab))
+#define GDK_DEVICE_WINTAB_CLASS(c)     (G_TYPE_CHECK_CLASS_CAST ((c), GDK_TYPE_DEVICE_WINTAB, GdkDeviceWintabClass))
+#define GDK_IS_DEVICE_WINTAB(o)        (G_TYPE_CHECK_INSTANCE_TYPE ((o), GDK_TYPE_DEVICE_WINTAB))
+#define GDK_IS_DEVICE_WINTAB_CLASS(c)  (G_TYPE_CHECK_CLASS_TYPE ((c), GDK_TYPE_DEVICE_WINTAB))
+#define GDK_DEVICE_WINTAB_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GDK_TYPE_DEVICE_WINTAB, GdkDeviceWintabClass))
+
+typedef struct _GdkDeviceWintab GdkDeviceWintab;
+typedef struct _GdkDeviceWintabClass GdkDeviceWintabClass;
+
+struct _GdkDeviceWintab
+{
+  GdkDevice parent_instance;
+
+  gint *last_axis_data;
+  gint button_state;
+
+  /* WINTAB stuff: */
+  HCTX hctx;
+  /* Cursor number */
+  UINT cursor;
+  /* The cursor's CSR_PKTDATA */
+  WTPKT pktdata;
+  /* Azimuth and altitude axis */
+  AXIS orientation_axes[2];
+};
+
+struct _GdkDeviceWintabClass
+{
+  GdkDeviceClass parent_class;
+};
+
+GType gdk_device_wintab_get_type (void) G_GNUC_CONST;
+
+GdkEventMask _gdk_device_wintab_get_events (GdkDeviceWintab *device,
+                                            GdkWindow       *window);
+gboolean     _gdk_device_wintab_get_window_coords (GdkWindow *window,
+                                                   gdouble   *root_x,
+                                                   gdouble   *root_y);
+void         _gdk_device_wintab_update_window_coords (GdkWindow *window);
+
+void         _gdk_device_wintab_translate_axes (GdkDeviceWintab *device,
+                                                GdkWindow       *window,
+                                                gdouble         *axes,
+                                                gdouble         *x,
+                                                gdouble         *y);
+
+G_END_DECLS
+
+#endif /* __GDK_DEVICE_WINTAB_H__ */
diff --git a/gdk/win32/gdkdevicemanager-win32.c b/gdk/win32/gdkdevicemanager-win32.c
new file mode 100644
index 0000000..eea8142
--- /dev/null
+++ b/gdk/win32/gdkdevicemanager-win32.c
@@ -0,0 +1,1089 @@
+/* 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 <stdlib.h>
+#include <stdio.h>
+#include <math.h>
+
+#include <gdk/gdk.h>
+#include "gdkprivate-win32.h"
+#include "gdkdevicemanager-win32.h"
+#include "gdkdeviceprivate.h"
+#include "gdkdevice-win32.h"
+#include "gdkdevice-wintab.h"
+
+#include <windows.h>
+#include <wintab.h>
+
+#define PACKETDATA (PK_CONTEXT | PK_CURSOR | PK_BUTTONS | PK_X | PK_Y  | PK_NORMAL_PRESSURE | PK_ORIENTATION)
+/* We want everything in absolute mode */
+#define PACKETMODE (0)
+#include <pktdef.h>
+
+#define DEBUG_WINTAB 1		/* Verbose debug messages enabled */
+#define PROXIMITY_OUT_DELAY 200 /* In milliseconds, see set_ignore_core */
+#define TWOPI (2 * G_PI)
+
+static GList     *wintab_contexts = NULL;
+static GdkWindow *wintab_window = NULL;
+static guint      ignore_core_timer = 0;
+extern gint       _gdk_input_ignore_core;
+
+typedef UINT (WINAPI *t_WTInfoA) (UINT a, UINT b, LPVOID c);
+typedef UINT (WINAPI *t_WTInfoW) (UINT a, UINT b, LPVOID c);
+typedef BOOL (WINAPI *t_WTEnable) (HCTX a, BOOL b);
+typedef HCTX (WINAPI *t_WTOpenA) (HWND a, LPLOGCONTEXTA b, BOOL c);
+typedef BOOL (WINAPI *t_WTOverlap) (HCTX a, BOOL b);
+typedef BOOL (WINAPI *t_WTPacket) (HCTX a, UINT b, LPVOID c);
+typedef int (WINAPI *t_WTQueueSizeSet) (HCTX a, int b);
+
+static t_WTInfoA p_WTInfoA;
+static t_WTInfoW p_WTInfoW;
+static t_WTEnable p_WTEnable;
+static t_WTOpenA p_WTOpenA;
+static t_WTOverlap p_WTOverlap;
+static t_WTPacket p_WTPacket;
+static t_WTQueueSizeSet p_WTQueueSizeSet;
+
+
+static void    gdk_device_manager_win32_finalize    (GObject *object);
+static void    gdk_device_manager_win32_constructed (GObject *object);
+
+static GList * gdk_device_manager_win32_list_devices (GdkDeviceManager *device_manager,
+                                                      GdkDeviceType     type);
+
+
+G_DEFINE_TYPE (GdkDeviceManagerWin32, gdk_device_manager_win32, GDK_TYPE_DEVICE_MANAGER)
+
+static void
+gdk_device_manager_win32_class_init (GdkDeviceManagerWin32Class *klass)
+{
+  GdkDeviceManagerClass *device_manager_class = GDK_DEVICE_MANAGER_CLASS (klass);
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+  object_class->finalize = gdk_device_manager_win32_finalize;
+  object_class->constructed = gdk_device_manager_win32_constructed;
+  device_manager_class->list_devices = gdk_device_manager_win32_list_devices;
+}
+
+static GdkDevice *
+create_core_pointer (GdkDeviceManager *device_manager)
+{
+  return g_object_new (GDK_TYPE_DEVICE_WIN32,
+                       "name", "Core Pointer",
+                       "type", GDK_DEVICE_TYPE_MASTER,
+                       "input-source", GDK_SOURCE_MOUSE,
+                       "input-mode", GDK_MODE_SCREEN,
+                       "has-cursor", TRUE,
+                       "display", _gdk_display,
+                       "device-manager", device_manager,
+                       NULL);
+}
+
+static GdkDevice *
+create_core_keyboard (GdkDeviceManager *device_manager)
+{
+  return g_object_new (GDK_TYPE_DEVICE_WIN32,
+                       "name", "Core Keyboard",
+                       "type", GDK_DEVICE_TYPE_MASTER,
+                       "input-source", GDK_SOURCE_KEYBOARD,
+                       "input-mode", GDK_MODE_SCREEN,
+                       "has-cursor", FALSE,
+                       "display", _gdk_display,
+                       "device-manager", device_manager,
+                       NULL);
+}
+
+static void
+gdk_device_manager_win32_init (GdkDeviceManagerWin32 *device_manager_win32)
+{
+}
+
+static void
+gdk_device_manager_win32_finalize (GObject *object)
+{
+  GdkDeviceManagerWin32 *device_manager_win32;
+
+  device_manager_win32 = GDK_DEVICE_MANAGER_WIN32 (object);
+
+  g_object_unref (device_manager_win32->core_pointer);
+  g_object_unref (device_manager_win32->core_keyboard);
+
+  G_OBJECT_CLASS (gdk_device_manager_win32_parent_class)->finalize (object);
+}
+
+#if DEBUG_WINTAB
+
+static void
+print_lc(LOGCONTEXT *lc)
+{
+  g_print ("lcName = %s\n", lc->lcName);
+  g_print ("lcOptions =");
+  if (lc->lcOptions & CXO_SYSTEM) g_print (" CXO_SYSTEM");
+  if (lc->lcOptions & CXO_PEN) g_print (" CXO_PEN");
+  if (lc->lcOptions & CXO_MESSAGES) g_print (" CXO_MESSAGES");
+  if (lc->lcOptions & CXO_MARGIN) g_print (" CXO_MARGIN");
+  if (lc->lcOptions & CXO_MGNINSIDE) g_print (" CXO_MGNINSIDE");
+  if (lc->lcOptions & CXO_CSRMESSAGES) g_print (" CXO_CSRMESSAGES");
+  g_print ("\n");
+  g_print ("lcStatus =");
+  if (lc->lcStatus & CXS_DISABLED) g_print (" CXS_DISABLED");
+  if (lc->lcStatus & CXS_OBSCURED) g_print (" CXS_OBSCURED");
+  if (lc->lcStatus & CXS_ONTOP) g_print (" CXS_ONTOP");
+  g_print ("\n");
+  g_print ("lcLocks =");
+  if (lc->lcLocks & CXL_INSIZE) g_print (" CXL_INSIZE");
+  if (lc->lcLocks & CXL_INASPECT) g_print (" CXL_INASPECT");
+  if (lc->lcLocks & CXL_SENSITIVITY) g_print (" CXL_SENSITIVITY");
+  if (lc->lcLocks & CXL_MARGIN) g_print (" CXL_MARGIN");
+  g_print ("\n");
+  g_print ("lcMsgBase = %#x, lcDevice = %#x, lcPktRate = %d\n",
+	  lc->lcMsgBase, lc->lcDevice, lc->lcPktRate);
+  g_print ("lcPktData =");
+  if (lc->lcPktData & PK_CONTEXT) g_print (" PK_CONTEXT");
+  if (lc->lcPktData & PK_STATUS) g_print (" PK_STATUS");
+  if (lc->lcPktData & PK_TIME) g_print (" PK_TIME");
+  if (lc->lcPktData & PK_CHANGED) g_print (" PK_CHANGED");
+  if (lc->lcPktData & PK_SERIAL_NUMBER) g_print (" PK_SERIAL_NUMBER");
+  if (lc->lcPktData & PK_CURSOR) g_print (" PK_CURSOR");
+  if (lc->lcPktData & PK_BUTTONS) g_print (" PK_BUTTONS");
+  if (lc->lcPktData & PK_X) g_print (" PK_X");
+  if (lc->lcPktData & PK_Y) g_print (" PK_Y");
+  if (lc->lcPktData & PK_Z) g_print (" PK_Z");
+  if (lc->lcPktData & PK_NORMAL_PRESSURE) g_print (" PK_NORMAL_PRESSURE");
+  if (lc->lcPktData & PK_TANGENT_PRESSURE) g_print (" PK_TANGENT_PRESSURE");
+  if (lc->lcPktData & PK_ORIENTATION) g_print (" PK_ORIENTATION");
+  if (lc->lcPktData & PK_ROTATION) g_print (" PK_ROTATION");
+  g_print ("\n");
+  g_print ("lcPktMode =");
+  if (lc->lcPktMode & PK_CONTEXT) g_print (" PK_CONTEXT");
+  if (lc->lcPktMode & PK_STATUS) g_print (" PK_STATUS");
+  if (lc->lcPktMode & PK_TIME) g_print (" PK_TIME");
+  if (lc->lcPktMode & PK_CHANGED) g_print (" PK_CHANGED");
+  if (lc->lcPktMode & PK_SERIAL_NUMBER) g_print (" PK_SERIAL_NUMBER");
+  if (lc->lcPktMode & PK_CURSOR) g_print (" PK_CURSOR");
+  if (lc->lcPktMode & PK_BUTTONS) g_print (" PK_BUTTONS");
+  if (lc->lcPktMode & PK_X) g_print (" PK_X");
+  if (lc->lcPktMode & PK_Y) g_print (" PK_Y");
+  if (lc->lcPktMode & PK_Z) g_print (" PK_Z");
+  if (lc->lcPktMode & PK_NORMAL_PRESSURE) g_print (" PK_NORMAL_PRESSURE");
+  if (lc->lcPktMode & PK_TANGENT_PRESSURE) g_print (" PK_TANGENT_PRESSURE");
+  if (lc->lcPktMode & PK_ORIENTATION) g_print (" PK_ORIENTATION");
+  if (lc->lcPktMode & PK_ROTATION) g_print (" PK_ROTATION");
+  g_print ("\n");
+  g_print ("lcMoveMask =");
+  if (lc->lcMoveMask & PK_CONTEXT) g_print (" PK_CONTEXT");
+  if (lc->lcMoveMask & PK_STATUS) g_print (" PK_STATUS");
+  if (lc->lcMoveMask & PK_TIME) g_print (" PK_TIME");
+  if (lc->lcMoveMask & PK_CHANGED) g_print (" PK_CHANGED");
+  if (lc->lcMoveMask & PK_SERIAL_NUMBER) g_print (" PK_SERIAL_NUMBER");
+  if (lc->lcMoveMask & PK_CURSOR) g_print (" PK_CURSOR");
+  if (lc->lcMoveMask & PK_BUTTONS) g_print (" PK_BUTTONS");
+  if (lc->lcMoveMask & PK_X) g_print (" PK_X");
+  if (lc->lcMoveMask & PK_Y) g_print (" PK_Y");
+  if (lc->lcMoveMask & PK_Z) g_print (" PK_Z");
+  if (lc->lcMoveMask & PK_NORMAL_PRESSURE) g_print (" PK_NORMAL_PRESSURE");
+  if (lc->lcMoveMask & PK_TANGENT_PRESSURE) g_print (" PK_TANGENT_PRESSURE");
+  if (lc->lcMoveMask & PK_ORIENTATION) g_print (" PK_ORIENTATION");
+  if (lc->lcMoveMask & PK_ROTATION) g_print (" PK_ROTATION");
+  g_print ("\n");
+  g_print ("lcBtnDnMask = %#x, lcBtnUpMask = %#x\n",
+	  (guint) lc->lcBtnDnMask, (guint) lc->lcBtnUpMask);
+  g_print ("lcInOrgX = %ld, lcInOrgY = %ld, lcInOrgZ = %ld\n",
+	  lc->lcInOrgX, lc->lcInOrgY, lc->lcInOrgZ);
+  g_print ("lcInExtX = %ld, lcInExtY = %ld, lcInExtZ = %ld\n",
+	  lc->lcInExtX, lc->lcInExtY, lc->lcInExtZ);
+  g_print ("lcOutOrgX = %ld, lcOutOrgY = %ld, lcOutOrgZ = %ld\n",
+	  lc->lcOutOrgX, lc->lcOutOrgY, lc->lcOutOrgZ);
+  g_print ("lcOutExtX = %ld, lcOutExtY = %ld, lcOutExtZ = %ld\n",
+	  lc->lcOutExtX, lc->lcOutExtY, lc->lcOutExtZ);
+  g_print ("lcSensX = %g, lcSensY = %g, lcSensZ = %g\n",
+	  lc->lcSensX / 65536., lc->lcSensY / 65536., lc->lcSensZ / 65536.);
+  g_print ("lcSysMode = %d\n", lc->lcSysMode);
+  g_print ("lcSysOrgX = %d, lcSysOrgY = %d\n",
+	  lc->lcSysOrgX, lc->lcSysOrgY);
+  g_print ("lcSysExtX = %d, lcSysExtY = %d\n",
+	  lc->lcSysExtX, lc->lcSysExtY);
+  g_print ("lcSysSensX = %g, lcSysSensY = %g\n",
+	  lc->lcSysSensX / 65536., lc->lcSysSensY / 65536.);
+}
+
+static void
+print_cursor (int index)
+{
+  int size;
+  int i;
+  char *name;
+  BOOL active;
+  WTPKT wtpkt;
+  BYTE buttons;
+  BYTE buttonbits;
+  char *btnnames;
+  char *p;
+  BYTE buttonmap[32];
+  BYTE sysbtnmap[32];
+  BYTE npbutton;
+  UINT npbtnmarks[2];
+  UINT *npresponse;
+  BYTE tpbutton;
+  UINT tpbtnmarks[2];
+  UINT *tpresponse;
+  DWORD physid;
+  UINT mode;
+  UINT minpktdata;
+  UINT minbuttons;
+  UINT capabilities;
+
+  size = (*p_WTInfoA) (WTI_CURSORS + index, CSR_NAME, NULL);
+  name = g_malloc (size + 1);
+  (*p_WTInfoA) (WTI_CURSORS + index, CSR_NAME, name);
+  g_print ("NAME: %s\n", name);
+  (*p_WTInfoA) (WTI_CURSORS + index, CSR_ACTIVE, &active);
+  g_print ("ACTIVE: %s\n", active ? "YES" : "NO");
+  (*p_WTInfoA) (WTI_CURSORS + index, CSR_PKTDATA, &wtpkt);
+  g_print ("PKTDATA: %#x:", (guint) wtpkt);
+#define BIT(x) if (wtpkt & PK_##x) g_print (" " #x)
+  BIT (CONTEXT);
+  BIT (STATUS);
+  BIT (TIME);
+  BIT (CHANGED);
+  BIT (SERIAL_NUMBER);
+  BIT (BUTTONS);
+  BIT (X);
+  BIT (Y);
+  BIT (Z);
+  BIT (NORMAL_PRESSURE);
+  BIT (TANGENT_PRESSURE);
+  BIT (ORIENTATION);
+  BIT (ROTATION);
+#undef BIT
+  g_print ("\n");
+  (*p_WTInfoA) (WTI_CURSORS + index, CSR_BUTTONS, &buttons);
+  g_print ("BUTTONS: %d\n", buttons);
+  (*p_WTInfoA) (WTI_CURSORS + index, CSR_BUTTONBITS, &buttonbits);
+  g_print ("BUTTONBITS: %d\n", buttonbits);
+  size = (*p_WTInfoA) (WTI_CURSORS + index, CSR_BTNNAMES, NULL);
+  g_print ("BTNNAMES:");
+  if (size > 0)
+    {
+      btnnames = g_malloc (size + 1);
+      (*p_WTInfoA) (WTI_CURSORS + index, CSR_BTNNAMES, btnnames);
+      p = btnnames;
+      while (*p)
+        {
+          g_print (" %s", p);
+          p += strlen (p) + 1;
+        }
+    }
+  g_print ("\n");
+  (*p_WTInfoA) (WTI_CURSORS + index, CSR_BUTTONMAP, buttonmap);
+  g_print ("BUTTONMAP:");
+  for (i = 0; i < buttons; i++)
+    g_print (" %d", buttonmap[i]);
+  g_print ("\n");
+  (*p_WTInfoA) (WTI_CURSORS + index, CSR_SYSBTNMAP, sysbtnmap);
+  g_print ("SYSBTNMAP:");
+  for (i = 0; i < buttons; i++)
+    g_print (" %d", sysbtnmap[i]);
+  g_print ("\n");
+  (*p_WTInfoA) (WTI_CURSORS + index, CSR_NPBUTTON, &npbutton);
+  g_print ("NPBUTTON: %d\n", npbutton);
+  (*p_WTInfoA) (WTI_CURSORS + index, CSR_NPBTNMARKS, npbtnmarks);
+  g_print ("NPBTNMARKS: %d %d\n", npbtnmarks[0], npbtnmarks[1]);
+  size = (*p_WTInfoA) (WTI_CURSORS + index, CSR_NPRESPONSE, NULL);
+  g_print ("NPRESPONSE:");
+  if (size > 0)
+    {
+      npresponse = g_malloc (size);
+      (*p_WTInfoA) (WTI_CURSORS + index, CSR_NPRESPONSE, npresponse);
+      for (i = 0; i < size / sizeof (UINT); i++)
+        g_print (" %d", npresponse[i]);
+    }
+  g_print ("\n");
+  (*p_WTInfoA) (WTI_CURSORS + index, CSR_TPBUTTON, &tpbutton);
+  g_print ("TPBUTTON: %d\n", tpbutton);
+  (*p_WTInfoA) (WTI_CURSORS + index, CSR_TPBTNMARKS, tpbtnmarks);
+  g_print ("TPBTNMARKS: %d %d\n", tpbtnmarks[0], tpbtnmarks[1]);
+  size = (*p_WTInfoA) (WTI_CURSORS + index, CSR_TPRESPONSE, NULL);
+  g_print ("TPRESPONSE:");
+  if (size > 0)
+    {
+      tpresponse = g_malloc (size);
+      (*p_WTInfoA) (WTI_CURSORS + index, CSR_TPRESPONSE, tpresponse);
+      for (i = 0; i < size / sizeof (UINT); i++)
+        g_print (" %d", tpresponse[i]);
+    }
+  g_print ("\n");
+  (*p_WTInfoA) (WTI_CURSORS + index, CSR_PHYSID, &physid);
+  g_print ("PHYSID: %#x\n", (guint) physid);
+  (*p_WTInfoA) (WTI_CURSORS + index, CSR_CAPABILITIES, &capabilities);
+  g_print ("CAPABILITIES: %#x:", capabilities);
+#define BIT(x) if (capabilities & CRC_##x) g_print (" " #x)
+  BIT (MULTIMODE);
+  BIT (AGGREGATE);
+  BIT (INVERT);
+#undef BIT
+  g_print ("\n");
+  if (capabilities & CRC_MULTIMODE)
+    {
+      (*p_WTInfoA) (WTI_CURSORS + index, CSR_MODE, &mode);
+      g_print ("MODE: %d\n", mode);
+    }
+  if (capabilities & CRC_AGGREGATE)
+    {
+      (*p_WTInfoA) (WTI_CURSORS + index, CSR_MINPKTDATA, &minpktdata);
+      g_print ("MINPKTDATA: %d\n", minpktdata);
+      (*p_WTInfoA) (WTI_CURSORS + index, CSR_MINBUTTONS, &minbuttons);
+      g_print ("MINBUTTONS: %d\n", minbuttons);
+    }
+}
+#endif
+
+static void
+_gdk_input_wintab_init_check (GdkDeviceManagerWin32 *device_manager)
+{
+  static gboolean wintab_initialized = FALSE;
+  GdkDeviceWintab *device;
+  GdkWindowAttr wa;
+  WORD specversion;
+  HCTX *hctx;
+  UINT ndevices, ncursors, ncsrtypes, firstcsr, hardware;
+  BOOL active;
+  DWORD physid;
+  AXIS axis_x, axis_y, axis_npressure, axis_or[3];
+  int i, devix, cursorix, num_axes = 0;
+  wchar_t devname[100], csrname[100];
+  gchar *devname_utf8, *csrname_utf8, *device_name;
+  BOOL defcontext_done;
+  HMODULE wintab32;
+
+  if (wintab_initialized)
+    return;
+
+  wintab_initialized = TRUE;
+
+  wintab_contexts = NULL;
+
+  if (_gdk_input_ignore_wintab)
+    return;
+
+  if ((wintab32 = LoadLibrary ("wintab32.dll")) == NULL)
+    return;
+
+  if ((p_WTInfoA = (t_WTInfoA) GetProcAddress (wintab32, "WTInfoA")) == NULL)
+    return;
+  if ((p_WTInfoW = (t_WTInfoW) GetProcAddress (wintab32, "WTInfoW")) == NULL)
+    return;
+  if ((p_WTEnable = (t_WTEnable) GetProcAddress (wintab32, "WTEnable")) == NULL)
+    return;
+  if ((p_WTOpenA = (t_WTOpenA) GetProcAddress (wintab32, "WTOpenA")) == NULL)
+    return;
+  if ((p_WTOverlap = (t_WTOverlap) GetProcAddress (wintab32, "WTOverlap")) == NULL)
+    return;
+  if ((p_WTPacket = (t_WTPacket) GetProcAddress (wintab32, "WTPacket")) == NULL)
+    return;
+  if ((p_WTQueueSizeSet = (t_WTQueueSizeSet) GetProcAddress (wintab32, "WTQueueSizeSet")) == NULL)
+    return;
+
+  if (!(*p_WTInfoA) (0, 0, NULL))
+    return;
+
+  (*p_WTInfoA) (WTI_INTERFACE, IFC_SPECVERSION, &specversion);
+  GDK_NOTE (INPUT, g_print ("Wintab interface version %d.%d\n",
+			    HIBYTE (specversion), LOBYTE (specversion)));
+  (*p_WTInfoA) (WTI_INTERFACE, IFC_NDEVICES, &ndevices);
+  (*p_WTInfoA) (WTI_INTERFACE, IFC_NCURSORS, &ncursors);
+#if DEBUG_WINTAB
+  GDK_NOTE (INPUT, g_print ("NDEVICES: %d, NCURSORS: %d\n",
+			    ndevices, ncursors));
+#endif
+  /* Create a dummy window to receive wintab events */
+  wa.wclass = GDK_INPUT_OUTPUT;
+  wa.event_mask = GDK_ALL_EVENTS_MASK;
+  wa.width = 2;
+  wa.height = 2;
+  wa.x = -100;
+  wa.y = -100;
+  wa.window_type = GDK_WINDOW_TOPLEVEL;
+  if ((wintab_window = gdk_window_new (NULL, &wa, GDK_WA_X|GDK_WA_Y)) == NULL)
+    {
+      g_warning ("gdk_input_wintab_init: gdk_window_new failed");
+      return;
+    }
+  g_object_ref (wintab_window);
+
+  for (devix = 0; devix < ndevices; devix++)
+    {
+      LOGCONTEXT lc;
+
+      /* We open the Wintab device (hmm, what if there are several, or
+       * can there even be several, probably not?) as a system
+       * pointing device, i.e. it controls the normal Windows
+       * cursor. This seems much more natural.
+       */
+
+      (*p_WTInfoW) (WTI_DEVICES + devix, DVC_NAME, devname);
+      devname_utf8 = g_utf16_to_utf8 (devname, -1, NULL, NULL, NULL);
+#ifdef DEBUG_WINTAB
+      GDK_NOTE (INPUT, (g_print("Device %d: %s\n", devix, devname_utf8)));
+#endif
+      (*p_WTInfoA) (WTI_DEVICES + devix, DVC_NCSRTYPES, &ncsrtypes);
+      (*p_WTInfoA) (WTI_DEVICES + devix, DVC_FIRSTCSR, &firstcsr);
+      (*p_WTInfoA) (WTI_DEVICES + devix, DVC_HARDWARE, &hardware);
+      (*p_WTInfoA) (WTI_DEVICES + devix, DVC_X, &axis_x);
+      (*p_WTInfoA) (WTI_DEVICES + devix, DVC_Y, &axis_y);
+      (*p_WTInfoA) (WTI_DEVICES + devix, DVC_NPRESSURE, &axis_npressure);
+      (*p_WTInfoA) (WTI_DEVICES + devix, DVC_ORIENTATION, axis_or);
+
+      defcontext_done = FALSE;
+      if (HIBYTE (specversion) > 1 || LOBYTE (specversion) >= 1)
+        {
+          /* Try to get device-specific default context */
+          /* Some drivers, e.g. Aiptek, don't provide this info */
+          if ((*p_WTInfoA) (WTI_DSCTXS + devix, 0, &lc) > 0)
+            defcontext_done = TRUE;
+#if DEBUG_WINTAB
+          if (defcontext_done)
+            GDK_NOTE (INPUT, (g_print("Using device-specific default context\n")));
+          else
+            GDK_NOTE (INPUT, (g_print("Note: Driver did not provide device specific default context info despite claiming to support version 1.1\n")));
+#endif
+        }
+
+      if (!defcontext_done)
+        (*p_WTInfoA) (WTI_DEFSYSCTX, 0, &lc);
+#if DEBUG_WINTAB
+      GDK_NOTE (INPUT, (g_print("Default context:\n"), print_lc(&lc)));
+#endif
+      lc.lcOptions |= CXO_MESSAGES;
+      lc.lcStatus = 0;
+      lc.lcMsgBase = WT_DEFBASE;
+      lc.lcPktRate = 0;
+      lc.lcPktData = PACKETDATA;
+      lc.lcPktMode = PACKETMODE;
+      lc.lcMoveMask = PACKETDATA;
+      lc.lcBtnUpMask = lc.lcBtnDnMask = ~0;
+      lc.lcOutOrgX = axis_x.axMin;
+      lc.lcOutOrgY = axis_y.axMin;
+      lc.lcOutExtX = axis_x.axMax - axis_x.axMin;
+      lc.lcOutExtY = axis_y.axMax - axis_y.axMin;
+      lc.lcOutExtY = -lc.lcOutExtY; /* We want Y growing downward */
+#if DEBUG_WINTAB
+      GDK_NOTE (INPUT, (g_print("context for device %d:\n", devix),
+			print_lc(&lc)));
+#endif
+      hctx = g_new (HCTX, 1);
+      if ((*hctx = (*p_WTOpenA) (GDK_WINDOW_HWND (wintab_window), &lc, TRUE)) == NULL)
+        {
+          g_warning ("gdk_input_wintab_init: WTOpen failed");
+          return;
+        }
+      GDK_NOTE (INPUT, g_print ("opened Wintab device %d %p\n",
+                                devix, *hctx));
+
+      wintab_contexts = g_list_append (wintab_contexts, hctx);
+#if 0
+      (*p_WTEnable) (*hctx, TRUE);
+#endif
+      (*p_WTOverlap) (*hctx, TRUE);
+
+#if DEBUG_WINTAB
+      GDK_NOTE (INPUT, (g_print("context for device %d after WTOpen:\n", devix),
+			print_lc(&lc)));
+#endif
+      /* Increase packet queue size to reduce the risk of lost packets.
+       * According to the specs, if the function fails we must try again
+       * with a smaller queue size.
+       */
+      GDK_NOTE (INPUT, g_print("Attempting to increase queue size\n"));
+      for (i = 32; i >= 1; i >>= 1)
+        {
+          if ((*p_WTQueueSizeSet) (*hctx, i))
+            {
+              GDK_NOTE (INPUT, g_print("Queue size set to %d\n", i));
+              break;
+            }
+        }
+      if (!i)
+        GDK_NOTE (INPUT, g_print("Whoops, no queue size could be set\n"));
+      for (cursorix = firstcsr; cursorix < firstcsr + ncsrtypes; cursorix++)
+        {
+#ifdef DEBUG_WINTAB
+          GDK_NOTE (INPUT, (g_print("Cursor %d:\n", cursorix), print_cursor (cursorix)));
+#endif
+          active = FALSE;
+          (*p_WTInfoA) (WTI_CURSORS + cursorix, CSR_ACTIVE, &active);
+          if (!active)
+            continue;
+
+          /* Wacom tablets seem to report cursors corresponding to
+           * nonexistent pens or pucks. At least my ArtPad II reports
+           * six cursors: a puck, pressure stylus and eraser stylus,
+           * and then the same three again. I only have a
+           * pressure-sensitive pen. The puck instances, and the
+           * second instances of the styluses report physid zero. So
+           * at least for Wacom, skip cursors with physid zero.
+           */
+          (*p_WTInfoA) (WTI_CURSORS + cursorix, CSR_PHYSID, &physid);
+          if (wcscmp (devname, L"WACOM Tablet") == 0 && physid == 0)
+            continue;
+
+          (*p_WTInfoW) (WTI_CURSORS + cursorix, CSR_NAME, csrname);
+          csrname_utf8 = g_utf16_to_utf8 (csrname, -1, NULL, NULL, NULL);
+          device_name = g_strconcat (devname_utf8, " ", csrname_utf8, NULL);
+
+          device = g_object_new (GDK_TYPE_DEVICE_WINTAB,
+                                 "name", device_name,
+                                 "type", GDK_DEVICE_TYPE_SLAVE,
+                                 "source", GDK_SOURCE_PEN,
+                                 "mode", GDK_MODE_SCREEN,
+                                 "has-cursor", FALSE,
+                                 "display", _gdk_display,
+                                 "device-manager", device_manager,
+                                 NULL);
+
+          g_free (csrname_utf8);
+
+          device->hctx = *hctx;
+          device->cursor = cursorix;
+          (*p_WTInfoA) (WTI_CURSORS + cursorix, CSR_PKTDATA, &device->pktdata);
+
+          if (device->pktdata & PK_X)
+            {
+              _gdk_device_add_axis (GDK_DEVICE (device),
+                                    GDK_NONE,
+                                    GDK_AXIS_X,
+                                    axis_x.axMin,
+                                    axis_x.axMax,
+                                    axis_x.axResolution / 65535);
+              num_axes++;
+            }
+
+          if (device->pktdata & PK_Y)
+            {
+              _gdk_device_add_axis (GDK_DEVICE (device),
+                                    GDK_NONE,
+                                    GDK_AXIS_Y,
+                                    axis_y.axMin,
+                                    axis_y.axMax,
+                                    axis_y.axResolution / 65535);
+              num_axes++;
+            }
+
+
+          if (device->pktdata & PK_NORMAL_PRESSURE)
+            {
+              _gdk_device_add_axis (GDK_DEVICE (device),
+                                    GDK_NONE,
+                                    GDK_AXIS_PRESSURE,
+                                    axis_npressure.axMin,
+                                    axis_npressure.axMax,
+                                    axis_npressure.axResolution / 65535);
+              num_axes++;
+            }
+
+          /* The wintab driver for the Wacom ArtPad II reports
+           * PK_ORIENTATION in CSR_PKTDATA, but the tablet doesn't
+           * actually sense tilt. Catch this by noticing that the
+           * orientation axis's azimuth resolution is zero.
+           */
+          if ((device->pktdata & PK_ORIENTATION) && axis_or[0].axResolution == 0)
+            {
+              device->orientation_axes[0] = axis_or[0];
+              device->orientation_axes[1] = axis_or[1];
+
+              /* Wintab gives us aximuth and altitude, which
+               * we convert to x and y tilt in the -1000..1000 range
+               */
+              _gdk_device_add_axis (GDK_DEVICE (device),
+                                    GDK_NONE,
+                                    GDK_AXIS_XTILT,
+                                    -1000,
+                                    1000,
+                                    1000);
+
+              _gdk_device_add_axis (GDK_DEVICE (device),
+                                    GDK_NONE,
+                                    GDK_AXIS_YTILT,
+                                    -1000,
+                                    1000,
+                                    1000);
+              num_axes += 2;
+            }
+
+          device->last_axis_data = g_new (gint, num_axes);
+
+          GDK_NOTE (INPUT, g_print ("device: (%d) %s axes: %d\n",
+                                    cursorix,
+                                    device_name,
+                                    num_axes));
+
+#if 0
+          for (i = 0; i < gdkdev->info.num_axes; i++)
+            GDK_NOTE (INPUT, g_print ("... axis %d: %d--%d %d\n",
+                                      i,
+                                      gdkdev->axes[i].min_value,
+                                      gdkdev->axes[i].max_value,
+                                      gdkdev->axes[i].resolution));
+#endif
+
+          device_manager->wintab_devices = g_list_append (device_manager->wintab_devices,
+                                                          device);
+
+          g_free (device_name);
+        }
+
+      g_free (devname_utf8);
+    }
+}
+
+static void
+gdk_device_manager_win32_constructed (GObject *object)
+{
+  GdkDeviceManagerWin32 *device_manager;
+
+  device_manager = GDK_DEVICE_MANAGER_WIN32 (object);
+  device_manager->core_pointer = create_core_pointer (GDK_DEVICE_MANAGER (device_manager));
+  device_manager->core_keyboard = create_core_keyboard (GDK_DEVICE_MANAGER (device_manager));
+
+  _gdk_device_set_associated_device (device_manager->core_pointer, device_manager->core_keyboard);
+  _gdk_device_set_associated_device (device_manager->core_keyboard, device_manager->core_pointer);
+
+  _gdk_input_wintab_init_check (device_manager);
+}
+
+static GList *
+gdk_device_manager_win32_list_devices (GdkDeviceManager *device_manager,
+                                       GdkDeviceType     type)
+{
+  GdkDeviceManagerWin32 *device_manager_win32;
+  GList *devices = NULL;
+
+  device_manager_win32 = (GdkDeviceManagerWin32 *) device_manager;
+
+  if (type == GDK_DEVICE_TYPE_MASTER)
+    {
+      devices = g_list_prepend (devices, device_manager_win32->core_keyboard);
+      devices = g_list_prepend (devices, device_manager_win32->core_pointer);
+    }
+  else if (type == GDK_DEVICE_TYPE_FLOATING)
+    devices = g_list_copy (device_manager_win32->wintab_devices);
+
+  return devices;
+}
+
+void
+_gdk_input_set_tablet_active (void)
+{
+  GList *tmp_list;
+  HCTX *hctx;
+
+  /* Bring the contexts to the top of the overlap order when one of the
+   * application's windows is activated */
+
+  if (!wintab_contexts)
+    return; /* No tablet devices found, or Wintab not initialized yet */
+
+  GDK_NOTE (INPUT, g_print ("_gdk_input_set_tablet_active: "
+                            "Bringing Wintab contexts to the top of the overlap order\n"));
+
+  tmp_list = wintab_contexts;
+
+  while (tmp_list)
+    {
+      hctx = (HCTX *) (tmp_list->data);
+      (*p_WTOverlap) (*hctx, TRUE);
+      tmp_list = tmp_list->next;
+    }
+}
+
+static void
+decode_tilt (gint   *axis_data,
+             AXIS   *axes,
+             PACKET *packet)
+{
+  double az, el;
+
+  /* As I don't have a tilt-sensing tablet,
+   * I cannot test this code.
+   */
+  az = TWOPI * packet->pkOrientation.orAzimuth /
+    (axes[0].axResolution / 65536.);
+  el = TWOPI * packet->pkOrientation.orAltitude /
+    (axes[1].axResolution / 65536.);
+
+  /* X tilt */
+  axis_data[0] = cos (az) * cos (el) * 1000;
+  /* Y tilt */
+  axis_data[1] = sin (az) * cos (el) * 1000;
+}
+
+/*
+ * Get the currently active keyboard modifiers (ignoring the mouse buttons)
+ * We could use gdk_window_get_pointer but that function does a lot of other
+ * expensive things besides getting the modifiers. This code is somewhat based
+ * on build_pointer_event_state from gdkevents-win32.c
+ */
+static guint
+get_modifier_key_state (void)
+{
+  guint state;
+
+  state = 0;
+  /* High-order bit is up/down, low order bit is toggled/untoggled */
+  if (GetKeyState (VK_CONTROL) < 0)
+    state |= GDK_CONTROL_MASK;
+  if (GetKeyState (VK_SHIFT) < 0)
+    state |= GDK_SHIFT_MASK;
+  if (GetKeyState (VK_MENU) < 0)
+    state |= GDK_MOD1_MASK;
+  if (GetKeyState (VK_CAPITAL) & 0x1)
+    state |= GDK_LOCK_MASK;
+
+  return state;
+}
+
+static gboolean
+ignore_core_timefunc (gpointer data)
+{
+  /* The delay has passed */
+  _gdk_input_ignore_core = FALSE;
+  ignore_core_timer = 0;
+
+  return FALSE; /* remove timeout */
+}
+
+/*
+ * Set or unset the _gdk_input_ignore_core variable that tells GDK
+ * to ignore events for the core pointer when the tablet is in proximity
+ * The unsetting is delayed slightly so that if a tablet event arrives
+ * just after proximity out, it does not cause a core pointer event
+ * which e.g. causes GIMP to switch tools.
+ */
+static void
+set_ignore_core (gboolean ignore)
+{
+  if (ignore)
+    {
+      _gdk_input_ignore_core = TRUE;
+      /* Remove any pending clear */
+      if (ignore_core_timer)
+        {
+          g_source_remove (ignore_core_timer);
+          ignore_core_timer = 0;
+        }
+    }
+  else if (!ignore_core_timer)
+    ignore_core_timer = gdk_threads_add_timeout (PROXIMITY_OUT_DELAY,
+                                                 ignore_core_timefunc, NULL);
+}
+
+static GdkDeviceWintab *
+_gdk_device_manager_find_wintab_device (HCTX hctx,
+                                        UINT cursor)
+{
+  GdkDeviceManagerWin32 *device_manager;
+  GdkDeviceWintab *device;
+  GList *tmp_list;
+
+  device_manager = GDK_DEVICE_MANAGER_WIN32 (gdk_display_get_device_manager (_gdk_display));
+  tmp_list = device_manager->wintab_devices;
+
+  while (tmp_list)
+    {
+      device = tmp_list->data;
+      tmp_list = tmp_list->next;
+
+      if (device->hctx == hctx &&
+          device->cursor == cursor)
+        return device;
+    }
+
+  return NULL;
+}
+
+gboolean
+_gdk_input_other_event (GdkEvent  *event,
+                        MSG       *msg,
+                        GdkWindow *window)
+{
+  GdkDisplay *display;
+  GdkWindowObject *obj;
+  GdkDeviceWintab *device = NULL;
+  GdkDeviceGrabInfo *last_grab;
+  GdkEventMask masktest;
+  guint key_state;
+  POINT pt;
+
+  PACKET packet;
+  gdouble root_x, root_y;
+  gint num_axes;
+  gint x, y;
+  guint translated_buttons, button_diff, button_mask;
+  /* Translation from tablet button state to GDK button state for
+   * buttons 1-3 - swap button 2 and 3.
+   */
+  static guint button_map[8] = {0, 1, 4, 5, 2, 3, 6, 7};
+
+  if (event->any.window != wintab_window)
+    {
+      g_warning ("_gdk_input_other_event: not wintab_window?");
+      return FALSE;
+    }
+
+  window = gdk_window_at_pointer (&x, &y);
+  if (window == NULL)
+    window = _gdk_root;
+
+  g_object_ref (window);
+  display = gdk_drawable_get_display (window);
+
+  GDK_NOTE (EVENTS_OR_INPUT,
+	    g_print ("_gdk_input_other_event: window=%p %+d%+d\n",
+               GDK_WINDOW_HWND (window), x, y));
+
+  if (msg->message == WT_PACKET)
+    {
+      if (!(*p_WTPacket) ((HCTX) msg->lParam, msg->wParam, &packet))
+        return FALSE;
+    }
+
+  obj = GDK_WINDOW_OBJECT (window);
+
+  switch (msg->message)
+    {
+    case WT_PACKET:
+      /* Don't produce any button or motion events while a window is being
+       * moved or resized, see bug #151090.
+       */
+      if (_modal_operation_in_progress)
+        {
+          GDK_NOTE (EVENTS_OR_INPUT, g_print ("... ignored when moving/sizing\n"));
+          return FALSE;
+        }
+
+      if ((device = _gdk_device_manager_find_wintab_device ((HCTX) msg->lParam,
+                                                            packet.pkCursor)) == NULL)
+        return FALSE;
+
+      if (gdk_device_get_mode (GDK_DEVICE (device)) == GDK_MODE_DISABLED)
+        return FALSE;
+
+      last_grab = _gdk_display_get_last_device_grab (_gdk_display, GDK_DEVICE (device));
+
+      if (last_grab && last_grab->window)
+        {
+          g_object_unref (window);
+
+          window = g_object_ref (last_grab->window);
+          obj = GDK_WINDOW_OBJECT (window);
+        }
+
+      if (window == _gdk_root)
+        {
+          GDK_NOTE (EVENTS_OR_INPUT, g_print ("... is root\n"));
+          return FALSE;
+        }
+
+      num_axes = 0;
+      if (device->pktdata & PK_X)
+        device->last_axis_data[num_axes++] = packet.pkX;
+      if (device->pktdata & PK_Y)
+        device->last_axis_data[num_axes++] = packet.pkY;
+      if (device->pktdata & PK_NORMAL_PRESSURE)
+        device->last_axis_data[num_axes++] = packet.pkNormalPressure;
+      if (device->pktdata & PK_ORIENTATION)
+        {
+          decode_tilt (device->last_axis_data + num_axes,
+                       device->orientation_axes, &packet);
+          num_axes += 2;
+        }
+
+      translated_buttons = button_map[packet.pkButtons & 0x07] | (packet.pkButtons & ~0x07);
+
+      if (translated_buttons != device->button_state)
+        {
+          /* At least one button has changed state so produce a button event
+           * If more than one button has changed state (unlikely),
+           * just care about the first and act on the next the next time
+           * we get a packet
+           */
+          button_diff = translated_buttons ^ device->button_state;
+
+          /* Gdk buttons are numbered 1.. */
+          event->button.button = 1;
+
+          for (button_mask = 1; button_mask != 0x80000000;
+               button_mask <<= 1, event->button.button++)
+            {
+              if (button_diff & button_mask)
+                {
+                  /* Found a button that has changed state */
+                  break;
+                }
+            }
+
+          if (!(translated_buttons & button_mask))
+            {
+              event->any.type = GDK_BUTTON_RELEASE;
+              masktest = GDK_BUTTON_RELEASE_MASK;
+            }
+          else
+            {
+              event->any.type = GDK_BUTTON_PRESS;
+              masktest = GDK_BUTTON_PRESS_MASK;
+            }
+          device->button_state ^= button_mask;
+        }
+      else
+        {
+          event->any.type = GDK_MOTION_NOTIFY;
+          masktest = GDK_POINTER_MOTION_MASK;
+          if (device->button_state & (1 << 0))
+            masktest |= GDK_BUTTON_MOTION_MASK | GDK_BUTTON1_MOTION_MASK;
+          if (device->button_state & (1 << 1))
+            masktest |= GDK_BUTTON_MOTION_MASK | GDK_BUTTON2_MOTION_MASK;
+          if (device->button_state & (1 << 2))
+            masktest |= GDK_BUTTON_MOTION_MASK | GDK_BUTTON3_MOTION_MASK;
+        }
+
+      /* Now we can check if the window wants the event, and
+       * propagate if necessary.
+       */
+      while (gdk_window_get_device_events (window, GDK_DEVICE (device)) == 0)
+        {
+          GDK_NOTE (EVENTS_OR_INPUT, g_print ("... not selected\n"));
+
+          if (obj->parent == GDK_WINDOW_OBJECT (_gdk_root))
+            return FALSE;
+
+          /* It is not good to propagate the extended events up to the parent
+           * if this window wants normal (not extended) motion/button events */
+          if (obj->event_mask & masktest)
+            {
+              GDK_NOTE (EVENTS_OR_INPUT,
+                        g_print ("... wants ordinary event, ignoring this\n"));
+              return FALSE;
+            }
+
+          pt.x = x;
+          pt.y = y;
+          ClientToScreen (GDK_WINDOW_HWND (window), &pt);
+          g_object_unref (window);
+          window = (GdkWindow *) obj->parent;
+          obj = GDK_WINDOW_OBJECT (window);
+          g_object_ref (window);
+          ScreenToClient (GDK_WINDOW_HWND (window), &pt);
+          x = pt.x;
+          y = pt.y;
+          GDK_NOTE (EVENTS_OR_INPUT, g_print ("... propagating to %p %+d%+d\n",
+                                              GDK_WINDOW_HWND (window), x, y));
+        }
+
+      if (gdk_window_get_device_events (window, GDK_DEVICE (device)) == 0)
+        return FALSE;
+
+      event->any.window = window;
+      key_state = get_modifier_key_state ();
+      if (event->any.type == GDK_BUTTON_PRESS ||
+          event->any.type == GDK_BUTTON_RELEASE)
+        {
+          event->button.time = _gdk_win32_get_next_tick (msg->time);
+          gdk_event_set_device (event, GDK_DEVICE (device));
+
+          event->button.axes = g_new (gdouble, num_axes);
+          _gdk_device_wintab_get_window_coords (window, &root_x, &root_y);
+
+          _gdk_device_wintab_translate_axes (device,
+                                             window,
+                                             event->button.axes,
+                                             &event->button.x,
+                                             &event->button.y);
+
+          event->button.x_root = event->button.x + root_x;
+          event->button.y_root = event->button.y + root_y;
+
+          event->button.state =
+            key_state | ((device->button_state << 8)
+                         & (GDK_BUTTON1_MASK | GDK_BUTTON2_MASK
+                            | GDK_BUTTON3_MASK | GDK_BUTTON4_MASK
+                            | GDK_BUTTON5_MASK));
+
+          GDK_NOTE (EVENTS_OR_INPUT,
+                    g_print ("WINTAB button %s:%d %g,%g\n",
+                             (event->button.type == GDK_BUTTON_PRESS ?
+                              "press" : "release"),
+                             event->button.button,
+                             event->button.x, event->button.y));
+        }
+      else
+        {
+          event->motion.time = _gdk_win32_get_next_tick (msg->time);
+          event->motion.is_hint = FALSE;
+          gdk_event_set_device (event, GDK_DEVICE (device));
+
+          event->motion.axes = g_new (gdouble, num_axes);
+          _gdk_device_wintab_get_window_coords (window, &root_x, &root_y);
+
+          _gdk_device_wintab_translate_axes (device,
+                                             window,
+                                             event->motion.axes,
+                                             &event->motion.x,
+                                             &event->motion.y);
+
+          event->motion.x_root = event->motion.x + root_x;
+          event->motion.y_root = event->motion.y + root_y;
+
+          event->motion.state =
+            key_state | ((device->button_state << 8)
+                         & (GDK_BUTTON1_MASK | GDK_BUTTON2_MASK
+                            | GDK_BUTTON3_MASK | GDK_BUTTON4_MASK
+                            | GDK_BUTTON5_MASK));
+
+          GDK_NOTE (EVENTS_OR_INPUT,
+                    g_print ("WINTAB motion: %g,%g\n",
+                             event->motion.x, event->motion.y));
+        }
+      return TRUE;
+
+    case WT_PROXIMITY:
+      if (LOWORD (msg->lParam) == 0)
+        {
+          event->proximity.type = GDK_PROXIMITY_OUT;
+          set_ignore_core (FALSE);
+        }
+      else
+        {
+          event->proximity.type = GDK_PROXIMITY_IN;
+          set_ignore_core (TRUE);
+        }
+      event->proximity.time = _gdk_win32_get_next_tick (msg->time);
+      gdk_event_set_device (event, GDK_DEVICE (device));
+
+      GDK_NOTE (EVENTS_OR_INPUT,
+                g_print ("WINTAB proximity %s\n",
+                         (event->proximity.type == GDK_PROXIMITY_IN ?
+                          "in" : "out")));
+      return TRUE;
+    }
+
+  return FALSE;
+}
diff --git a/gdk/win32/gdkdevicemanager-win32.h b/gdk/win32/gdkdevicemanager-win32.h
new file mode 100644
index 0000000..69d26b7
--- /dev/null
+++ b/gdk/win32/gdkdevicemanager-win32.h
@@ -0,0 +1,59 @@
+/* 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_WIN32_H__
+#define __GDK_DEVICE_MANAGER_WIN32_H__
+
+#include <gdk/gdkdevicemanager.h>
+
+G_BEGIN_DECLS
+
+#define GDK_TYPE_DEVICE_MANAGER_WIN32         (gdk_device_manager_win32_get_type ())
+#define GDK_DEVICE_MANAGER_WIN32(o)           (G_TYPE_CHECK_INSTANCE_CAST ((o), GDK_TYPE_DEVICE_MANAGER_WIN32, GdkDeviceManagerWin32))
+#define GDK_DEVICE_MANAGER_WIN32_CLASS(c)     (G_TYPE_CHECK_CLASS_CAST ((c), GDK_TYPE_DEVICE_MANAGER_WIN32, GdkDeviceManagerWin32Class))
+#define GDK_IS_DEVICE_MANAGER_WIN32(o)        (G_TYPE_CHECK_INSTANCE_TYPE ((o), GDK_TYPE_DEVICE_MANAGER_WIN32))
+#define GDK_IS_DEVICE_MANAGER_WIN32_CLASS(c)  (G_TYPE_CHECK_CLASS_TYPE ((c), GDK_TYPE_DEVICE_MANAGER_WIN32))
+#define GDK_DEVICE_MANAGER_WIN32_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GDK_TYPE_DEVICE_MANAGER_WIN32, GdkDeviceManagerWin32Class))
+
+typedef struct _GdkDeviceManagerWin32 GdkDeviceManagerWin32;
+typedef struct _GdkDeviceManagerWin32Class GdkDeviceManagerWin32Class;
+
+struct _GdkDeviceManagerWin32
+{
+  GdkDeviceManager parent_object;
+  GdkDevice *core_pointer;
+  GdkDevice *core_keyboard;
+  GList *wintab_devices;
+};
+
+struct _GdkDeviceManagerWin32Class
+{
+  GdkDeviceManagerClass parent_class;
+};
+
+GType gdk_device_manager_win32_get_type (void) G_GNUC_CONST;
+
+void     _gdk_input_set_tablet_active (void);
+gboolean _gdk_input_other_event       (GdkEvent  *event,
+                                       MSG       *msg,
+                                       GdkWindow *window);
+
+G_END_DECLS
+
+#endif /* __GDK_DEVICE_MANAGER_WIN32_H__ */
diff --git a/gdk/win32/gdkdnd-win32.c b/gdk/win32/gdkdnd-win32.c
index f0b57a9..6436fb7 100644
--- a/gdk/win32/gdkdnd-win32.c
+++ b/gdk/win32/gdkdnd-win32.c
@@ -98,6 +98,7 @@ typedef enum {
  * this is used on both source and destination sides.
  */
 struct _GdkDragContextPrivateWin32 {
+  GdkDevice *device;
   gboolean being_finalized;
   gint ref_count;
   IUnknown *iface;
@@ -203,6 +204,39 @@ gdk_drag_context_new (void)
   return g_object_new (GDK_TYPE_DRAG_CONTEXT, NULL);
 }
 
+GdkDevice *
+gdk_drag_context_get_device (GdkDragContext *context)
+{
+  GdkDragContextPrivateWin32 *private;
+
+  g_return_val_if_fail (GDK_IS_DRAG_CONTEXT (context), NULL);
+
+  private = PRIVATE_DATA (context);
+
+  return private->device;
+}
+
+void
+gdk_drag_context_set_device (GdkDragContext *context,
+                             GdkDevice      *device)
+{
+  GdkDragContextPrivateWin32 *private;
+
+  g_return_if_fail (GDK_IS_DRAG_CONTEXT (context));
+  g_return_if_fail (GDK_IS_DEVICE (device));
+
+  private = PRIVATE_DATA (context);
+
+  if (private->device)
+    {
+      g_object_unref (private->device);
+      private->device = NULL;
+    }
+
+  if (device)
+    private->device = g_object_ref (device);
+}
+
 static GdkDragContext *
 gdk_drag_context_find (gboolean   is_source,
 		       GdkWindow *source,
@@ -425,24 +459,28 @@ dnd_event_put (GdkEventType    type,
 	       const POINTL    pt,
 	       gboolean        to_dest_window)
 {
-  GdkEvent e;
-  e.type = type;
+  GdkEvent *e;
+
+  e = gdk_event_new (type);
+
   if (to_dest_window)
-    e.dnd.window = context->dest_window;
+    e->dnd.window = context->dest_window;
   else
-    e.dnd.window = context->source_window;
-  e.dnd.send_event = FALSE;
-  e.dnd.context = context;
-  e.dnd.time = GDK_CURRENT_TIME;
-  e.dnd.x_root = pt.x + _gdk_offset_x;
-  e.dnd.y_root = pt.x + _gdk_offset_y;
+    e->dnd.window = context->source_window;
+  e->dnd.send_event = FALSE;
+  e->dnd.context = g_object_ref (context);
+  e->dnd.time = GDK_CURRENT_TIME;
+  e->dnd.x_root = pt.x + _gdk_offset_x;
+  e->dnd.y_root = pt.x + _gdk_offset_y;
 
-  g_object_ref (e.dnd.context);
-  if (e.dnd.window != NULL)
-    g_object_ref (e.dnd.window);
+  if (e->dnd.window != NULL)
+    g_object_ref (e->dnd.window);
 
-  GDK_NOTE (EVENTS, _gdk_win32_print_event (&e));
-  gdk_event_put (&e);
+  gdk_event_set_device (e, gdk_drag_context_get_device (context));
+
+  GDK_NOTE (EVENTS, _gdk_win32_print_event (e));
+  gdk_event_put (e);
+  gdk_event_free (e);
 }
 
 static HRESULT STDMETHODCALLTYPE
@@ -1173,6 +1211,7 @@ target_context_new (GdkWindow *window)
 {
   target_drag_context *result;
   GdkDragContextPrivateWin32 *private;
+  GdkDevice *device;
 
   result = g_new0 (target_drag_context, 1);
 
@@ -1182,6 +1221,9 @@ target_context_new (GdkWindow *window)
   result->context->protocol = GDK_DRAG_PROTO_OLE2;
   result->context->is_source = FALSE;
 
+  device = gdk_display_get_core_pointer (_gdk_display);
+  gdk_drag_context_set_device (result->context, device);
+
   result->context->source_window = NULL;
 
   result->context->dest_window = window;
@@ -1208,6 +1250,7 @@ source_context_new (GdkWindow *window,
 {
   source_drag_context *result;
   GdkDragContextPrivateWin32 *private;
+  GdkDevice *device;
 
   result = g_new0 (source_drag_context, 1);
 
@@ -1217,6 +1260,9 @@ source_context_new (GdkWindow *window,
   result->context->protocol = GDK_DRAG_PROTO_OLE2;
   result->context->is_source = TRUE;
 
+  device = gdk_display_get_core_pointer (_gdk_display);
+  gdk_drag_context_set_device (result->context, device);
+
   result->context->source_window = window;
   g_object_ref (window);
 
@@ -1428,6 +1474,7 @@ gdk_dropfiles_filter (GdkXEvent *xev,
   POINT pt;
   gint nfiles, i;
   gchar *fileName, *linkedFile;
+  GdkDevice *device;
 
   if (msg->message == WM_DROPFILES)
     {
@@ -1437,6 +1484,9 @@ gdk_dropfiles_filter (GdkXEvent *xev,
       context->protocol = GDK_DRAG_PROTO_WIN32_DROPFILES;
       context->is_source = FALSE;
 
+      device = gdk_display_get_core_pointer (_gdk_display);
+      gdk_drag_context_set_device (context, device);
+
       context->source_window = _gdk_root;
       g_object_ref (context->source_window);
 
@@ -1452,6 +1502,7 @@ gdk_dropfiles_filter (GdkXEvent *xev,
 
       event->dnd.type = GDK_DROP_START;
       event->dnd.context = current_dest_drag;
+      gdk_event_set_device (event, gdk_drag_context_get_device (current_dest_drag));
 
       hdrop = (HANDLE) msg->wParam;
       DragQueryPoint (hdrop, &pt);
@@ -1615,7 +1666,7 @@ static void
 local_send_leave (GdkDragContext *context,
 		  guint32         time)
 {
-  GdkEvent tmp_event;
+  GdkEvent *tmp_event;
 
   GDK_NOTE (DND, g_print ("local_send_leave: context=%p current_dest_drag=%p\n",
 			  context,
@@ -1625,17 +1676,20 @@ local_send_leave (GdkDragContext *context,
       (current_dest_drag->protocol == GDK_DRAG_PROTO_LOCAL) &&
       (current_dest_drag->source_window == context->source_window))
     {
-      tmp_event.dnd.type = GDK_DRAG_LEAVE;
-      tmp_event.dnd.window = context->dest_window;
+      tmp_event = gdk_event_new (GDK_DRAG_LEAVE);
+
+      tmp_event->dnd.window = g_object_ref (context->dest_window);
       /* Pass ownership of context to the event */
-      tmp_event.dnd.send_event = FALSE;
-      tmp_event.dnd.context = current_dest_drag;
-      tmp_event.dnd.time = GDK_CURRENT_TIME; /* FIXME? */
+      tmp_event->dnd.send_event = FALSE;
+      tmp_event->dnd.context = g_object_ref (current_dest_drag);
+      tmp_event->dnd.time = GDK_CURRENT_TIME; /* FIXME? */
+      gdk_event_set_device (tmp_event, gdk_drag_context_get_device (context));
 
       current_dest_drag = NULL;
 
-      GDK_NOTE (EVENTS, _gdk_win32_print_event (&tmp_event));
-      gdk_event_put (&tmp_event);
+      GDK_NOTE (EVENTS, _gdk_win32_print_event (tmp_event));
+      gdk_event_put (tmp_event);
+      gdk_event_free (tmp_event);
     }
 }
 
@@ -1643,9 +1697,10 @@ static void
 local_send_enter (GdkDragContext *context,
 		  guint32         time)
 {
-  GdkEvent tmp_event;
+  GdkEvent *tmp_event;
   GdkDragContextPrivateWin32 *private;
   GdkDragContext *new_context;
+  GdkDevice *device;
 
   GDK_NOTE (DND, g_print ("local_send_enter: context=%p current_dest_drag=%p\n",
 			  context,
@@ -1663,6 +1718,9 @@ local_send_enter (GdkDragContext *context,
   new_context->protocol = GDK_DRAG_PROTO_LOCAL;
   new_context->is_source = FALSE;
 
+  device = gdk_display_get_core_pointer (_gdk_display);
+  gdk_drag_context_set_device (new_context, device);
+
   new_context->source_window = context->source_window;
   g_object_ref (new_context->source_window);
 
@@ -1676,16 +1734,18 @@ local_send_enter (GdkDragContext *context,
 			 GDK_PROPERTY_CHANGE_MASK);
   new_context->actions = context->actions;
 
-  tmp_event.type = GDK_DRAG_ENTER;
-  tmp_event.dnd.window = context->dest_window;
-  tmp_event.dnd.send_event = FALSE;
-  tmp_event.dnd.context = new_context;
-  tmp_event.dnd.time = GDK_CURRENT_TIME; /* FIXME? */
+  tmp_event = gdk_event_new (GDK_DRAG_ENTER);
+  tmp_event->dnd.window = g_object_ref (context->dest_window);
+  tmp_event->dnd.send_event = FALSE;
+  tmp_event->dnd.context = g_object_ref (new_context);
+  tmp_event->dnd.time = GDK_CURRENT_TIME; /* FIXME? */
+  gdk_event_set_device (tmp_event, gdk_drag_context_get_device (context));
 
   current_dest_drag = new_context;
 
-  GDK_NOTE (EVENTS, _gdk_win32_print_event (&tmp_event));
-  gdk_event_put (&tmp_event);
+  GDK_NOTE (EVENTS, _gdk_win32_print_event (tmp_event));
+  gdk_event_put (tmp_event);
+  gdk_event_free (tmp_event);
 }
 
 static void
@@ -1695,7 +1755,7 @@ local_send_motion (GdkDragContext *context,
 		   GdkDragAction   action,
 		   guint32         time)
 {
-  GdkEvent tmp_event;
+  GdkEvent *tmp_event;
 
   GDK_NOTE (DND, g_print ("local_send_motion: context=%p (%d,%d) current_dest_drag=%p\n",
 			  context, x_root, y_root,
@@ -1705,24 +1765,26 @@ local_send_motion (GdkDragContext *context,
       (current_dest_drag->protocol == GDK_DRAG_PROTO_LOCAL) &&
       (current_dest_drag->source_window == context->source_window))
     {
-      tmp_event.type = GDK_DRAG_MOTION;
-      tmp_event.dnd.window = current_dest_drag->dest_window;
-      tmp_event.dnd.send_event = FALSE;
-      tmp_event.dnd.context = current_dest_drag;
-      tmp_event.dnd.time = time;
+      tmp_event = gdk_event_new (GDK_DRAG_MOTION);
+      tmp_event->dnd.window = g_object_ref (current_dest_drag->dest_window);
+      tmp_event->dnd.send_event = FALSE;
+      tmp_event->dnd.context = g_object_ref (current_dest_drag);
+      tmp_event->dnd.time = time;
+      gdk_event_set_device (tmp_event, gdk_drag_context_get_device (current_dest_drag));
 
       current_dest_drag->suggested_action = action;
 
-      tmp_event.dnd.x_root = x_root;
-      tmp_event.dnd.y_root = y_root;
+      tmp_event->dnd.x_root = x_root;
+      tmp_event->dnd.y_root = y_root;
 
       PRIVATE_DATA (current_dest_drag)->last_pt.x = x_root - _gdk_offset_x;
       PRIVATE_DATA (current_dest_drag)->last_pt.y = y_root - _gdk_offset_y;
 
       PRIVATE_DATA (context)->drag_status = GDK_DRAG_STATUS_MOTION_WAIT;
 
-      GDK_NOTE (EVENTS, _gdk_win32_print_event (&tmp_event));
-      gdk_event_put (&tmp_event);
+      GDK_NOTE (EVENTS, _gdk_win32_print_event (tmp_event));
+      gdk_event_put (tmp_event);
+      gdk_event_free (tmp_event);
     }
 }
 
@@ -1730,7 +1792,7 @@ static void
 local_send_drop (GdkDragContext *context,
 		 guint32         time)
 {
-  GdkEvent tmp_event;
+  GdkEvent *tmp_event;
 
   GDK_NOTE (DND, g_print ("local_send_drop: context=%p current_dest_drag=%p\n",
 			  context,
@@ -1744,19 +1806,21 @@ local_send_drop (GdkDragContext *context,
       private = PRIVATE_DATA (current_dest_drag);
 
       /* Pass ownership of context to the event */
-      tmp_event.type = GDK_DROP_START;
-      tmp_event.dnd.window = current_dest_drag->dest_window;
-      tmp_event.dnd.send_event = FALSE;
-      tmp_event.dnd.context = current_dest_drag;
-      tmp_event.dnd.time = GDK_CURRENT_TIME;
+      tmp_event = gdk_event_new (GDK_DROP_START);
+      tmp_event->dnd.window = g_object_ref (current_dest_drag->dest_window);
+      tmp_event->dnd.send_event = FALSE;
+      tmp_event->dnd.context = g_object_ref (current_dest_drag);
+      tmp_event->dnd.time = GDK_CURRENT_TIME;
+      gdk_event_set_device (tmp_event, gdk_drag_context_get_device (current_dest_drag));
 
-      tmp_event.dnd.x_root = private->last_pt.x + _gdk_offset_x;
-      tmp_event.dnd.y_root = private->last_pt.y + _gdk_offset_y;
+      tmp_event->dnd.x_root = private->last_pt.x + _gdk_offset_x;
+      tmp_event->dnd.y_root = private->last_pt.y + _gdk_offset_y;
 
       current_dest_drag = NULL;
 
-      GDK_NOTE (EVENTS, _gdk_win32_print_event (&tmp_event));
-      gdk_event_put (&tmp_event);
+      GDK_NOTE (EVENTS, _gdk_win32_print_event (tmp_event));
+      gdk_event_put (tmp_event);
+      gdk_event_free (tmp_event);
     }
 
 }
@@ -1787,11 +1851,15 @@ gdk_drag_begin (GdkWindow *window,
   if (!use_ole2_dnd)
     {
       GdkDragContext *new_context;
+      GdkDevice *device;
 
       g_return_val_if_fail (window != NULL, NULL);
 
       new_context = gdk_drag_context_new ();
 
+      device = gdk_display_get_core_pointer (_gdk_display);
+      gdk_drag_context_set_device (new_context, device);
+
       new_context->is_source = TRUE;
 
       new_context->source_window = window;
@@ -2043,7 +2111,7 @@ gdk_drag_motion (GdkDragContext *context,
 	}
       else
 	{
-	  GdkEvent tmp_event;
+	  GdkEvent *tmp_event;
 
 	  /* Send a leave to the last destination */
 	  gdk_drag_do_leave (context, time);
@@ -2076,18 +2144,20 @@ gdk_drag_motion (GdkDragContext *context,
 	  /* Push a status event, to let the client know that
 	   * the drag changed
 	   */
-	  tmp_event.type = GDK_DRAG_STATUS;
-	  tmp_event.dnd.window = context->source_window;
+    tmp_event = gdk_event_new (GDK_DRAG_STATUS);
+	  tmp_event->dnd.window = g_object_ref (context->source_window);
 	  /* We use this to signal a synthetic status. Perhaps
 	   * we should use an extra field...
 	   */
-	  tmp_event.dnd.send_event = TRUE;
+	  tmp_event->dnd.send_event = TRUE;
 
-	  tmp_event.dnd.context = context;
-	  tmp_event.dnd.time = time;
+	  tmp_event->dnd.context = g_object_ref (context);
+	  tmp_event->dnd.time = time;
+    gdk_event_set_device (tmp_event, gdk_drag_context_get_device (context));
 
-	  GDK_NOTE (EVENTS, _gdk_win32_print_event (&tmp_event));
-	  gdk_event_put (&tmp_event);
+	  GDK_NOTE (EVENTS, _gdk_win32_print_event (tmp_event));
+	  gdk_event_put (tmp_event);
+    gdk_event_free (tmp_event);
 	}
 
       /* Send a drag-motion event */
@@ -2176,7 +2246,7 @@ gdk_drag_status (GdkDragContext *context,
 {
   GdkDragContextPrivateWin32 *private;
   GdkDragContext *src_context;
-  GdkEvent tmp_event;
+  GdkEvent *tmp_event;
 
   g_return_if_fail (context != NULL);
 
@@ -2205,19 +2275,21 @@ gdk_drag_status (GdkDragContext *context,
 	  if (private->drag_status == GDK_DRAG_STATUS_MOTION_WAIT)
 	    private->drag_status = GDK_DRAG_STATUS_DRAG;
 
-	  tmp_event.type = GDK_DRAG_STATUS;
-	  tmp_event.dnd.window = context->source_window;
-	  tmp_event.dnd.send_event = FALSE;
-	  tmp_event.dnd.context = src_context;
-	  tmp_event.dnd.time = GDK_CURRENT_TIME; /* FIXME? */
+	  tmp_event = gdk_event_new (GDK_DRAG_STATUS);
+	  tmp_event->dnd.window = g_object_ref (context->source_window);
+	  tmp_event->dnd.send_event = FALSE;
+	  tmp_event->dnd.context = g_object_ref (src_context);
+	  tmp_event->dnd.time = GDK_CURRENT_TIME; /* FIXME? */
+    gdk_event_set_device (tmp_event, gdk_drag_context_get_device (src_context));
 
 	  if (action == GDK_ACTION_DEFAULT)
 	    action = 0;
 
 	  src_context->action = action;
 
-	  GDK_NOTE (EVENTS, _gdk_win32_print_event (&tmp_event));
-	  gdk_event_put (&tmp_event);
+	  GDK_NOTE (EVENTS, _gdk_win32_print_event (tmp_event));
+	  gdk_event_put (tmp_event);
+    gdk_event_free (tmp_event);
 	}
     }
 }
@@ -2246,7 +2318,7 @@ gdk_drop_finish (GdkDragContext *context,
 {
   GdkDragContextPrivateWin32 *private;
   GdkDragContext *src_context;
-  GdkEvent tmp_event;
+  GdkEvent *tmp_event;
 
   g_return_if_fail (context != NULL);
 
@@ -2261,13 +2333,15 @@ gdk_drop_finish (GdkDragContext *context,
 					   context->dest_window);
       if (src_context)
 	{
-	  tmp_event.type = GDK_DROP_FINISHED;
-	  tmp_event.dnd.window = src_context->source_window;
-	  tmp_event.dnd.send_event = FALSE;
-	  tmp_event.dnd.context = src_context;
-
-	  GDK_NOTE (EVENTS, _gdk_win32_print_event (&tmp_event));
-	  gdk_event_put (&tmp_event);
+    tmp_event = gdk_event_new (GDK_DROP_FINISHED);
+	  tmp_event->dnd.window = g_object_ref (src_context->source_window);
+	  tmp_event->dnd.send_event = FALSE;
+	  tmp_event->dnd.context = g_object_ref (src_context);
+    gdk_event_set_device (tmp_event, gdk_drag_context_get_device (src_context));
+
+	  GDK_NOTE (EVENTS, _gdk_win32_print_event (tmp_event));
+	  gdk_event_put (tmp_event);
+    gdk_event_free (tmp_event);
 	}
     }
   else
diff --git a/gdk/win32/gdkevents-win32.c b/gdk/win32/gdkevents-win32.c
index 219da60..b5c6174 100644
--- a/gdk/win32/gdkevents-win32.c
+++ b/gdk/win32/gdkevents-win32.c
@@ -47,8 +47,10 @@
 
 #include "gdk.h"
 #include "gdkprivate-win32.h"
-#include "gdkinput-win32.h"
 #include "gdkkeysyms.h"
+#include "gdkdevicemanager-win32.h"
+#include "gdkdeviceprivate.h"
+#include "gdkdevice-wintab.h"
 
 #include <windowsx.h>
 
@@ -101,6 +103,7 @@ static gboolean is_modally_blocked (GdkWindow   *window);
  */
 
 static GList *client_filters;	/* Filters for client messages */
+extern gint       _gdk_input_ignore_core;
 
 static HCURSOR p_grab_cursor;
 
@@ -199,31 +202,44 @@ _gdk_win32_get_next_tick (gulong suggested_tick)
 }
 
 static void
-generate_focus_event (GdkWindow *window,
-		      gboolean   in)
+generate_focus_event (GdkDeviceManager *device_manager,
+                      GdkWindow        *window,
+                      gboolean          in)
 {
+  GdkDevice *device;
   GdkEvent *event;
 
+  device = GDK_DEVICE_MANAGER_WIN32 (device_manager)->core_keyboard;
+
   event = gdk_event_new (GDK_FOCUS_CHANGE);
   event->focus_change.window = window;
   event->focus_change.in = in;
+  gdk_event_set_device (event, device);
 
   append_event (event);
 }
 
 static void
-generate_grab_broken_event (GdkWindow *window,
-			    gboolean   keyboard,
-			    GdkWindow *grab_window)
+generate_grab_broken_event (GdkDeviceManager *device_manager,
+                            GdkWindow        *window,
+                            gboolean          keyboard,
+                            GdkWindow        *grab_window)
 {
   GdkEvent *event = gdk_event_new (GDK_GRAB_BROKEN);
+  GdkDevice *device;
+
+  if (keyboard)
+    device = GDK_DEVICE_MANAGER_WIN32 (device_manager)->core_keyboard;
+  else
+    device = GDK_DEVICE_MANAGER_WIN32 (device_manager)->core_pointer;
 
   event->grab_broken.window = window;
   event->grab_broken.send_event = 0;
   event->grab_broken.keyboard = keyboard;
   event->grab_broken.implicit = FALSE;
   event->grab_broken.grab_window = grab_window;
-	  
+  gdk_event_set_device (event, device);
+
   append_event (event);
 }
 
@@ -443,17 +459,19 @@ event_mask_string (GdkEventMask mask)
 #endif
 
 GdkGrabStatus
-_gdk_windowing_pointer_grab (GdkWindow    *window,
-			     GdkWindow    *native_window,
-			     gboolean	owner_events,
-			     GdkEventMask	event_mask,
-			     GdkWindow    *confine_to,
-			     GdkCursor    *cursor,
-			     guint32	time)
+_gdk_windowing_device_grab (GdkDevice    *device,
+                            GdkWindow    *window,
+                            GdkWindow    *native_window,
+                            gboolean      owner_events,
+                            GdkEventMask	event_mask,
+                            GdkWindow    *confine_to,
+                            GdkCursor    *cursor,
+                            guint32	      time)
 {
   HCURSOR hcursor;
   GdkCursorPrivate *cursor_private;
   gint return_val;
+  GdkWindowImplWin32 *impl = GDK_WINDOW_IMPL_WIN32 (((GdkWindowObject *) native_window)->impl);
 
   g_return_val_if_fail (window != NULL, 0);
   g_return_val_if_fail (GDK_IS_WINDOW (window), 0);
@@ -466,54 +484,53 @@ _gdk_windowing_pointer_grab (GdkWindow    *window,
   else if ((hcursor = CopyCursor (cursor_private->hcursor)) == NULL)
     WIN32_API_FAILED ("CopyCursor");
 
-  return_val = _gdk_input_grab_pointer (native_window,
-					owner_events,
-					event_mask,
-					confine_to,
-					time);
+  return_val = GDK_DEVICE_GET_CLASS (device)->grab (device,
+                                                    native_window,
+                                                    owner_events,
+                                                    event_mask,
+                                                    confine_to,
+                                                    cursor,
+                                                    time);
 
-  if (return_val == GDK_GRAB_SUCCESS)
+  /* TODO_CSW: grab brokens, confine window, input_grab */
+  if (p_grab_cursor != NULL)
     {
-      GdkWindowImplWin32 *impl = GDK_WINDOW_IMPL_WIN32 (((GdkWindowObject *) native_window)->impl);
-
-      SetCapture (GDK_WINDOW_HWND (native_window));
-      /* TODO_CSW: grab brokens, confine window, input_grab */
-      if (p_grab_cursor != NULL)
-	{
-	  if (GetCursor () == p_grab_cursor)
-	    SetCursor (NULL);
-	  DestroyCursor (p_grab_cursor);
-	}
-
-      p_grab_cursor = hcursor;
+      if (GetCursor () == p_grab_cursor)
+        SetCursor (NULL);
+      DestroyCursor (p_grab_cursor);
+    }
 
-      if (p_grab_cursor != NULL)
-	SetCursor (p_grab_cursor);
-      else if (impl->hcursor != NULL)
-	SetCursor (impl->hcursor);
-      else
-	SetCursor (LoadCursor (NULL, IDC_ARROW));
+  p_grab_cursor = hcursor;
 
-    }
+  if (p_grab_cursor != NULL)
+    SetCursor (p_grab_cursor);
+  else if (impl->hcursor != NULL)
+    SetCursor (impl->hcursor);
+  else
+    SetCursor (LoadCursor (NULL, IDC_ARROW));
 
   return return_val;
 }
 
 void
-gdk_display_pointer_ungrab (GdkDisplay *display,
-                            guint32     time)
+gdk_device_ungrab (GdkDevice *device,
+                   guint32    time)
 {
-  GdkPointerGrabInfo *info;
+  GdkDeviceGrabInfo *info;
+  GdkDisplay *display;
+
+  g_return_if_fail (GDK_IS_DEVICE (device));
+
+  display = gdk_device_get_display (device);
+  info = _gdk_display_get_last_device_grab (display, device);
 
-  info = _gdk_display_get_last_pointer_grab (display);
   if (info)
     {
       info->serial_end = 0;
-      ReleaseCapture ();
+      GDK_DEVICE_GET_CLASS (device)->ungrab (device, time);
     }
-  /* TODO_CSW: cursor, confines, etc */
 
-  _gdk_display_pointer_grab_update (display, 0);
+  _gdk_display_device_grab_update (display, device, 0);
 }
 
 
@@ -525,8 +542,11 @@ find_window_for_mouse_event (GdkWindow* reported_window,
   POINTS points;
   POINT pt;
   GdkWindow* other_window = NULL;
+  GdkDeviceManagerWin32 *device_manager;
+
+  device_manager = GDK_DEVICE_MANAGER_WIN32 (gdk_display_get_device_manager (_gdk_display));
 
-  if (!_gdk_display_get_last_pointer_grab (_gdk_display))
+  if (!_gdk_display_get_last_device_grab (_gdk_display, device_manager->core_pointer))
     return reported_window;
 
   points = MAKEPOINTS (msg->lParam);
@@ -562,41 +582,6 @@ find_window_for_mouse_event (GdkWindow* reported_window,
   return other_window;
 }
 
-GdkGrabStatus
-gdk_keyboard_grab (GdkWindow *window,
-		   gboolean   owner_events,
-		   guint32    time)
-{
-  GdkDisplay *display;
-  GdkWindow  *toplevel;
-
-  g_return_val_if_fail (window != NULL, 0);
-  g_return_val_if_fail (GDK_IS_WINDOW (window), 0);
-  
-  GDK_NOTE (EVENTS, g_print ("gdk_keyboard_grab %p%s\n",
-			     GDK_WINDOW_HWND (window), owner_events ? " OWNER_EVENTS" : ""));
-
-  display = gdk_drawable_get_display (window);
-  toplevel = gdk_window_get_toplevel (window);
-
-  _gdk_display_set_has_keyboard_grab (display,
-				      window,
-				      toplevel,
-				      owner_events,
-				      0,
-				      time);
-
-  return GDK_GRAB_SUCCESS;
-}
-
-void
-gdk_display_keyboard_ungrab (GdkDisplay *display,
-                             guint32 time)
-{
-  GDK_NOTE (EVENTS, g_print ("gdk_display_keyboard_ungrab\n"));
-  _gdk_display_unset_has_keyboard_grab (display, FALSE);
-}
-
 void 
 gdk_display_add_client_message_filter (GdkDisplay   *display,
 				       GdkAtom       message_type,
@@ -1195,11 +1180,11 @@ do_show_window (GdkWindow *window, gboolean hide_window)
 }
 
 static void
-synthesize_enter_or_leave_event (GdkWindow    	*window,
-				 MSG          	*msg,
-				 GdkEventType 	 type,
-				 GdkCrossingMode mode,
-				 GdkNotifyType detail)
+synthesize_enter_or_leave_event (GdkWindow        *window,
+                                 MSG          	  *msg,
+                                 GdkEventType 	   type,
+                                 GdkCrossingMode   mode,
+                                 GdkNotifyType     detail)
 {
   GdkEvent *event;
   POINT pt;
@@ -1219,12 +1204,13 @@ synthesize_enter_or_leave_event (GdkWindow    	*window,
   event->crossing.detail = detail;
   event->crossing.focus = TRUE; /* FIXME: Set correctly */
   event->crossing.state = 0;	/* FIXME: Set correctly */
+  gdk_event_set_device (event, _gdk_display->core_pointer);
 
   append_event (event);
   
   if (type == GDK_ENTER_NOTIFY &&
       ((GdkWindowObject *) window)->extension_events != 0)
-    _gdk_input_enter_event (window);
+    _gdk_device_wintab_update_window_coords (window);
 }
 			 
 static void
@@ -1684,10 +1670,10 @@ handle_display_change (void)
 }
 
 static void
-generate_button_event (GdkEventType type,
-		       gint         button,
-		       GdkWindow   *window,
-		       MSG         *msg)
+generate_button_event (GdkEventType      type,
+                       gint              button,
+                       GdkWindow        *window,
+                       MSG              *msg)
 {
   GdkEvent *event = gdk_event_new (type);
 
@@ -1700,7 +1686,7 @@ generate_button_event (GdkEventType type,
   event->button.axes = NULL;
   event->button.state = build_pointer_event_state (msg);
   event->button.button = button;
-  event->button.device = _gdk_display->core_pointer;
+  gdk_event_set_device (event, _gdk_display->core_pointer);
 
   append_event (event);
 }
@@ -1868,7 +1854,10 @@ gdk_event_translate (MSG  *msg,
 
   GdkWindow *orig_window, *new_window, *toplevel;
 
-  GdkPointerGrabInfo *grab = NULL;
+  GdkDeviceManager *device_manager;
+
+  GdkDeviceGrabInfo *keyboard_grab = NULL;
+  GdkDeviceGrabInfo *pointer_grab = NULL;
   GdkWindow *grab_window = NULL;
 
   static gint update_colors_counter = 0;
@@ -1929,7 +1918,14 @@ gdk_event_translate (MSG  *msg,
 	}
       return FALSE;
     }
-  
+
+  device_manager = gdk_display_get_device_manager (_gdk_display);
+
+  keyboard_grab = _gdk_display_get_last_device_grab (_gdk_display,
+                                                     GDK_DEVICE_MANAGER_WIN32 (device_manager)->core_keyboard);
+  pointer_grab = _gdk_display_get_last_device_grab (_gdk_display,
+                                                    GDK_DEVICE_MANAGER_WIN32 (device_manager)->core_pointer);
+
   g_object_ref (window);
 
   /* window's refcount has now been increased, so code below should
@@ -2047,7 +2043,7 @@ gdk_event_translate (MSG  *msg,
       /* Let the system handle Alt-Tab, Alt-Space and Alt-F4 unless
        * the keyboard is grabbed.
        */
-      if (_gdk_display->keyboard_grab.window == NULL &&
+      if (!keyboard_grab &&
 	  (msg->wParam == VK_TAB ||
 	   msg->wParam == VK_SPACE ||
 	   msg->wParam == VK_F4))
@@ -2071,9 +2067,10 @@ gdk_event_translate (MSG  *msg,
 	  in_ime_composition)
 	break;
 
-      if (!propagate (&window, msg,
-		      _gdk_display->keyboard_grab.window,
-		      _gdk_display->keyboard_grab.owner_events,
+      if (keyboard_grab &&
+          !propagate (&window, msg,
+		      keyboard_grab->window,
+		      keyboard_grab->owner_events,
 		      GDK_ALL_EVENTS_MASK,
 		      doesnt_want_key, FALSE))
 	break;
@@ -2090,6 +2087,7 @@ gdk_event_translate (MSG  *msg,
       event->key.string = NULL;
       event->key.length = 0;
       event->key.hardware_keycode = msg->wParam;
+      gdk_event_set_device (event, GDK_DEVICE_MANAGER_WIN32 (device_manager)->core_keyboard);
       if (HIWORD (msg->lParam) & KF_EXTENDED)
 	{
 	  switch (msg->wParam)
@@ -2175,9 +2173,10 @@ gdk_event_translate (MSG  *msg,
       if (!(msg->lParam & GCS_RESULTSTR))
 	break;
 
-      if (!propagate (&window, msg,
-		      _gdk_display->keyboard_grab.window,
-		      _gdk_display->keyboard_grab.owner_events,
+      if (keyboard_grab &&
+          !propagate (&window, msg,
+		      keyboard_grab->window,
+		      keyboard_grab->owner_events,
 		      GDK_ALL_EVENTS_MASK,
 		      doesnt_want_char, FALSE))
 	break;
@@ -2201,6 +2200,7 @@ gdk_event_translate (MSG  *msg,
 	      /* Build a key press event */
 	      event = gdk_event_new (GDK_KEY_PRESS);
 	      event->key.window = window;
+        gdk_event_set_device (event, GDK_DEVICE_MANAGER_WIN32 (device_manager)->core_keyboard);
 	      build_wm_ime_composition_event (event, msg, wbuf[i], key_state);
 
 	      append_event (event);
@@ -2211,6 +2211,7 @@ gdk_event_translate (MSG  *msg,
 	      /* Build a key release event.  */
 	      event = gdk_event_new (GDK_KEY_RELEASE);
 	      event->key.window = window;
+        gdk_event_set_device (event, GDK_DEVICE_MANAGER_WIN32 (device_manager)->core_keyboard);
 	      build_wm_ime_composition_event (event, msg, wbuf[i], key_state);
 
 	      append_event (event);
@@ -2307,10 +2308,10 @@ gdk_event_translate (MSG  *msg,
 	      current_toplevel ? GDK_WINDOW_HWND (current_toplevel) : NULL, 
 	      toplevel ? GDK_WINDOW_HWND (toplevel) : NULL));
 	  if (current_toplevel)
-	    synthesize_enter_or_leave_event (current_toplevel, msg, 
-					     GDK_LEAVE_NOTIFY, GDK_CROSSING_NORMAL, GDK_NOTIFY_ANCESTOR);
-	  synthesize_enter_or_leave_event (toplevel, msg, 
-					   GDK_ENTER_NOTIFY, GDK_CROSSING_NORMAL, GDK_NOTIFY_ANCESTOR);
+	    synthesize_enter_or_leave_event (current_toplevel, msg,
+                                       GDK_LEAVE_NOTIFY, GDK_CROSSING_NORMAL, GDK_NOTIFY_ANCESTOR);
+	  synthesize_enter_or_leave_event (toplevel, msg,
+                                     GDK_ENTER_NOTIFY, GDK_CROSSING_NORMAL, GDK_NOTIFY_ANCESTOR);
 	  assign_object (&current_toplevel, toplevel);
 	  track_mouse_event (TME_LEAVE, GDK_WINDOW_HWND (toplevel));
 	}
@@ -2336,7 +2337,7 @@ gdk_event_translate (MSG  *msg,
       event->motion.axes = NULL;
       event->motion.state = build_pointer_event_state (msg);
       event->motion.is_hint = FALSE;
-      event->motion.device = _gdk_display->core_pointer;
+      gdk_event_set_device (event, _gdk_display->core_pointer);
 
       append_event (event);
 
@@ -2365,8 +2366,8 @@ gdk_event_translate (MSG  *msg,
 	{
 	  /* we are only interested if we don't know the new window */
 	  if (current_toplevel)
-	    synthesize_enter_or_leave_event (current_toplevel, msg, 
-					     GDK_LEAVE_NOTIFY, GDK_CROSSING_NORMAL, GDK_NOTIFY_ANCESTOR);
+	    synthesize_enter_or_leave_event (current_toplevel, msg,
+                                       GDK_LEAVE_NOTIFY, GDK_CROSSING_NORMAL, GDK_NOTIFY_ANCESTOR);
 	  assign_object (&current_toplevel, NULL);
 	}
       else
@@ -2412,7 +2413,7 @@ gdk_event_translate (MSG  *msg,
       event->scroll.x_root = (gint16) GET_X_LPARAM (msg->lParam) + _gdk_offset_x;
       event->scroll.y_root = (gint16) GET_Y_LPARAM (msg->lParam) + _gdk_offset_y;
       event->scroll.state = build_pointer_event_state (msg);
-      event->scroll.device = _gdk_display->core_pointer;
+      gdk_event_set_device (event, _gdk_display->core_pointer);
 
       append_event (event);
       
@@ -2512,16 +2513,16 @@ gdk_event_translate (MSG  *msg,
        break;
 
     case WM_KILLFOCUS:
-      if (_gdk_display->keyboard_grab.window != NULL &&
-	  !GDK_WINDOW_DESTROYED (_gdk_display->keyboard_grab.window))
+      if (keyboard_grab != NULL &&
+	  !GDK_WINDOW_DESTROYED (keyboard_grab->window))
 	{
-	  generate_grab_broken_event (_gdk_display->keyboard_grab.window, FALSE, NULL);
+	  generate_grab_broken_event (device_manager, keyboard_grab->window, FALSE, NULL);
 	}
 
       /* fallthrough */
     case WM_SETFOCUS:
-      if (_gdk_display->keyboard_grab.window != NULL &&
-	  !_gdk_display->keyboard_grab.owner_events)
+      if (keyboard_grab != NULL &&
+	  !keyboard_grab->owner_events)
 	break;
 
       if (!(((GdkWindowObject *) window)->event_mask & GDK_FOCUS_CHANGE_MASK))
@@ -2530,7 +2531,7 @@ gdk_event_translate (MSG  *msg,
       if (GDK_WINDOW_DESTROYED (window))
 	break;
 
-      generate_focus_event (window, (msg->message == WM_SETFOCUS));
+      generate_focus_event (device_manager, window, (msg->message == WM_SETFOCUS));
       return_val = TRUE;
       break;
 
@@ -2558,11 +2559,8 @@ gdk_event_translate (MSG  *msg,
       GDK_NOTE (EVENTS, g_print (" %#x %#x",
 				 LOWORD (msg->lParam), HIWORD (msg->lParam)));
 
-      grab = _gdk_display_get_last_pointer_grab (_gdk_display);
-      if (grab != NULL)
-	{
-	  grab_window = grab->window;
-	}
+      if (pointer_grab != NULL)
+        grab_window = pointer_grab->window;
 
       if (grab_window == NULL && LOWORD (msg->lParam) != HTCLIENT)
 	break;
@@ -2617,14 +2615,14 @@ gdk_event_translate (MSG  *msg,
 	      SetForegroundWindow (GDK_WINDOW_HWND (impl->transient_owner));
 	    }
 
-	  grab = _gdk_display_get_last_pointer_grab (_gdk_display);
-	  if (grab != NULL)
+	  if (pointer_grab != NULL)
 	    {
-	      if (grab->window == window)
+	      if (pointer_grab->window == window)
 		gdk_pointer_ungrab (msg->time);
 	    }
 
-	  if (_gdk_display->keyboard_grab.window == window)
+	  if (keyboard_grab &&
+        keyboard_grab->window == window)
 	    gdk_keyboard_ungrab (msg->time);
 	}
 
@@ -2655,13 +2653,13 @@ gdk_event_translate (MSG  *msg,
       if (msg->wParam == SIZE_MINIMIZED)
 	{
 	  /* Don't generate any GDK event. This is *not* an UNMAP. */
-	  grab = _gdk_display_get_last_pointer_grab (_gdk_display);
-	  if (grab != NULL)
+	  if (pointer_grab != NULL)
 	    {
-	      if (grab->window == window)
+	      if (pointer_grab->window == window)
 		gdk_pointer_ungrab (msg->time);
 	    }
-	  if (_gdk_display->keyboard_grab.window == window)
+	  if (keyboard_grab &&
+        keyboard_grab->window == window)
 	    gdk_keyboard_ungrab (msg->time);
 
 	  gdk_synthesize_window_state (window,
@@ -2707,7 +2705,7 @@ gdk_event_translate (MSG  *msg,
 	    ((GdkWindowObject *) window)->resize_count -= 1;
 	  
 	  if (((GdkWindowObject *) window)->extension_events != 0)
-	    _gdk_input_configure_event (window);
+      _gdk_device_wintab_update_window_coords (window);
 
 	  return_val = TRUE;
 	}
@@ -3089,14 +3087,14 @@ gdk_event_translate (MSG  *msg,
       break;
 
     case WM_DESTROY:
-      grab = _gdk_display_get_last_pointer_grab (_gdk_display);
-      if (grab != NULL)
+      if (pointer_grab != NULL)
 	{
-	  if (grab->window == window)
+	  if (pointer_grab->window == window)
 	    gdk_pointer_ungrab (msg->time);
 	}
 
-      if (_gdk_display->keyboard_grab.window == window)
+      if (keyboard_grab &&
+          keyboard_grab->window == window)
 	gdk_keyboard_ungrab (msg->time);
 
       if ((window != NULL) && (msg->hwnd != GetDesktopWindow ()))
diff --git a/gdk/win32/gdkinput.c b/gdk/win32/gdkinput.c
index e5ab44b..32de7fc 100644
--- a/gdk/win32/gdkinput.c
+++ b/gdk/win32/gdkinput.c
@@ -37,63 +37,13 @@
 #include "gdkinput.h"
 
 #include "gdkprivate-win32.h"
-#include "gdkinput-win32.h"
-
-static GdkDeviceAxis gdk_input_core_axes[] = {
-  { GDK_AXIS_X, 0, 0 },
-  { GDK_AXIS_Y, 0, 0 }
-};
-
-/* Global variables  */
+#include "gdkdevicemanager-win32.h"
 
 gint              _gdk_input_ignore_core;
 
 GList            *_gdk_input_devices;
 GList            *_gdk_input_windows;
 
-void
-_gdk_init_input_core (GdkDisplay *display)
-{
-  display->core_pointer = g_object_new (GDK_TYPE_DEVICE, NULL);
-  
-  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;
-}
-
-GType
-gdk_device_get_type (void)
-{
-  static GType object_type = 0;
-
-  if (!object_type)
-    {
-      const GTypeInfo object_info =
-	{
-	  sizeof (GdkDeviceClass),
-	  (GBaseInitFunc) NULL,
-	  (GBaseFinalizeFunc) NULL,
-	  (GClassInitFunc) NULL,
-	  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);
-    }
-  
-  return object_type;
-}
-
 GList *
 gdk_devices_list (void)
 {
@@ -105,188 +55,9 @@ gdk_display_list_devices (GdkDisplay *dpy)
 {
   g_return_val_if_fail (dpy == _gdk_display, NULL);
 
-  _gdk_input_wintab_init_check ();
   return _gdk_input_devices;
 }
 
-G_CONST_RETURN gchar *
-gdk_device_get_name (GdkDevice *device)
-{
-  g_return_val_if_fail (GDK_IS_DEVICE (device), NULL);
-
-  return device->name;
-}
-
-GdkInputSource
-gdk_device_get_source (GdkDevice *device)
-{
-  g_return_val_if_fail (GDK_IS_DEVICE (device), 0);
-
-  return device->source;
-}
-
-GdkInputMode
-gdk_device_get_mode (GdkDevice *device)
-{
-  g_return_val_if_fail (GDK_IS_DEVICE (device), 0);
-
-  return device->mode;
-}
-
-gboolean
-gdk_device_get_has_cursor (GdkDevice *device)
-{
-  g_return_val_if_fail (GDK_IS_DEVICE (device), FALSE);
-
-  return device->has_cursor;
-}
-
-void
-gdk_device_set_source (GdkDevice      *device,
-		       GdkInputSource  source)
-{
-  g_return_if_fail (device != NULL);
-
-  device->source = source;
-}
-
-void
-gdk_device_get_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);
-
-  if (!device->keys[index].keyval &&
-      !device->keys[index].modifiers)
-    return;
-
-  if (keyval)
-    *keyval = device->keys[index].keyval;
-
-  if (modifiers)
-    *modifiers = device->keys[index].modifiers;
-}
-
-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;
-}
-
-GdkAxisUse
-gdk_device_get_axis_use (GdkDevice *device,
-                         guint      index)
-{
-  g_return_val_if_fail (GDK_IS_DEVICE (device), GDK_AXIS_IGNORE);
-  g_return_val_if_fail (index < device->num_axes, GDK_AXIS_IGNORE);
-
-  return device->axes[index].use;
-}
-
-void
-gdk_device_set_axis_use (GdkDevice   *device,
-			 guint        index,
-			 GdkAxisUse   use)
-{
-  g_return_if_fail (device != NULL);
-  g_return_if_fail (index < device->num_axes);
-
-  device->axes[index].use = use;
-
-  switch (use)
-    {
-    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;
-    }
-}
-
-gboolean
-gdk_device_get_history  (GdkDevice         *device,
-			 GdkWindow         *window,
-			 guint32            start,
-			 guint32            stop,
-			 GdkTimeCoord    ***events,
-			 gint              *n_events)
-{
-  g_return_val_if_fail (window != NULL, FALSE);
-  g_return_val_if_fail (GDK_IS_WINDOW (window), FALSE);
-  g_return_val_if_fail (events != NULL, FALSE);
-  g_return_val_if_fail (n_events != NULL, FALSE);
-
-  if (n_events)
-    *n_events = 0;
-  if (events)
-    *events = NULL;
-
-  if (GDK_WINDOW_DESTROYED (window))
-    return FALSE;
-    
-  if (GDK_IS_CORE (device))
-    return FALSE;
-  else
-    return _gdk_device_get_history (device, window, start, stop, events, n_events);
-}
-
-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);
-}
-
-GdkInputWindow *
-_gdk_input_window_find(GdkWindow *window)
-{
-  GList *tmp_list;
-
-  for (tmp_list=_gdk_input_windows; tmp_list; tmp_list=tmp_list->next)
-    if (((GdkInputWindow *)(tmp_list->data))->window == window)
-      return (GdkInputWindow *)(tmp_list->data);
-
-  return NULL;      /* Not found */
-}
-
 /* FIXME: this routine currently needs to be called between creation
    and the corresponding configure event (because it doesn't get the
    root_relative_geometry).  This should work with
@@ -295,212 +66,60 @@ _gdk_input_window_find(GdkWindow *window)
 
 void
 gdk_input_set_extension_events (GdkWindow *window, gint mask,
-				GdkExtensionMode mode)
+                                GdkExtensionMode mode)
 {
+  GdkDeviceManager *device_manager;
   GdkWindowObject *window_private;
-  GList *tmp_list;
-  GdkInputWindow *iw;
+  GList *devices, *d;
 
-  g_return_if_fail (window != NULL);
   g_return_if_fail (GDK_IS_WINDOW (window));
 
-  window_private = (GdkWindowObject*) window;
   if (GDK_WINDOW_DESTROYED (window))
     return;
 
   if (mode == GDK_EXTENSION_EVENTS_NONE)
     mask = 0;
 
-  if (mask != 0)
-    {
-      _gdk_input_wintab_init_check ();
-
-      iw = g_new(GdkInputWindow,1);
-
-      iw->window = window;
-      iw->mode = mode;
-
-      iw->obscuring = NULL;
-      iw->num_obscuring = 0;
-      iw->grabbed = FALSE;
+  window_private = (GdkWindowObject *) window;
+  window_private->extension_events = mask;
 
-      _gdk_input_windows = g_list_append(_gdk_input_windows,iw);
-      window_private->extension_events = mask;
+  device_manager = gdk_display_get_device_manager (_gdk_display);
+  devices = gdk_device_manager_list_devices (device_manager,
+                                             GDK_DEVICE_TYPE_FLOATING);
 
-      /* Add enter window events to the event mask */
-      if (g_list_length (_gdk_input_devices) > 1)
-	gdk_window_set_events (window,
-			       gdk_window_get_events (window) | 
-			       GDK_ENTER_NOTIFY_MASK);
-    }
-  else
+  for (d = devices; d; d = d->next)
     {
-      iw = _gdk_input_window_find (window);
-      if (iw)
-	{
-	  _gdk_input_windows = g_list_remove(_gdk_input_windows,iw);
-	  g_free(iw);
-	}
+      GdkDevice *dev;
+      gint dev_mask;
 
-      window_private->extension_events = 0;
-    }
+      dev = d->data;
+      dev_mask = mask;
 
-  for (tmp_list = _gdk_input_devices; tmp_list; tmp_list = tmp_list->next)
-    {
-      GdkDevicePrivate *gdkdev = tmp_list->data;
+      if (gdk_device_get_mode (dev) == GDK_MODE_DISABLED ||
+          (!gdk_device_get_has_cursor (dev) && mode == GDK_EXTENSION_EVENTS_CURSOR))
+        dev_mask = 0;
 
-      if (!GDK_IS_CORE (gdkdev))
-	{
-	  if (mask != 0 && gdkdev->info.mode != GDK_MODE_DISABLED
-	      && (gdkdev->info.has_cursor || mode == GDK_EXTENSION_EVENTS_ALL))
-	    _gdk_input_enable_window (window,gdkdev);
-	  else
-	    _gdk_input_disable_window (window,gdkdev);
-	}
+      gdk_window_set_device_events (window, dev, mask);
     }
-}
-
-void
-_gdk_input_window_destroy (GdkWindow *window)
-{
-  GdkInputWindow *input_window;
 
-  input_window = _gdk_input_window_find (window);
-  g_return_if_fail (input_window != NULL);
-
-  _gdk_input_windows = g_list_remove (_gdk_input_windows,input_window);
-  g_free(input_window);
-}
-
-void
-_gdk_input_crossing_event (GdkWindow *window,
-			   gboolean enter)
-{
-  if (enter)
-    {
-#if 0 /* No idea what to do... */
-      GdkWindowObject *priv = (GdkWindowObject *)window;
-      GdkInputWindow *input_window;
-      gint root_x, root_y;
-#if 0
-      gdk_input_check_proximity(display);
-#endif
-      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;
-	}
-#endif
-    }
-  else
-    _gdk_input_ignore_core = FALSE;
+  g_list_free (devices);
 }
 
 void
-_gdk_input_exit (void)
+_gdk_input_init (GdkDisplay *display)
 {
-  GList *tmp_list;
-  GdkDevicePrivate *gdkdev;
+  GdkDeviceManagerWin32 *device_manager;
 
-  for (tmp_list = _gdk_input_devices; tmp_list; tmp_list = tmp_list->next)
-    {
-      gdkdev = (GdkDevicePrivate *)(tmp_list->data);
-      if (!GDK_IS_CORE (gdkdev))
-	{
-	  gdk_device_set_mode (&gdkdev->info, GDK_MODE_DISABLED);
+  _gdk_input_ignore_core = FALSE;
 
-	  g_free(gdkdev->info.name);
-	  g_free(gdkdev->axes);
-	  g_free(gdkdev->info.axes);
-	  g_free(gdkdev->info.keys);
-	  g_free(gdkdev);
-	}
-    }
-
-  g_list_free(_gdk_input_devices);
-
-  for (tmp_list = _gdk_input_windows; tmp_list; tmp_list = tmp_list->next)
-    g_free(tmp_list->data);
-
-  g_list_free(_gdk_input_windows);
-}
-
-gboolean
-gdk_device_get_axis (GdkDevice  *device,
-		     gdouble    *axes,
-		     GdkAxisUse  use,
-		     gdouble    *value)
-{
-  gint i;
-  
-  g_return_val_if_fail (device != NULL, FALSE);
+  device_manager = g_object_new (GDK_TYPE_DEVICE_MANAGER_WIN32,
+                                 "display", display,
+                                 NULL);
+  display->device_manager = GDK_DEVICE_MANAGER (device_manager);
 
-  if (axes == NULL)
-    return FALSE;
-  
-  for (i=0; i<device->num_axes; i++)
-    if (device->axes[i].use == use)
-      {
-	if (value)
-	  *value = axes[i];
-	return TRUE;
-      }
-  
-  return FALSE;
-}
-
-gboolean
-gdk_device_set_mode (GdkDevice   *device,
-		     GdkInputMode mode)
-{
-  GList *tmp_list;
-  GdkDevicePrivate *gdkdev;
-  GdkInputMode old_mode;
-  GdkInputWindow *input_window;
-
-  if (GDK_IS_CORE (device))
-    return FALSE;
-
-  gdkdev = (GdkDevicePrivate *)device;
-
-  if (device->mode == mode)
-    return TRUE;
-
-  old_mode = device->mode;
-  device->mode = mode;
-
-  if (mode == GDK_MODE_WINDOW)
-    {
-      device->has_cursor = FALSE;
-      for (tmp_list = _gdk_input_windows; tmp_list; tmp_list = tmp_list->next)
-	{
-	  input_window = (GdkInputWindow *)tmp_list->data;
-	  if (input_window->mode != GDK_EXTENSION_EVENTS_CURSOR)
-	    _gdk_input_enable_window (input_window->window, gdkdev);
-	  else
-	    if (old_mode != GDK_MODE_DISABLED)
-	      _gdk_input_disable_window (input_window->window, gdkdev);
-	}
-    }
-  else if (mode == GDK_MODE_SCREEN)
-    {
-      device->has_cursor = TRUE;
-      for (tmp_list = _gdk_input_windows; tmp_list; tmp_list = tmp_list->next)
-	_gdk_input_enable_window (((GdkInputWindow *)tmp_list->data)->window,
-				  gdkdev);
-    }
-  else  /* mode == GDK_MODE_DISABLED */
-    {
-      for (tmp_list = _gdk_input_windows; tmp_list; tmp_list = tmp_list->next)
-	{
-	  input_window = (GdkInputWindow *)tmp_list->data;
-	  if (old_mode != GDK_MODE_WINDOW ||
-	      input_window->mode != GDK_EXTENSION_EVENTS_CURSOR)
-	    _gdk_input_disable_window (input_window->window, gdkdev);
-	}
-    }
+  display->core_pointer = device_manager->core_pointer;
 
-  return TRUE;
+  _gdk_input_devices = g_list_append (NULL, display->core_pointer);
+  _gdk_input_devices = g_list_concat (_gdk_input_devices,
+                                      g_list_copy (device_manager->wintab_devices));
 }
diff --git a/gdk/win32/gdkmain-win32.c b/gdk/win32/gdkmain-win32.c
index 94b7ac7..4ea4c8f 100644
--- a/gdk/win32/gdkmain-win32.c
+++ b/gdk/win32/gdkmain-win32.c
@@ -39,10 +39,11 @@
 #include "gdkinternals.h"
 #include "gdkintl.h"
 #include "gdkprivate-win32.h"
-#include "gdkinput-win32.h"
 
 #include <objbase.h>
 
+#include <windows.h>
+#include <wintab.h>
 #include <imm.h>
 
 static gboolean gdk_synchronize = FALSE;
diff --git a/gdk/win32/gdkwindow-win32.c b/gdk/win32/gdkwindow-win32.c
index 55ca651..d44d8f4 100644
--- a/gdk/win32/gdkwindow-win32.c
+++ b/gdk/win32/gdkwindow-win32.c
@@ -33,7 +33,8 @@
 #include "gdk.h"
 #include "gdkwindowimpl.h"
 #include "gdkprivate-win32.h"
-#include "gdkinput-win32.h"
+#include "gdkdeviceprivate.h"
+#include "gdkdevicemanager-win32.h"
 #include "gdkenumtypes.h"
 
 static GdkColormap* gdk_window_impl_win32_get_colormap (GdkDrawable *drawable);
@@ -787,9 +788,6 @@ _gdk_win32_window_destroy (GdkWindow *window,
   GDK_NOTE (MISC, g_print ("_gdk_win32_window_destroy: %p\n",
 			   GDK_WINDOW_HWND (window)));
 
-  if (private->extension_events != 0)
-    _gdk_input_window_destroy (window);
-
   /* Remove ourself from the modal stack */
   _gdk_remove_modal_window (window);
 
@@ -1829,12 +1827,12 @@ gdk_win32_window_set_back_pixmap (GdkWindow *window,
 }
 
 static void
-gdk_win32_window_set_cursor (GdkWindow *window,
-			     GdkCursor *cursor)
+gdk_win32_window_set_device_cursor (GdkWindow *window,
+                                    GdkDevice *device,
+                                    GdkCursor *cursor)
 {
   GdkWindowImplWin32 *impl;
   GdkCursorPrivate *cursor_private;
-  GdkWindowObject *parent_window;
   HCURSOR hcursor;
   HCURSOR hprevcursor;
   
@@ -1859,6 +1857,8 @@ gdk_win32_window_set_cursor (GdkWindow *window,
    */
   hprevcursor = impl->hcursor;
 
+  GDK_DEVICE_GET_CLASS (device)->set_window_cursor (device, window, cursor);
+
   if (hcursor == NULL)
     impl->hcursor = NULL;
   else
@@ -1875,62 +1875,10 @@ gdk_win32_window_set_cursor (GdkWindow *window,
 			       hcursor, impl->hcursor));
     }
 
-  if (impl->hcursor != NULL)
-    {
-      /* If the pointer is over our window, set new cursor */
-      GdkWindow *curr_window = gdk_window_get_pointer (window, NULL, NULL, NULL);
-      if (curr_window == window ||
-	  (curr_window && window == gdk_window_get_toplevel (curr_window)))
-        SetCursor (impl->hcursor);
-      else
-	{
-	  /* Climb up the tree and find whether our window is the
-	   * first ancestor that has cursor defined, and if so, set
-	   * new cursor.
-	   */
-	  GdkWindowObject *curr_window_obj = GDK_WINDOW_OBJECT (curr_window);
-	  while (curr_window_obj &&
-		 !GDK_WINDOW_IMPL_WIN32 (curr_window_obj->impl)->hcursor)
-	    {
-	      curr_window_obj = curr_window_obj->parent;
-	      if (curr_window_obj == GDK_WINDOW_OBJECT (window))
-		{
-	          SetCursor (impl->hcursor);
-		  break;
-		}
-	    }
-	}
-    }
-
-  /* Destroy the previous cursor: Need to make sure it's no longer in
-   * use before we destroy it, in case we're not over our window but
-   * the cursor is still set to our old one.
-   */
+  /* Destroy the previous cursor */
   if (hprevcursor != NULL)
     {
-      if (GetCursor () == hprevcursor)
-	{
-	  /* Look for a suitable cursor to use instead */
-	  hcursor = NULL;
-          parent_window = GDK_WINDOW_OBJECT (window)->parent;
-          while (hcursor == NULL)
-	    {
-	      if (parent_window)
-		{
-		  impl = GDK_WINDOW_IMPL_WIN32 (parent_window->impl);
-		  hcursor = impl->hcursor;
-		  parent_window = parent_window->parent;
-		}
-	      else
-		{
-		  hcursor = LoadCursor (NULL, IDC_ARROW);
-		}
-	    }
-          SetCursor (hcursor);
-        }
-
       GDK_NOTE (MISC, g_print ("... DestroyCursor (%p)\n", hprevcursor));
-      
       API_CALL (DestroyCursor, (hprevcursor));
     }
 }
@@ -2113,87 +2061,43 @@ gdk_window_get_frame_extents (GdkWindow    *window,
 			   r.left, r.top));
 }
 
-
-static GdkModifierType
-get_current_mask (void)
-{
-  GdkModifierType mask;
-  BYTE kbd[256];
-
-  GetKeyboardState (kbd);
-  mask = 0;
-  if (kbd[VK_SHIFT] & 0x80)
-    mask |= GDK_SHIFT_MASK;
-  if (kbd[VK_CAPITAL] & 0x80)
-    mask |= GDK_LOCK_MASK;
-  if (kbd[VK_CONTROL] & 0x80)
-    mask |= GDK_CONTROL_MASK;
-  if (kbd[VK_MENU] & 0x80)
-    mask |= GDK_MOD1_MASK;
-  if (kbd[VK_LBUTTON] & 0x80)
-    mask |= GDK_BUTTON1_MASK;
-  if (kbd[VK_MBUTTON] & 0x80)
-    mask |= GDK_BUTTON2_MASK;
-  if (kbd[VK_RBUTTON] & 0x80)
-    mask |= GDK_BUTTON3_MASK;
-
-  return mask;
-}
-    
 static gboolean
-gdk_window_win32_get_pointer (GdkWindow       *window,
-			      gint            *x,
-			      gint            *y,
-			      GdkModifierType *mask)
+gdk_window_win32_get_device_state (GdkWindow       *window,
+                                   GdkDevice       *device,
+                                   gint            *x,
+                                   gint            *y,
+                                   GdkModifierType *mask)
 {
-  gboolean return_val;
-  POINT point;
-  HWND hwnd, hwndc;
+  GdkWindow *child;
 
   g_return_val_if_fail (window == NULL || GDK_IS_WINDOW (window), FALSE);
-  
-  return_val = TRUE;
-
-  hwnd = GDK_WINDOW_HWND (window);
-  GetCursorPos (&point);
-  ScreenToClient (hwnd, &point);
 
-  *x = point.x;
-  *y = point.y;
-
-  if (window == _gdk_root)
-    {
-      *x += _gdk_offset_x;
-      *y += _gdk_offset_y;
-    }
-
-  hwndc = ChildWindowFromPoint (hwnd, point);
-  if (hwndc != NULL && hwndc != hwnd &&
-      !gdk_win32_handle_table_lookup ((GdkNativeWindow) hwndc))
-    return_val = FALSE; /* Direct child unknown to gdk */
-
-  *mask = get_current_mask ();
-  
-  return return_val;
+  GDK_DEVICE_GET_CLASS (device)->query_state (device, window,
+                                              NULL, &child,
+                                              NULL, NULL,
+                                              x, y, mask);
+  return (child != NULL);
 }
 
 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)
 {
-  POINT point;
-
   g_return_if_fail (display == _gdk_display);
-  
-  *screen = _gdk_screen;
-  GetCursorPos (&point);
-  *x = point.x + _gdk_offset_x;
-  *y = point.y + _gdk_offset_y;
 
-  *mask = get_current_mask ();
+  if (screen)
+    *screen = _gdk_screen;
+
+  GDK_DEVICE_GET_CLASS (device)->query_state (device,
+                                              gdk_screen_get_root_window (_gdk_screen),
+                                              NULL, NULL,
+                                              x, y,
+                                              NULL, NULL,
+                                              mask);
 }
 
 void
@@ -2202,64 +2106,40 @@ gdk_display_warp_pointer (GdkDisplay *display,
 			  gint        x,
 			  gint        y)
 {
+  GdkDeviceManagerWin32 *device_manager;
+
   g_return_if_fail (display == _gdk_display);
   g_return_if_fail (screen == _gdk_screen);
 
-  SetCursorPos (x - _gdk_offset_x, y - _gdk_offset_y);
+  device_manager = GDK_DEVICE_MANAGER_WIN32 (gdk_display_get_device_manager (display));
+  GDK_DEVICE_GET_CLASS (device_manager->core_pointer)->warp (device_manager->core_pointer,
+                                                             screen, x, y);
 }
 
-GdkWindow*
-_gdk_windowing_window_at_pointer (GdkDisplay *display,
-				  gint       *win_x,
-				  gint       *win_y,
-				  GdkModifierType *mask,
-				  gboolean    get_toplevel)
+void
+gdk_display_warp_device (GdkDisplay *display,
+                         GdkDevice  *device,
+                         GdkScreen  *screen,
+                         gint        x,
+                         gint        y)
 {
-  GdkWindow *window;
-  POINT point, pointc;
-  HWND hwnd, hwndc;
-  RECT rect;
-
-  GetCursorPos (&pointc);
-  point = pointc;
-  hwnd = WindowFromPoint (point);
-
-  if (hwnd == NULL)
-    {
-      window = _gdk_root;
-      *win_x = pointc.x + _gdk_offset_x;
-      *win_y = pointc.y + _gdk_offset_y;
-      return window;
-    }
-      
-  ScreenToClient (hwnd, &point);
-
-  do {
-    if (get_toplevel &&
-	(window = gdk_win32_handle_table_lookup ((GdkNativeWindow) hwnd)) != NULL &&
-	GDK_WINDOW_TYPE (window) != GDK_WINDOW_FOREIGN)
-      break;
-
-    hwndc = ChildWindowFromPoint (hwnd, point);
-    ClientToScreen (hwnd, &point);
-    ScreenToClient (hwndc, &point);
-  } while (hwndc != hwnd && (hwnd = hwndc, 1));
-
-  window = gdk_win32_handle_table_lookup ((GdkNativeWindow) hwnd);
-
-  if (window && (win_x || win_y))
-    {
-      GetClientRect (hwnd, &rect);
-      *win_x = point.x - rect.left;
-      *win_y = point.y - rect.top;
-    }
+  g_return_if_fail (display == _gdk_display);
+  g_return_if_fail (screen == _gdk_screen);
+  g_return_if_fail (GDK_IS_DEVICE (device));
+  g_return_if_fail (display == gdk_device_get_display (device));
 
-  GDK_NOTE (MISC, g_print ("_gdk_windowing_window_at_pointer: %+d%+d %p%s\n",
-			   *win_x, *win_y,
-			   hwnd,
-			   (window == NULL ? " NULL" : "")));
+  GDK_DEVICE_GET_CLASS (device)->warp (device, screen, x, y);
+}
 
-  return window;
+GdkWindow*
+_gdk_windowing_window_at_device_position (GdkDisplay      *display,
+                                          GdkDevice       *device,
+                                          gint            *win_x,
+                                          gint            *win_y,
+                                          GdkModifierType *mask,
+                                          gboolean         get_toplevel)
+{
+  return GDK_DEVICE_GET_CLASS (device)->window_at_position (device, win_x, win_y, mask, get_toplevel);
 }
 
 static GdkEventMask  
@@ -3485,9 +3365,9 @@ gdk_window_impl_iface_init (GdkWindowImplIface *iface)
   iface->set_background = gdk_win32_window_set_background;
   iface->set_back_pixmap = gdk_win32_window_set_back_pixmap;
   iface->reparent = gdk_win32_window_reparent;
-  iface->set_cursor = gdk_win32_window_set_cursor;
+  iface->set_device_cursor = gdk_win32_window_set_device_cursor;
   iface->get_geometry = gdk_win32_window_get_geometry;
-  iface->get_pointer = gdk_window_win32_get_pointer;
+  iface->get_device_state = gdk_window_win32_get_device_state;
   iface->get_root_coords = gdk_win32_window_get_root_coords;
   iface->shape_combine_region = gdk_win32_window_shape_combine_region;
   iface->input_shape_combine_region = gdk_win32_input_shape_combine_region;
@@ -3496,6 +3376,4 @@ gdk_window_impl_iface_init (GdkWindowImplIface *iface)
   iface->queue_antiexpose = _gdk_win32_window_queue_antiexpose;
   iface->queue_translation = _gdk_win32_window_queue_translation;
   iface->destroy = _gdk_win32_window_destroy;
-  iface->input_window_destroy = _gdk_input_window_destroy;
-  iface->input_window_crossing = _gdk_input_crossing_event;
 }
diff --git a/gdk/x11/Makefile.am b/gdk/x11/Makefile.am
index fd4067c..8f7abfe 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,16 @@ 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
-else
-libgdk_x11_la_SOURCES += gdkinput-none.c
+libgdk_x11_la_SOURCES += gdkdevicemanager-xi.c gdkdevice-xi.c
+if XINPUT_2
+libgdk_x11_la_SOURCES += gdkdevicemanager-xi2.c gdkdevice-xi2.c
+endif
 endif
 
 
diff --git a/gdk/x11/gdkdevice-core.c b/gdk/x11/gdkdevice-core.c
new file mode 100644
index 0000000..7ff8c4d
--- /dev/null
+++ b/gdk/x11/gdkdevice-core.c
@@ -0,0 +1,501 @@
+/* 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 <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 && last != root &&
+          (window = gdk_window_lookup_for_display (display, last)) != NULL &&
+          GDK_WINDOW_TYPE (window) != GDK_WINDOW_FOREIGN)
+        {
+          xwindow = last;
+          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..04424b5
--- /dev/null
+++ b/gdk/x11/gdkdevice-core.h
@@ -0,0 +1,52 @@
+/* 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;
+};
+
+G_GNUC_INTERNAL
+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..b82ff18
--- /dev/null
+++ b/gdk/x11/gdkdevice-xi.c
@@ -0,0 +1,624 @@
+/* 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 <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..55f33d3
--- /dev/null
+++ b/gdk/x11/gdkdevice-xi.h
@@ -0,0 +1,88 @@
+/* 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;
+};
+
+G_GNUC_INTERNAL
+GType gdk_device_xi_get_type (void) G_GNUC_CONST;
+
+G_GNUC_INTERNAL
+void     gdk_device_xi_update_window_info (GdkWindow *window);
+
+G_GNUC_INTERNAL
+void     gdk_device_xi_update_axes (GdkDevice *device,
+                                    gint       axes_count,
+                                    gint       first_axis,
+                                    gint      *axis_data);
+G_GNUC_INTERNAL
+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..d7bc64e
--- /dev/null
+++ b/gdk/x11/gdkdevice-xi2.c
@@ -0,0 +1,621 @@
+/* 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 <X11/extensions/XInput2.h>
+#include "gdkdevice-xi2.h"
+#include "gdkintl.h"
+#include "gdkx.h"
+
+
+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)
+{
+  GdkDeviceXI2Private *priv;
+
+  device->priv = priv = G_TYPE_INSTANCE_GET_PRIVATE (device,
+                                                     GDK_TYPE_DEVICE_XI2,
+                                                     GdkDeviceXI2Private);
+}
+
+static void
+gdk_device_xi2_get_property (GObject    *object,
+                             guint       prop_id,
+                             GValue     *value,
+                             GParamSpec *pspec)
+{
+  GdkDeviceXI2Private *priv;
+
+  priv = GDK_DEVICE_XI2 (object)->priv;
+
+  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 (object)->priv;
+
+  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)
+{
+  GdkDeviceXI2Private *priv;
+  GdkDisplay *display;
+  XIDeviceInfo *info;
+  gint i, j, ndevices;
+
+  priv = GDK_DEVICE_XI2 (device)->priv;
+  display = gdk_device_get_display (device);
+
+  if (axes)
+    {
+      info = XIQueryDevice(GDK_DISPLAY_XDISPLAY (display),
+                           priv->device_id, &ndevices);
+
+      for (i = 0, j = 0; i < info->num_classes; i++)
+        {
+          XIAnyClassInfo *class_info = info->classes[i];
+          GdkAxisUse use;
+          gdouble value;
+
+          if (class_info->type != XIValuatorClass)
+            continue;
+
+          value = ((XIValuatorClassInfo *) class_info)->value;
+          use = _gdk_device_get_axis_use (device, j);
+
+          switch (use)
+            {
+            case GDK_AXIS_X:
+            case GDK_AXIS_Y:
+            case GDK_AXIS_IGNORE:
+              if (device->mode == GDK_MODE_WINDOW)
+                _gdk_device_translate_window_coord (device, window, j, value, &axes[j]);
+              else
+                {
+                  gint root_x, root_y;
+
+                  /* FIXME: Maybe root coords chaching should happen here */
+                  gdk_window_get_origin (window, &root_x, &root_y);
+                  _gdk_device_translate_screen_coord (device, window,
+                                                      root_x, root_y,
+                                                      j, value,
+                                                      &axes[j]);
+                }
+              break;
+            default:
+              _gdk_device_translate_axis (device, j, value, &axes[j]);
+              break;
+            }
+
+          j++;
+        }
+
+      XIFreeDeviceInfo (info);
+    }
+
+  if (mask)
+    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 (device)->priv;
+
+  /* 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 (device)->priv;
+  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 (device)->priv;
+  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 (device)->priv;
+  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 (device)->priv;
+  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 (device)->priv;
+  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 && last != root &&
+          (window = gdk_window_lookup_for_display (display, last)) != NULL &&
+          GDK_WINDOW_TYPE (window) != GDK_WINDOW_FOREIGN)
+        {
+          xwindow = last;
+          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 (device)->priv;
+
+  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 (!XIMaskIsSet (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..67e4dc9
--- /dev/null
+++ b/gdk/x11/gdkdevice-xi2.h
@@ -0,0 +1,64 @@
+/* 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 _GdkDeviceXI2Private GdkDeviceXI2Private;
+typedef struct _GdkDeviceXI2Class GdkDeviceXI2Class;
+
+struct _GdkDeviceXI2
+{
+  GdkDevice parent_instance;
+
+  /*< private >*/
+  GdkDeviceXI2Private *priv;
+};
+
+struct _GdkDeviceXI2Class
+{
+  GdkDeviceClass parent_class;
+};
+
+G_GNUC_INTERNAL
+GType gdk_device_xi2_get_type (void) G_GNUC_CONST;
+
+G_GNUC_INTERNAL
+guchar * gdk_device_xi2_translate_event_mask (GdkEventMask  event_mask,
+                                              int          *len);
+G_GNUC_INTERNAL
+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..0e7b8c3
--- /dev/null
+++ b/gdk/x11/gdkdevicemanager-core.c
@@ -0,0 +1,904 @@
+/* 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 <gdk/gdktypes.h>
+#include <gdk/gdkdevicemanager.h>
+#include "gdkdevicemanager-core.h"
+#include "gdkeventtranslator.h"
+#include "gdkdevice-core.h"
+#include "gdkkeysyms.h"
+#include "gdkprivate-x11.h"
+#include "gdkx.h"
+#include "gdkalias.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_list_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->list_devices = gdk_device_manager_core_list_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_associated_device (device_manager->core_pointer, device_manager->core_keyboard);
+  _gdk_device_set_associated_device (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);
+  GdkModifierType consumed, state;
+  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;
+  gdk_event_set_device (event, 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, &consumed);
+
+  state = event->key.state & ~consumed;
+  _gdk_keymap_add_virtual_modifiers_compat (keymap, &state);
+  event->key.state |= 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 = gdk_event_new (GDK_FOCUS_CHANGE);
+  event->focus_change.window = g_object_ref (window);
+  event->focus_change.send_event = FALSE;
+  event->focus_change.in = in;
+  gdk_event_set_device (event, device_manager->core_keyboard);
+
+  gdk_event_put (event);
+  gdk_event_free (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 = (gdouble) xevent->xbutton.x;
+	  event->scroll.y = (gdouble) xevent->xbutton.y;
+	  event->scroll.x_root = (gdouble) xevent->xbutton.x_root;
+	  event->scroll.y_root = (gdouble) 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 = (gdouble) xevent->xbutton.x;
+	  event->button.y = (gdouble) xevent->xbutton.y;
+	  event->button.x_root = (gdouble) xevent->xbutton.x_root;
+	  event->button.y_root = (gdouble) 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 = (gdouble) xevent->xbutton.x;
+      event->button.y = (gdouble) xevent->xbutton.y;
+      event->button.x_root = (gdouble) xevent->xbutton.x_root;
+      event->button.y_root = (gdouble) 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 = (gdouble) xevent->xmotion.x;
+      event->motion.y = (gdouble) xevent->xmotion.y;
+      event->motion.x_root = (gdouble) xevent->xmotion.x_root;
+      event->motion.y_root = (gdouble) 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;
+      gdk_event_set_device (event, 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 = (gdouble) xevent->xcrossing.x;
+      event->crossing.y = (gdouble) xevent->xcrossing.y;
+      event->crossing.x_root = (gdouble) xevent->xcrossing.x_root;
+      event->crossing.y_root = (gdouble) 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;
+      gdk_event_set_device (event, 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 = (gdouble) xevent->xcrossing.x;
+      event->crossing.y = (gdouble) xevent->xcrossing.y;
+      event->crossing.x_root = (gdouble) xevent->xcrossing.x_root;
+      event->crossing.y_root = (gdouble) 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:
+        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_list_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;
+}
+
+#define __GDK_DEVICE_MANAGER_CORE_C__
+#include "gdkaliasdef.c"
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..335764b
--- /dev/null
+++ b/gdk/x11/gdkdevicemanager-x11.c
@@ -0,0 +1,76 @@
+/* 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_XFREE
+#include "gdkdevicemanager-xi.h"
+#ifdef XINPUT_2
+#include "gdkdevicemanager-xi2.h"
+#endif
+#endif
+
+GdkDeviceManager *
+_gdk_device_manager_new (GdkDisplay *display)
+{
+  if (!g_getenv ("GDK_CORE_DEVICE_EVENTS"))
+    {
+#if defined (XINPUT_2) || defined (XINPUT_XFREE)
+      int opcode, firstevent, firsterror;
+      Display *xdisplay;
+
+      xdisplay = GDK_DISPLAY_XDISPLAY (display);
+
+      if (XQueryExtension (xdisplay, "XInputExtension",
+                           &opcode, &firstevent, &firsterror))
+        {
+#ifdef XINPUT_2
+          int major, minor;
+
+          major = 2;
+          minor = 0;
+
+          if (_gdk_enable_multidevice &&
+              XIQueryVersion (xdisplay, &major, &minor) != BadRequest)
+            {
+              GdkDeviceManagerXI2 *device_manager_xi2;
+
+              device_manager_xi2 = g_object_new (GDK_TYPE_DEVICE_MANAGER_XI2,
+                                                 "display", display,
+                                                 NULL);
+              device_manager_xi2->opcode = opcode;
+
+              return GDK_DEVICE_MANAGER (device_manager_xi2);
+            }
+          else
+#endif /* XINPUT_2 */
+            return g_object_new (GDK_TYPE_DEVICE_MANAGER_XI,
+                                 "display", display,
+                                 "event-base", firstevent,
+                                 NULL);
+        }
+#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..c1c3390
--- /dev/null
+++ b/gdk/x11/gdkdevicemanager-xi.c
@@ -0,0 +1,650 @@
+/* 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 "gdkdevicemanager-xi.h"
+#include "gdkeventtranslator.h"
+#include "gdkdevice-xi.h"
+#include "gdkintl.h"
+#include "gdkx.h"
+
+#include <X11/extensions/XInput.h>
+
+
+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_list_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->list_devices = gdk_device_manager_xi_list_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;
+
+  device_manager->priv = priv = G_TYPE_INSTANCE_GET_PRIVATE (device_manager,
+                                                             GDK_TYPE_DEVICE_MANAGER_XI,
+                                                             GdkDeviceManagerXIPrivate);
+
+  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 (object)->priv;
+  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 (object)->priv;
+
+  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 (object)->priv;
+
+  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 (object)->priv;
+
+  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 (device_manager)->priv;
+
+  /* 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;
+  GdkDeviceManagerXI *device_manager;
+  GdkEventTranslatorIface *parent_iface;
+  GdkDeviceXI *device_xi;
+  GdkDevice *device;
+  GdkWindow *window;
+
+  parent_iface = g_type_interface_peek_parent (GDK_EVENT_TRANSLATOR_GET_IFACE (translator));
+  device_manager = GDK_DEVICE_MANAGER_XI (translator);
+  priv = device_manager->priv;
+
+  if (!priv->ignore_core_events &&
+      parent_iface->translate_event (translator, display, event, xevent))
+    return TRUE;
+
+  device = lookup_device (device_manager, 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_list_devices (GdkDeviceManager *device_manager,
+                                    GdkDeviceType     type)
+{
+  GdkDeviceManagerXIPrivate *priv;
+
+  priv = GDK_DEVICE_MANAGER_XI (device_manager)->priv;
+
+  if (type == GDK_DEVICE_TYPE_MASTER)
+    return GDK_DEVICE_MANAGER_CLASS (gdk_device_manager_xi_parent_class)->list_devices (device_manager, type);
+  else if (type == GDK_DEVICE_TYPE_FLOATING)
+    {
+      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..6fd7d6d
--- /dev/null
+++ b/gdk/x11/gdkdevicemanager-xi.h
@@ -0,0 +1,58 @@
+/* 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 _GdkDeviceManagerXIPrivate GdkDeviceManagerXIPrivate;
+typedef struct _GdkDeviceManagerXIClass GdkDeviceManagerXIClass;
+
+struct _GdkDeviceManagerXI
+{
+  GdkDeviceManagerCore parent_object;
+  GdkDevice *core_pointer;
+  GdkDevice *core_keyboard;
+
+  /*< private >*/
+  GdkDeviceManagerXIPrivate *priv;
+};
+
+struct _GdkDeviceManagerXIClass
+{
+  GdkDeviceManagerCoreClass parent_class;
+};
+
+G_GNUC_INTERNAL
+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..9915a3a
--- /dev/null
+++ b/gdk/x11/gdkdevicemanager-xi2.c
@@ -0,0 +1,1146 @@
+/* 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 <string.h>
+#include <gdk/gdkdeviceprivate.h>
+
+#include "gdkdevicemanager-xi2.h"
+#include "gdkeventtranslator.h"
+#include "gdkdevice-xi2.h"
+#include "gdkkeysyms.h"
+#include "gdkprivate-x11.h"
+#include "gdkx.h"
+#include "gdkalias.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_list_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->list_devices = gdk_device_manager_xi2_list_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_associated_device (device, relative);
+  _gdk_device_set_associated_device (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_list_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);
+        GdkModifierType consumed, state;
+        GdkDevice *device;
+
+        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);
+
+        device = g_hash_table_lookup (device_manager->id_table,
+                                      GUINT_TO_POINTER (xev->deviceid));
+        gdk_event_set_device (event, device);
+
+        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, &consumed);
+
+        state = event->key.state & ~consumed;
+        _gdk_keymap_add_virtual_modifiers_compat (keymap, &state);
+        event->key.state |= state;
+
+        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;
+        GdkDevice *device;
+
+        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);
+
+        device = g_hash_table_lookup (device_manager->id_table,
+                                      GINT_TO_POINTER (xev->deviceid));
+        gdk_event_set_device (event, device);
+
+        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);
+}
+
+#define __GDK_DEVICE_MANAGER_XI2_C__
+#include "gdkaliasdef.c"
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 6cded79..a8c714d 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,1013 @@ _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
+#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);
+    }
+  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_list_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_list_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 +1234,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 +1427,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 +1624,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 +1640,47 @@ 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.
+ * Release any grab on @device.
  *
- * Since: 2.2
+ * Since: 3.0
  */
 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
  *
@@ -829,7 +1832,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);
 }
@@ -1479,6 +2487,185 @@ 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
+ *
+ * Deprecated: 3.0. Use gdk_device_manager_list_devices() instead.
+ **/
+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);
+}
 
 #define __GDK_DISPLAY_X11_C__
 #include "gdkaliasdef.c"
diff --git a/gdk/x11/gdkdnd-x11.c b/gdk/x11/gdkdnd-x11.c
index 8f67300..acce83b 100644
--- a/gdk/x11/gdkdnd-x11.c
+++ b/gdk/x11/gdkdnd-x11.c
@@ -96,6 +96,7 @@ struct _GdkDragContextPrivateX11 {
   guint version;                /* Xdnd protocol version */
 
   GSList *window_caches;
+  GdkDevice *device;
 };
 
 #define PRIVATE_DATA(context) ((GdkDragContextPrivateX11 *) GDK_DRAG_CONTEXT (context)->windowing_data)
@@ -220,6 +221,55 @@ gdk_drag_context_new (void)
   return g_object_new (GDK_TYPE_DRAG_CONTEXT, NULL);
 }
 
+/**
+ * gdk_drag_context_set_device:
+ * @context: a #GdkDragContext
+ * @device: a #GdkDevice
+ *
+ * Associates a #GdkDevice to @context, so all Drag and Drop events
+ * for @context are emitted as if they came from this device.
+ **/
+void
+gdk_drag_context_set_device (GdkDragContext *context,
+                             GdkDevice      *device)
+{
+  GdkDragContextPrivateX11 *private;
+
+  g_return_if_fail (GDK_IS_DRAG_CONTEXT (context));
+  g_return_if_fail (GDK_IS_DEVICE (device));
+
+  private = PRIVATE_DATA (context);
+
+  if (private->device)
+    {
+      g_object_unref (private->device);
+      private->device = NULL;
+    }
+
+  if (device)
+    private->device = g_object_ref (device);
+}
+
+/**
+ * gdk_drag_context_get_device:
+ * @context: a #GdkDragContext
+ *
+ * Returns the #GdkDevice associated to the drag context.
+ *
+ * Returns: The #GdkDevice associated to @context.
+ **/
+GdkDevice *
+gdk_drag_context_get_device (GdkDragContext *context)
+{
+  GdkDragContextPrivateX11 *private;
+
+  g_return_val_if_fail (GDK_IS_DRAG_CONTEXT (context), NULL);
+
+  private = PRIVATE_DATA (context);
+
+  return private->device;
+}
+
 static GdkDragContext *
 gdk_drag_context_find (GdkDisplay *display,
 		       gboolean    is_source,
@@ -2083,6 +2133,7 @@ xdnd_status_filter (GdkXEvent *xev,
       event->dnd.send_event = FALSE;
       event->dnd.type = GDK_DRAG_STATUS;
       event->dnd.context = context;
+      gdk_event_set_device (event, gdk_drag_context_get_device (context));
       g_object_ref (context);
 
       event->dnd.time = GDK_CURRENT_TIME; /* FIXME? */
@@ -2130,6 +2181,7 @@ xdnd_finished_filter (GdkXEvent *xev,
       
       event->dnd.type = GDK_DROP_FINISHED;
       event->dnd.context = context;
+      gdk_event_set_device (event, gdk_drag_context_get_device (context));
       g_object_ref (context);
 
       event->dnd.time = GDK_CURRENT_TIME; /* FIXME? */
@@ -2237,7 +2289,7 @@ send_client_message_async_cb (Window   window,
       context->dest_window &&
       window == GDK_WINDOW_XID (context->dest_window))
     {
-      GdkEvent temp_event;
+      GdkEvent *temp_event;
       GdkDragContextPrivateX11 *private = PRIVATE_DATA (context);
 
       g_object_unref (context->dest_window);
@@ -2246,13 +2298,16 @@ send_client_message_async_cb (Window   window,
 
       private->drag_status = GDK_DRAG_STATUS_DRAG;
 
-      temp_event.dnd.type = GDK_DRAG_STATUS;
-      temp_event.dnd.window = context->source_window;
-      temp_event.dnd.send_event = TRUE;
-      temp_event.dnd.context = context;
-      temp_event.dnd.time = GDK_CURRENT_TIME;
+      temp_event = gdk_event_new (GDK_DRAG_STATUS);
+      temp_event->dnd.window = g_object_ref (context->source_window);
+      temp_event->dnd.send_event = TRUE;
+      temp_event->dnd.context = g_object_ref (context);
+      temp_event->dnd.time = GDK_CURRENT_TIME;
+      gdk_event_set_device (temp_event, gdk_drag_context_get_device (context));
 
-      gdk_event_put (&temp_event);
+      gdk_event_put (temp_event);
+
+      gdk_event_free (temp_event);
     }
 
   g_object_unref (context);
@@ -2309,13 +2364,15 @@ xdnd_send_xevent (GdkDragContext *context,
 	  if (gdk_x11_get_xatom_by_name_for_display (display, xdnd_filters[i].atom_name) ==
 	      event_send->xclient.message_type)
 	    {
-	      GdkEvent temp_event;
-	      temp_event.any.window = window;
+	      GdkEvent *temp_event;
+
+              temp_event = gdk_event_new (GDK_NOTHING);
+              temp_event->any.window = g_object_ref (window);
 
-	      if  ((*xdnd_filters[i].func) (event_send, &temp_event, NULL) == GDK_FILTER_TRANSLATE)
+	      if ((*xdnd_filters[i].func) (event_send, temp_event, NULL) == GDK_FILTER_TRANSLATE)
 		{
-		  gdk_event_put (&temp_event);
-		  g_object_unref (temp_event.dnd.context);
+		  gdk_event_put (temp_event);
+                  gdk_event_free (temp_event);
 		}
 	      
 	      return TRUE;
@@ -2806,6 +2863,9 @@ xdnd_enter_filter (GdkXEvent *xev,
   new_context->protocol = GDK_DRAG_PROTO_XDND;
   PRIVATE_DATA(new_context)->version = version;
 
+  /* FIXME: Should extend DnD protocol to have device info */
+  gdk_drag_context_set_device (new_context, gdk_display_get_core_pointer (display));
+
   new_context->source_window = gdk_window_lookup_for_display (display, source_window);
   if (new_context->source_window)
     g_object_ref (new_context->source_window);
@@ -2872,6 +2932,7 @@ xdnd_enter_filter (GdkXEvent *xev,
 
   event->dnd.type = GDK_DRAG_ENTER;
   event->dnd.context = new_context;
+  gdk_event_set_device (event, gdk_drag_context_get_device (new_context));
   g_object_ref (new_context);
 
   display_x11->current_dest_drag = new_context;
@@ -2909,6 +2970,7 @@ xdnd_leave_filter (GdkXEvent *xev,
       event->dnd.type = GDK_DRAG_LEAVE;
       /* Pass ownership of context to the event */
       event->dnd.context = display_x11->current_dest_drag;
+      gdk_event_set_device (event, gdk_drag_context_get_device (event->dnd.context));
 
       display_x11->current_dest_drag = NULL;
 
@@ -2952,6 +3014,7 @@ xdnd_position_filter (GdkXEvent *xev,
     {
       event->dnd.type = GDK_DRAG_MOTION;
       event->dnd.context = display_x11->current_dest_drag;
+      gdk_event_set_device (event, gdk_drag_context_get_device (event->dnd.context));
       g_object_ref (display_x11->current_dest_drag);
 
       event->dnd.time = time;
@@ -3007,6 +3070,7 @@ xdnd_drop_filter (GdkXEvent *xev,
       event->dnd.type = GDK_DROP_START;
 
       event->dnd.context = display_x11->current_dest_drag;
+      gdk_event_set_device (event, gdk_drag_context_get_device (event->dnd.context));
       g_object_ref (display_x11->current_dest_drag);
 
       event->dnd.time = time;
@@ -3086,6 +3150,8 @@ gdk_drag_begin (GdkWindow     *window,
 		GList         *targets)
 {
   GdkDragContext *new_context;
+  GdkDisplay *display;
+  GdkDevice *device;
   
   g_return_val_if_fail (window != NULL, NULL);
   g_return_val_if_fail (GDK_WINDOW_IS_X11 (window), NULL);
@@ -3100,6 +3166,10 @@ gdk_drag_begin (GdkWindow     *window,
   
   new_context->actions = 0;
 
+  display = gdk_drawable_get_display (GDK_DRAWABLE (window));
+  device = gdk_display_get_core_pointer (display);
+  gdk_drag_context_set_device (new_context, device);
+
   return new_context;
 }
 
@@ -3421,7 +3491,7 @@ gdk_drag_motion (GdkDragContext *context,
 
   if (context->dest_window != dest_window)
     {
-      GdkEvent temp_event;
+      GdkEvent *temp_event;
 
       /* Send a leave to the last destination */
       gdk_drag_do_leave (context, time);
@@ -3465,18 +3535,19 @@ gdk_drag_motion (GdkDragContext *context,
       /* Push a status event, to let the client know that
        * the drag changed 
        */
-
-      temp_event.dnd.type = GDK_DRAG_STATUS;
-      temp_event.dnd.window = context->source_window;
+      temp_event = gdk_event_new (GDK_DRAG_STATUS);
+      temp_event->dnd.window = g_object_ref (context->source_window);
       /* We use this to signal a synthetic status. Perhaps
        * we should use an extra field...
        */
-      temp_event.dnd.send_event = TRUE;
+      temp_event->dnd.send_event = TRUE;
 
-      temp_event.dnd.context = context;
-      temp_event.dnd.time = time;
+      temp_event->dnd.context = g_object_ref (context);
+      temp_event->dnd.time = time;
+      gdk_event_set_device (temp_event, gdk_drag_context_get_device (context));
 
-      gdk_event_put (&temp_event);
+      gdk_event_put (temp_event);
+      gdk_event_free (temp_event);
     }
   else
     {
@@ -3505,7 +3576,7 @@ gdk_drag_motion (GdkDragContext *context,
 
 	    case GDK_DRAG_PROTO_ROOTWIN:
 	      {
-		GdkEvent temp_event;
+		GdkEvent *temp_event;
 		/* GTK+ traditionally has used application/x-rootwin-drop,
 		 * but the XDND spec specifies x-rootwindow-drop.
 		 */
@@ -3520,13 +3591,15 @@ gdk_drag_motion (GdkDragContext *context,
 		else
 		  context->action = 0;
 
-		temp_event.dnd.type = GDK_DRAG_STATUS;
-		temp_event.dnd.window = context->source_window;
-		temp_event.dnd.send_event = FALSE;
-		temp_event.dnd.context = context;
-		temp_event.dnd.time = time;
+                temp_event = gdk_event_new (GDK_DRAG_STATUS);
+		temp_event->dnd.window = g_object_ref (context->source_window);
+		temp_event->dnd.send_event = FALSE;
+		temp_event->dnd.context = g_object_ref (context);
+		temp_event->dnd.time = time;
+                gdk_event_set_device (temp_event, gdk_drag_context_get_device (context));
 
-		gdk_event_put (&temp_event);
+		gdk_event_put (temp_event);
+                gdk_event_free (temp_event);
 	      }
 	      break;
 	    case GDK_DRAG_PROTO_NONE:
diff --git a/gdk/x11/gdkeventsource.c b/gdk/x11/gdkeventsource.c
new file mode 100644
index 0000000..e0ab3be
--- /dev/null
+++ b/gdk/x11/gdkeventsource.c
@@ -0,0 +1,427 @@
+/* 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 "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 = gdk_event_new (GDK_FOCUS_CHANGE);
+      focus_event->focus_change.window = g_object_ref (event->window);
+      focus_event->focus_change.send_event = FALSE;
+      focus_event->focus_change.in = focus_in;
+      gdk_event_set_device (focus_event, gdk_event_get_device ((GdkEvent *) event));
+
+      gdk_event_put (focus_event);
+      gdk_event_free (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);
+
+  if (filter_window)
+    {
+      /* Apply per-window filters */
+      GdkWindowObject *filter_private = (GdkWindowObject *) filter_window;
+      GdkFilterReturn result;
+
+      event->any.window = g_object_ref (filter_window);
+
+      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_append (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..4fc0dbe
--- /dev/null
+++ b/gdk/x11/gdkeventsource.h
@@ -0,0 +1,46 @@
+/* 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;
+
+G_GNUC_INTERNAL
+GSource * gdk_event_source_new            (GdkDisplay *display);
+
+G_GNUC_INTERNAL
+void      gdk_event_source_add_translator (GdkEventSource     *source,
+                                           GdkEventTranslator *translator);
+
+G_GNUC_INTERNAL
+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..753dfc8
--- /dev/null
+++ b/gdk/x11/gdkeventtranslator.c
@@ -0,0 +1,100 @@
+/* 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 "gdkeventtranslator.h"
+#include "gdkalias.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);
+}
+
+#define __GDK_EVENT_TRANSLATOR_C__
+#include "gdkaliasdef.c"
diff --git a/gdk/x11/gdkeventtranslator.h b/gdk/x11/gdkeventtranslator.h
new file mode 100644
index 0000000..62c99e3
--- /dev/null
+++ b/gdk/x11/gdkeventtranslator.h
@@ -0,0 +1,65 @@
+/* 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.c b/gdk/x11/gdkinput.c
index fb2f810..208d2c3 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)
-    {
-      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);
-
-  object_class->dispose  = gdk_device_dispose;
-}
+  GList *windows; /* GdkWindow:s with extension_events set */
 
-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:
@@ -144,6 +52,8 @@ gdk_device_dispose (GObject *object)
  * The list is statically allocated and should not be freed.
  *
  * Return value: a list of #GdkDevice
+ *
+ * Deprecated: 3.0. Use gdk_device_manager_list_devices() instead.
  **/
 GList *
 gdk_devices_list (void)
@@ -151,348 +61,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;
-}
-
-/**
- * gdk_device_get_name:
- * @device: a #GdkDevice
- *
- * Determines the name of the device.
- *
- * Return value: a name
- *
- * Since: 2.22
- **/
-const gchar *
-gdk_device_get_name (GdkDevice *device)
-{
-  g_return_val_if_fail (GDK_IS_DEVICE (device), NULL);
-
-  return device->name;
-}
-
-/**
- * gdk_device_get_source:
- * @device: a #GdkDevice
- *
- * Determines the type of the device.
- *
- * Return value: a #GdkInputSource
- *
- * Since: 2.22
- **/
-GdkInputSource
-gdk_device_get_source (GdkDevice *device)
-{
-  g_return_val_if_fail (GDK_IS_DEVICE (device), 0);
-
-  return device->source;
-}
-
-/**
- * gdk_device_get_mode:
- * @device: a #GdkDevice
- *
- * Determines the mode of the device.
- *
- * Return value: a #GdkInputSource
- *
- * Since: 2.22
- **/
-GdkInputMode
-gdk_device_get_mode (GdkDevice *device)
-{
-  g_return_val_if_fail (GDK_IS_DEVICE (device), 0);
-
-  return device->mode;
-}
-
-/**
- * gdk_device_get_has_cursor:
- * @device: a #GdkDevice
- *
- * Determines whether the pointer follows device motion.
- *
- * Return value: %TRUE if the pointer follows device motion
- *
- * Since: 2.22
- **/
-gboolean
-gdk_device_get_has_cursor (GdkDevice *device)
-{
-  g_return_val_if_fail (GDK_IS_DEVICE (device), FALSE);
-
-  return device->has_cursor;
-}
-
-void
-gdk_device_set_source (GdkDevice      *device,
-		       GdkInputSource  source)
-{
-  g_return_if_fail (device != NULL);
-
-  device->source = source;
-}
-
-/**
- * gdk_device_get_key:
- * @device: a #GdkDevice.
- * @index: the index of the macro button to get.
- * @keyval: return value for the keyval.
- * @modifiers: return value for modifiers.
- *
- * If @index has a valid keyval, this function will
- * fill in @keyval and @modifiers with the keyval settings.
- *
- * Since: 2.22
- **/
-void
-gdk_device_get_key (GdkDevice       *device,
-                    guint            index,
-                    guint           *keyval,
-                    GdkModifierType *modifiers)
+static void
+_gdk_input_select_device_events (GdkWindow *impl_window,
+                                 GdkDevice *device)
 {
-  g_return_if_fail (GDK_IS_DEVICE (device));
-  g_return_if_fail (index < device->num_keys);
-
-  if (!device->keys[index].keyval &&
-      !device->keys[index].modifiers)
+  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;
 
-  if (keyval)
-    *keyval = device->keys[index].keyval;
-
-  if (modifiers)
-    *modifiers = device->keys[index].modifiers;
-}
-
-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;
-}
-
-/**
- * gdk_device_get_axis_use:
- * @device: a #GdkDevice.
- * @index: the index of the axis.
- *
- * Returns the axis use for @index.
- *
- * Returns: a #GdkAxisUse specifying how the axis is used.
- *
- * Since: 2.22
- **/
-GdkAxisUse
-gdk_device_get_axis_use (GdkDevice *device,
-                         guint      index)
-{
-  g_return_val_if_fail (GDK_IS_DEVICE (device), GDK_AXIS_IGNORE);
-  g_return_val_if_fail (index < device->num_axes, GDK_AXIS_IGNORE);
-
-  return device->axes[index].use;
-}
-
-void
-gdk_device_set_axis_use (GdkDevice   *device,
-			 guint        index,
-			 GdkAxisUse   use)
-{
-  g_return_if_fail (device != NULL);
-  g_return_if_fail (index < device->num_axes);
-
-  device->axes[index].use = use;
-
-  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;
-    }
-}
-
-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;
-}
+      w = l->data;
 
-/**
- * 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: (array length=n_events) (out) (transfer none): 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;
+      if (has_cursor || (w->extension_events & GDK_ALL_DEVICES_MASK))
+        {
+          event_mask = w->extension_events;
 
-  g_return_val_if_fail (GDK_WINDOW_IS_X11 (window), FALSE);
+          if (event_mask)
+            event_mask |= GDK_PROXIMITY_OUT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK;
 
-  impl_window = _gdk_window_get_impl_window (window);
-
-  if (GDK_WINDOW_DESTROYED (window))
-    /* Nothing */ ;
-  else if (GDK_IS_CORE (device))
-    {
-      XTimeCoord *xcoords;
-
-      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;
-}
-
-/**
- * gdk_device_free_history:
- * @events: (inout) (transfer none): an array of #GdkTimeCoord.
- * @n_events: the length of the array.
- *
- * Frees an array of #GdkTimeCoord that was returned by gdk_device_get_history().
- */
-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
@@ -526,9 +134,21 @@ unset_extension_events (GdkWindow *window)
   window_private->extension_events = 0;
 }
 
+/**
+ * gdk_input_set_extension_events:
+ * @window: a #GdkWindow.
+ * @mask: the event mask
+ * @mode: the type of extension events that are desired.
+ *
+ * Turns extension events on or off for a particular window,
+ * and specifies the event mask for extension events.
+ *
+ * Deprecated: 3.0. Use gdk_window_set_device_events() instead.
+ **/
 void
-gdk_input_set_extension_events (GdkWindow *window, gint mask,
-				GdkExtensionMode mode)
+gdk_input_set_extension_events (GdkWindow        *window,
+                                gint              mask,
+				GdkExtensionMode  mode)
 {
   GdkWindowObject *window_private;
   GdkWindowObject *impl_window;
@@ -565,15 +185,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;
 	}
 
@@ -589,10 +202,8 @@ gdk_input_set_extension_events (GdkWindow *window, gint mask,
 #ifndef XINPUT_NONE
   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);
     }
 #endif /* !XINPUT_NONE */
 }
@@ -603,40 +214,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/gdkmain-x11.c b/gdk/x11/gdkmain-x11.c
index f12c709..bf53d65 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,8 +118,8 @@ gdk_get_use_xshm (void)
   return GDK_DISPLAY_X11 (gdk_display_get_default ())->use_xshm;
 }
 
-static GdkGrabStatus
-gdk_x11_convert_grab_status (gint status)
+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_list_devices (device_manager, GDK_DEVICE_TYPE_MASTER);
+  devices = g_list_concat (devices, gdk_device_manager_list_devices (device_manager, GDK_DEVICE_TYPE_SLAVE));
+  devices = g_list_concat (devices, gdk_device_manager_list_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_list_devices (device_manager, GDK_DEVICE_TYPE_MASTER);
+  devices = g_list_concat (devices, gdk_device_manager_list_devices (device_manager, GDK_DEVICE_TYPE_SLAVE));
+  devices = g_list_concat (devices, gdk_device_manager_list_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 05c033a..00cde1b 100644
--- a/gdk/x11/gdkprivate-x11.h
+++ b/gdk/x11/gdkprivate-x11.h
@@ -177,8 +177,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);
@@ -186,7 +185,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);
@@ -198,6 +196,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 3350162..60df9b1 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)
     {
@@ -1434,5 +1456,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 92a64fb..1eedd43 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
@@ -2647,41 +2663,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 *
@@ -3008,106 +3013,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;
 }
 
@@ -3131,6 +3142,8 @@ gdk_window_x11_get_pointer (GdkWindow       *window,
  * for the color picker in the #GtkColorSelectionDialog.
  *
  * Since: 2.8
+ *
+ * Deprecated: 3.0. Use gdk_display_warp_device() instead.
  */ 
 void
 gdk_display_warp_pointer (GdkDisplay *display,
@@ -3138,37 +3151,64 @@ 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.
+ *
+ * Since: 3.0
+ **/
+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
@@ -3176,35 +3216,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 && xwindow_last != root &&
-	      (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) {
@@ -3265,16 +3294,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;
 }
 
@@ -3305,21 +3335,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);
     }
 }
 
@@ -5529,10 +5556,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;
@@ -5540,10 +5567,63 @@ 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;
   iface->supports_native_bg = TRUE;
 }
 
+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__
 #include "gdkaliasdef.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/gtk.symbols b/gtk/gtk.symbols
index 7a9644d..8f33402 100644
--- a/gtk/gtk.symbols
+++ b/gtk/gtk.symbols
@@ -804,6 +804,7 @@ gtk_combo_box_new_text
 gtk_combo_box_new_with_model
 gtk_combo_box_popdown
 gtk_combo_box_popup
+gtk_combo_box_popup_for_device
 gtk_combo_box_prepend_text
 gtk_combo_box_remove_text
 gtk_combo_box_set_active
@@ -1950,6 +1951,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
 gtk_false G_GNUC_CONST
@@ -1963,6 +1965,8 @@ gtk_get_event_widget
 gtk_grab_add
 gtk_grab_get_current
 gtk_grab_remove
+gtk_device_grab_add
+gtk_device_grab_remove
 gtk_propagate_event
 gtk_quit_add
 gtk_quit_add_destroy
@@ -2020,6 +2024,7 @@ gtk_menu_get_type G_GNUC_CONST
 gtk_menu_new
 gtk_menu_popdown
 gtk_menu_popup
+gtk_menu_popup_for_device
 gtk_menu_reorder_child
 gtk_menu_reposition
 gtk_menu_set_accel_group
@@ -4304,6 +4309,7 @@ gtk_widget_activate
 gtk_widget_is_composited
 gtk_widget_add_accelerator
 gtk_widget_add_events
+gtk_widget_add_device_events
 gtk_widget_add_mnemonic_label
 gtk_widget_can_activate_accel
 gtk_widget_child_focus
@@ -4338,6 +4344,7 @@ gtk_widget_get_direction
 gtk_widget_get_display
 gtk_widget_get_double_buffered
 gtk_widget_get_events
+gtk_widget_get_device_events
 gtk_widget_get_extension_events
 gtk_widget_get_has_tooltip
 gtk_widget_get_modifier_style
@@ -4424,6 +4431,7 @@ gtk_widget_set_default_direction
 gtk_widget_set_direction
 gtk_widget_set_double_buffered
 gtk_widget_set_events
+gtk_widget_set_device_events
 gtk_widget_set_extension_events
 gtk_widget_set_has_tooltip
 gtk_widget_set_name
@@ -4470,6 +4478,9 @@ gtk_widget_set_realized
 gtk_widget_get_realized
 gtk_widget_set_mapped
 gtk_widget_get_mapped
+gtk_widget_get_support_multidevice
+gtk_widget_set_support_multidevice
+gtk_widget_device_is_shadowed
 #endif
 #endif
 
@@ -4523,6 +4534,7 @@ gtk_window_group_get_type G_GNUC_CONST
 gtk_window_group_new
 gtk_window_group_remove_window
 gtk_window_group_list_windows
+gtk_window_group_get_current_device_grab
 gtk_window_has_toplevel_focus
 gtk_window_iconify
 gtk_window_is_active
diff --git a/gtk/gtkaboutdialog.c b/gtk/gtkaboutdialog.c
index 54e70d6..6b17f1c 100644
--- a/gtk/gtkaboutdialog.c
+++ b/gtk/gtkaboutdialog.c
@@ -194,9 +194,10 @@ static void                 follow_if_link                  (GtkAboutDialog
                                                              GtkTextView        *text_view,
                                                              GtkTextIter        *iter);
 static void                 set_cursor_if_appropriate       (GtkAboutDialog     *about,
-                                                             GtkTextView        *text_view,
-                                                             gint                x,
-                                                             gint                y);
+							     GtkTextView        *text_view,
+                                                             GdkDevice          *device,
+							     gint                x,
+							     gint                y);
 static void                 display_credits_dialog          (GtkWidget          *button,
                                                              gpointer            data);
 static void                 display_license_dialog          (GtkWidget          *button,
@@ -1866,9 +1867,10 @@ text_view_event_after (GtkWidget      *text_view,
 
 static void
 set_cursor_if_appropriate (GtkAboutDialog *about,
-                           GtkTextView    *text_view,
-                           gint            x,
-                           gint            y)
+			   GtkTextView    *text_view,
+                           GdkDevice      *device,
+			   gint            x,
+			   gint            y)
 {
   GtkAboutDialogPrivate *priv = about->priv;
   GSList *tags = NULL, *tagp = NULL;
@@ -1896,9 +1898,9 @@ set_cursor_if_appropriate (GtkAboutDialog *about,
       priv->hovering_over_link = hovering_over_link;
 
       if (hovering_over_link)
-        gdk_window_set_cursor (gtk_text_view_get_window (text_view, GTK_TEXT_WINDOW_TEXT), priv->hand_cursor);
+        gdk_window_set_device_cursor (gtk_text_view_get_window (text_view, GTK_TEXT_WINDOW_TEXT), device, priv->hand_cursor);
       else
-        gdk_window_set_cursor (gtk_text_view_get_window (text_view, GTK_TEXT_WINDOW_TEXT), priv->regular_cursor);
+        gdk_window_set_device_cursor (gtk_text_view_get_window (text_view, GTK_TEXT_WINDOW_TEXT), device, priv->regular_cursor);
     }
 
   if (tags)
@@ -1916,7 +1918,7 @@ text_view_motion_notify_event (GtkWidget *text_view,
                                          GTK_TEXT_WINDOW_WIDGET,
                                          event->x, event->y, &x, &y);
 
-  set_cursor_if_appropriate (about, GTK_TEXT_VIEW (text_view), x, y);
+  set_cursor_if_appropriate (about, GTK_TEXT_VIEW (text_view), event->device, x, y);
 
   gdk_event_request_motions (event);
 
@@ -1929,15 +1931,27 @@ text_view_visibility_notify_event (GtkWidget          *text_view,
                                    GdkEventVisibility *event,
                                    GtkAboutDialog     *about)
 {
+  GdkDeviceManager *device_manager;
+  GdkDisplay *display;
+  GList *devices, *d;
   gint wx, wy, bx, by;
 
-  gdk_window_get_pointer (gtk_widget_get_window (text_view), &wx, &wy, NULL);
+  display = gdk_drawable_get_display (event->window);
+  device_manager = gdk_display_get_device_manager (display);
+  devices = gdk_device_manager_list_devices (device_manager, GDK_DEVICE_TYPE_MASTER);
 
-  gtk_text_view_window_to_buffer_coords (GTK_TEXT_VIEW (text_view),
-                                         GTK_TEXT_WINDOW_WIDGET,
-                                         wx, wy, &bx, &by);
+  for (d = devices; d; d = d->next)
+    {
+      GdkDevice *dev = d->data;
 
-  set_cursor_if_appropriate (about, GTK_TEXT_VIEW (text_view), bx, by);
+      gdk_window_get_device_position (text_view->window, dev, &wx, &wy, NULL);
+
+      gtk_text_view_window_to_buffer_coords (GTK_TEXT_VIEW (text_view),
+                                             GTK_TEXT_WINDOW_WIDGET,
+                                             wx, wy, &bx, &by);
+
+      set_cursor_if_appropriate (about, GTK_TEXT_VIEW (text_view), dev, bx, by);
+    }
 
   return FALSE;
 }
diff --git a/gtk/gtkbutton.c b/gtk/gtkbutton.c
index 37367b3..4ebe9a0 100644
--- a/gtk/gtkbutton.c
+++ b/gtk/gtkbutton.c
@@ -88,9 +88,9 @@ struct _GtkButtonPrivate
   GtkWidget      *image;
   guint           align_set             : 1;
   guint           image_is_stock        : 1;
-  guint           has_grab              : 1;
   guint           use_action_appearance : 1;
   guint32         grab_time;
+  GdkDevice      *grab_keyboard;
   GtkPositionType image_position;
   GtkAction      *action;
 };
@@ -1715,22 +1715,28 @@ gtk_real_button_activate (GtkButton *button)
 {
   GtkWidget *widget = GTK_WIDGET (button);
   GtkButtonPrivate *priv;
+  GdkDevice *device;
   guint32 time;
 
   priv = GTK_BUTTON_GET_PRIVATE (button);
+  device = gtk_get_current_event_device ();
+
+  g_return_if_fail (device && device->source == GDK_SOURCE_KEYBOARD);
 
   if (gtk_widget_get_realized (widget) && !button->activate_timeout)
     {
       time = gtk_get_current_event_time ();
-      if (gdk_keyboard_grab (button->event_window, TRUE, time) == 
-	  GDK_GRAB_SUCCESS)
-	{
-	  priv->has_grab = TRUE;
+
+      if (gdk_device_grab (device, button->event_window,
+                           GDK_OWNERSHIP_WINDOW, TRUE,
+                           GDK_KEY_PRESS | GDK_KEY_RELEASE,
+                           NULL, time) == GDK_GRAB_SUCCESS)
+        {
+          gtk_device_grab_add (widget, device, TRUE);
+	  priv->grab_keyboard = device;
 	  priv->grab_time = time;
 	}
 
-      gtk_grab_add (widget);
-      
       button->activate_timeout = gdk_threads_add_timeout (ACTIVATE_TIMEOUT,
 						button_activate_timeout,
 						button);
@@ -1752,12 +1758,12 @@ gtk_button_finish_activate (GtkButton *button,
   g_source_remove (button->activate_timeout);
   button->activate_timeout = 0;
 
-  if (priv->has_grab)
+  if (priv->grab_keyboard)
     {
-      gdk_display_keyboard_ungrab (gtk_widget_get_display (widget),
-				   priv->grab_time);
+      gdk_device_ungrab (priv->grab_keyboard, priv->grab_time);
+      gtk_device_grab_remove (widget, priv->grab_keyboard);
+      priv->grab_keyboard = NULL;
     }
-  gtk_grab_remove (widget);
 
   button->button_down = FALSE;
 
@@ -2247,8 +2253,14 @@ gtk_button_grab_notify (GtkWidget *widget,
 			gboolean   was_grabbed)
 {
   GtkButton *button = GTK_BUTTON (widget);
+  GtkButtonPrivate *priv = GTK_BUTTON_GET_PRIVATE (button);
   gboolean save_in;
 
+  if (button->activate_timeout &&
+      priv->grab_keyboard &&
+      gtk_widget_device_is_shadowed (widget, priv->grab_keyboard))
+    gtk_button_finish_activate (button, FALSE);
+
   if (!was_grabbed)
     {
       save_in = button->in_button;
diff --git a/gtk/gtkcellrendereraccel.c b/gtk/gtkcellrendereraccel.c
index dedbfda..9dfd2c2 100644
--- a/gtk/gtkcellrendereraccel.c
+++ b/gtk/gtkcellrendereraccel.c
@@ -30,6 +30,8 @@
 #include "gtkalias.h"
 
 
+#define GTK_CELL_RENDERER_ACCEL_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GTK_TYPE_CELL_RENDERER_ACCEL, GtkCellRendererAccelPrivate))
+
 static void gtk_cell_renderer_accel_get_property (GObject         *object,
                                                   guint            param_id,
                                                   GValue          *value,
@@ -72,6 +74,14 @@ enum {
   PROP_ACCEL_MODE
 };
 
+typedef struct GtkCellRendererAccelPrivate GtkCellRendererAccelPrivate;
+
+struct GtkCellRendererAccelPrivate
+{
+  GdkDevice *grab_keyboard;
+  GdkDevice *grab_pointer;
+};
+
 static guint signals[LAST_SIGNAL] = { 0 };
 
 G_DEFINE_TYPE (GtkCellRendererAccel, gtk_cell_renderer_accel, GTK_TYPE_CELL_RENDERER_TEXT)
@@ -213,6 +223,8 @@ gtk_cell_renderer_accel_class_init (GtkCellRendererAccelClass *cell_accel_class)
 					 g_cclosure_marshal_VOID__STRING,
 					 G_TYPE_NONE, 1,
 					 G_TYPE_STRING);
+
+  g_type_class_add_private (object_class, sizeof (GtkCellRendererAccelPrivate));
 }
 
 
@@ -405,6 +417,7 @@ grab_key_callback (GtkWidget            *widget,
                    GdkEventKey          *event,
                    GtkCellRendererAccel *accel)
 {
+  GtkCellRendererAccelPrivate *priv;
   GdkModifierType accel_mods = 0;
   guint accel_key;
   gchar *path;
@@ -413,6 +426,7 @@ grab_key_callback (GtkWidget            *widget,
   GdkModifierType consumed_modifiers;
   GdkDisplay *display;
 
+  priv = GTK_CELL_RENDERER_ACCEL_GET_PRIVATE (accel);
   display = gtk_widget_get_display (widget);
 
   if (event->is_modifier)
@@ -471,9 +485,9 @@ grab_key_callback (GtkWidget            *widget,
   edited = TRUE;
 
  out:
-  gtk_grab_remove (accel->grab_widget);
-  gdk_display_keyboard_ungrab (display, event->time);
-  gdk_display_pointer_ungrab (display, event->time);
+  gtk_device_grab_remove (accel->grab_widget, priv->grab_pointer);
+  gdk_device_ungrab (priv->grab_keyboard, event->time);
+  gdk_device_ungrab (priv->grab_pointer, event->time);
 
   path = g_strdup (g_object_get_data (G_OBJECT (accel->edit_widget), "gtk-cell-renderer-text"));
 
@@ -481,7 +495,9 @@ grab_key_callback (GtkWidget            *widget,
   gtk_cell_editable_remove_widget (GTK_CELL_EDITABLE (accel->edit_widget));
   accel->edit_widget = NULL;
   accel->grab_widget = NULL;
-  
+  priv->grab_keyboard = NULL;
+  priv->grab_pointer = NULL;
+
   if (edited)
     g_signal_emit (accel, signals[ACCEL_EDITED], 0, path, 
 		   accel_key, accel_mods, event->hardware_keycode);
@@ -497,11 +513,16 @@ static void
 ungrab_stuff (GtkWidget            *widget,
               GtkCellRendererAccel *accel)
 {
-  GdkDisplay *display = gtk_widget_get_display (widget);
+  GtkCellRendererAccelPrivate *priv;
+
+  priv = GTK_CELL_RENDERER_ACCEL_GET_PRIVATE (accel);
+
+  gtk_device_grab_remove (accel->grab_widget, priv->grab_pointer);
+  gdk_device_ungrab (priv->grab_keyboard, GDK_CURRENT_TIME);
+  gdk_device_ungrab (priv->grab_pointer, GDK_CURRENT_TIME);
 
-  gtk_grab_remove (accel->grab_widget);
-  gdk_display_keyboard_ungrab (display, GDK_CURRENT_TIME);
-  gdk_display_pointer_ungrab (display, GDK_CURRENT_TIME);
+  priv->grab_keyboard = NULL;
+  priv->grab_pointer = NULL;
 
   g_signal_handlers_disconnect_by_func (G_OBJECT (accel->grab_widget),
                                         G_CALLBACK (grab_key_callback),
@@ -548,34 +569,62 @@ gtk_cell_renderer_accel_start_editing (GtkCellRenderer      *cell,
                                        GdkRectangle         *cell_area,
                                        GtkCellRendererState  flags)
 {
+  GtkCellRendererAccelPrivate *priv;
   GtkCellRendererText *celltext;
   GtkCellRendererAccel *accel;
   GtkWidget *label;
   GtkWidget *eventbox;
-  
+  GdkDevice *device, *keyb, *pointer;
+  guint32 time;
+
   celltext = GTK_CELL_RENDERER_TEXT (cell);
   accel = GTK_CELL_RENDERER_ACCEL (cell);
+  priv = GTK_CELL_RENDERER_ACCEL_GET_PRIVATE (cell);
 
   /* If the cell isn't editable we return NULL. */
   if (celltext->editable == FALSE)
     return NULL;
 
   g_return_val_if_fail (widget->window != NULL, NULL);
-  
-  if (gdk_keyboard_grab (widget->window, FALSE,
-                         gdk_event_get_time (event)) != GDK_GRAB_SUCCESS)
+
+  if (event)
+    device = gdk_event_get_device (event);
+  else
+    device = gtk_get_current_event_device ();
+
+  if (!device)
+    return NULL;
+
+  if (device->source == GDK_SOURCE_KEYBOARD)
+    {
+      keyb = device;
+      pointer = gdk_device_get_associated_device (device);
+    }
+  else
+    {
+      pointer = device;
+      keyb = gdk_device_get_associated_device (device);
+    }
+
+  time = gdk_event_get_time (event);
+
+  if (gdk_device_grab (keyb, widget->window,
+                       GDK_OWNERSHIP_WINDOW, FALSE,
+                       GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK,
+                       NULL, time) != GDK_GRAB_SUCCESS)
     return NULL;
 
-  if (gdk_pointer_grab (widget->window, FALSE,
-                        GDK_BUTTON_PRESS_MASK,
-                        NULL, NULL,
-                        gdk_event_get_time (event)) != GDK_GRAB_SUCCESS)
+  if (gdk_device_grab (pointer, widget->window,
+                       GDK_OWNERSHIP_WINDOW, FALSE,
+                       GDK_BUTTON_PRESS_MASK,
+                       NULL, time) != GDK_GRAB_SUCCESS)
     {
-      gdk_display_keyboard_ungrab (gtk_widget_get_display (widget),
-                                   gdk_event_get_time (event));
+      gdk_device_ungrab (keyb, time);
       return NULL;
     }
-  
+
+  priv->grab_keyboard = keyb;
+  priv->grab_pointer = pointer;
   accel->grab_widget = widget;
 
   g_signal_connect (G_OBJECT (widget), "key-press-event",
@@ -609,7 +658,7 @@ gtk_cell_renderer_accel_start_editing (GtkCellRenderer      *cell,
   
   gtk_widget_show_all (accel->edit_widget);
 
-  gtk_grab_add (accel->grab_widget);
+  gtk_device_grab_add (accel->grab_widget, pointer, TRUE);
 
   g_signal_connect (G_OBJECT (accel->edit_widget), "unrealize",
                     G_CALLBACK (ungrab_stuff), accel);
diff --git a/gtk/gtkcolorsel.c b/gtk/gtkcolorsel.c
index 34d2d4a..10ec272 100644
--- a/gtk/gtkcolorsel.c
+++ b/gtk/gtkcolorsel.c
@@ -149,6 +149,8 @@ struct _ColorSelectionPrivate
   /* Window for grabbing on */
   GtkWidget *dropper_grab_widget;
   guint32    grab_time;
+  GdkDevice *keyboard_device;
+  GdkDevice *pointer_device;
 
   /* Connection to settings */
   gulong settings_connection;
@@ -1631,10 +1633,11 @@ make_picker_cursor (GdkScreen *screen)
 }
 
 static void
-grab_color_at_mouse (GdkScreen *screen,
-		     gint       x_root,
-		     gint       y_root,
-		     gpointer   data)
+grab_color_at_pointer (GdkScreen *screen,
+                       GdkDevice *device,
+                       gint       x_root,
+                       gint       y_root,
+                       gpointer   data)
 {
   GdkImage *image;
   guint32 pixel;
@@ -1651,7 +1654,7 @@ grab_color_at_mouse (GdkScreen *screen,
     {
       gint x, y;
       GdkDisplay *display = gdk_screen_get_display (screen);
-      GdkWindow *window = gdk_display_get_window_at_pointer (display, &x, &y);
+      GdkWindow *window = gdk_display_get_window_at_device_position (display, device, &x, &y);
       if (!window)
 	return;
       image = gdk_drawable_get_image (window, x, y, 1, 1);
@@ -1682,18 +1685,19 @@ shutdown_eyedropper (GtkWidget *widget)
 {
   GtkColorSelection *colorsel;
   ColorSelectionPrivate *priv;
-  GdkDisplay *display = gtk_widget_get_display (widget);
 
   colorsel = GTK_COLOR_SELECTION (widget);
-  priv = colorsel->private_data;    
+  priv = colorsel->private_data;
 
   if (priv->has_grab)
     {
-      gdk_display_keyboard_ungrab (display, priv->grab_time);
-      gdk_display_pointer_ungrab (display, priv->grab_time);
-      gtk_grab_remove (priv->dropper_grab_widget);
+      gdk_device_ungrab (priv->keyboard_device, priv->grab_time);
+      gdk_device_ungrab (priv->pointer_device, priv->grab_time);
+      gtk_device_grab_remove (priv->dropper_grab_widget, priv->pointer_device);
 
       priv->has_grab = FALSE;
+      priv->keyboard_device = NULL;
+      priv->pointer_device = NULL;
     }
 }
 
@@ -1702,8 +1706,9 @@ mouse_motion (GtkWidget      *invisible,
 	      GdkEventMotion *event,
 	      gpointer        data)
 {
-  grab_color_at_mouse (gdk_event_get_screen ((GdkEvent *)event),
-		       event->x_root, event->y_root, data); 
+  grab_color_at_pointer (gdk_event_get_screen ((GdkEvent *) event),
+                         gdk_event_get_device ((GdkEvent *) event),
+                         event->x_root, event->y_root, data);
 }
 
 static gboolean
@@ -1716,8 +1721,9 @@ mouse_release (GtkWidget      *invisible,
   if (event->button != 1)
     return FALSE;
 
-  grab_color_at_mouse (gdk_event_get_screen ((GdkEvent *)event),
-		       event->x_root, event->y_root, data);
+  grab_color_at_pointer (gdk_event_get_screen ((GdkEvent *) event),
+                         gdk_event_get_device ((GdkEvent *) event),
+                         event->x_root, event->y_root, data);
 
   shutdown_eyedropper (GTK_WIDGET (data));
   
@@ -1739,12 +1745,15 @@ key_press (GtkWidget   *invisible,
            gpointer     data)
 {  
   GdkDisplay *display = gtk_widget_get_display (invisible);
-  GdkScreen *screen = gdk_event_get_screen ((GdkEvent *)event);
+  GdkScreen *screen = gdk_event_get_screen ((GdkEvent *) event);
+  GdkDevice *device, *pointer_device;
   guint state = event->state & gtk_accelerator_get_default_mod_mask ();
   gint x, y;
   gint dx, dy;
 
-  gdk_display_get_pointer (display, NULL, &x, &y, NULL);
+  device = gdk_event_get_device ((GdkEvent * ) event);
+  pointer_device = gdk_device_get_associated_device (device);
+  gdk_display_get_device_state (display, pointer_device, NULL, &x, &y, NULL);
 
   dx = 0;
   dy = 0;
@@ -1756,7 +1765,7 @@ key_press (GtkWidget   *invisible,
     case GDK_ISO_Enter:
     case GDK_KP_Enter:
     case GDK_KP_Space:
-      grab_color_at_mouse (screen, x, y, data);
+      grab_color_at_pointer (screen, pointer_device, x, y, data);
       /* fall through */
 
     case GDK_Escape:
@@ -1797,8 +1806,8 @@ key_press (GtkWidget   *invisible,
       return FALSE;
     }
 
-  gdk_display_warp_pointer (display, screen, x + dx, y + dy);
-  
+  gdk_display_warp_device (display, pointer_device, screen, x + dx, y + dy);
+
   return TRUE;
 
 }
@@ -1838,12 +1847,26 @@ get_screen_color (GtkWidget *button)
   GtkColorSelection *colorsel = g_object_get_data (G_OBJECT (button), "COLORSEL");
   ColorSelectionPrivate *priv = colorsel->private_data;
   GdkScreen *screen = gtk_widget_get_screen (GTK_WIDGET (button));
+  GdkDevice *device, *keyb_device, *pointer_device;
   GdkCursor *picker_cursor;
   GdkGrabStatus grab_status;
   GtkWidget *grab_widget, *toplevel;
 
   guint32 time = gtk_get_current_event_time ();
-  
+
+  device = gtk_get_current_event_device ();
+
+  if (device->source == GDK_SOURCE_KEYBOARD)
+    {
+      keyb_device = device;
+      pointer_device = gdk_device_get_associated_device (device);
+    }
+  else
+    {
+      pointer_device = device;
+      keyb_device = gdk_device_get_associated_device (device);
+    }
+
   if (priv->dropper_grab_widget == NULL)
     {
       grab_widget = gtk_window_new (GTK_WINDOW_POPUP);
@@ -1867,29 +1890,38 @@ get_screen_color (GtkWidget *button)
       priv->dropper_grab_widget = grab_widget;
     }
 
-  if (gdk_keyboard_grab (priv->dropper_grab_widget->window,
-                         FALSE, time) != GDK_GRAB_SUCCESS)
+  if (gdk_device_grab (keyb_device,
+                       priv->dropper_grab_widget->window,
+                       GDK_OWNERSHIP_APPLICATION, FALSE,
+                       GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK,
+                       NULL, time) != GDK_GRAB_SUCCESS)
     return;
-  
+
   picker_cursor = make_picker_cursor (screen);
-  grab_status = gdk_pointer_grab (priv->dropper_grab_widget->window,
-				  FALSE,
-				  GDK_BUTTON_RELEASE_MASK | GDK_BUTTON_PRESS_MASK | GDK_POINTER_MOTION_MASK,
-				  NULL,
-				  picker_cursor,
-				  time);
+  grab_status = gdk_device_grab (pointer_device,
+                                 priv->dropper_grab_widget->window,
+                                 GDK_OWNERSHIP_APPLICATION,
+                                 FALSE,
+                                 GDK_BUTTON_RELEASE_MASK | GDK_BUTTON_PRESS_MASK | GDK_POINTER_MOTION_MASK,
+                                 picker_cursor,
+                                 time);
   gdk_cursor_unref (picker_cursor);
-  
+
   if (grab_status != GDK_GRAB_SUCCESS)
     {
-      gdk_display_keyboard_ungrab (gtk_widget_get_display (button), time);
+      gdk_device_ungrab (keyb_device, time);
       return;
     }
 
-  gtk_grab_add (priv->dropper_grab_widget);
+  gtk_device_grab_add (priv->dropper_grab_widget,
+                       pointer_device,
+                       TRUE);
+
   priv->grab_time = time;
   priv->has_grab = TRUE;
-  
+  priv->keyboard_device = keyb_device;
+  priv->pointer_device = pointer_device;
+
   g_signal_connect (priv->dropper_grab_widget, "button-press-event",
                     G_CALLBACK (mouse_press), colorsel);
   g_signal_connect (priv->dropper_grab_widget, "key-press-event",
diff --git a/gtk/gtkcombobox.c b/gtk/gtkcombobox.c
index 23b159b..64c8e05 100644
--- a/gtk/gtkcombobox.c
+++ b/gtk/gtkcombobox.c
@@ -127,6 +127,9 @@ struct _GtkComboBoxPrivate
   gpointer                    row_separator_data;
   GDestroyNotify              row_separator_destroy;
 
+  GdkDevice *grab_pointer;
+  GdkDevice *grab_keyboard;
+
   gchar *tearoff_title;
 };
 
@@ -1861,27 +1864,31 @@ gtk_combo_box_menu_popup (GtkComboBox *combo_box,
 
 static gboolean
 popup_grab_on_window (GdkWindow *window,
-		      guint32    activate_time,
-		      gboolean   grab_keyboard)
-{
-  if ((gdk_pointer_grab (window, TRUE,
-			 GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
-			 GDK_POINTER_MOTION_MASK,
-			 NULL, NULL, activate_time) == 0))
+                      GdkDevice *keyboard,
+                      GdkDevice *pointer,
+		      guint32    activate_time)
+{
+  if (keyboard &&
+      gdk_device_grab (keyboard, window,
+                       GDK_OWNERSHIP_WINDOW, TRUE,
+                       GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK,
+                       NULL, activate_time) != GDK_GRAB_SUCCESS)
+    return FALSE;
+
+  if (pointer &&
+      gdk_device_grab (pointer, window,
+                       GDK_OWNERSHIP_WINDOW, TRUE,
+                       GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
+                       GDK_POINTER_MOTION_MASK,
+                       NULL, activate_time) != GDK_GRAB_SUCCESS)
     {
-      if (!grab_keyboard ||
-	  gdk_keyboard_grab (window, TRUE,
-			     activate_time) == 0)
-	return TRUE;
-      else
-	{
-	  gdk_display_pointer_ungrab (gdk_drawable_get_display (window),
-				      activate_time);
-	  return FALSE;
-	}
+      if (keyboard)
+        gdk_device_ungrab (keyboard, activate_time);
+
+      return FALSE;
     }
 
-  return FALSE;
+  return TRUE;
 }
 
 /**
@@ -1903,13 +1910,30 @@ gtk_combo_box_popup (GtkComboBox *combo_box)
   g_signal_emit (combo_box, combo_box_signals[POPUP], 0);
 }
 
-static void
-gtk_combo_box_real_popup (GtkComboBox *combo_box)
+/**
+ * gtk_combo_box_popup_for_device:
+ * @combo_box: a #GtkComboBox
+ * @device: a #GdkDevice
+ *
+ * Pops up the menu or dropdown list of @combo_box, the popup window
+ * will be grabbed so only @device and its associated pointer/keyboard
+ * are the only #GdkDevice<!-- -->s able to send events to it.
+ *
+ * Since: 3.0
+ **/
+void
+gtk_combo_box_popup_for_device (GtkComboBox *combo_box,
+                                GdkDevice   *device)
 {
   GtkComboBoxPrivate *priv = combo_box->priv;
   gint x, y, width, height;
   GtkTreePath *path = NULL, *ppath;
   GtkWidget *toplevel;
+  GdkDevice *keyboard, *pointer;
+  guint32 time;
+
+  g_return_if_fail (GTK_IS_COMBO_BOX (combo_box));
+  g_return_if_fail (GDK_IS_DEVICE (device));
 
   if (!gtk_widget_get_realized (GTK_WIDGET (combo_box)))
     return;
@@ -1917,6 +1941,22 @@ gtk_combo_box_real_popup (GtkComboBox *combo_box)
   if (gtk_widget_get_mapped (priv->popup_widget))
     return;
 
+  if (priv->grab_pointer && priv->grab_keyboard)
+    return;
+
+  time = gtk_get_current_event_time ();
+
+  if (device->source == GDK_SOURCE_KEYBOARD)
+    {
+      keyboard = device;
+      pointer = gdk_device_get_associated_device (device);
+    }
+  else
+    {
+      pointer = device;
+      keyboard = gdk_device_get_associated_device (device);
+    }
+
   if (GTK_IS_MENU (priv->popup_widget))
     {
       gtk_combo_box_menu_popup (combo_box, 
@@ -1966,13 +2006,40 @@ gtk_combo_box_real_popup (GtkComboBox *combo_box)
     gtk_widget_grab_focus (priv->tree_view);
 
   if (!popup_grab_on_window (priv->popup_window->window,
-			     GDK_CURRENT_TIME, TRUE))
+			     keyboard, pointer, time))
     {
       gtk_widget_hide (priv->popup_window);
       return;
     }
 
-  gtk_grab_add (priv->popup_window);
+  gtk_device_grab_add (priv->popup_window, pointer, TRUE);
+  priv->grab_pointer = pointer;
+  priv->grab_keyboard = keyboard;
+}
+
+static void
+gtk_combo_box_real_popup (GtkComboBox *combo_box)
+{
+  GdkDevice *device;
+
+  device = gtk_get_current_event_device ();
+
+  if (!device)
+    {
+      GdkDeviceManager *device_manager;
+      GdkDisplay *display;
+      GList *devices;
+
+      display = gtk_widget_get_display (GTK_WIDGET (combo_box));
+      device_manager = gdk_display_get_device_manager (display);
+
+      /* No device was set, pick the first master device */
+      devices = gdk_device_manager_list_devices (device_manager, GDK_DEVICE_TYPE_MASTER);
+      device = devices->data;
+      g_list_free (devices);
+    }
+
+  gtk_combo_box_popup_for_device (combo_box, device);
 }
 
 static gboolean
@@ -2014,10 +2081,13 @@ gtk_combo_box_popdown (GtkComboBox *combo_box)
   if (!gtk_widget_get_realized (GTK_WIDGET (combo_box)))
     return;
 
-  gtk_grab_remove (priv->popup_window);
+  gtk_device_grab_remove (priv->popup_window, priv->grab_pointer);
   gtk_widget_hide_all (priv->popup_window);
   gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (priv->button),
                                 FALSE);
+
+  priv->grab_pointer = NULL;
+  priv->grab_keyboard = NULL;
 }
 
 static gint
@@ -3896,7 +3966,7 @@ gtk_combo_box_list_button_pressed (GtkWidget      *widget,
       !gtk_widget_has_focus (priv->button))
     gtk_widget_grab_focus (priv->button);
 
-  gtk_combo_box_popup (combo_box);
+  gtk_combo_box_popup_for_device (combo_box, event->device);
 
   gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (priv->button), TRUE);
 
@@ -4092,7 +4162,9 @@ gtk_combo_box_list_scroll_timeout (GtkComboBox *combo_box)
 
   if (priv->auto_scroll)
     {
-      gdk_window_get_pointer (priv->tree_view->window, &x, &y, NULL);
+      gdk_window_get_device_position (priv->tree_view->window,
+                                      priv->grab_pointer,
+                                      &x, &y, NULL);
       gtk_combo_box_list_auto_scroll (combo_box, x, y);
     }
 
diff --git a/gtk/gtkcombobox.h b/gtk/gtkcombobox.h
index cbe40f9..f5141a7 100644
--- a/gtk/gtkcombobox.h
+++ b/gtk/gtkcombobox.h
@@ -133,6 +133,8 @@ gchar        *gtk_combo_box_get_active_text  (GtkComboBox     *combo_box);
 
 /* programmatic control */
 void          gtk_combo_box_popup            (GtkComboBox     *combo_box);
+void          gtk_combo_box_popup_for_device (GtkComboBox     *combo_box,
+                                              GdkDevice       *device);
 void          gtk_combo_box_popdown          (GtkComboBox     *combo_box);
 AtkObject*    gtk_combo_box_get_popup_accessible (GtkComboBox *combo_box);
 
diff --git a/gtk/gtkdnd.c b/gtk/gtkdnd.c
index 0c73e18..03f0436 100644
--- a/gtk/gtkdnd.c
+++ b/gtk/gtkdnd.c
@@ -398,8 +398,12 @@ gtk_drag_get_ipc_widget (GtkWidget *widget)
   return result;
 }
 
-
-#ifdef GDK_WINDOWING_X11
+/* FIXME: modifying the XEvent window as in root_key_filter() isn't
+ * going to work with XGE/XI2, since the actual event to handle would
+ * be allocated/freed before GDK gets to translate the event.
+ * Active grabs on the keyboard are used instead at the moment...
+ */
+#if defined (GDK_WINDOWING_X11) && !defined (XINPUT_2)
 
 /*
  * We want to handle a handful of keys during DND, e.g. Escape to abort.
@@ -418,7 +422,7 @@ root_key_filter (GdkXEvent *xevent,
                  GdkEvent  *event,
                  gpointer   data)
 {
-  XEvent *ev = (XEvent *)xevent;
+  XEvent *ev = (XEvent *) xevent;
 
   if ((ev->type == KeyPress || ev->type == KeyRelease) &&
       ev->xkey.root == ev->xkey.window)
@@ -458,6 +462,7 @@ static GrabKey grab_keys[] = {
 
 static void
 grab_dnd_keys (GtkWidget *widget,
+               GdkDevice *device,
                guint32    time)
 {
   guint i;
@@ -488,6 +493,7 @@ grab_dnd_keys (GtkWidget *widget,
 
 static void
 ungrab_dnd_keys (GtkWidget *widget,
+                 GdkDevice *device,
                  guint32    time)
 {
   guint i;
@@ -513,23 +519,28 @@ ungrab_dnd_keys (GtkWidget *widget,
   gdk_error_trap_pop ();
 }
 
-#else
+#else /* GDK_WINDOWING_X11 && !XINPUT_2 */
 
 static void
 grab_dnd_keys (GtkWidget *widget,
+               GdkDevice *device,
                guint32    time)
 {
-  gdk_keyboard_grab (widget->window, FALSE, time);
+  gdk_device_grab (device, widget->window,
+                   GDK_OWNERSHIP_APPLICATION, FALSE,
+                   GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK,
+                   NULL, time);
 }
 
 static void
 ungrab_dnd_keys (GtkWidget *widget,
+                 GdkDevice *device,
                  guint32    time)
 {
-  gdk_display_keyboard_ungrab (gtk_widget_get_display (widget), time);
+  gdk_device_ungrab (device, time);
 }
 
-#endif
+#endif /* GDK_WINDOWING_X11 */
 
 
 /***************************************************************
@@ -545,9 +556,20 @@ gtk_drag_release_ipc_widget (GtkWidget *widget)
 {
   GtkWindow *window = GTK_WINDOW (widget);
   GdkScreen *screen = gtk_widget_get_screen (widget);
+  GdkDragContext *context = g_object_get_data (G_OBJECT (widget), "drag-context");
   GSList *drag_widgets = g_object_get_data (G_OBJECT (screen),
 					    "gtk-dnd-ipc-widgets");
-  ungrab_dnd_keys (widget, GDK_CURRENT_TIME);
+  GdkDevice *pointer, *keyboard;
+
+  if (context)
+    {
+      pointer = gdk_drag_context_get_device (context);
+      keyboard = gdk_device_get_associated_device (pointer);
+
+      if (keyboard)
+        ungrab_dnd_keys (widget, keyboard, GDK_CURRENT_TIME);
+    }
+
   if (window->group)
     gtk_window_group_remove_window (window->group, window);
   drag_widgets = g_slist_prepend (drag_widgets, widget);
@@ -940,11 +962,13 @@ gtk_drag_update_cursor (GtkDragSourceInfo *info)
   
   if (cursor != info->cursor)
     {
-      gdk_pointer_grab (info->ipc_widget->window, FALSE,
-			GDK_POINTER_MOTION_MASK |
-			GDK_BUTTON_RELEASE_MASK,
-			NULL,
-			cursor, info->grab_time);
+      GdkDevice *pointer;
+
+      pointer = gdk_drag_context_get_device (info->context);
+      gdk_device_grab (pointer, info->ipc_widget->window,
+                       GDK_OWNERSHIP_APPLICATION, FALSE,
+                       GDK_POINTER_MOTION_MASK | GDK_BUTTON_RELEASE_MASK,
+                       cursor, info->grab_time);
       info->cursor = cursor;
     }
 }
@@ -2379,7 +2403,9 @@ gtk_drag_begin_internal (GtkWidget         *widget,
   GdkDragContext *context;
   GtkWidget *ipc_widget;
   GdkCursor *cursor;
- 
+  GdkDevice *pointer, *keyboard;
+
+  pointer = keyboard = NULL;
   ipc_widget = gtk_drag_get_ipc_widget (widget);
   
   gtk_drag_get_event_actions (event, button, actions,
@@ -2390,24 +2416,45 @@ gtk_drag_begin_internal (GtkWidget         *widget,
 				NULL);
   
   if (event)
-    time = gdk_event_get_time (event);
+    {
+      time = gdk_event_get_time (event);
+      pointer = gdk_event_get_device (event);
+
+      if (pointer->source == GDK_SOURCE_KEYBOARD)
+        {
+          keyboard = pointer;
+          pointer = gdk_device_get_associated_device (keyboard);
+        }
+      else
+        keyboard = gdk_device_get_associated_device (pointer);
+    }
+  else
+    {
+      pointer = gdk_display_get_core_pointer (gtk_widget_get_display (widget));
+      keyboard = gdk_device_get_associated_device (pointer);
+    }
 
-  if (gdk_pointer_grab (ipc_widget->window, FALSE,
-			GDK_POINTER_MOTION_MASK |
-			GDK_BUTTON_RELEASE_MASK, NULL,
-			cursor, time) != GDK_GRAB_SUCCESS)
+  if (!pointer)
+    return NULL;
+
+  if (gdk_device_grab (pointer, ipc_widget->window,
+                       GDK_OWNERSHIP_APPLICATION, FALSE,
+                       GDK_POINTER_MOTION_MASK |
+                       GDK_BUTTON_RELEASE_MASK,
+                       cursor, time) != GDK_GRAB_SUCCESS)
     {
       gtk_drag_release_ipc_widget (ipc_widget);
       return NULL;
     }
 
-  grab_dnd_keys (ipc_widget, time);
+  if (keyboard)
+    grab_dnd_keys (ipc_widget, keyboard, time);
 
   /* We use a GTK grab here to override any grabs that the widget
    * we are dragging from might have held
    */
-  gtk_grab_add (ipc_widget);
-  
+  gtk_device_grab_add (ipc_widget, pointer, FALSE);
+
   tmp_list = g_list_last (target_list->list);
   while (tmp_list)
     {
@@ -2420,6 +2467,7 @@ gtk_drag_begin_internal (GtkWidget         *widget,
   source_widgets = g_slist_prepend (source_widgets, ipc_widget);
 
   context = gdk_drag_begin (ipc_widget->window, targets);
+  gdk_drag_context_set_device (context, pointer);
   g_list_free (targets);
   
   info = gtk_drag_get_source_info (context, TRUE);
@@ -2451,10 +2499,10 @@ gtk_drag_begin_internal (GtkWidget         *widget,
       info->cur_x = event->motion.x_root;
       info->cur_y = event->motion.y_root;
     }
-  else 
+  else
     {
-      gdk_display_get_pointer (gtk_widget_get_display (widget),
-			       &info->cur_screen, &info->cur_x, &info->cur_y, NULL);
+      gdk_display_get_device_state (gtk_widget_get_display (widget), pointer,
+                                    &info->cur_screen, &info->cur_x, &info->cur_y, NULL);
     }
 
   g_signal_emit_by_name (widget, "drag-begin", info->context);
@@ -2510,11 +2558,11 @@ gtk_drag_begin_internal (GtkWidget         *widget,
   
       if (cursor != info->cursor)
         {
-	  gdk_pointer_grab (widget->window, FALSE,
-	 	            GDK_POINTER_MOTION_MASK |
-		  	    GDK_BUTTON_RELEASE_MASK,
-			    NULL,
-			    cursor, time);
+          gdk_device_grab (pointer, widget->window,
+                           GDK_OWNERSHIP_APPLICATION, FALSE,
+                           GDK_POINTER_MOTION_MASK |
+                           GDK_BUTTON_RELEASE_MASK,
+                           cursor, time);
           info->cursor = cursor;
         }
     }
@@ -3468,11 +3516,13 @@ _gtk_drag_source_handle_event (GtkWidget *widget,
 					  info);
 	    if (info->cursor != cursor)
 	      {
-		gdk_pointer_grab (widget->window, FALSE,
-				  GDK_POINTER_MOTION_MASK |
-				  GDK_BUTTON_RELEASE_MASK,
-				  NULL,
-				  cursor, info->grab_time);
+                GdkDevice *pointer;
+
+                pointer = gdk_drag_context_get_device (context);
+                gdk_device_grab (pointer, widget->window,
+                                 GDK_OWNERSHIP_APPLICATION, FALSE,
+                                 GDK_POINTER_MOTION_MASK | GDK_BUTTON_RELEASE_MASK,
+                                 cursor, info->grab_time);
 		info->cursor = cursor;
 	      }
 	    
@@ -4062,7 +4112,10 @@ gtk_drag_end (GtkDragSourceInfo *info, guint32 time)
 {
   GdkEvent *send_event;
   GtkWidget *source_widget = info->widget;
-  GdkDisplay *display = gtk_widget_get_display (source_widget);
+  GdkDevice *pointer, *keyboard;
+
+  pointer = gdk_drag_context_get_device (info->context);
+  keyboard = gdk_device_get_associated_device (pointer);
 
   if (info->update_idle)
     {
@@ -4094,9 +4147,9 @@ gtk_drag_end (GtkDragSourceInfo *info, guint32 time)
 					gtk_drag_key_cb,
 					info);
 
-  gdk_display_pointer_ungrab (display, time);
-  ungrab_dnd_keys (info->ipc_widget, time);
-  gtk_grab_remove (info->ipc_widget);
+  gdk_device_ungrab (pointer, time);
+  ungrab_dnd_keys (info->ipc_widget, keyboard, time);
+  gtk_device_grab_remove (info->ipc_widget, pointer);
 
   /* Send on a release pair to the original 
    * widget to convince it to release its grab. We need to
@@ -4114,7 +4167,7 @@ gtk_drag_end (GtkDragSourceInfo *info, guint32 time)
   send_event->button.axes = NULL;
   send_event->button.state = 0;
   send_event->button.button = info->button;
-  send_event->button.device = gdk_display_get_core_pointer (display);
+  send_event->button.device = pointer;
   send_event->button.x_root = 0;
   send_event->button.y_root = 0;
 
@@ -4160,15 +4213,16 @@ gtk_drag_motion_cb (GtkWidget      *widget,
   if (event->is_hint)
     {
       GdkDisplay *display = gtk_widget_get_display (widget);
-      
-      gdk_display_get_pointer (display, &screen, &x_root, &y_root, NULL);
+
+      gdk_display_get_device_state (display, event->device,
+                                    &screen, &x_root, &y_root, NULL);
       event->x_root = x_root;
       event->y_root = y_root;
     }
   else
     screen = gdk_event_get_screen ((GdkEvent *)event);
 
-  gtk_drag_update (info, screen, event->x_root, event->y_root, (GdkEvent *)event);
+  gtk_drag_update (info, screen, event->x_root, event->y_root, (GdkEvent *) event);
 
   return TRUE;
 }
@@ -4192,10 +4246,12 @@ gtk_drag_key_cb (GtkWidget         *widget,
   GtkDragSourceInfo *info = (GtkDragSourceInfo *)data;
   GdkModifierType state;
   GdkWindow *root_window;
+  GdkDevice *pointer;
   gint dx, dy;
 
   dx = dy = 0;
   state = event->state & gtk_accelerator_get_default_mod_mask ();
+  pointer = gdk_device_get_associated_device (gdk_event_get_device ((GdkEvent *) event));
 
   if (event->type == GDK_KEY_PRESS)
     {
@@ -4244,16 +4300,16 @@ gtk_drag_key_cb (GtkWidget         *widget,
    * that would be overkill.
    */
   root_window = gtk_widget_get_root_window (widget);
-  gdk_window_get_pointer (root_window, NULL, NULL, &state);
+  gdk_window_get_device_position (root_window, pointer, NULL, NULL, &state);
   event->state = state;
 
   if (dx != 0 || dy != 0)
     {
       info->cur_x += dx;
       info->cur_y += dy;
-      gdk_display_warp_pointer (gtk_widget_get_display (widget), 
-				gtk_widget_get_screen (widget), 
-				info->cur_x, info->cur_y);
+      gdk_display_warp_device (gtk_widget_get_display (widget), pointer,
+                               gtk_widget_get_screen (widget),
+                               info->cur_x, info->cur_y);
     }
 
   gtk_drag_update (info, info->cur_screen, info->cur_x, info->cur_y, (GdkEvent *)event);
@@ -4287,8 +4343,11 @@ gtk_drag_grab_notify_cb (GtkWidget        *widget,
 			 gpointer          data)
 {
   GtkDragSourceInfo *info = (GtkDragSourceInfo *)data;
+  GdkDevice *pointer;
+
+  pointer = gdk_drag_context_get_device (info->context);
 
-  if (!was_grabbed)
+  if (gtk_widget_device_is_shadowed (widget, pointer))
     {
       /* We have to block callbacks to avoid recursion here, because
 	 gtk_drag_cancel calls gtk_grab_remove (via gtk_drag_end) */
diff --git a/gtk/gtkentry.c b/gtk/gtkentry.c
index 963cf33..3c7aa25 100644
--- a/gtk/gtkentry.c
+++ b/gtk/gtkentry.c
@@ -139,6 +139,8 @@ struct _GtkEntryPrivate
   gint start_y;
 
   gchar *im_module;
+
+  GdkDevice *completion_device;
 };
 
 typedef struct _GtkEntryPasswordHint GtkEntryPasswordHint;
@@ -9157,6 +9159,7 @@ static gint
 gtk_entry_completion_timeout (gpointer data)
 {
   GtkEntryCompletion *completion = GTK_ENTRY_COMPLETION (data);
+  GtkEntryPrivate *priv = GTK_ENTRY_GET_PRIVATE (completion->priv->entry);
 
   completion->priv->completion_timeout = 0;
 
@@ -9186,7 +9189,7 @@ gtk_entry_completion_timeout (gpointer data)
 	  if (gtk_widget_get_visible (completion->priv->popup_window))
 	    _gtk_entry_completion_resize_popup (completion);
           else
-	    _gtk_entry_completion_popup (completion);
+	    _gtk_entry_completion_popup (completion, priv->completion_device);
 	}
       else 
 	_gtk_entry_completion_popdown (completion);
@@ -9491,7 +9494,9 @@ static void
 gtk_entry_completion_changed (GtkWidget *entry,
                               gpointer   user_data)
 {
+  GtkEntryPrivate *priv = GTK_ENTRY_GET_PRIVATE (entry);
   GtkEntryCompletion *completion = GTK_ENTRY_COMPLETION (user_data);
+  GdkDevice *device;
 
   /* (re)install completion timeout */
   if (completion->priv->completion_timeout)
@@ -9509,6 +9514,14 @@ gtk_entry_completion_changed (GtkWidget *entry,
       return;
     }
 
+  device = gtk_get_current_event_device ();
+
+  if (device && device->source == GDK_SOURCE_KEYBOARD)
+    device = gdk_device_get_associated_device (device);
+
+  if (device)
+    priv->completion_device = device;
+
   completion->priv->completion_timeout =
     gdk_threads_add_timeout (COMPLETION_TIMEOUT,
                    gtk_entry_completion_timeout,
diff --git a/gtk/gtkentrycompletion.c b/gtk/gtkentrycompletion.c
index 8237387..6225b41 100644
--- a/gtk/gtkentrycompletion.c
+++ b/gtk/gtkentrycompletion.c
@@ -1473,7 +1473,8 @@ _gtk_entry_completion_resize_popup (GtkEntryCompletion *completion)
 }
 
 void
-_gtk_entry_completion_popup (GtkEntryCompletion *completion)
+_gtk_entry_completion_popup (GtkEntryCompletion *completion,
+                             GdkDevice          *device)
 {
   GtkTreeViewColumn *column;
   GList *renderers;
@@ -1488,6 +1489,9 @@ _gtk_entry_completion_popup (GtkEntryCompletion *completion)
   if (!gtk_widget_has_focus (completion->priv->entry))
     return;
 
+  if (completion->priv->grab_device)
+    return;
+
   completion->priv->ignore_enter = TRUE;
     
   column = gtk_tree_view_get_column (GTK_TREE_VIEW (completion->priv->action_view), 0);
@@ -1520,13 +1524,16 @@ _gtk_entry_completion_popup (GtkEntryCompletion *completion)
                          gtk_widget_get_screen (completion->priv->entry));
 
   gtk_widget_show (completion->priv->popup_window);
-    
-  gtk_grab_add (completion->priv->popup_window);
-  gdk_pointer_grab (completion->priv->popup_window->window, TRUE,
-                    GDK_BUTTON_PRESS_MASK |
-                    GDK_BUTTON_RELEASE_MASK |
-                    GDK_POINTER_MOTION_MASK,
-                    NULL, NULL, GDK_CURRENT_TIME);
+
+  gtk_device_grab_add (completion->priv->popup_window, device, TRUE);
+  gdk_device_grab (device, completion->priv->popup_window->window,
+                   GDK_OWNERSHIP_WINDOW, TRUE,
+                   GDK_BUTTON_PRESS_MASK |
+                   GDK_BUTTON_RELEASE_MASK |
+                   GDK_POINTER_MOTION_MASK,
+                   NULL, GDK_CURRENT_TIME);
+
+  completion->priv->grab_device = device;
 }
 
 void
@@ -1536,9 +1543,14 @@ _gtk_entry_completion_popdown (GtkEntryCompletion *completion)
     return;
 
   completion->priv->ignore_enter = FALSE;
-  
-  gdk_pointer_ungrab (GDK_CURRENT_TIME);
-  gtk_grab_remove (completion->priv->popup_window);
+
+  if (completion->priv->grab_device)
+    {
+      gdk_device_ungrab (completion->priv->grab_device, GDK_CURRENT_TIME);
+      gtk_device_grab_remove (completion->priv->popup_window,
+                              completion->priv->grab_device);
+      completion->priv->grab_device = NULL;
+    }
 
   gtk_widget_hide (completion->priv->popup_window);
 }
diff --git a/gtk/gtkentryprivate.h b/gtk/gtkentryprivate.h
index a767800..baf0b9f 100644
--- a/gtk/gtkentryprivate.h
+++ b/gtk/gtkentryprivate.h
@@ -69,10 +69,13 @@ struct _GtkEntryCompletionPrivate
   gchar *completion_prefix;
 
   GSource *check_completion_idle;
+
+  GdkDevice *grab_device;
 };
 
 gboolean _gtk_entry_completion_resize_popup (GtkEntryCompletion *completion);
-void     _gtk_entry_completion_popup        (GtkEntryCompletion *completion);
+void     _gtk_entry_completion_popup        (GtkEntryCompletion *completion,
+                                             GdkDevice          *device);
 void     _gtk_entry_completion_popdown      (GtkEntryCompletion *completion);
 
 void      _gtk_entry_get_borders            (GtkEntry  *entry,
diff --git a/gtk/gtkhandlebox.c b/gtk/gtkhandlebox.c
index 5ff6050..119ba10 100644
--- a/gtk/gtkhandlebox.c
+++ b/gtk/gtkhandlebox.c
@@ -42,6 +42,7 @@ struct _GtkHandleBoxPrivate
 {
   gint orig_x;
   gint orig_y;
+  GdkDevice *grab_device;
 };
 
 enum {
@@ -1114,22 +1115,25 @@ gtk_handle_box_button_press (GtkWidget      *widget,
 		  hb->attach_allocation.height = 0;
 		}
 	      hb->in_drag = TRUE;
+              private->grab_device = event->device;
 	      fleur = gdk_cursor_new_for_display (gtk_widget_get_display (widget),
 						  GDK_FLEUR);
-	      if (gdk_pointer_grab (invisible->window,
-				    FALSE,
-				    (GDK_BUTTON1_MOTION_MASK |
-				     GDK_POINTER_MOTION_HINT_MASK |
-				     GDK_BUTTON_RELEASE_MASK),
-				    NULL,
-				    fleur,
-				    event->time) != 0)
+	      if (gdk_device_grab (event->device,
+                                   invisible->window,
+                                   GDK_OWNERSHIP_WINDOW,
+                                   FALSE,
+                                   (GDK_BUTTON1_MOTION_MASK |
+                                    GDK_POINTER_MOTION_HINT_MASK |
+                                    GDK_BUTTON_RELEASE_MASK),
+                                   fleur,
+                                   event->time) != GDK_GRAB_SUCCESS)
 		{
 		  hb->in_drag = FALSE;
+                  private->grab_device = NULL;
 		}
 	      else
 		{
-		  gtk_grab_add (invisible);
+                  gtk_device_grab_add (invisible, private->grab_device, TRUE);
 		  g_signal_connect (invisible, "event",
 				    G_CALLBACK (gtk_handle_box_grab_event), hb);
 		}
@@ -1169,9 +1173,10 @@ gtk_handle_box_motion (GtkWidget      *widget,
   new_x = 0;
   new_y = 0;
   screen = gtk_widget_get_screen (widget);
-  gdk_display_get_pointer (gdk_screen_get_display (screen),
-			   &pointer_screen, 
-			   &new_x, &new_y, NULL);
+  gdk_display_get_device_state (gdk_screen_get_display (screen),
+                                event->device,
+                                &pointer_screen,
+                                &new_x, &new_y, NULL);
   if (pointer_screen != screen)
     {
       GtkHandleBoxPrivate *private = gtk_handle_box_get_private (hb);
@@ -1418,15 +1423,18 @@ static void
 gtk_handle_box_end_drag (GtkHandleBox *hb,
 			 guint32       time)
 {
+  GtkHandleBoxPrivate *private = gtk_handle_box_get_private (hb);
   GtkWidget *invisible = gtk_handle_box_get_invisible ();
-		
+
   hb->in_drag = FALSE;
 
-  gtk_grab_remove (invisible);
-  gdk_pointer_ungrab (time);
+  gtk_device_grab_remove (invisible, private->grab_device);
+  gdk_device_ungrab (private->grab_device, time);
   g_signal_handlers_disconnect_by_func (invisible,
 					G_CALLBACK (gtk_handle_box_grab_event),
 					hb);
+
+  private->grab_device = NULL;
 }
 
 #define __GTK_HANDLE_BOX_C__
diff --git a/gtk/gtkiconview.c b/gtk/gtkiconview.c
index 4f07208..0942ef5 100644
--- a/gtk/gtkiconview.c
+++ b/gtk/gtkiconview.c
@@ -124,6 +124,7 @@ struct _GtkIconViewPrivate
   gboolean doing_rubberband;
   gint rubberband_x1, rubberband_y1;
   gint rubberband_x2, rubberband_y2;
+  GdkDevice *rubberband_device;
 
   guint scroll_timeout_id;
   gint scroll_value_diff;
@@ -308,6 +309,7 @@ static void                 gtk_icon_view_set_cursor_item                (GtkIco
 									  GtkIconViewItem        *item,
 									  gint                    cursor_cell);
 static void                 gtk_icon_view_start_rubberbanding            (GtkIconView            *icon_view,
+                                                                          GdkDevice              *device,
 									  gint                    x,
 									  gint                    y);
 static void                 gtk_icon_view_stop_rubberbanding             (GtkIconView            *icon_view);
@@ -2217,7 +2219,7 @@ gtk_icon_view_button_press (GtkWidget      *widget,
 	    }
 	  
 	  if (icon_view->priv->selection_mode == GTK_SELECTION_MULTIPLE)
-	    gtk_icon_view_start_rubberbanding (icon_view, event->x, event->y);
+            gtk_icon_view_start_rubberbanding (icon_view, event->device, event->x, event->y);
 	}
 
       /* don't draw keyboard focus around an clicked-on item */
@@ -2309,7 +2311,9 @@ gtk_icon_view_update_rubberband (gpointer data)
   
   icon_view = GTK_ICON_VIEW (data);
 
-  gdk_window_get_pointer (icon_view->priv->bin_window, &x, &y, NULL);
+  gdk_window_get_device_position (icon_view->priv->bin_window,
+                                  icon_view->priv->rubberband_device,
+                                  &x, &y, NULL);
 
   x = MAX (x, 0);
   y = MAX (y, 0);
@@ -2360,12 +2364,14 @@ gtk_icon_view_update_rubberband (gpointer data)
 
 static void
 gtk_icon_view_start_rubberbanding (GtkIconView  *icon_view,
+                                   GdkDevice    *device,
 				   gint          x,
 				   gint          y)
 {
   GList *items;
 
-  g_assert (!icon_view->priv->doing_rubberband);
+  if (icon_view->priv->rubberband_device)
+    return;
 
   for (items = icon_view->priv->items; items; items = items->next)
     {
@@ -2380,8 +2386,9 @@ gtk_icon_view_start_rubberbanding (GtkIconView  *icon_view,
   icon_view->priv->rubberband_y2 = y;
 
   icon_view->priv->doing_rubberband = TRUE;
+  icon_view->priv->rubberband_device = device;
 
-  gtk_grab_add (GTK_WIDGET (icon_view));
+  gtk_device_grab_add (GTK_WIDGET (icon_view), device, TRUE);
 }
 
 static void
@@ -2390,10 +2397,12 @@ gtk_icon_view_stop_rubberbanding (GtkIconView *icon_view)
   if (!icon_view->priv->doing_rubberband)
     return;
 
+  gtk_device_grab_remove (GTK_WIDGET (icon_view),
+                          icon_view->priv->rubberband_device);
+
   icon_view->priv->doing_rubberband = FALSE;
+  icon_view->priv->rubberband_device = NULL;
 
-  gtk_grab_remove (GTK_WIDGET (icon_view));
-  
   gtk_widget_queue_draw (GTK_WIDGET (icon_view));
 }
 
diff --git a/gtk/gtklabel.c b/gtk/gtklabel.c
index 611cebb..a027f85 100644
--- a/gtk/gtklabel.c
+++ b/gtk/gtklabel.c
@@ -4786,7 +4786,6 @@ gtk_label_motion (GtkWidget      *widget,
   GtkLabel *label = GTK_LABEL (widget);
   GtkLabelSelectionInfo *info = label->select_info;
   gint index;
-  gint x, y;
 
   if (info == NULL)
     return FALSE;
@@ -4799,8 +4798,7 @@ gtk_label_motion (GtkWidget      *widget,
 
       if (info->selection_anchor == info->selection_end)
         {
-          gdk_window_get_pointer (event->window, &x, &y, NULL);
-          if (get_layout_index (label, x, y, &index))
+          if (get_layout_index (label, event->x, event->y, &index))
             {
               for (l = info->links; l != NULL; l = l->next)
                 {
@@ -4842,8 +4840,6 @@ gtk_label_motion (GtkWidget      *widget,
   if ((event->state & GDK_BUTTON1_MASK) == 0)
     return FALSE;
 
-  gdk_window_get_pointer (info->window, &x, &y, NULL);
- 
   if (info->in_drag)
     {
       if (gtk_drag_check_threshold (widget,
@@ -4868,6 +4864,9 @@ gtk_label_motion (GtkWidget      *widget,
     }
   else
     {
+      gint x, y;
+
+      gdk_window_get_device_position (info->window, event->device, &x, &y, NULL);
       get_layout_index (label, x, y, &index);
 
       if (info->select_words)
diff --git a/gtk/gtkmain.c b/gtk/gtkmain.c
index ed88d38..ea54a4b 100644
--- a/gtk/gtkmain.c
+++ b/gtk/gtkmain.c
@@ -1316,12 +1316,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,
@@ -1396,6 +1394,7 @@ rewrite_event_for_grabs (GdkEvent *event)
   gpointer grab_widget_ptr;
   gboolean owner_events;
   GdkDisplay *display;
+  GdkDevice *device;
 
   switch (event->type)
     {
@@ -1407,20 +1406,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;
     }
@@ -1440,9 +1434,10 @@ void
 gtk_main_do_event (GdkEvent *event)
 {
   GtkWidget *event_widget;
-  GtkWidget *grab_widget;
+  GtkWidget *grab_widget = NULL;
   GtkWindowGroup *window_group;
   GdkEvent *rewritten_event = NULL;
+  GdkDevice *device;
   GList *tmp_list;
 
   if (event->type == GDK_SETTING)
@@ -1489,13 +1484,9 @@ gtk_main_do_event (GdkEvent *event)
       event = rewritten_event;
       event_widget = gtk_get_event_widget (event);
     }
-  
-  window_group = gtk_main_get_window_group (event_widget);
 
-  /* Push the event onto a stack of current events for
-   * gtk_current_event_get().
-   */
-  current_events = g_list_prepend (current_events, event);
+  window_group = gtk_main_get_window_group (event_widget);
+  device = gdk_event_get_device (event);
 
   /* If there is a grab in effect...
    */
@@ -1511,11 +1502,34 @@ gtk_main_do_event (GdkEvent *event)
 	  gtk_widget_is_ancestor (event_widget, grab_widget))
 	grab_widget = event_widget;
     }
-  else
+  else if (device)
     {
-      grab_widget = event_widget;
+      grab_widget = gtk_window_group_get_current_device_grab (window_group, device);
+
+      if (grab_widget &&
+          gtk_widget_get_sensitive (event_widget) &&
+          gtk_widget_is_ancestor (event_widget, grab_widget))
+        grab_widget = event_widget;
+    }
+
+  if (!grab_widget)
+    grab_widget = event_widget;
+
+  /* If the widget receiving events is actually blocked by another device GTK+ grab */
+  if (device &&
+      _gtk_window_group_widget_is_blocked_for_device (window_group, grab_widget, device))
+    {
+      if (rewritten_event)
+        gdk_event_free (rewritten_event);
+
+      return;
     }
 
+  /* Push the event onto a stack of current events for
+   * gtk_current_event_get().
+   */
+  current_events = g_list_prepend (current_events, event);
+
   /* Not all events get sent to the grabbing widget.
    * The delete, destroy, expose, focus change and resize
    *  events still get sent to the event widget because
@@ -1636,14 +1650,17 @@ gtk_main_do_event (GdkEvent *event)
       break;
       
     case GDK_ENTER_NOTIFY:
-      GTK_PRIVATE_SET_FLAG (event_widget, GTK_HAS_POINTER);
-      _gtk_widget_set_pointer_window (event_widget, event->any.window);
+      _gtk_widget_set_device_window (event_widget,
+                                     gdk_event_get_device (event),
+                                     event->any.window);
       if (gtk_widget_is_sensitive (grab_widget))
 	gtk_widget_event (grab_widget, event);
       break;
       
     case GDK_LEAVE_NOTIFY:
-      GTK_PRIVATE_UNSET_FLAG (event_widget, GTK_HAS_POINTER);
+      _gtk_widget_set_device_window (event_widget,
+                                     gdk_event_get_device (event),
+                                     NULL);
       if (gtk_widget_is_sensitive (grab_widget))
 	gtk_widget_event (grab_widget, event);
       break;
@@ -1718,16 +1735,73 @@ typedef struct
   gboolean   was_grabbed;
   gboolean   is_grabbed;
   gboolean   from_grab;
+  GList     *notified_windows;
+  GdkDevice *device;
 } GrabNotifyInfo;
 
 static void
+synth_crossing_for_grab_notify (GtkWidget       *from,
+                                GtkWidget       *to,
+                                GrabNotifyInfo  *info,
+                                GList           *devices,
+                                GdkCrossingMode  mode)
+{
+  while (devices)
+    {
+      GdkDevice *device = devices->data;
+      GdkWindow *from_window, *to_window;
+
+      /* Do not propagate events more than once to
+       * the same windows if non-multidevice aware.
+       */
+      if (!from)
+        from_window = NULL;
+      else
+        {
+          from_window = _gtk_widget_get_device_window (from, device);
+
+          if (from_window &&
+              !gdk_window_get_support_multidevice (from_window) &&
+              g_list_find (info->notified_windows, from_window))
+            from_window = NULL;
+        }
+
+      if (!to)
+        to_window = NULL;
+      else
+        {
+          to_window = _gtk_widget_get_device_window (to, device);
+
+          if (to_window &&
+              !gdk_window_get_support_multidevice (to_window) &&
+              g_list_find (info->notified_windows, to_window))
+            to_window = NULL;
+        }
+
+      if (from_window || to_window)
+        {
+          _gtk_widget_synthesize_crossing ((from_window) ? from : NULL,
+                                           (to_window) ? to : NULL,
+                                           device, mode);
+
+          if (from_window)
+            info->notified_windows = g_list_prepend (info->notified_windows, from_window);
+
+          if (to_window)
+            info->notified_windows = g_list_prepend (info->notified_windows, to_window);
+        }
+
+      devices = devices->next;
+    }
+}
+
+static void
 gtk_grab_notify_foreach (GtkWidget *child,
 			 gpointer   data)
-                        
 {
   GrabNotifyInfo *info = data;
- 
   gboolean was_grabbed, is_grabbed, was_shadowed, is_shadowed;
+  GList *devices;
 
   was_grabbed = info->was_grabbed;
   is_grabbed = info->is_grabbed;
@@ -1742,42 +1816,55 @@ gtk_grab_notify_foreach (GtkWidget *child,
 
   if ((was_shadowed || is_shadowed) && GTK_IS_CONTAINER (child))
     gtk_container_forall (GTK_CONTAINER (child), gtk_grab_notify_foreach, info);
-  
+
+  if (info->device &&
+      _gtk_widget_get_device_window (child, info->device))
+    {
+      /* Device specified and is on widget */
+      devices = g_list_prepend (NULL, info->device);
+    }
+  else
+    devices = _gtk_widget_list_devices (child);
+
   if (is_shadowed)
     {
       GTK_PRIVATE_SET_FLAG (child, GTK_SHADOWED);
-      if (!was_shadowed && GTK_WIDGET_HAS_POINTER (child)
-	  && gtk_widget_is_sensitive (child))
-	_gtk_widget_synthesize_crossing (child, info->new_grab_widget,
-					 GDK_CROSSING_GTK_GRAB);
+      if (!was_shadowed && devices &&
+	  gtk_widget_is_sensitive (child))
+        synth_crossing_for_grab_notify (child, info->new_grab_widget,
+                                        info, devices,
+                                        GDK_CROSSING_GTK_GRAB);
     }
   else
     {
       GTK_PRIVATE_UNSET_FLAG (child, GTK_SHADOWED);
-      if (was_shadowed && GTK_WIDGET_HAS_POINTER (child)
-	  && gtk_widget_is_sensitive (child))
-	_gtk_widget_synthesize_crossing (info->old_grab_widget, child,
-					 info->from_grab ? GDK_CROSSING_GTK_GRAB
-					 : GDK_CROSSING_GTK_UNGRAB);
+      if (was_shadowed && devices &&
+          gtk_widget_is_sensitive (child))
+        synth_crossing_for_grab_notify (info->old_grab_widget, child,
+                                        info, devices,
+                                        info->from_grab ? GDK_CROSSING_GTK_GRAB :
+                                        GDK_CROSSING_GTK_UNGRAB);
     }
 
   if (was_shadowed != is_shadowed)
     _gtk_widget_grab_notify (child, was_shadowed);
-  
+
   g_object_unref (child);
-  
+  g_list_free (devices);
+
   info->was_grabbed = was_grabbed;
   info->is_grabbed = is_grabbed;
 }
 
 static void
 gtk_grab_notify (GtkWindowGroup *group,
+                 GdkDevice      *device,
 		 GtkWidget      *old_grab_widget,
 		 GtkWidget      *new_grab_widget,
 		 gboolean        from_grab)
 {
   GList *toplevels;
-  GrabNotifyInfo info;
+  GrabNotifyInfo info = { 0 };
 
   if (old_grab_widget == new_grab_widget)
     return;
@@ -1785,12 +1872,13 @@ gtk_grab_notify (GtkWindowGroup *group,
   info.old_grab_widget = old_grab_widget;
   info.new_grab_widget = new_grab_widget;
   info.from_grab = from_grab;
+  info.device = device;
 
   g_object_ref (group);
 
   toplevels = gtk_window_list_toplevels ();
   g_list_foreach (toplevels, (GFunc)g_object_ref, NULL);
-			    
+
   while (toplevels)
     {
       GtkWindow *toplevel = toplevels->data;
@@ -1804,6 +1892,7 @@ gtk_grab_notify (GtkWindowGroup *group,
       g_object_unref (toplevel);
     }
 
+  g_list_free (info.notified_windows);
   g_object_unref (group);
 }
 
@@ -1829,7 +1918,7 @@ gtk_grab_add (GtkWidget *widget)
       g_object_ref (widget);
       group->grabs = g_slist_prepend (group->grabs, widget);
 
-      gtk_grab_notify (group, old_grab_widget, widget, TRUE);
+      gtk_grab_notify (group, NULL, old_grab_widget, widget, TRUE);
     }
 }
 
@@ -1865,12 +1954,72 @@ gtk_grab_remove (GtkWidget *widget)
       else
 	new_grab_widget = NULL;
 
-      gtk_grab_notify (group, widget, new_grab_widget, FALSE);
+      gtk_grab_notify (group, NULL, widget, new_grab_widget, FALSE);
       
       g_object_unref (widget);
     }
 }
 
+/**
+ * gtk_device_grab_add:
+ * @widget: a #GtkWidget
+ * @device: a #GtkDevice to grab on.
+ * @block_others: %TRUE to prevent other devices to interact with @widget.
+ *
+ * Adds a GTK+ grab on @device, so all the events on @device and its
+ * associated pointer or keyboard (if any) are delivered to @widget.
+ * If the @block_others parameter is %TRUE, any other devices will be
+ * unable to interact with @widget during the grab.
+ *
+ * Since: 3.0
+ **/
+void
+gtk_device_grab_add (GtkWidget        *widget,
+                     GdkDevice        *device,
+                     gboolean          block_others)
+{
+  GtkWindowGroup *group;
+  GtkWidget *old_grab_widget;
+
+  g_return_if_fail (GTK_IS_WIDGET (widget));
+  g_return_if_fail (GDK_IS_DEVICE (device));
+
+  group = gtk_main_get_window_group (widget);
+  old_grab_widget = gtk_window_group_get_current_device_grab (group, device);
+
+  if (old_grab_widget != widget)
+    _gtk_window_group_add_device_grab (group, widget, device, block_others);
+
+  gtk_grab_notify (group, device, old_grab_widget, widget, TRUE);
+}
+
+/**
+ * gtk_device_grab_remove:
+ * @widget: a #GtkWidget
+ * @device: a #GdkDevice
+ *
+ * Removes a device grab from the given widget. You have to pair calls
+ * to gtk_device_grab_add() and gtk_device_grab_remove().
+ *
+ * Since: 3.0
+ **/
+void
+gtk_device_grab_remove (GtkWidget *widget,
+                        GdkDevice *device)
+{
+  GtkWindowGroup *group;
+  GtkWidget *new_grab_widget;
+
+  g_return_if_fail (GTK_IS_WIDGET (widget));
+  g_return_if_fail (GDK_IS_DEVICE (device));
+
+  group = gtk_main_get_window_group (widget);
+  _gtk_window_group_remove_device_grab (group, widget, device);
+  new_grab_widget = gtk_window_group_get_current_device_grab (group, device);
+
+  gtk_grab_notify (group, device, widget, new_grab_widget, FALSE);
+}
+
 void
 gtk_init_add (GtkFunction function,
 	      gpointer	  data)
@@ -2125,6 +2274,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 13f0fcb..9b4c686 100644
--- a/gtk/gtkmain.h
+++ b/gtk/gtkmain.h
@@ -138,6 +138,12 @@ void	   gtk_grab_add		   (GtkWidget	       *widget);
 GtkWidget* gtk_grab_get_current	   (void);
 void	   gtk_grab_remove	   (GtkWidget	       *widget);
 
+void       gtk_device_grab_add     (GtkWidget          *widget,
+                                    GdkDevice          *device,
+                                    gboolean            block_others);
+void       gtk_device_grab_remove  (GtkWidget          *widget,
+                                    GdkDevice          *device);
+
 void	   gtk_init_add		   (GtkFunction	       function,
 				    gpointer	       data);
 void	   gtk_quit_add_destroy	   (guint	       main_level,
@@ -160,6 +166,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/gtkmenu.c b/gtk/gtkmenu.c
index 0bbfae4..e999994 100644
--- a/gtk/gtkmenu.c
+++ b/gtk/gtkmenu.c
@@ -64,6 +64,7 @@
 
 typedef struct _GtkMenuAttachData	GtkMenuAttachData;
 typedef struct _GtkMenuPrivate  	GtkMenuPrivate;
+typedef struct _GtkMenuPopdownData      GtkMenuPopdownData;
 
 struct _GtkMenuAttachData
 {
@@ -100,6 +101,12 @@ struct _GtkMenuPrivate
   guint no_toggle_size        : 1;
 };
 
+struct _GtkMenuPopdownData
+{
+  GtkMenu *menu;
+  GdkDevice *device;
+};
+
 typedef struct
 {
   gint left_attach;
@@ -1370,33 +1377,38 @@ gtk_menu_tearoff_bg_copy (GtkMenu *menu)
 
 static gboolean
 popup_grab_on_window (GdkWindow *window,
-		      guint32    activate_time,
-		      gboolean   grab_keyboard)
+                      GdkDevice *keyboard,
+                      GdkDevice *pointer,
+		      guint32    activate_time)
 {
-  if ((gdk_pointer_grab (window, TRUE,
-			 GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
-			 GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK |
-			 GDK_POINTER_MOTION_MASK,
-			 NULL, NULL, activate_time) == 0))
+  if (keyboard &&
+      gdk_device_grab (keyboard, window,
+                       GDK_OWNERSHIP_WINDOW, TRUE,
+                       GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK,
+                       NULL, activate_time) != GDK_GRAB_SUCCESS)
+    return FALSE;
+
+  if (pointer &&
+      gdk_device_grab (pointer, window,
+                       GDK_OWNERSHIP_WINDOW, TRUE,
+                       GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
+                       GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK |
+                       GDK_POINTER_MOTION_MASK,
+                       NULL, activate_time) != GDK_GRAB_SUCCESS)
     {
-      if (!grab_keyboard ||
-	  gdk_keyboard_grab (window, TRUE,
-			     activate_time) == 0)
-	return TRUE;
-      else
-	{
-	  gdk_display_pointer_ungrab (gdk_drawable_get_display (window),
-				      activate_time);
-	  return FALSE;
-	}
+      if (keyboard)
+        gdk_device_ungrab (keyboard, activate_time);
+
+      return FALSE;
     }
 
-  return FALSE;
+  return TRUE;
 }
 
 /**
- * gtk_menu_popup:
+ * gtk_menu_popup_for_device:
  * @menu: a #GtkMenu.
+ * @device: (allow-none): a #GdkDevice
  * @parent_menu_shell: (allow-none): the menu shell containing the triggering menu item, or %NULL
  * @parent_menu_item: (allow-none): the menu item whose activation triggered the popup, or %NULL
  * @func: (allow-none): a user supplied function used to position the menu, or %NULL
@@ -1406,9 +1418,9 @@ popup_grab_on_window (GdkWindow *window,
  *
  * Displays a menu and makes it available for selection.  Applications can use
  * this function to display context-sensitive menus, and will typically supply
- * %NULL for the @parent_menu_shell, @parent_menu_item, @func and @data 
+ * %NULL for the @parent_menu_shell, @parent_menu_item, @func and @data
  * parameters. The default menu positioning function will position the menu
- * at the current mouse cursor position.
+ * at the current position of @device (or its corresponding pointer).
  *
  * The @button parameter should be the mouse button pressed to initiate
  * the menu popup. If the menu popup was initiated by something other than
@@ -1421,15 +1433,18 @@ popup_grab_on_window (GdkWindow *window,
  * a mouse click or key press) that caused the initiation of the popup.
  * Only if no such event is available, gtk_get_current_event_time() can
  * be used instead.
+ *
+ * Since: 3.0
  */
 void
-gtk_menu_popup (GtkMenu		    *menu,
-		GtkWidget	    *parent_menu_shell,
-		GtkWidget	    *parent_menu_item,
-		GtkMenuPositionFunc  func,
-		gpointer	     data,
-		guint		     button,
-		guint32		     activate_time)
+gtk_menu_popup_for_device (GtkMenu             *menu,
+                           GdkDevice           *device,
+                           GtkWidget           *parent_menu_shell,
+                           GtkWidget           *parent_menu_item,
+                           GtkMenuPositionFunc  func,
+                           gpointer             data,
+                           guint                button,
+                           guint32              activate_time)
 {
   GtkWidget *widget;
   GtkWidget *xgrab_shell;
@@ -1439,13 +1454,26 @@ gtk_menu_popup (GtkMenu		    *menu,
   gboolean grab_keyboard;
   GtkMenuPrivate *priv;
   GtkWidget *parent_toplevel;
+  GdkDevice *keyboard, *pointer;
 
   g_return_if_fail (GTK_IS_MENU (menu));
+  g_return_if_fail (GDK_IS_DEVICE (device));
 
   widget = GTK_WIDGET (menu);
   menu_shell = GTK_MENU_SHELL (menu);
   priv = gtk_menu_get_private (menu);
 
+  if (device->source == GDK_SOURCE_KEYBOARD)
+    {
+      keyboard = device;
+      pointer = gdk_device_get_associated_device (device);
+    }
+  else
+    {
+      pointer = device;
+      keyboard = gdk_device_get_associated_device (device);
+    }
+
   menu_shell->parent_menu_shell = parent_menu_shell;
 
   priv->seen_item_enter = FALSE;
@@ -1493,10 +1521,16 @@ gtk_menu_popup (GtkMenu		    *menu,
   grab_keyboard = gtk_menu_shell_get_take_focus (menu_shell);
   gtk_window_set_accept_focus (GTK_WINDOW (menu->toplevel), grab_keyboard);
 
+  if (!grab_keyboard)
+    keyboard = NULL;
+
   if (xgrab_shell && xgrab_shell != widget)
     {
-      if (popup_grab_on_window (xgrab_shell->window, activate_time, grab_keyboard))
-	GTK_MENU_SHELL (xgrab_shell)->have_xgrab = TRUE;
+      if (popup_grab_on_window (xgrab_shell->window, keyboard, pointer, activate_time))
+        {
+          _gtk_menu_shell_set_grab_devices (GTK_MENU_SHELL (xgrab_shell), keyboard, pointer);
+          GTK_MENU_SHELL (xgrab_shell)->have_xgrab = TRUE;
+        }
     }
   else
     {
@@ -1504,8 +1538,11 @@ gtk_menu_popup (GtkMenu		    *menu,
 
       xgrab_shell = widget;
       transfer_window = menu_grab_transfer_window_get (menu);
-      if (popup_grab_on_window (transfer_window, activate_time, grab_keyboard))
-	GTK_MENU_SHELL (xgrab_shell)->have_xgrab = TRUE;
+      if (popup_grab_on_window (transfer_window, keyboard, pointer, activate_time))
+        {
+          _gtk_menu_shell_set_grab_devices (GTK_MENU_SHELL (xgrab_shell), keyboard, pointer);
+          GTK_MENU_SHELL (xgrab_shell)->have_xgrab = TRUE;
+        }
     }
 
   if (!GTK_MENU_SHELL (xgrab_shell)->have_xgrab)
@@ -1519,6 +1556,7 @@ gtk_menu_popup (GtkMenu		    *menu,
       return;
     }
 
+  _gtk_menu_shell_set_grab_devices (GTK_MENU_SHELL (menu), keyboard, pointer);
   menu_shell->active = TRUE;
   menu_shell->button = button;
 
@@ -1614,8 +1652,9 @@ gtk_menu_popup (GtkMenu		    *menu,
   gtk_widget_show (menu->toplevel);
 
   if (xgrab_shell == widget)
-    popup_grab_on_window (widget->window, activate_time, grab_keyboard); /* Should always succeed */
-  gtk_grab_add (GTK_WIDGET (menu));
+    popup_grab_on_window (widget->window, keyboard, pointer, activate_time); /* Should always succeed */
+
+  gtk_device_grab_add (GTK_WIDGET (menu), pointer, TRUE);
 
   if (parent_menu_shell)
     {
@@ -1630,11 +1669,77 @@ gtk_menu_popup (GtkMenu		    *menu,
   _gtk_menu_shell_update_mnemonics (menu_shell);
 }
 
+/**
+ * gtk_menu_popup:
+ * @menu: a #GtkMenu.
+ * @parent_menu_shell: (allow-none): the menu shell containing the triggering menu item, or %NULL
+ * @parent_menu_item: (allow-none): the menu item whose activation triggered the popup, or %NULL
+ * @func: (allow-none): a user supplied function used to position the menu, or %NULL
+ * @data: (allow-none): user supplied data to be passed to @func.
+ * @button: the mouse button which was pressed to initiate the event.
+ * @activate_time: the time at which the activation event occurred.
+ *
+ * Displays a menu and makes it available for selection.  Applications can use
+ * this function to display context-sensitive menus, and will typically supply
+ * %NULL for the @parent_menu_shell, @parent_menu_item, @func and @data 
+ * parameters. The default menu positioning function will position the menu
+ * at the current mouse cursor position.
+ *
+ * The @button parameter should be the mouse button pressed to initiate
+ * the menu popup. If the menu popup was initiated by something other than
+ * a mouse button press, such as a mouse button release or a keypress,
+ * @button should be 0.
+ *
+ * The @activate_time parameter is used to conflict-resolve initiation of
+ * concurrent requests for mouse/keyboard grab requests. To function
+ * properly, this needs to be the time stamp of the user event (such as
+ * a mouse click or key press) that caused the initiation of the popup.
+ * Only if no such event is available, gtk_get_current_event_time() can
+ * be used instead.
+ */
+void
+gtk_menu_popup (GtkMenu		    *menu,
+		GtkWidget	    *parent_menu_shell,
+		GtkWidget	    *parent_menu_item,
+		GtkMenuPositionFunc  func,
+		gpointer	     data,
+		guint		     button,
+		guint32		     activate_time)
+{
+  GdkDevice *device;
+
+  g_return_if_fail (GTK_IS_MENU (menu));
+
+  device = gtk_get_current_event_device ();
+
+  if (!device)
+    {
+      GdkDisplay *display;
+      GdkDeviceManager *device_manager;
+      GList *devices;
+
+      display = gtk_widget_get_display (GTK_WIDGET (menu));
+      device_manager = gdk_display_get_device_manager (display);
+      devices = gdk_device_manager_list_devices (device_manager, GDK_DEVICE_TYPE_MASTER);
+
+      device = devices->data;
+
+      g_list_free (devices);
+    }
+
+  gtk_menu_popup_for_device (menu, device,
+                             parent_menu_shell,
+                             parent_menu_item,
+                             func, data,
+                             button, activate_time);
+}
+
 void
 gtk_menu_popdown (GtkMenu *menu)
 {
   GtkMenuPrivate *private;
   GtkMenuShell *menu_shell;
+  GdkDevice *pointer;
 
   g_return_if_fail (GTK_IS_MENU (menu));
   
@@ -1676,15 +1781,16 @@ gtk_menu_popdown (GtkMenu *menu)
 	} 
       else
 	{
-	  /* We popped up the menu from the tearoff, so we need to 
+          GdkDevice *keyboard, *pointer;
+
+          /* We popped up the menu from the tearoff, so we need to
 	   * release the grab - we aren't actually hiding the menu.
 	   */
-	  if (menu_shell->have_xgrab)
+	  if (menu_shell->have_xgrab &&
+              _gtk_menu_shell_get_grab_devices (menu_shell, &keyboard, &pointer))
 	    {
-	      GdkDisplay *display = gtk_widget_get_display (GTK_WIDGET (menu));
-	      
-	      gdk_display_pointer_ungrab (display, GDK_CURRENT_TIME);
-	      gdk_display_keyboard_ungrab (display, GDK_CURRENT_TIME);
+	      gdk_device_ungrab (keyboard, GDK_CURRENT_TIME);
+	      gdk_device_ungrab (pointer, GDK_CURRENT_TIME);
 	    }
 	}
 
@@ -1700,7 +1806,13 @@ gtk_menu_popdown (GtkMenu *menu)
     gtk_widget_hide (GTK_WIDGET (menu));
 
   menu_shell->have_xgrab = FALSE;
-  gtk_grab_remove (GTK_WIDGET (menu));
+
+  _gtk_menu_shell_get_grab_devices (menu_shell, NULL, &pointer);
+
+  if (pointer)
+    gtk_device_grab_remove (GTK_WIDGET (menu), pointer);
+
+  _gtk_menu_shell_set_grab_devices (menu_shell, NULL, NULL);
 
   menu_grab_transfer_window_destroy (menu);
 }
@@ -3293,6 +3405,7 @@ gtk_menu_motion_notify (GtkWidget      *widget,
 	  send_event->crossing.x = event->x;
 	  send_event->crossing.y = event->y;
           send_event->crossing.state = event->state;
+          gdk_event_set_device (send_event, gdk_event_get_device ((GdkEvent *) event));
 
 	  /* We send the event to 'widget', the currently active menu,
 	   * instead of 'menu', the menu that the pointer is in. This
@@ -3967,14 +4080,17 @@ gtk_menu_stop_navigating_submenu (GtkMenu *menu)
 static gboolean
 gtk_menu_stop_navigating_submenu_cb (gpointer user_data)
 {
-  GtkMenu *menu = user_data;
+  GtkMenuPopdownData *popdown_data = user_data;
+  GtkMenu *menu = popdown_data->menu;
   GdkWindow *child_window;
 
   gtk_menu_stop_navigating_submenu (menu);
   
   if (gtk_widget_get_realized (GTK_WIDGET (menu)))
     {
-      child_window = gdk_window_get_pointer (menu->bin_window, NULL, NULL, NULL);
+      child_window = gdk_window_get_device_position (menu->bin_window,
+                                                     popdown_data->device,
+                                                     NULL, NULL, NULL);
 
       if (child_window)
 	{
@@ -3983,6 +4099,7 @@ gtk_menu_stop_navigating_submenu_cb (gpointer user_data)
 	  send_event->crossing.window = g_object_ref (child_window);
 	  send_event->crossing.time = GDK_CURRENT_TIME; /* Bogus */
 	  send_event->crossing.send_event = TRUE;
+          gdk_event_set_device (send_event, popdown_data->device);
 
 	  GTK_WIDGET_CLASS (gtk_menu_parent_class)->enter_notify_event (GTK_WIDGET (menu), (GdkEventCrossing *)send_event);
 
@@ -4089,6 +4206,7 @@ gtk_menu_set_submenu_navigation_region (GtkMenu          *menu,
   gint height = 0;
   GdkPoint point[3];
   GtkWidget *event_widget;
+  GtkMenuPopdownData *popdown_data;
 
   g_return_if_fail (menu_item->submenu != NULL);
   g_return_if_fail (event != NULL);
@@ -4163,9 +4281,15 @@ gtk_menu_set_submenu_navigation_region (GtkMenu          *menu,
 		    "gtk-menu-popdown-delay", &popdown_delay,
 		    NULL);
 
-      menu->navigation_timeout = gdk_threads_add_timeout (popdown_delay,
-                                                          gtk_menu_stop_navigating_submenu_cb,
-                                                          menu);
+      popdown_data = g_new (GtkMenuPopdownData, 1);
+      popdown_data->menu = menu;
+      popdown_data->device = gdk_event_get_device ((GdkEvent *) event);
+
+      menu->navigation_timeout = gdk_threads_add_timeout_full (G_PRIORITY_DEFAULT,
+                                                               popdown_delay,
+                                                               gtk_menu_stop_navigating_submenu_cb,
+                                                               popdown_data,
+                                                               (GDestroyNotify) g_free);
 
 #ifdef DRAW_STAY_UP_TRIANGLE
       draw_stay_up_triangle (gdk_get_default_root_window(),
@@ -4202,15 +4326,17 @@ gtk_menu_position (GtkMenu *menu)
   GdkScreen *screen;
   GdkScreen *pointer_screen;
   GdkRectangle monitor;
-  
+  GdkDevice *pointer;
+
   g_return_if_fail (GTK_IS_MENU (menu));
 
   widget = GTK_WIDGET (menu);
 
   screen = gtk_widget_get_screen (widget);
-  gdk_display_get_pointer (gdk_screen_get_display (screen),
-			   &pointer_screen, &x, &y, NULL);
-  
+  _gtk_menu_shell_get_grab_devices (GTK_MENU_SHELL (menu), NULL, &pointer);
+  gdk_display_get_device_state (gdk_screen_get_display (screen),
+                                pointer, &pointer_screen, &x, &y, NULL);
+
   /* We need the requisition to figure out the right place to
    * popup the menu. In fact, we always need to ask here, since
    * if a size_request was queued while we weren't popped up,
@@ -5324,16 +5450,24 @@ gtk_menu_grab_notify (GtkWidget *widget,
   GtkWidget *toplevel;
   GtkWindowGroup *group;
   GtkWidget *grab;
+  GdkDevice *pointer;
+
+  _gtk_menu_shell_get_grab_devices (GTK_MENU_SHELL (widget), NULL, &pointer);
+
+  if (!pointer ||
+      !gtk_widget_device_is_shadowed (widget, pointer))
+    return;
 
   toplevel = gtk_widget_get_toplevel (widget);
+
+  if (!GTK_IS_WINDOW (toplevel))
+    return;
+
   group = gtk_window_get_group (GTK_WINDOW (toplevel));
-  grab = _gtk_window_group_get_current_grab (group); 
+  grab = gtk_window_group_get_current_device_grab (group, pointer);
 
-  if (!was_grabbed)
-    {
-      if (GTK_MENU_SHELL (widget)->active && !GTK_IS_MENU_SHELL (grab))
-        gtk_menu_shell_cancel (GTK_MENU_SHELL (widget));
-    }
+  if (GTK_MENU_SHELL (widget)->active && !GTK_IS_MENU_SHELL (grab))
+    gtk_menu_shell_cancel (GTK_MENU_SHELL (widget));
 }
 
 /**
diff --git a/gtk/gtkmenu.h b/gtk/gtkmenu.h
index 6a944c0..0929472 100644
--- a/gtk/gtkmenu.h
+++ b/gtk/gtkmenu.h
@@ -133,6 +133,15 @@ void	   gtk_menu_popup		  (GtkMenu	       *menu,
 					   gpointer		data,
 					   guint		button,
 					   guint32		activate_time);
+void       gtk_menu_popup_for_device      (GtkMenu             *menu,
+                                           GdkDevice           *device,
+                                           GtkWidget           *parent_menu_shell,
+                                           GtkWidget           *parent_menu_item,
+                                           GtkMenuPositionFunc  func,
+                                           gpointer             data,
+                                           guint                button,
+                                           guint32              activate_time);
+
 
 /* Position the menu according to its position function. Called
  * from gtkmenuitem.c when a menu-item changes its allocation
diff --git a/gtk/gtkmenushell.c b/gtk/gtkmenushell.c
index c3e107f..615f9a4 100644
--- a/gtk/gtkmenushell.c
+++ b/gtk/gtkmenushell.c
@@ -135,6 +135,9 @@ struct _GtkMenuShellPrivate
   GtkMnemonicHash *mnemonic_hash;
   GtkKeyHash *key_hash;
 
+  GdkDevice *grab_keyboard;
+  GdkDevice *grab_pointer;
+
   guint take_focus : 1;
   guint activated_submenu : 1;
   /* This flag is a crutch to keep mnemonics in the same menu
@@ -548,7 +551,9 @@ _gtk_menu_shell_activate (GtkMenuShell *menu_shell)
 {
   if (!menu_shell->active)
     {
-      gtk_grab_add (GTK_WIDGET (menu_shell));
+      gtk_device_grab_add (GTK_WIDGET (menu_shell),
+                           gtk_get_current_event_device (),
+                           TRUE);
       menu_shell->have_grab = TRUE;
       menu_shell->active = TRUE;
     }
@@ -1073,6 +1078,8 @@ gtk_real_menu_shell_deactivate (GtkMenuShell *menu_shell)
 {
   if (menu_shell->active)
     {
+      GtkMenuShellPrivate *priv = GTK_MENU_SHELL_GET_PRIVATE (menu_shell);
+
       menu_shell->button = 0;
       menu_shell->active = FALSE;
       menu_shell->activate_time = 0;
@@ -1086,15 +1093,16 @@ gtk_real_menu_shell_deactivate (GtkMenuShell *menu_shell)
       if (menu_shell->have_grab)
 	{
 	  menu_shell->have_grab = FALSE;
-	  gtk_grab_remove (GTK_WIDGET (menu_shell));
+          gtk_device_grab_remove (GTK_WIDGET (menu_shell), priv->grab_pointer);
 	}
       if (menu_shell->have_xgrab)
 	{
-	  GdkDisplay *display = gtk_widget_get_display (GTK_WIDGET (menu_shell));
+          gdk_device_ungrab (priv->grab_pointer, GDK_CURRENT_TIME);
+          gdk_device_ungrab (priv->grab_keyboard, GDK_CURRENT_TIME);
 
 	  menu_shell->have_xgrab = FALSE;
-	  gdk_display_pointer_ungrab (display, GDK_CURRENT_TIME);
-	  gdk_display_keyboard_ungrab (display, GDK_CURRENT_TIME);
+          priv->grab_pointer = NULL;
+          priv->grab_keyboard = NULL;
 	}
 
       menu_shell->keyboard_mode = FALSE;
@@ -1743,6 +1751,39 @@ _gtk_menu_shell_remove_mnemonic (GtkMenuShell *menu_shell,
   gtk_menu_shell_reset_key_hash (menu_shell);
 }
 
+void
+_gtk_menu_shell_set_grab_devices (GtkMenuShell *menu_shell,
+                                  GdkDevice    *keyboard,
+                                  GdkDevice    *pointer)
+{
+  GtkMenuShellPrivate *priv = GTK_MENU_SHELL_GET_PRIVATE (menu_shell);
+
+  g_return_if_fail (GTK_IS_MENU_SHELL (menu_shell));
+  g_return_if_fail (!keyboard || GDK_IS_DEVICE (keyboard));
+  g_return_if_fail (!pointer || GDK_IS_DEVICE (pointer));
+
+  priv->grab_keyboard = keyboard;
+  priv->grab_pointer = pointer;
+}
+
+gboolean
+_gtk_menu_shell_get_grab_devices (GtkMenuShell  *menu_shell,
+                                  GdkDevice    **keyboard,
+                                  GdkDevice    **pointer)
+{
+  GtkMenuShellPrivate *priv = GTK_MENU_SHELL_GET_PRIVATE (menu_shell);
+
+  g_return_val_if_fail (GTK_IS_MENU_SHELL (menu_shell), FALSE);
+
+  if (keyboard)
+    *keyboard = priv->grab_keyboard;
+
+  if (pointer)
+    *pointer = priv->grab_pointer;
+
+  return TRUE;
+}
+
 /**
  * gtk_menu_shell_get_take_focus:
  * @menu_shell: a #GtkMenuShell
diff --git a/gtk/gtkmenushell.h b/gtk/gtkmenushell.h
index e0d042b..f8c5a32 100644
--- a/gtk/gtkmenushell.h
+++ b/gtk/gtkmenushell.h
@@ -118,6 +118,14 @@ void _gtk_menu_shell_select_last       (GtkMenuShell *menu_shell,
 					gboolean      search_sensitive);
 void  _gtk_menu_shell_activate         (GtkMenuShell *menu_shell);
 gint  _gtk_menu_shell_get_popup_delay  (GtkMenuShell *menu_shell);
+
+void     _gtk_menu_shell_set_grab_devices (GtkMenuShell *menu_shell,
+                                           GdkDevice    *keyboard,
+                                           GdkDevice    *pointer);
+gboolean _gtk_menu_shell_get_grab_devices (GtkMenuShell  *menu_shell,
+                                           GdkDevice    **keyboard,
+                                           GdkDevice    **pointer);
+
 void  gtk_menu_shell_cancel            (GtkMenuShell *menu_shell);
 
 void  _gtk_menu_shell_add_mnemonic     (GtkMenuShell *menu_shell,
diff --git a/gtk/gtknotebook.c b/gtk/gtknotebook.c
index 62008ba..bb528fd 100644
--- a/gtk/gtknotebook.c
+++ b/gtk/gtknotebook.c
@@ -2751,7 +2751,8 @@ get_drop_position (GtkNotebook *notebook,
 static void
 show_drag_window (GtkNotebook        *notebook,
 		  GtkNotebookPrivate *priv,
-		  GtkNotebookPage    *page)
+		  GtkNotebookPage    *page,
+                  GdkDevice          *device)
 {
   GtkWidget *widget = GTK_WIDGET (notebook);
 
@@ -2786,10 +2787,10 @@ show_drag_window (GtkNotebook        *notebook,
   gdk_window_show (priv->drag_window);
 
   /* the grab will dissapear when the window is hidden */
-  gdk_pointer_grab (priv->drag_window,
-		    FALSE,
-		    GDK_POINTER_MOTION_MASK | GDK_BUTTON_RELEASE_MASK,
-		    NULL, NULL, GDK_CURRENT_TIME);
+  gdk_device_grab (device, priv->drag_window,
+                   GDK_OWNERSHIP_WINDOW, FALSE,
+                   GDK_POINTER_MOTION_MASK | GDK_BUTTON_RELEASE_MASK,
+                   NULL, GDK_CURRENT_TIME);
 }
 
 /* This function undoes the reparenting that happens both when drag_window
@@ -3129,7 +3130,7 @@ gtk_notebook_motion_notify (GtkWidget      *widget,
 	  if (priv->operation != DRAG_OPERATION_REORDER)
 	    {
 	      priv->operation = DRAG_OPERATION_REORDER;
-	      show_drag_window (notebook, priv, page);
+	      show_drag_window (notebook, priv, page, event->device);
 	    }
 
 	  gtk_notebook_pages_allocate (notebook);
diff --git a/gtk/gtkpaned.c b/gtk/gtkpaned.c
index d9c9177..69e766d 100644
--- a/gtk/gtkpaned.c
+++ b/gtk/gtkpaned.c
@@ -150,6 +150,7 @@ struct _GtkPanedPrivate
   GtkWidget      *saved_focus;
   GtkPaned       *first_paned;
   guint32         grab_time;
+  GdkDevice      *grab_device;
 };
 
 
@@ -1215,18 +1216,20 @@ gtk_paned_button_press (GtkWidget      *widget,
     {
       /* We need a server grab here, not gtk_grab_add(), since
        * we don't want to pass events on to the widget's children */
-      if (gdk_pointer_grab (paned->handle, FALSE,
-			    GDK_POINTER_MOTION_HINT_MASK
-			    | GDK_BUTTON1_MOTION_MASK
-			    | GDK_BUTTON_RELEASE_MASK
-			    | GDK_ENTER_NOTIFY_MASK
-			    | GDK_LEAVE_NOTIFY_MASK,
-			    NULL, NULL,
-			    event->time) != GDK_GRAB_SUCCESS)
+      if (gdk_device_grab (event->device,
+                           paned->handle,
+                           GDK_OWNERSHIP_WINDOW, FALSE,
+                           GDK_POINTER_MOTION_HINT_MASK
+                           | GDK_BUTTON1_MOTION_MASK
+                           | GDK_BUTTON_RELEASE_MASK
+                           | GDK_ENTER_NOTIFY_MASK
+                           | GDK_LEAVE_NOTIFY_MASK,
+                           NULL, event->time) != GDK_GRAB_SUCCESS)
 	return FALSE;
 
       paned->in_drag = TRUE;
       paned->priv->grab_time = event->time;
+      paned->priv->grab_device = event->device;
 
       if (paned->priv->orientation == GTK_ORIENTATION_HORIZONTAL)
 	paned->drag_pos = event->x;
@@ -1258,8 +1261,10 @@ stop_drag (GtkPaned *paned)
   paned->in_drag = FALSE;
   paned->drag_pos = -1;
   paned->position_set = TRUE;
-  gdk_display_pointer_ungrab (gtk_widget_get_display (GTK_WIDGET (paned)),
-			      paned->priv->grab_time);
+
+  gdk_device_ungrab (paned->priv->grab_device,
+                     paned->priv->grab_time);
+  paned->priv->grab_device = NULL;
 }
 
 static void
@@ -1267,8 +1272,12 @@ gtk_paned_grab_notify (GtkWidget *widget,
 		       gboolean   was_grabbed)
 {
   GtkPaned *paned = GTK_PANED (widget);
+  GdkDevice *grab_device;
+
+  grab_device = paned->priv->grab_device;
 
-  if (!was_grabbed && paned->in_drag)
+  if (paned->in_drag && grab_device &&
+      gtk_widget_device_is_shadowed (widget, grab_device))
     stop_drag (paned);
 }
 
diff --git a/gtk/gtkplug-x11.c b/gtk/gtkplug-x11.c
index 95dcf1a..d0df6f5 100644
--- a/gtk/gtkplug-x11.c
+++ b/gtk/gtkplug-x11.c
@@ -25,6 +25,21 @@
  * GTK+ at ftp://ftp.gtk.org/pub/gtk/. 
  */
 
+#include "config.h"
+
+#ifdef XINPUT_2
+
+/* Hack to have keyboard events interpreted
+ * regardless of the default device manager
+ */
+#define GDK_COMPILATION
+#include "x11/gdkdevicemanager-core.h"
+#include "x11/gdkdevicemanager-xi2.h"
+#include "x11/gdkeventtranslator.h"
+#undef GDK_COMPILATION
+
+#endif /* XINPUT_2 */
+
 #include "gtkmain.h"
 #include "gtkmarshalers.h"
 #include "gtkplug.h"
@@ -208,7 +223,7 @@ _gtk_plug_windowing_filter_func (GdkXEvent *gdk_xevent,
   XEvent *xevent = (XEvent *)gdk_xevent;
 
   GdkFilterReturn return_val;
-  
+
   return_val = GDK_FILTER_CONTINUE;
 
   switch (xevent->type)
@@ -326,6 +341,61 @@ _gtk_plug_windowing_filter_func (GdkXEvent *gdk_xevent,
 	
 	break;
       }
+
+#ifdef XINPUT_2
+    case KeyPress:
+    case KeyRelease:
+      {
+        static GdkDeviceManager *core_device_manager = NULL;
+        GdkDeviceManager *device_manager;
+        GdkEvent *translated_event;
+        GList *devices, *d;
+        GdkDevice *keyboard = NULL;
+
+        device_manager = gdk_display_get_device_manager (display);
+
+        /* bail out if the device manager already
+         * interprets core keyboard events.
+         */
+        if (!GDK_IS_DEVICE_MANAGER_XI2 (device_manager))
+          return GDK_FILTER_CONTINUE;
+
+        /* Find out the first keyboard device, the
+         * generated event will be assigned to it.
+         */
+        devices = gdk_device_manager_list_devices (device_manager, GDK_DEVICE_TYPE_MASTER);
+
+        for (d = devices; d; d = d->next)
+          {
+            GdkDevice *device = d->data;
+
+            if (device->source == GDK_SOURCE_KEYBOARD)
+              keyboard = device;
+          }
+
+        g_list_free (devices);
+
+        if (!keyboard)
+          return GDK_FILTER_CONTINUE;
+
+        /* This is a crude hack so key events
+         * are interpreted as if there was a
+         * GdkDeviceManagerCore available.
+         */
+        if (G_UNLIKELY (!core_device_manager))
+          core_device_manager = g_object_new (GDK_TYPE_DEVICE_MANAGER_CORE,
+                                         "display", display,
+                                         NULL);
+
+        translated_event = gdk_event_translator_translate (GDK_EVENT_TRANSLATOR (core_device_manager), display, xevent);
+        gdk_event_set_device (translated_event, keyboard);
+
+        gtk_main_do_event (translated_event);
+        gdk_event_free (translated_event);
+
+        return_val = GDK_FILTER_REMOVE;
+      }
+#endif
     }
 
   return return_val;
diff --git a/gtk/gtkprivate.h b/gtk/gtkprivate.h
index 4c5efb3..8db5c48 100644
--- a/gtk/gtkprivate.h
+++ b/gtk/gtkprivate.h
@@ -50,7 +50,6 @@ typedef enum
   PRIVATE_GTK_REQUEST_NEEDED        = 1 <<  13,  /* Whether we need to call gtk_widget_size_request */
   PRIVATE_GTK_WIDTH_REQUEST_NEEDED  = 1 <<  14,  /* Whether we need to call gtk_extended_layout_get_desired_width */
   PRIVATE_GTK_HEIGHT_REQUEST_NEEDED = 1 <<  15   /* Whether we need to call gtk_extended_layout_get_desired_height */
-
 } GtkPrivateFlags;
 
 /* Macros for extracting a widgets private_flags from GtkWidget.
diff --git a/gtk/gtkrange.c b/gtk/gtkrange.c
index 744871d..fa92d25 100644
--- a/gtk/gtkrange.c
+++ b/gtk/gtkrange.c
@@ -139,6 +139,8 @@ struct _GtkRangeLayout
   gint *mark_pos;
   gint n_marks;
   gboolean recalc_marks;
+
+  GdkDevice *grab_device;
 };
 
 
@@ -2032,16 +2034,28 @@ gtk_range_expose (GtkWidget      *widget,
 
 static void
 range_grab_add (GtkRange      *range,
+                GdkDevice     *device,
                 MouseLocation  location,
                 gint           button)
 {
-  /* we don't actually gtk_grab, since a button is down */
+  GtkRangeLayout *layout = range->layout;
+
+  if (device == layout->grab_device)
+    return;
+
+  if (layout->grab_device != NULL)
+    {
+      g_warning ("GtkRange already had a grab device, releasing device grab");
+      gtk_device_grab_remove (GTK_WIDGET (range), layout->grab_device);
+    }
+
+  /* we don't actually gdk_grab, since a button is down */
+  gtk_device_grab_add (GTK_WIDGET (range), device, TRUE);
 
-  gtk_grab_add (GTK_WIDGET (range));
-  
   range->layout->grab_location = location;
   range->layout->grab_button = button;
-  
+  range->layout->grab_device = device;
+
   if (gtk_range_update_mouse_location (range))
     gtk_widget_queue_draw (GTK_WIDGET (range));
 }
@@ -2049,10 +2063,16 @@ range_grab_add (GtkRange      *range,
 static void
 range_grab_remove (GtkRange *range)
 {
+  GtkRangeLayout *layout = range->layout;
   MouseLocation location;
 
-  gtk_grab_remove (GTK_WIDGET (range));
- 
+  if (layout->grab_device)
+    {
+      gtk_device_grab_remove (GTK_WIDGET (range),
+                              layout->grab_device);
+      layout->grab_device = NULL;
+    }
+
   location = range->layout->grab_location; 
   range->layout->grab_location = MOUSE_OUTSIDE;
   range->layout->grab_button = 0;
@@ -2177,9 +2197,15 @@ static gboolean
 gtk_range_key_press (GtkWidget   *widget,
 		     GdkEventKey *event)
 {
+  GdkDevice *device;
   GtkRange *range = GTK_RANGE (widget);
+  GtkRangeLayout *layout = range->layout;
+
+  device = gdk_event_get_device ((GdkEvent *) event);
+  device = gdk_device_get_associated_device (device);
 
-  if (event->keyval == GDK_Escape &&
+  if (device == layout->grab_device &&
+      event->keyval == GDK_Escape &&
       range->layout->grab_location != MOUSE_OUTSIDE)
     {
       stop_scrolling (range);
@@ -2199,6 +2225,7 @@ gtk_range_button_press (GtkWidget      *widget,
 			GdkEventButton *event)
 {
   GtkRange *range = GTK_RANGE (widget);
+  GdkDevice *device;
   
   if (!gtk_widget_has_focus (widget))
     gtk_widget_grab_focus (widget);
@@ -2207,8 +2234,10 @@ gtk_range_button_press (GtkWidget      *widget,
   if (range->layout->grab_location != MOUSE_OUTSIDE)
     return FALSE;
 
+  device = gdk_event_get_device ((GdkEvent *) event);
   range->layout->mouse_x = event->x;
   range->layout->mouse_y = event->y;
+
   if (gtk_range_update_mouse_location (range))
     gtk_widget_queue_draw (widget);
     
@@ -2225,7 +2254,7 @@ gtk_range_button_press (GtkWidget      *widget,
                                     event->y : event->x);
       
       range->trough_click_forward = click_value > range->adjustment->value;
-      range_grab_add (range, MOUSE_TROUGH, event->button);
+      range_grab_add (range, device, MOUSE_TROUGH, event->button);
       
       scroll = range_get_scroll_for_grab (range);
       
@@ -2242,7 +2271,7 @@ gtk_range_button_press (GtkWidget      *widget,
       GdkRectangle *stepper_area;
       GtkScrollType scroll;
       
-      range_grab_add (range, range->layout->mouse_location, event->button);
+      range_grab_add (range, device, range->layout->mouse_location, event->button);
 
       stepper_area = get_area (range, range->layout->mouse_location);
       gtk_widget_queue_draw_area (widget,
@@ -2309,7 +2338,7 @@ gtk_range_button_press (GtkWidget      *widget,
           range->slide_initial_coordinate = event->x;
         }
 
-      range_grab_add (range, MOUSE_SLIDER, event->button);
+      range_grab_add (range, device, MOUSE_SLIDER, event->button);
 
       gtk_widget_style_get (widget, "activate-slider", &activate_slider, NULL);
 
@@ -2386,8 +2415,12 @@ gtk_range_grab_broken (GtkWidget          *widget,
 		       GdkEventGrabBroken *event)
 {
   GtkRange *range = GTK_RANGE (widget);
+  GdkDevice *device;
 
-  if (range->layout->grab_location != MOUSE_OUTSIDE)
+  device = gdk_event_get_device ((GdkEvent *) event);
+
+  if (device == range->layout->grab_device &&
+      range->layout->grab_location != MOUSE_OUTSIDE)
     {
       if (range->layout->grab_location == MOUSE_SLIDER)
 	update_slider_position (range, range->layout->mouse_x, range->layout->mouse_y);
@@ -2405,6 +2438,7 @@ gtk_range_button_release (GtkWidget      *widget,
 			  GdkEventButton *event)
 {
   GtkRange *range = GTK_RANGE (widget);
+  GdkDevice *device;
 
   if (event->window == range->event_window)
     {
@@ -2413,13 +2447,17 @@ gtk_range_button_release (GtkWidget      *widget,
     }
   else
     {
-      gdk_window_get_pointer (range->event_window,
-			      &range->layout->mouse_x,
-			      &range->layout->mouse_y,
-			      NULL);
+      gdk_window_get_device_position (range->event_window,
+                                      event->device,
+                                      &range->layout->mouse_x,
+                                      &range->layout->mouse_y,
+                                      NULL);
     }
-  
-  if (range->layout->grab_button == event->button)
+
+  device = gdk_event_get_device ((GdkEvent *) event);
+
+  if (range->layout->grab_device == device &&
+      range->layout->grab_button == event->button)
     {
       if (range->layout->grab_location == MOUSE_SLIDER)
         update_slider_position (range, range->layout->mouse_x, range->layout->mouse_y);
@@ -2551,7 +2589,10 @@ static void
 gtk_range_grab_notify (GtkWidget *widget,
 		       gboolean   was_grabbed)
 {
-  if (!was_grabbed)
+  GtkRangeLayout *layout = GTK_RANGE (widget)->layout;
+
+  if (layout->grab_device &&
+      gtk_widget_device_is_shadowed (widget, layout->grab_device))
     stop_scrolling (GTK_RANGE (widget));
 }
 
diff --git a/gtk/gtkscalebutton.c b/gtk/gtkscalebutton.c
index f994d6e..dce3937 100644
--- a/gtk/gtkscalebutton.c
+++ b/gtk/gtkscalebutton.c
@@ -116,6 +116,9 @@ struct _GtkScaleButtonPrivate
 
   gchar **icon_list;
 
+  GdkDevice *grab_pointer;
+  GdkDevice *grab_keyboard;
+
   GtkAdjustment *adjustment; /* needed because it must be settable in init() */
 };
 
@@ -901,6 +904,7 @@ gtk_scale_popup (GtkWidget *widget,
   GdkDisplay *display;
   GdkScreen *screen;
   gboolean is_moved;
+  GdkDevice *device, *keyboard, *pointer;
 
   is_moved = FALSE;
   button = GTK_SCALE_BUTTON (widget);
@@ -982,21 +986,27 @@ gtk_scale_popup (GtkWidget *widget,
       /* Move the dock, but set is_moved so we
        * don't forward the first click later on,
        * as it could make the scale go to the bottom */
-      if (y < rect.y) {
-	y = rect.y;
-	is_moved = TRUE;
-      } else if (y + d->allocation.height > rect.height + rect.y) {
-	y = rect.y + rect.height - d->allocation.height;
-	is_moved = TRUE;
-      }
+      if (y < rect.y)
+        {
+          y = rect.y;
+          is_moved = TRUE;
+        }
+      else if (y + d->allocation.height > rect.height + rect.y)
+        {
+          y = rect.y + rect.height - d->allocation.height;
+          is_moved = TRUE;
+        }
 
-      if (x < rect.x) {
-	x = rect.x;
-	is_moved = TRUE;
-      } else if (x + d->allocation.width > rect.width + rect.x) {
-	x = rect.x + rect.width - d->allocation.width;
-	is_moved = TRUE;
-      }
+      if (x < rect.x)
+        {
+          x = rect.x;
+          is_moved = TRUE;
+        }
+      else if (x + d->allocation.width > rect.width + rect.x)
+        {
+          x = rect.x + rect.width - d->allocation.width;
+          is_moved = TRUE;
+        }
     }
 
   gtk_window_move (GTK_WINDOW (priv->dock), x, y);
@@ -1004,28 +1014,46 @@ gtk_scale_popup (GtkWidget *widget,
   if (event->type == GDK_BUTTON_PRESS)
     GTK_WIDGET_CLASS (gtk_scale_button_parent_class)->button_press_event (widget, (GdkEventButton *) event);
 
+  device = gdk_event_get_device (event);
+
+  if (device->source == GDK_SOURCE_KEYBOARD)
+    {
+      keyboard = device;
+      pointer = gdk_device_get_associated_device (device);
+    }
+  else
+    {
+      pointer = device;
+      keyboard = gdk_device_get_associated_device (device);
+    }
+
   /* grab focus */
-  gtk_grab_add (priv->dock);
+  gtk_device_grab_add (priv->dock, pointer, TRUE);
 
-  if (gdk_pointer_grab (priv->dock->window, TRUE,
-			GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
-			GDK_POINTER_MOTION_MASK, NULL, NULL, time)
-      != GDK_GRAB_SUCCESS)
+  if (gdk_device_grab (pointer, priv->dock->window,
+                       GDK_OWNERSHIP_WINDOW, TRUE,
+                       GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
+                       GDK_POINTER_MOTION_MASK, NULL, time) != GDK_GRAB_SUCCESS)
     {
-      gtk_grab_remove (priv->dock);
+      gtk_device_grab_remove (priv->dock, pointer);
       gtk_widget_hide (priv->dock);
       return FALSE;
     }
 
-  if (gdk_keyboard_grab (priv->dock->window, TRUE, time) != GDK_GRAB_SUCCESS)
+  if (gdk_device_grab (keyboard, priv->dock->window,
+                       GDK_OWNERSHIP_WINDOW, TRUE,
+                       GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK,
+                       NULL, time) != GDK_GRAB_SUCCESS)
     {
-      gdk_display_pointer_ungrab (display, time);
-      gtk_grab_remove (priv->dock);
+      gdk_device_ungrab (pointer, time);
+      gtk_device_grab_remove (priv->dock, pointer);
       gtk_widget_hide (priv->dock);
       return FALSE;
     }
 
   gtk_widget_grab_focus (priv->dock);
+  priv->grab_keyboard = keyboard;
+  priv->grab_pointer = pointer;
 
   if (event->type == GDK_BUTTON_PRESS && !is_moved)
     {
@@ -1080,8 +1108,21 @@ gtk_scale_button_popup (GtkWidget *widget)
 {
   GdkEvent *ev;
 
-  ev = gdk_event_new (GDK_KEY_RELEASE);
-  gtk_scale_popup (widget, ev, GDK_CURRENT_TIME);
+  /* This is a callback for a keybinding signal,
+   * current event should  be the key event that
+   * triggered it.
+   */
+  ev = gtk_get_current_event ();
+
+  if (ev->type != GDK_KEY_PRESS &&
+      ev->type != GDK_KEY_RELEASE)
+    {
+      gdk_event_free (ev);
+      ev = gdk_event_new (GDK_KEY_RELEASE);
+      ev->key.time = GDK_CURRENT_TIME;
+    }
+
+  gtk_scale_popup (widget, ev, ev->key.time);
   gdk_event_free (ev);
 }
 
@@ -1100,22 +1141,35 @@ gtk_scale_button_grab_notify (GtkScaleButton *button,
 {
   GdkDisplay *display;
   GtkScaleButtonPrivate *priv;
-
-  if (was_grabbed != FALSE)
-    return;
+  GtkWidget *toplevel, *grab_widget;
+  GtkWindowGroup *group;
 
   priv = button->priv;
 
-  if (!gtk_widget_has_grab (priv->dock))
+  if (!priv->grab_pointer ||
+      !gtk_widget_device_is_shadowed (GTK_WIDGET (button), priv->grab_pointer))
     return;
 
-  if (gtk_widget_is_ancestor (gtk_grab_get_current (), priv->dock))
+  toplevel = gtk_widget_get_toplevel (GTK_WIDGET (button));
+
+  if (GTK_IS_WINDOW (toplevel))
+    group = gtk_window_get_group (GTK_WINDOW (toplevel));
+  else
+    group = gtk_window_get_group (NULL);
+
+  grab_widget = gtk_window_group_get_current_device_grab (group, priv->grab_pointer);
+
+  if (grab_widget &&
+      gtk_widget_is_ancestor (grab_widget, priv->dock))
     return;
 
   display = gtk_widget_get_display (priv->dock);
-  gdk_display_keyboard_ungrab (display, GDK_CURRENT_TIME);
-  gdk_display_pointer_ungrab (display, GDK_CURRENT_TIME);
-  gtk_grab_remove (priv->dock);
+  gdk_device_ungrab (priv->grab_keyboard, GDK_CURRENT_TIME);
+  gdk_device_ungrab (priv->grab_pointer, GDK_CURRENT_TIME);
+  gtk_device_grab_remove (priv->dock, priv->grab_pointer);
+
+  priv->grab_keyboard = NULL;
+  priv->grab_pointer = NULL;
 
   /* hide again */
   gtk_widget_hide (priv->dock);
@@ -1227,7 +1281,7 @@ cb_dock_grab_notify (GtkWidget *widget,
 
 static gboolean
 cb_dock_grab_broken_event (GtkWidget *widget,
-			   gboolean   was_grabbed,
+                           gboolean   was_grabbed,
 			   gpointer   user_data)
 {
   GtkScaleButton *button = (GtkScaleButton *) user_data;
@@ -1253,9 +1307,12 @@ gtk_scale_button_release_grab (GtkScaleButton *button,
 
   /* ungrab focus */
   display = gtk_widget_get_display (GTK_WIDGET (button));
-  gdk_display_keyboard_ungrab (display, event->time);
-  gdk_display_pointer_ungrab (display, event->time);
-  gtk_grab_remove (priv->dock);
+  gdk_device_ungrab (priv->grab_keyboard, event->time);
+  gdk_device_ungrab (priv->grab_pointer, event->time);
+  gtk_device_grab_remove (priv->dock, priv->grab_pointer);
+
+  priv->grab_keyboard = NULL;
+  priv->grab_pointer = NULL;
 
   /* hide again */
   gtk_widget_hide (priv->dock);
@@ -1297,9 +1354,12 @@ gtk_scale_button_popdown (GtkWidget *widget)
 
   /* ungrab focus */
   display = gtk_widget_get_display (widget);
-  gdk_display_keyboard_ungrab (display, GDK_CURRENT_TIME);
-  gdk_display_pointer_ungrab (display, GDK_CURRENT_TIME);
-  gtk_grab_remove (priv->dock);
+  gdk_device_ungrab (priv->grab_keyboard, GDK_CURRENT_TIME);
+  gdk_device_ungrab (priv->grab_pointer, GDK_CURRENT_TIME);
+  gtk_device_grab_remove (priv->dock, priv->grab_pointer);
+
+  priv->grab_keyboard = NULL;
+  priv->grab_pointer = NULL;
 
   /* hide again */
   gtk_widget_hide (priv->dock);
@@ -1418,7 +1478,7 @@ gtk_scale_button_scale_press (GtkWidget      *widget,
   /* the scale will grab input; if we have input grabbed, all goes
    * horribly wrong, so let's not do that.
    */
-  gtk_grab_remove (priv->dock);
+  gtk_device_grab_remove (priv->dock, event->device);
 
   return GTK_WIDGET_CLASS (_gtk_scale_button_scale_parent_class)->button_press_event (widget, event);
 }
@@ -1453,7 +1513,7 @@ gtk_scale_button_scale_release (GtkWidget      *widget,
    * find that, so we do this complex 'first-call-parent-then-do-actual-
    * action' thingy...
    */
-  gtk_grab_add (button->priv->dock);
+  gtk_device_grab_add (button->priv->dock, event->device, TRUE);
 
   return res;
 }
diff --git a/gtk/gtksocket.c b/gtk/gtksocket.c
index dfb20f0..5e6a1fb 100644
--- a/gtk/gtksocket.c
+++ b/gtk/gtksocket.c
@@ -558,7 +558,7 @@ activate_key (GtkAccelGroup  *accel_group,
 
   if (gdk_event && gdk_event->type == GDK_KEY_PRESS && socket->plug_window)
     {
-      _gtk_socket_windowing_send_key_event (socket, gdk_event, TRUE);
+      _gtk_socket_windowing_send_key_event (socket, gdk_event, FALSE);
       retval = TRUE;
     }
 
diff --git a/gtk/gtkspinbutton.c b/gtk/gtkspinbutton.c
index 32934bc..71d7934 100644
--- a/gtk/gtkspinbutton.c
+++ b/gtk/gtkspinbutton.c
@@ -903,10 +903,12 @@ gtk_spin_button_enter_notify (GtkWidget        *widget,
 
   if (event->window == spin->panel)
     {
+      GdkDevice *device;
       gint x;
       gint y;
 
-      gdk_window_get_pointer (spin->panel, &x, &y, NULL);
+      device = gdk_event_get_device ((GdkEvent *) event);
+      gdk_window_get_device_position (spin->panel, device, &x, &y, NULL);
 
       if (y <= widget->requisition.height / 2)
 	spin->in_child = GTK_ARROW_UP;
@@ -915,7 +917,7 @@ gtk_spin_button_enter_notify (GtkWidget        *widget,
 
       gtk_widget_queue_draw (GTK_WIDGET (spin));
     }
- 
+
   if (GTK_WIDGET_CLASS (gtk_spin_button_parent_class)->enter_notify_event)
     return GTK_WIDGET_CLASS (gtk_spin_button_parent_class)->enter_notify_event (widget, event);
 
diff --git a/gtk/gtktextview.c b/gtk/gtktextview.c
index 2819c79..2c629f0 100644
--- a/gtk/gtktextview.c
+++ b/gtk/gtktextview.c
@@ -109,6 +109,7 @@ struct _GtkTextViewPrivate
   guint blink_time;  /* time in msec the cursor has blinked since last user event */
   guint im_spot_idle;
   gchar *im_module;
+  GdkDevice *grab_device;
   guint scroll_after_paste : 1;
 };
 
@@ -4084,7 +4085,12 @@ static void
 gtk_text_view_grab_notify (GtkWidget *widget,
 		 	   gboolean   was_grabbed)
 {
-  if (!was_grabbed)
+  GtkTextViewPrivate *priv;
+
+  priv = GTK_TEXT_VIEW_GET_PRIVATE (widget);
+
+  if (priv->grab_device &&
+      gtk_widget_device_is_shadowed (widget, priv->grab_device))
     {
       gtk_text_view_end_selection_drag (GTK_TEXT_VIEW (widget));
       gtk_text_view_unobscure_mouse_cursor (GTK_TEXT_VIEW (widget));
@@ -5992,6 +5998,7 @@ gtk_text_view_unselect (GtkTextView *text_view)
 
 static void
 get_iter_at_pointer (GtkTextView *text_view,
+                     GdkDevice   *device,
                      GtkTextIter *iter,
 		     gint        *x,
 		     gint        *y)
@@ -5999,9 +6006,9 @@ get_iter_at_pointer (GtkTextView *text_view,
   gint xcoord, ycoord;
   GdkModifierType state;
 
-  gdk_window_get_pointer (text_view->text_window->bin_window,
-                          &xcoord, &ycoord, &state);
-  
+  gdk_window_get_device_position (text_view->text_window->bin_window,
+                                  device, &xcoord, &ycoord, &state);
+
   gtk_text_layout_get_iter_at_pixel (text_view->layout,
                                      iter,
                                      xcoord + text_view->xoffset,
@@ -6015,12 +6022,13 @@ get_iter_at_pointer (GtkTextView *text_view,
 
 static void
 move_mark_to_pointer_and_scroll (GtkTextView *text_view,
-                                 const gchar *mark_name)
+                                 const gchar *mark_name,
+                                 GdkDevice   *device)
 {
   GtkTextIter newplace;
   GtkTextMark *mark;
 
-  get_iter_at_pointer (text_view, &newplace, NULL, NULL);
+  get_iter_at_pointer (text_view, device, &newplace, NULL, NULL);
   
   mark = gtk_text_buffer_get_mark (get_buffer (text_view), mark_name);
   
@@ -6045,7 +6053,6 @@ selection_scan_timeout (gpointer data)
 
   text_view = GTK_TEXT_VIEW (data);
 
-  DV(g_print (G_STRLOC": calling move_mark_to_pointer_and_scroll\n"));
   gtk_text_view_scroll_mark_onscreen (text_view, 
 				      gtk_text_buffer_get_insert (get_buffer (text_view)));
 
@@ -6074,10 +6081,12 @@ drag_scan_timeout (gpointer data)
   GtkTextIter newplace;
   gint x, y, width, height;
   gdouble pointer_xoffset, pointer_yoffset;
+  GdkDevice *device;
 
   text_view = GTK_TEXT_VIEW (data);
+  device = gdk_display_get_core_pointer (gtk_widget_get_display (GTK_WIDGET (data)));
 
-  get_iter_at_pointer (text_view, &newplace, &x, &y);
+  get_iter_at_pointer (text_view, device, &newplace, &x, &y);
   gdk_drawable_get_size (text_view->text_window->bin_window, &width, &height);
 
   gtk_text_buffer_move_mark (get_buffer (text_view),
@@ -6214,11 +6223,17 @@ selection_motion_event_handler (GtkTextView    *text_view,
 				GdkEventMotion *event, 
 				SelectionData  *data)
 {
+  GtkTextViewPrivate *priv;
+
+  priv = GTK_TEXT_VIEW_GET_PRIVATE (text_view);
   gdk_event_request_motions (event);
 
+  if (priv->grab_device != event->device)
+    return FALSE;
+
   if (data->granularity == SELECT_CHARACTERS) 
     {
-      move_mark_to_pointer_and_scroll (text_view, "insert");
+      move_mark_to_pointer_and_scroll (text_view, "insert", event->device);
     }
   else 
     {
@@ -6231,7 +6246,7 @@ selection_motion_event_handler (GtkTextView    *text_view,
       gtk_text_buffer_get_iter_at_mark (buffer, &orig_start, data->orig_start);
       gtk_text_buffer_get_iter_at_mark (buffer, &orig_end, data->orig_end);
 
-      get_iter_at_pointer (text_view, &cursor, NULL, NULL);
+      get_iter_at_pointer (text_view, event->device, &cursor, NULL, NULL);
       
       start = cursor;
       extend_selection (text_view, data->granularity, &start, &end);
@@ -6265,6 +6280,7 @@ gtk_text_view_start_selection_drag (GtkTextView       *text_view,
                                     const GtkTextIter *iter,
                                     GdkEventButton    *button)
 {
+  GtkTextViewPrivate *priv;
   GtkTextIter cursor, ins, bound;
   GtkTextIter orig_start, orig_end;
   GtkTextBuffer *buffer;
@@ -6272,7 +6288,8 @@ gtk_text_view_start_selection_drag (GtkTextView       *text_view,
 
   if (text_view->selection_drag_handler != 0)
     return;
-  
+
+  priv = GTK_TEXT_VIEW_GET_PRIVATE (text_view);
   data = g_new0 (SelectionData, 1);
 
   if (button->type == GDK_2BUTTON_PRESS)
@@ -6282,7 +6299,10 @@ gtk_text_view_start_selection_drag (GtkTextView       *text_view,
   else 
     data->granularity = SELECT_CHARACTERS;
 
-  gtk_grab_add (GTK_WIDGET (text_view));
+  priv->grab_device = button->device;
+  gtk_device_grab_add (GTK_WIDGET (text_view),
+                       priv->grab_device,
+                       TRUE);
 
   buffer = get_buffer (text_view);
   
@@ -6332,7 +6352,6 @@ gtk_text_view_start_selection_drag (GtkTextView       *text_view,
                                                   &orig_start, TRUE);
   data->orig_end = gtk_text_buffer_create_mark (buffer, NULL,
                                                 &orig_end, TRUE);
-
   gtk_text_view_check_cursor_blink (text_view);
 
   text_view->selection_drag_handler = g_signal_connect_data (text_view,
@@ -6346,6 +6365,13 @@ gtk_text_view_start_selection_drag (GtkTextView       *text_view,
 static gboolean
 gtk_text_view_end_selection_drag (GtkTextView    *text_view) 
 {
+  GtkTextViewPrivate *priv;
+
+  priv = GTK_TEXT_VIEW_GET_PRIVATE (text_view);
+
+  if (!priv->grab_device)
+    return FALSE;
+
   if (text_view->selection_drag_handler == 0)
     return FALSE;
 
@@ -6358,7 +6384,9 @@ gtk_text_view_end_selection_drag (GtkTextView    *text_view)
       text_view->scroll_timeout = 0;
     }
 
-  gtk_grab_remove (GTK_WIDGET (text_view));
+  gtk_device_grab_remove (GTK_WIDGET (text_view),
+                          priv->grab_device);
+  priv->grab_device = NULL;
 
   return TRUE;
 }
diff --git a/gtk/gtktooltip.c b/gtk/gtktooltip.c
index 204a2b6..d5a301b 100644
--- a/gtk/gtktooltip.c
+++ b/gtk/gtktooltip.c
@@ -1246,6 +1246,7 @@ _gtk_tooltip_focus_in (GtkWidget *widget)
   gboolean return_value = FALSE;
   GdkDisplay *display;
   GtkTooltip *tooltip;
+  GdkDevice *device;
 
   /* Get current tooltip for this display */
   display = gtk_widget_get_display (widget);
@@ -1256,12 +1257,23 @@ _gtk_tooltip_focus_in (GtkWidget *widget)
   if (!tooltip || !tooltip->keyboard_mode_enabled)
     return;
 
+  device = gtk_get_current_event_device ();
+
+  if (device && device->source == GDK_SOURCE_KEYBOARD)
+    device = gdk_device_get_associated_device (device);
+
+  /* This function should be called by either a focus in event,
+   * or a key binding. In either case there should be a device.
+   */
+  if (!device)
+    return;
+
   if (tooltip->keyboard_widget)
     g_object_unref (tooltip->keyboard_widget);
 
   tooltip->keyboard_widget = g_object_ref (widget);
 
-  gdk_window_get_pointer (widget->window, &x, &y, NULL);
+  gdk_window_get_device_position (widget->window, device, &x, &y, NULL);
 
   return_value = gtk_tooltip_run_requery (&widget, tooltip, &x, &y);
   if (!return_value)
diff --git a/gtk/gtktreeprivate.h b/gtk/gtktreeprivate.h
index c44d618..e8887e0 100644
--- a/gtk/gtktreeprivate.h
+++ b/gtk/gtktreeprivate.h
@@ -406,7 +406,8 @@ void _gtk_tree_view_column_unset_tree_view  (GtkTreeViewColumn *column);
 void _gtk_tree_view_column_set_width        (GtkTreeViewColumn *column,
 					     gint               width);
 void _gtk_tree_view_column_start_drag       (GtkTreeView       *tree_view,
-					     GtkTreeViewColumn *column);
+					     GtkTreeViewColumn *column,
+                                             GdkDevice         *device);
 gboolean _gtk_tree_view_column_cell_event   (GtkTreeViewColumn  *tree_column,
 					     GtkCellEditable   **editable_widget,
 					     GdkEvent           *event,
diff --git a/gtk/gtktreeview.c b/gtk/gtktreeview.c
index 533bb17..b75c75d 100644
--- a/gtk/gtktreeview.c
+++ b/gtk/gtktreeview.c
@@ -396,8 +396,9 @@ static void     update_prelight                              (GtkTreeView
 
 /* interactive search */
 static void     gtk_tree_view_ensure_interactive_directory (GtkTreeView *tree_view);
-static void     gtk_tree_view_search_dialog_hide     (GtkWidget        *search_dialog,
-							 GtkTreeView      *tree_view);
+static void     gtk_tree_view_search_dialog_hide        (GtkWidget        *search_dialog,
+                                                         GtkTreeView      *tree_view,
+                                                         GdkDevice        *device);
 static void     gtk_tree_view_search_position_func      (GtkTreeView      *tree_view,
 							 GtkWidget        *search_dialog,
 							 gpointer          user_data);
@@ -457,6 +458,7 @@ static void gtk_tree_view_real_start_editing (GtkTreeView       *tree_view,
 static void gtk_tree_view_stop_editing                  (GtkTreeView *tree_view,
 							 gboolean     cancel_editing);
 static gboolean gtk_tree_view_real_start_interactive_search (GtkTreeView *tree_view,
+                                                             GdkDevice   *device,
 							     gboolean     keybinding);
 static gboolean gtk_tree_view_start_interactive_search      (GtkTreeView *tree_view);
 static GtkTreeViewColumn *gtk_tree_view_get_drop_column (GtkTreeView       *tree_view,
@@ -5493,7 +5495,9 @@ gtk_tree_view_key_press (GtkWidget   *widget,
       if (tree_view->priv->imcontext_changed ||    /* we're in a preedit */
 	  (retval && text_modified))               /* ...or the text was modified */
 	{
-	  if (gtk_tree_view_real_start_interactive_search (tree_view, FALSE))
+	  if (gtk_tree_view_real_start_interactive_search (tree_view,
+                                                           gdk_event_get_device ((GdkEvent *) event),
+                                                           FALSE))
 	    {
 	      gtk_widget_grab_focus (GTK_WIDGET (tree_view));
 	      return TRUE;
@@ -5602,7 +5606,8 @@ gtk_tree_view_focus_out (GtkWidget     *widget,
 
   /* destroy interactive search dialog */
   if (tree_view->priv->search_window)
-    gtk_tree_view_search_dialog_hide (tree_view->priv->search_window, tree_view);
+    gtk_tree_view_search_dialog_hide (tree_view->priv->search_window, tree_view,
+                                      gdk_event_get_device ((GdkEvent *) event));
 
   return FALSE;
 }
@@ -9265,7 +9270,8 @@ gtk_tree_view_set_column_drag_info (GtkTreeView       *tree_view,
 
 void
 _gtk_tree_view_column_start_drag (GtkTreeView       *tree_view,
-				  GtkTreeViewColumn *column)
+				  GtkTreeViewColumn *column,
+                                  GdkDevice         *device)
 {
   GdkEvent *send_event;
   GtkAllocation allocation;
@@ -9314,6 +9320,7 @@ _gtk_tree_view_column_start_drag (GtkTreeView       *tree_view,
   send_event->crossing.subwindow = NULL;
   send_event->crossing.detail = GDK_NOTIFY_ANCESTOR;
   send_event->crossing.time = GDK_CURRENT_TIME;
+  gdk_event_set_device (send_event, device);
 
   gtk_propagate_event (column->button, send_event);
   gdk_event_free (send_event);
@@ -9327,9 +9334,9 @@ _gtk_tree_view_column_start_drag (GtkTreeView       *tree_view,
   send_event->button.axes = NULL;
   send_event->button.state = 0;
   send_event->button.button = 1;
-  send_event->button.device = gdk_display_get_core_pointer (display);
   send_event->button.x_root = 0;
   send_event->button.y_root = 0;
+  gdk_event_set_device (send_event, device);
 
   gtk_propagate_event (column->button, send_event);
   gdk_event_free (send_event);
@@ -10249,7 +10256,7 @@ gtk_tree_view_real_select_cursor_parent (GtkTreeView *tree_view)
 static gboolean
 gtk_tree_view_search_entry_flush_timeout (GtkTreeView *tree_view)
 {
-  gtk_tree_view_search_dialog_hide (tree_view->priv->search_window, tree_view);
+  gtk_tree_view_search_dialog_hide (tree_view->priv->search_window, tree_view, NULL);
   tree_view->priv->typeselect_flush_timeout = 0;
 
   return FALSE;
@@ -10258,17 +10265,43 @@ gtk_tree_view_search_entry_flush_timeout (GtkTreeView *tree_view)
 /* Cut and paste from gtkwindow.c */
 static void
 send_focus_change (GtkWidget *widget,
+                   GdkDevice *device,
 		   gboolean   in)
 {
-  GdkEvent *fevent = gdk_event_new (GDK_FOCUS_CHANGE);
+  GdkDeviceManager *device_manager;
+  GList *devices, *d;
+
+  device_manager = gdk_display_get_device_manager (gtk_widget_get_display (widget));
+  devices = gdk_device_manager_list_devices (device_manager, GDK_DEVICE_TYPE_MASTER);
+  devices = g_list_concat (devices, gdk_device_manager_list_devices (device_manager, GDK_DEVICE_TYPE_SLAVE));
+  devices = g_list_concat (devices, gdk_device_manager_list_devices (device_manager, GDK_DEVICE_TYPE_FLOATING));
+
+  for (d = devices; d; d = d->next)
+    {
+      GdkDevice *dev = d->data;
+      GdkEvent *fevent;
+
+      if (dev->source != GDK_SOURCE_KEYBOARD)
+        continue;
+
+      /* Skip non-master keyboards that haven't
+       * selected for events from this window
+       */
+      if (gdk_device_get_device_type (dev) != GDK_DEVICE_TYPE_MASTER &&
+          !gdk_window_get_device_events (widget->window, dev))
+        continue;
 
-  fevent->focus_change.type = GDK_FOCUS_CHANGE;
-  fevent->focus_change.window = g_object_ref (gtk_widget_get_window (widget));
-  fevent->focus_change.in = in;
+      fevent = gdk_event_new (GDK_FOCUS_CHANGE);
 
-  gtk_widget_send_focus_change (widget, fevent);
+      fevent->focus_change.type = GDK_FOCUS_CHANGE;
+      fevent->focus_change.window = g_object_ref (widget->window);
+      fevent->focus_change.in = in;
+      gdk_event_set_device (fevent, device);
 
-  gdk_event_free (fevent);
+      gtk_widget_send_focus_change (widget, fevent);
+
+      gdk_event_free (fevent);
+    }
 }
 
 static void
@@ -10352,6 +10385,7 @@ gtk_tree_view_ensure_interactive_directory (GtkTreeView *tree_view)
  */
 static gboolean
 gtk_tree_view_real_start_interactive_search (GtkTreeView *tree_view,
+                                             GdkDevice   *device,
 					     gboolean     keybinding)
 {
   /* We only start interactive search if we have focus or the columns
@@ -10424,7 +10458,7 @@ gtk_tree_view_real_start_interactive_search (GtkTreeView *tree_view,
   (entry_parent_class->grab_focus) (tree_view->priv->search_entry);
 
   /* send focus-in event */
-  send_focus_change (tree_view->priv->search_entry, TRUE);
+  send_focus_change (tree_view->priv->search_entry, device, TRUE);
 
   /* search first matching iter */
   gtk_tree_view_search_init (tree_view->priv->search_entry, tree_view);
@@ -10435,7 +10469,9 @@ gtk_tree_view_real_start_interactive_search (GtkTreeView *tree_view,
 static gboolean
 gtk_tree_view_start_interactive_search (GtkTreeView *tree_view)
 {
-  return gtk_tree_view_real_start_interactive_search (tree_view, TRUE);
+  return gtk_tree_view_real_start_interactive_search (tree_view,
+                                                      gtk_get_current_event_device (),
+                                                      TRUE);
 }
 
 /* this function returns the new width of the column being resized given
@@ -14075,7 +14111,8 @@ gtk_tree_view_get_search_position_func (GtkTreeView *tree_view)
 
 static void
 gtk_tree_view_search_dialog_hide (GtkWidget   *search_dialog,
-				  GtkTreeView *tree_view)
+				  GtkTreeView *tree_view,
+                                  GdkDevice   *device)
 {
   if (tree_view->priv->disable_popdown)
     return;
@@ -14095,10 +14132,10 @@ gtk_tree_view_search_dialog_hide (GtkWidget   *search_dialog,
   if (gtk_widget_get_visible (search_dialog))
     {
       /* send focus-in event */
-      send_focus_change (GTK_WIDGET (tree_view->priv->search_entry), FALSE);
+      send_focus_change (GTK_WIDGET (tree_view->priv->search_entry), device, FALSE);
       gtk_widget_hide (search_dialog);
       gtk_entry_set_text (GTK_ENTRY (tree_view->priv->search_entry), "");
-      send_focus_change (GTK_WIDGET (tree_view), TRUE);
+      send_focus_change (GTK_WIDGET (tree_view), device, TRUE);
     }
 }
 
@@ -14184,7 +14221,8 @@ gtk_tree_view_search_activate (GtkEntry    *entry,
   GtkRBTree *tree;
 
   gtk_tree_view_search_dialog_hide (tree_view->priv->search_window,
-				    tree_view);
+				    tree_view,
+                                    gtk_get_current_event_device ());
 
   /* If we have a row selected and it's the cursor row, we activate
    * the row XXX */
@@ -14225,7 +14263,7 @@ gtk_tree_view_search_delete_event (GtkWidget *widget,
 {
   g_return_val_if_fail (GTK_IS_WIDGET (widget), FALSE);
 
-  gtk_tree_view_search_dialog_hide (widget, tree_view);
+  gtk_tree_view_search_dialog_hide (widget, tree_view, NULL);
 
   return TRUE;
 }
@@ -14235,9 +14273,12 @@ gtk_tree_view_search_button_press_event (GtkWidget *widget,
 					 GdkEventButton *event,
 					 GtkTreeView *tree_view)
 {
+  GdkDevice *keyb_device;
+
   g_return_val_if_fail (GTK_IS_WIDGET (widget), FALSE);
 
-  gtk_tree_view_search_dialog_hide (widget, tree_view);
+  keyb_device = gdk_device_get_associated_device (event->device);
+  gtk_tree_view_search_dialog_hide (widget, tree_view, keyb_device);
 
   if (event->window == tree_view->priv->bin_window)
     gtk_tree_view_button_press (GTK_WIDGET (tree_view), event);
@@ -14294,7 +14335,8 @@ gtk_tree_view_search_key_press_event (GtkWidget *widget,
 	    event->keyval == GDK_KP_Tab ||
 	    event->keyval == GDK_ISO_Left_Tab))
     {
-      gtk_tree_view_search_dialog_hide (widget, tree_view);
+      gtk_tree_view_search_dialog_hide (widget, tree_view,
+                                        gdk_event_get_device ((GdkEvent *) event));
       return TRUE;
     }
 
diff --git a/gtk/gtktreeviewcolumn.c b/gtk/gtktreeviewcolumn.c
index 5c36375..0d953de 100644
--- a/gtk/gtktreeviewcolumn.c
+++ b/gtk/gtktreeviewcolumn.c
@@ -1097,7 +1097,8 @@ gtk_tree_view_column_button_event (GtkWidget *widget,
 				 (gint) ((GdkEventMotion *)event)->y)))
     {
       column->maybe_reordered = FALSE;
-      _gtk_tree_view_column_start_drag (GTK_TREE_VIEW (column->tree_view), column);
+      _gtk_tree_view_column_start_drag (GTK_TREE_VIEW (column->tree_view), column,
+                                        event->motion.device);
       return TRUE;
     }
   if (column->clickable == FALSE)
diff --git a/gtk/gtkwidget.c b/gtk/gtkwidget.c
index 6cd6ba6..b70cb2b 100644
--- a/gtk/gtkwidget.c
+++ b/gtk/gtkwidget.c
@@ -376,6 +376,7 @@ static GQuark		quark_aux_info = 0;
 static GQuark		quark_accel_path = 0;
 static GQuark		quark_accel_closures = 0;
 static GQuark		quark_event_mask = 0;
+static GQuark           quark_device_event_mask = 0;
 static GQuark		quark_extension_event_mode = 0;
 static GQuark		quark_parent_window = 0;
 static GQuark		quark_pointer_window = 0;
@@ -472,6 +473,7 @@ gtk_widget_class_init (GtkWidgetClass *klass)
   quark_accel_path = g_quark_from_static_string ("gtk-accel-path");
   quark_accel_closures = g_quark_from_static_string ("gtk-accel-closures");
   quark_event_mask = g_quark_from_static_string ("gtk-event-mask");
+  quark_device_event_mask = g_quark_from_static_string ("gtk-device-event-mask");
   quark_extension_event_mode = g_quark_from_static_string ("gtk-extension-event-mode");
   quark_parent_window = g_quark_from_static_string ("gtk-parent-window");
   quark_pointer_window = g_quark_from_static_string ("gtk-pointer-window");
@@ -3521,6 +3523,9 @@ gtk_widget_realize (GtkWidget *widget)
       mode = gtk_widget_get_extension_events (widget);
       if (mode != GDK_EXTENSION_EVENTS_NONE)
         gtk_widget_set_extension_events_internal (widget, mode, NULL);
+
+      if ((GTK_WIDGET_FLAGS (widget) & GTK_MULTIDEVICE) != 0)
+        gdk_window_set_support_multidevice (widget->window, TRUE);
     }
 }
 
@@ -5655,6 +5660,62 @@ _gtk_widget_set_has_grab (GtkWidget *widget,
 }
 
 /**
+ * gtk_widget_device_is_shadowed:
+ * @widget: a #GtkWidget
+ * @device: a #GdkDevice
+ *
+ * Returns %TRUE if @device has been shadowed by a GTK+
+ * device grab on another widget, so it would stop sending
+ * events to @widget. This may be used in the
+ * #GtkWidget::grab-notify signal to check for specific
+ * devices. See gtk_device_grab_add().
+ *
+ * Returns: %TRUE if there is an ongoing grab on @device
+ *          by another #GtkWidget than @widget.
+ *
+ * Since: 3.0
+ **/
+gboolean
+gtk_widget_device_is_shadowed (GtkWidget *widget,
+                               GdkDevice *device)
+{
+  GtkWindowGroup *group;
+  GtkWidget *grab_widget, *toplevel;
+
+  g_return_val_if_fail (GTK_IS_WIDGET (widget), FALSE);
+  g_return_val_if_fail (GDK_IS_DEVICE (device), FALSE);
+
+  if (!gtk_widget_get_realized (widget))
+    return TRUE;
+
+  toplevel = gtk_widget_get_toplevel (widget);
+
+  if (GTK_IS_WINDOW (toplevel))
+    group = gtk_window_get_group (GTK_WINDOW (toplevel));
+  else
+    group = gtk_window_get_group (NULL);
+
+  grab_widget = gtk_window_group_get_current_device_grab (group, device);
+
+  /* Widget not inside the hierarchy of grab_widget */
+  if (grab_widget &&
+      widget != grab_widget &&
+      !gtk_widget_is_ancestor (widget, grab_widget))
+    return TRUE;
+
+  if (group->grabs)
+    {
+      grab_widget = group->grabs->data;
+
+      if (widget != grab_widget &&
+          !gtk_widget_is_ancestor (widget, grab_widget))
+        return TRUE;
+    }
+
+  return FALSE;
+}
+
+/**
  * gtk_widget_set_name:
  * @widget: a #GtkWidget
  * @name: name for the widget
@@ -7890,10 +7951,53 @@ gtk_widget_set_events (GtkWidget *widget,
   g_object_notify (G_OBJECT (widget), "events");
 }
 
+/**
+ * gtk_widget_set_device_events:
+ * @widget: a #GtkWidget
+ * #device: a #GdkDevice
+ * @events: event mask
+ *
+ * Sets the device event mask (see #GdkEventMask) for a widget. The event
+ * mask determines which events a widget will receive from @device. Keep
+ * in mind that different widgets have different default event masks, and by
+ * changing the event mask you may disrupt a widget's functionality,
+ * so be careful. This function must be called while a widget is
+ * unrealized. Consider gtk_widget_add_device_events() for widgets that are
+ * already realized, or if you want to preserve the existing event
+ * mask. This function can't be used with #GTK_NO_WINDOW widgets;
+ * to get events on those widgets, place them inside a #GtkEventBox
+ * and receive events on the event box.
+ *
+ * Since: 3.0
+ **/
+void
+gtk_widget_set_device_events (GtkWidget    *widget,
+                              GdkDevice    *device,
+                              GdkEventMask  events)
+{
+  GHashTable *device_events;
+
+  g_return_if_fail (GTK_IS_WIDGET (widget));
+  g_return_if_fail (GDK_IS_DEVICE (device));
+  g_return_if_fail (!gtk_widget_get_realized (widget));
+
+  device_events = g_object_get_qdata (G_OBJECT (widget), quark_device_event_mask);
+
+  if (G_UNLIKELY (!device_events))
+    {
+      device_events = g_hash_table_new (NULL, NULL);
+      g_object_set_qdata_full (G_OBJECT (widget), quark_device_event_mask, device_events,
+                               (GDestroyNotify) g_hash_table_unref);
+    }
+
+  g_hash_table_insert (device_events, device, GUINT_TO_POINTER (events));
+}
+
 static void
 gtk_widget_add_events_internal (GtkWidget *widget,
-				gint       events,
-				GList     *window_list)
+                                GdkDevice *device,
+                                gint       events,
+                                GList     *window_list)
 {
   GList *l;
 
@@ -7904,15 +8008,18 @@ gtk_widget_add_events_internal (GtkWidget *widget,
 
       gdk_window_get_user_data (window, &user_data);
       if (user_data == widget)
-	{
-	  GList *children;
+        {
+          GList *children;
 
-	  gdk_window_set_events (window, gdk_window_get_events (window) | events);
+          if (device)
+            gdk_window_set_device_events (window, device, gdk_window_get_events (window) | events);
+          else
+            gdk_window_set_events (window, gdk_window_get_events (window) | events);
 
-	  children = gdk_window_get_children (window);
-	  gtk_widget_add_events_internal (widget, events, children);
-	  g_list_free (children);
-	}
+          children = gdk_window_get_children (window);
+          gtk_widget_add_events_internal (widget, device, events, children);
+          g_list_free (children);
+        }
     }
 }
 
@@ -7945,7 +8052,60 @@ gtk_widget_add_events (GtkWidget *widget,
       else
 	window_list = g_list_prepend (NULL, widget->window);
 
-      gtk_widget_add_events_internal (widget, events, window_list);
+      gtk_widget_add_events_internal (widget, NULL, events, window_list);
+
+      g_list_free (window_list);
+    }
+
+  g_object_notify (G_OBJECT (widget), "events");
+}
+
+/**
+ * gtk_widget_add_device_events:
+ * @widget: a #GtkWidget
+ * @device: a #GdkDevice
+ * @events: an event mask, see #GdkEventMask
+ *
+ * Adds the device events in the bitfield @events to the event mask for
+ * @widget. See gtk_widget_set_device_events() for details.
+ *
+ * Since: 3.0
+ **/
+void
+gtk_widget_add_device_events (GtkWidget    *widget,
+                              GdkDevice    *device,
+                              GdkEventMask  events)
+{
+  GdkEventMask old_events;
+  GHashTable *device_events;
+
+  g_return_if_fail (GTK_IS_WIDGET (widget));
+  g_return_if_fail (GDK_IS_DEVICE (device));
+
+  old_events = gtk_widget_get_device_events (widget, device);
+
+  device_events = g_object_get_qdata (G_OBJECT (widget), quark_device_event_mask);
+
+  if (G_UNLIKELY (!device_events))
+    {
+      device_events = g_hash_table_new (NULL, NULL);
+      g_object_set_qdata_full (G_OBJECT (widget), quark_device_event_mask, device_events,
+                               (GDestroyNotify) g_hash_table_unref);
+    }
+
+  g_hash_table_insert (device_events, device,
+                       GUINT_TO_POINTER (old_events | events));
+
+  if (gtk_widget_get_realized (widget))
+    {
+      GList *window_list;
+
+      if (!gtk_widget_get_has_window (widget))
+        window_list = gdk_window_get_children (widget->window);
+      else
+        window_list = g_list_prepend (NULL, widget->window);
+
+      gtk_widget_add_events_internal (widget, device, events, window_list);
 
       g_list_free (window_list);
     }
@@ -8170,6 +8330,35 @@ gtk_widget_get_events (GtkWidget *widget)
 }
 
 /**
+ * gtk_widget_get_device_events:
+ * @widget: a #GtkWidget
+ * @device: a #GdkDevice
+ *
+ * Returns the events mask for the widget corresponding to an specific device. These
+ * are the events that the widget will receive when @device operates on it.
+ *
+ * Returns: device event mask for @widget
+ *
+ * Since: 3.0
+ **/
+GdkEventMask
+gtk_widget_get_device_events (GtkWidget *widget,
+                              GdkDevice *device)
+{
+  GHashTable *device_events;
+
+  g_return_val_if_fail (GTK_IS_WIDGET (widget), 0);
+  g_return_val_if_fail (GDK_IS_DEVICE (device), 0);
+
+  device_events = g_object_get_qdata (G_OBJECT (widget), quark_device_event_mask);
+
+  if (!device_events)
+    return 0;
+
+  return GPOINTER_TO_UINT (g_hash_table_lookup (device_events, device));
+}
+
+/**
  * gtk_widget_get_extension_events:
  * @widget: a #GtkWidget
  * 
@@ -8755,59 +8944,147 @@ _gtk_widget_peek_colormap (void)
 }
 
 /*
- * _gtk_widget_set_pointer_window:
+ * _gtk_widget_set_device_window:
  * @widget: a #GtkWidget.
- * @pointer_window: the new pointer window.
+ * @device: a #GdkDevice.
+ * @window: the new device window.
  *
- * Sets pointer window for @widget.  Does not ref @pointer_window.
+ * Sets pointer window for @widget and @device.  Does not ref @window.
  * Actually stores it on the #GdkScreen, but you don't need to know that.
  */
 void
-_gtk_widget_set_pointer_window (GtkWidget *widget,
-                                GdkWindow *pointer_window)
+_gtk_widget_set_device_window (GtkWidget *widget,
+                               GdkDevice *device,
+                               GdkWindow *window)
 {
+  GdkScreen *screen;
+  GHashTable *device_window;
+
   g_return_if_fail (GTK_IS_WIDGET (widget));
+  g_return_if_fail (GDK_IS_DEVICE (device));
+  g_return_if_fail (!window || GDK_IS_WINDOW (window));
 
-  if (gtk_widget_get_realized (widget))
-    {
-      GdkScreen *screen = gdk_drawable_get_screen (widget->window);
+  if (!gtk_widget_get_realized (widget))
+    return;
+
+  screen = gdk_drawable_get_screen (widget->window);
+  device_window = g_object_get_qdata (G_OBJECT (screen), quark_pointer_window);
 
-      g_object_set_qdata (G_OBJECT (screen), quark_pointer_window,
-                          pointer_window);
+  if (G_UNLIKELY (!device_window))
+    {
+      device_window = g_hash_table_new (NULL, NULL);
+      g_object_set_qdata_full (G_OBJECT (screen),
+                               quark_pointer_window,
+                               device_window,
+                               (GDestroyNotify) g_hash_table_destroy);
     }
+
+  if (window)
+    g_hash_table_insert (device_window, device, window);
+  else
+    g_hash_table_remove (device_window, device);
 }
 
 /*
- * _gtk_widget_get_pointer_window:
+ * _gtk_widget_get_device_window:
  * @widget: a #GtkWidget.
+ * @device: a #GdkDevice.
  *
- * Return value: the pointer window set on the #GdkScreen @widget is attached
+ * Return value: the device window set on the #GdkScreen @widget is attached
  * to, or %NULL.
  */
 GdkWindow *
-_gtk_widget_get_pointer_window (GtkWidget *widget)
+_gtk_widget_get_device_window (GtkWidget *widget,
+                               GdkDevice *device)
 {
+  GdkScreen *screen;
+  GHashTable *device_window;
+  GdkWindow *window;
+  GtkWidget *w;
+
   g_return_val_if_fail (GTK_IS_WIDGET (widget), NULL);
+  g_return_val_if_fail (GDK_IS_DEVICE (device), NULL);
 
-  if (gtk_widget_get_realized (widget))
+  if (!gtk_widget_get_realized (widget))
+    return NULL;
+
+  screen = gdk_drawable_get_screen (widget->window);
+  device_window = g_object_get_qdata (G_OBJECT (screen), quark_pointer_window);
+
+  if (G_UNLIKELY (!device_window))
+    return NULL;
+
+  window = g_hash_table_lookup (device_window, device);
+
+  if (!window)
+    return NULL;
+
+  gdk_window_get_user_data (window, (gpointer *) &w);
+
+  if (widget != w)
+    return NULL;
+
+  return window;
+}
+
+/*
+ * _gtk_widget_list_devices:
+ * @widget: a #GtkWidget.
+ *
+ * Returns the list of #GdkDevices that is currently on top of any widget #GdkWindow.
+ * Free the list with g_list_free(), the elements are owned by GTK+ and must not
+ * be freed.
+ */
+GList *
+_gtk_widget_list_devices (GtkWidget *widget)
+{
+  GdkScreen *screen;
+  GHashTableIter iter;
+  GHashTable *device_window;
+  GList *devices = NULL;
+  gpointer key, value;
+
+  g_return_val_if_fail (GTK_IS_WIDGET (widget), NULL);
+
+  if (!gtk_widget_get_realized (widget))
+    return NULL;
+
+  screen = gdk_drawable_get_screen (widget->window);
+  device_window = g_object_get_qdata (G_OBJECT (screen), quark_pointer_window);
+
+  if (G_UNLIKELY (!device_window))
+    return NULL;
+
+  g_hash_table_iter_init (&iter, device_window);
+
+  while (g_hash_table_iter_next (&iter, &key, &value))
     {
-      GdkScreen *screen = gdk_drawable_get_screen (widget->window);
+      GdkDevice *device = key;
+      GdkWindow *window = value;
+      GtkWidget *w;
 
-      return g_object_get_qdata (G_OBJECT (screen), quark_pointer_window);
+      if (window)
+        {
+          gdk_window_get_user_data (window, (gpointer *) &w);
+
+          if (widget == w)
+            devices = g_list_prepend (devices, device);
+        }
     }
 
-  return NULL;
+  return devices;
 }
 
 static void
-synth_crossing (GtkWidget      *widget,
-		GdkEventType    type,
-		GdkWindow      *window,
-		GdkCrossingMode mode,
-		GdkNotifyType   detail)
+synth_crossing (GtkWidget       *widget,
+                GdkEventType     type,
+                GdkWindow       *window,
+                GdkDevice       *device,
+                GdkCrossingMode  mode,
+                GdkNotifyType    detail)
 {
   GdkEvent *event;
-  
+
   event = gdk_event_new (type);
 
   event->crossing.window = g_object_ref (window);
@@ -8820,6 +9097,7 @@ synth_crossing (GtkWidget      *widget,
   event->crossing.detail = detail;
   event->crossing.focus = FALSE;
   event->crossing.state = 0;
+  gdk_event_set_device (event, device);
 
   if (!widget)
     widget = gtk_get_event_widget (event);
@@ -8831,32 +9109,6 @@ synth_crossing (GtkWidget      *widget,
 }
 
 /*
- * _gtk_widget_is_pointer_widget:
- * @widget: a #GtkWidget
- *
- * Returns %TRUE if the pointer window belongs to @widget.
- */
-gboolean
-_gtk_widget_is_pointer_widget (GtkWidget *widget)
-{
-  if (GTK_WIDGET_HAS_POINTER (widget))
-    {
-      GdkWindow *win;
-      GtkWidget *wid;
-
-      win = _gtk_widget_get_pointer_window (widget);
-      if (win)
-        {
-          gdk_window_get_user_data (win, (gpointer *)&wid);
-          if (wid == widget)
-            return TRUE;
-        }
-    }
-
-  return FALSE;
-}
-
-/*
  * _gtk_widget_synthesize_crossing:
  * @from: the #GtkWidget the virtual pointer is leaving.
  * @to: the #GtkWidget the virtual pointer is moving to.
@@ -8888,20 +9140,30 @@ _gtk_widget_is_pointer_widget (GtkWidget *widget)
  *   - enter notify on real pointer window, detail Ancestor
  */
 void
-_gtk_widget_synthesize_crossing (GtkWidget      *from,
-				 GtkWidget      *to,
-				 GdkCrossingMode mode)
+_gtk_widget_synthesize_crossing (GtkWidget       *from,
+				 GtkWidget       *to,
+                                 GdkDevice       *device,
+				 GdkCrossingMode  mode)
 {
   GdkWindow *from_window = NULL, *to_window = NULL;
 
   g_return_if_fail (from != NULL || to != NULL);
 
   if (from != NULL)
-    from_window = GTK_WIDGET_HAS_POINTER (from)
-      ? _gtk_widget_get_pointer_window (from) : from->window;
+    {
+      from_window = _gtk_widget_get_device_window (from, device);
+
+      if (!from_window)
+        from_window = from->window;
+    }
+
   if (to != NULL)
-    to_window = GTK_WIDGET_HAS_POINTER (to)
-      ? _gtk_widget_get_pointer_window (to) : to->window;
+    {
+      to_window = _gtk_widget_get_device_window (to, device);
+
+      if (!to_window)
+        to_window = to->window;
+    }
 
   if (from_window == NULL && to_window == NULL)
     ;
@@ -8919,11 +9181,11 @@ _gtk_widget_synthesize_crossing (GtkWidget      *from,
 	}
 
       synth_crossing (from, GDK_LEAVE_NOTIFY, from_window,
-		      mode, GDK_NOTIFY_ANCESTOR);
+		      device, mode, GDK_NOTIFY_ANCESTOR);
       for (list = g_list_last (from_ancestors); list; list = list->prev)
 	{
 	  synth_crossing (NULL, GDK_LEAVE_NOTIFY, (GdkWindow *) list->data,
-			  mode, GDK_NOTIFY_VIRTUAL);
+			  device, mode, GDK_NOTIFY_VIRTUAL);
 	}
 
       /* XXX: enter/inferior on root window? */
@@ -8948,10 +9210,10 @@ _gtk_widget_synthesize_crossing (GtkWidget      *from,
       for (list = to_ancestors; list; list = list->next)
 	{
 	  synth_crossing (NULL, GDK_ENTER_NOTIFY, (GdkWindow *) list->data,
-			  mode, GDK_NOTIFY_VIRTUAL);
+			  device, mode, GDK_NOTIFY_VIRTUAL);
 	}
       synth_crossing (to, GDK_ENTER_NOTIFY, to_window,
-		      mode, GDK_NOTIFY_ANCESTOR);
+		      device, mode, GDK_NOTIFY_ANCESTOR);
 
       g_list_free (to_ancestors);
     }
@@ -8985,25 +9247,25 @@ _gtk_widget_synthesize_crossing (GtkWidget      *from,
 	{
 	  if (mode != GDK_CROSSING_GTK_UNGRAB)
 	    synth_crossing (from, GDK_LEAVE_NOTIFY, from_window,
-			    mode, GDK_NOTIFY_INFERIOR);
+			    device, mode, GDK_NOTIFY_INFERIOR);
 	  for (list = to_ancestors; list; list = list->next)
 	    synth_crossing (NULL, GDK_ENTER_NOTIFY, (GdkWindow *) list->data, 
-			    mode, GDK_NOTIFY_VIRTUAL);
+			    device, mode, GDK_NOTIFY_VIRTUAL);
 	  synth_crossing (to, GDK_ENTER_NOTIFY, to_window,
-			  mode, GDK_NOTIFY_ANCESTOR);
+			  device, mode, GDK_NOTIFY_ANCESTOR);
 	}
       else if (from_ancestor == to_window)
 	{
 	  synth_crossing (from, GDK_LEAVE_NOTIFY, from_window,
-			  mode, GDK_NOTIFY_ANCESTOR);
+			  device, mode, GDK_NOTIFY_ANCESTOR);
 	  for (list = g_list_last (from_ancestors); list; list = list->prev)
 	    {
 	      synth_crossing (NULL, GDK_LEAVE_NOTIFY, (GdkWindow *) list->data,
-			      mode, GDK_NOTIFY_VIRTUAL);
+			      device, mode, GDK_NOTIFY_VIRTUAL);
 	    }
 	  if (mode != GDK_CROSSING_GTK_GRAB)
 	    synth_crossing (to, GDK_ENTER_NOTIFY, to_window,
-			    mode, GDK_NOTIFY_INFERIOR);
+			    device, mode, GDK_NOTIFY_INFERIOR);
 	}
       else
 	{
@@ -9016,20 +9278,20 @@ _gtk_widget_synthesize_crossing (GtkWidget      *from,
 	    }
 
 	  synth_crossing (from, GDK_LEAVE_NOTIFY, from_window,
-			  mode, GDK_NOTIFY_NONLINEAR);
+			  device, mode, GDK_NOTIFY_NONLINEAR);
 
 	  for (list = g_list_last (from_ancestors); list; list = list->prev)
 	    {
 	      synth_crossing (NULL, GDK_LEAVE_NOTIFY, (GdkWindow *) list->data,
-			      mode, GDK_NOTIFY_NONLINEAR_VIRTUAL);
+			      device, mode, GDK_NOTIFY_NONLINEAR_VIRTUAL);
 	    }
 	  for (list = to_ancestors; list; list = list->next)
 	    {
 	      synth_crossing (NULL, GDK_ENTER_NOTIFY, (GdkWindow *) list->data,
-			      mode, GDK_NOTIFY_NONLINEAR_VIRTUAL);
+			      device, mode, GDK_NOTIFY_NONLINEAR_VIRTUAL);
 	    }
 	  synth_crossing (to, GDK_ENTER_NOTIFY, to_window,
-			  mode, GDK_NOTIFY_NONLINEAR);
+			  device, mode, GDK_NOTIFY_NONLINEAR);
 	}
       g_list_free (from_ancestors);
       g_list_free (to_ancestors);
@@ -9091,15 +9353,41 @@ gtk_widget_propagate_state (GtkWidget           *widget,
 
       g_signal_emit (widget, widget_signals[STATE_CHANGED], 0, old_state);
 
-      if (GTK_WIDGET_HAS_POINTER (widget) && !GTK_WIDGET_SHADOWED (widget))
-	{
-	  if (!gtk_widget_is_sensitive (widget))
-	    _gtk_widget_synthesize_crossing (widget, NULL, 
-					     GDK_CROSSING_STATE_CHANGED);
-	  else if (old_state == GTK_STATE_INSENSITIVE)
-	    _gtk_widget_synthesize_crossing (NULL, widget, 
-					     GDK_CROSSING_STATE_CHANGED);
-	}
+      if (!GTK_WIDGET_SHADOWED (widget))
+        {
+          GList *event_windows = NULL;
+          GList *devices, *d;
+
+          devices = _gtk_widget_list_devices (widget);
+
+          for (d = devices; d; d = d->next)
+            {
+              GdkWindow *window;
+              GdkDevice *device;
+
+              device = d->data;
+              window = _gtk_widget_get_device_window (widget, device);
+
+              /* Do not propagate more than once to the
+               * same window if non-multidevice aware.
+               */
+              if (!gdk_window_get_support_multidevice (window) &&
+                  g_list_find (event_windows, window))
+                continue;
+
+              if (!gtk_widget_is_sensitive (widget))
+                _gtk_widget_synthesize_crossing (widget, NULL, d->data,
+                                                 GDK_CROSSING_STATE_CHANGED);
+              else if (old_state == GTK_STATE_INSENSITIVE)
+                _gtk_widget_synthesize_crossing (NULL, widget, d->data,
+                                                 GDK_CROSSING_STATE_CHANGED);
+
+              event_windows = g_list_prepend (event_windows, window);
+            }
+
+          g_list_free (event_windows);
+          g_list_free (devices);
+        }
 
       if (GTK_IS_CONTAINER (widget))
 	{
@@ -11175,6 +11463,56 @@ gtk_widget_get_window (GtkWidget *widget)
   return widget->window;
 }
 
+/**
+ * gtk_widget_get_support_multidevice:
+ * @widget: a #GtkWidget
+ *
+ * Returns %TRUE if @widget is multiple pointer aware. See
+ * gtk_widget_set_support_multidevice() for more information.
+ *
+ * Returns: %TRUE is @widget is multidevice aware.
+ **/
+gboolean
+gtk_widget_get_support_multidevice (GtkWidget *widget)
+{
+  g_return_val_if_fail (GTK_IS_WIDGET (widget), FALSE);
+
+  return GTK_WIDGET_FLAGS (widget) & GTK_MULTIDEVICE;
+}
+
+/**
+ * gtk_widget_set_support_multidevice:
+ * @widget: a #GtkWidget
+ * @support_multidevice: %TRUE to support input from multiple devices.
+ *
+ * Enables or disables multiple pointer awareness. If this setting is %TRUE,
+ * @widget will start receiving multiple, per device enter/leave events. Note
+ * that if custom #GdkWindow<!-- -->s are created in #GtkWidget::realize,
+ * gdk_window_set_support_multidevice() will have to be called manually on them.
+ *
+ * Since: 3.0
+ **/
+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);
+    }
+
+  if (gtk_widget_get_realized (widget))
+    gdk_window_set_support_multidevice (widget->window, support_multidevice);
+}
+
 static void
 _gtk_widget_set_has_focus (GtkWidget *widget,
                            gboolean   has_focus)
diff --git a/gtk/gtkwidget.h b/gtk/gtkwidget.h
index 38b2dca..f42fa25 100644
--- a/gtk/gtkwidget.h
+++ b/gtk/gtkwidget.h
@@ -118,7 +118,8 @@ typedef enum
   GTK_APP_PAINTABLE    = 1 << 19,
   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 */
@@ -652,6 +653,10 @@ gboolean  gtk_widget_get_receives_default (GtkWidget           *widget);
 
 gboolean   gtk_widget_has_grab            (GtkWidget           *widget);
 
+gboolean   gtk_widget_device_is_shadowed  (GtkWidget           *widget,
+                                           GdkDevice           *device);
+
+
 void                  gtk_widget_set_name               (GtkWidget    *widget,
 							 const gchar  *name);
 G_CONST_RETURN gchar* gtk_widget_get_name               (GtkWidget    *widget);
@@ -733,6 +738,12 @@ void	   gtk_widget_set_events	  (GtkWidget	       *widget,
 					   gint			events);
 void       gtk_widget_add_events          (GtkWidget           *widget,
 					   gint	                events);
+void	   gtk_widget_set_device_events	  (GtkWidget	       *widget,
+                                           GdkDevice           *device,
+					   GdkEventMask		events);
+void       gtk_widget_add_device_events   (GtkWidget           *widget,
+                                           GdkDevice           *device,
+					   GdkEventMask         events);
 void	   gtk_widget_set_extension_events (GtkWidget		*widget,
 					    GdkExtensionMode	mode);
 
@@ -753,6 +764,11 @@ GtkClipboard *gtk_widget_get_clipboard   (GtkWidget *widget,
 GdkPixmap *   gtk_widget_get_snapshot    (GtkWidget    *widget,
                                           GdkRectangle *clip_rect);
 
+/* Multidevice support */
+gboolean         gtk_widget_get_support_multidevice (GtkWidget      *widget);
+void             gtk_widget_set_support_multidevice (GtkWidget      *widget,
+                                                     gboolean        support_multidevice);
+
 /* Accessibility support */
 AtkObject*       gtk_widget_get_accessible               (GtkWidget          *widget);
 
@@ -766,6 +782,8 @@ void         gtk_widget_set_colormap    (GtkWidget      *widget,
 					 GdkColormap    *colormap);
 
 gint	     gtk_widget_get_events	(GtkWidget	*widget);
+GdkEventMask gtk_widget_get_device_events (GtkWidget	*widget,
+                                           GdkDevice    *device);
 void	     gtk_widget_get_pointer	(GtkWidget	*widget,
 					 gint		*x,
 					 gint		*y);
@@ -959,12 +977,16 @@ void              _gtk_widget_propagate_screen_changed    (GtkWidget    *widget,
 							   GdkScreen    *previous_screen);
 void		  _gtk_widget_propagate_composited_changed (GtkWidget    *widget);
 
-void	   _gtk_widget_set_pointer_window  (GtkWidget      *widget,
+void	   _gtk_widget_set_device_window   (GtkWidget      *widget,
+                                            GdkDevice      *device,
 					    GdkWindow      *pointer_window);
-GdkWindow *_gtk_widget_get_pointer_window  (GtkWidget      *widget);
-gboolean   _gtk_widget_is_pointer_widget   (GtkWidget      *widget);
+GdkWindow *_gtk_widget_get_device_window   (GtkWidget      *widget,
+                                            GdkDevice      *device);
+GList *    _gtk_widget_list_devices        (GtkWidget      *widget);
+
 void       _gtk_widget_synthesize_crossing (GtkWidget      *from,
 					    GtkWidget      *to,
+                                            GdkDevice      *device,
 					    GdkCrossingMode mode);
 
 GdkColormap* _gtk_widget_peek_colormap (void);
diff --git a/gtk/gtkwindow.c b/gtk/gtkwindow.c
index 6ac9706..eab1ff5 100644
--- a/gtk/gtkwindow.c
+++ b/gtk/gtkwindow.c
@@ -167,6 +167,7 @@ struct _GtkWindowGeometryInfo
 };
 
 #define GTK_WINDOW_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), GTK_TYPE_WINDOW, GtkWindowPrivate))
+#define GTK_WINDOW_GROUP_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), GTK_TYPE_WINDOW_GROUP, GtkWindowGroupPrivate))
 
 typedef struct _GtkWindowPrivate GtkWindowPrivate;
 
@@ -199,6 +200,21 @@ struct _GtkWindowPrivate
   gchar *startup_id;
 };
 
+typedef struct _GtkDeviceGrabInfo GtkDeviceGrabInfo;
+typedef struct _GtkWindowGroupPrivate GtkWindowGroupPrivate;
+
+struct _GtkDeviceGrabInfo
+{
+  GtkWidget *widget;
+  GdkDevice *device;
+  guint block_others : 1;
+};
+
+struct _GtkWindowGroupPrivate
+{
+  GSList *device_grabs;
+};
+
 static void gtk_window_dispose            (GObject           *object);
 static void gtk_window_destroy            (GtkObject         *object);
 static void gtk_window_finalize           (GObject           *object);
@@ -5251,17 +5267,43 @@ static void
 do_focus_change (GtkWidget *widget,
 		 gboolean   in)
 {
-  GdkEvent *fevent = gdk_event_new (GDK_FOCUS_CHANGE);
-  
-  fevent->focus_change.type = GDK_FOCUS_CHANGE;
-  fevent->focus_change.window = widget->window;
-  fevent->focus_change.in = in;
-  if (widget->window)
-    g_object_ref (widget->window);
+  GdkDeviceManager *device_manager;
+  GList *devices, *d;
+
+  device_manager = gdk_display_get_device_manager (gtk_widget_get_display (widget));
+  devices = gdk_device_manager_list_devices (device_manager, GDK_DEVICE_TYPE_MASTER);
+  devices = g_list_concat (devices, gdk_device_manager_list_devices (device_manager, GDK_DEVICE_TYPE_SLAVE));
+  devices = g_list_concat (devices, gdk_device_manager_list_devices (device_manager, GDK_DEVICE_TYPE_FLOATING));
+
+  for (d = devices; d; d = d->next)
+    {
+      GdkDevice *dev = d->data;
+      GdkEvent *fevent;
 
-  gtk_widget_send_focus_change (widget, fevent);
+      if (dev->source != GDK_SOURCE_KEYBOARD)
+        continue;
 
-  gdk_event_free (fevent);
+      /* Skip non-master keyboards that haven't
+       * selected for events from this window
+       */
+      if (gdk_device_get_device_type (dev) != GDK_DEVICE_TYPE_MASTER &&
+          widget->window &&
+          !gdk_window_get_device_events (widget->window, dev))
+        continue;
+
+      fevent = gdk_event_new (GDK_FOCUS_CHANGE);
+
+      fevent->focus_change.type = GDK_FOCUS_CHANGE;
+      fevent->focus_change.window = widget->window;
+      if (widget->window)
+        g_object_ref (widget->window);
+      fevent->focus_change.in = in;
+      gdk_event_set_device (fevent, dev);
+
+      gtk_widget_send_focus_change (widget, fevent);
+
+      gdk_event_free (fevent);
+    }
 }
 
 static gint
@@ -5753,11 +5795,16 @@ get_monitor_containing_pointer (GtkWindow *window)
   gint monitor_num;
   GdkScreen *window_screen;
   GdkScreen *pointer_screen;
+  GdkDisplay *display;
+  GdkDevice *pointer;
 
   window_screen = gtk_window_check_screen (window);
-  gdk_display_get_pointer (gdk_screen_get_display (window_screen),
-                           &pointer_screen,
-                           &px, &py, NULL);
+  display = gdk_screen_get_display (window_screen);
+  pointer = gdk_display_get_core_pointer (display);
+
+  gdk_display_get_device_state (display, pointer,
+                                &pointer_screen,
+                                &px, &py, NULL);
 
   if (pointer_screen == window_screen)
     monitor_num = gdk_screen_get_monitor_at_point (pointer_screen, px, py);
@@ -5940,12 +5987,16 @@ gtk_window_compute_configure_request (GtkWindow    *window,
             gint screen_height = gdk_screen_get_height (screen);
 	    gint monitor_num;
 	    GdkRectangle monitor;
+            GdkDisplay *display;
+            GdkDevice *pointer;
             GdkScreen *pointer_screen;
             gint px, py;
-            
-            gdk_display_get_pointer (gdk_screen_get_display (screen),
-                                     &pointer_screen,
-                                     &px, &py, NULL);
+
+            display = gdk_screen_get_display (screen);
+            pointer = gdk_display_get_core_pointer (display);
+            gdk_display_get_device_state (display, pointer,
+                                          &pointer_screen,
+                                          &px, &py, NULL);
 
             if (pointer_screen == screen)
               monitor_num = gdk_screen_get_monitor_at_point (screen, px, py);
@@ -7594,6 +7645,7 @@ gtk_window_has_toplevel_focus (GtkWindow *window)
 static void
 gtk_window_group_class_init (GtkWindowGroupClass *klass)
 {
+  g_type_class_add_private (klass, sizeof (GtkWindowGroupPrivate));
 }
 
 GType
@@ -7641,6 +7693,8 @@ static void
 window_group_cleanup_grabs (GtkWindowGroup *group,
 			    GtkWindow      *window)
 {
+  GtkWindowGroupPrivate *priv;
+  GtkDeviceGrabInfo *info;
   GSList *tmp_list;
   GSList *to_remove = NULL;
 
@@ -7658,6 +7712,27 @@ window_group_cleanup_grabs (GtkWindowGroup *group,
       g_object_unref (to_remove->data);
       to_remove = g_slist_delete_link (to_remove, to_remove);
     }
+
+  priv = GTK_WINDOW_GROUP_GET_PRIVATE (group);
+  tmp_list = priv->device_grabs;
+
+  while (tmp_list)
+    {
+      info = tmp_list->data;
+
+      if (gtk_widget_get_toplevel (info->widget) == (GtkWidget *) window)
+        to_remove = g_slist_prepend (to_remove, info);
+
+      tmp_list = tmp_list->next;
+    }
+
+  while (to_remove)
+    {
+      info = to_remove->data;
+
+      gtk_device_grab_remove (info->widget, info->device);
+      to_remove = g_slist_delete_link (to_remove, to_remove);
+    }
 }
 
 /**
@@ -7784,6 +7859,131 @@ _gtk_window_group_get_current_grab (GtkWindowGroup *window_group)
   return NULL;
 }
 
+void
+_gtk_window_group_add_device_grab (GtkWindowGroup *window_group,
+                                   GtkWidget      *widget,
+                                   GdkDevice      *device,
+                                   gboolean        block_others)
+{
+  GtkWindowGroupPrivate *priv;
+  GtkDeviceGrabInfo *info;
+
+  priv = GTK_WINDOW_GROUP_GET_PRIVATE (window_group);
+
+  info = g_slice_new0 (GtkDeviceGrabInfo);
+  info->widget = widget;
+  info->device = device;
+  info->block_others = block_others;
+
+  priv->device_grabs = g_slist_prepend (priv->device_grabs, info);
+}
+
+void
+_gtk_window_group_remove_device_grab (GtkWindowGroup *window_group,
+                                      GtkWidget      *widget,
+                                      GdkDevice      *device)
+{
+  GtkWindowGroupPrivate *priv;
+  GtkDeviceGrabInfo *info;
+  GSList *list, *node = NULL;
+  GdkDevice *other_device;
+
+  priv = GTK_WINDOW_GROUP_GET_PRIVATE (window_group);
+  other_device = gdk_device_get_associated_device (device);
+  list = priv->device_grabs;
+
+  while (list)
+    {
+      info = list->data;
+
+      if (info->widget == widget &&
+          (info->device == device ||
+           info->device == other_device))
+        {
+          node = list;
+          break;
+        }
+
+      list = list->next;
+    }
+
+  if (node)
+    {
+      info = node->data;
+
+      priv->device_grabs = g_slist_delete_link (priv->device_grabs, node);
+      g_slice_free (GtkDeviceGrabInfo, info);
+    }
+}
+
+/**
+ * gtk_window_group_get_current_device_grab:
+ * @window_group: a #GtkWindowGroup
+ * @device: a #GdkDevice
+ *
+ * Returns the current grab widget for @device, or %NULL if none.
+ *
+ * Returns: The grab widget, or %NULL
+ **/
+GtkWidget *
+gtk_window_group_get_current_device_grab (GtkWindowGroup *window_group,
+                                          GdkDevice      *device)
+{
+  GtkWindowGroupPrivate *priv;
+  GtkDeviceGrabInfo *info;
+  GdkDevice *other_device;
+  GSList *list;
+
+  priv = GTK_WINDOW_GROUP_GET_PRIVATE (window_group);
+  list = priv->device_grabs;
+  other_device = gdk_device_get_associated_device (device);
+
+  while (list)
+    {
+      info = list->data;
+      list = list->next;
+
+      if (info->device == device ||
+          info->device == other_device)
+        return info->widget;
+    }
+
+  return NULL;
+}
+
+gboolean
+_gtk_window_group_widget_is_blocked_for_device (GtkWindowGroup *window_group,
+                                                GtkWidget      *widget,
+                                                GdkDevice      *device)
+{
+  GtkWindowGroupPrivate *priv;
+  GtkDeviceGrabInfo *info;
+  GdkDevice *other_device;
+  GSList *list;
+
+  priv = GTK_WINDOW_GROUP_GET_PRIVATE (window_group);
+  other_device = gdk_device_get_associated_device (device);
+  list = priv->device_grabs;
+
+  while (list)
+    {
+      info = list->data;
+      list = list->next;
+
+      /* Look for blocking grabs on other device pairs
+       * that have the passed widget within the GTK+ grab.
+       */
+      if (info->block_others &&
+          info->device != device &&
+          info->device != other_device &&
+          (info->widget == widget ||
+           gtk_widget_is_ancestor (widget, info->widget)))
+        return TRUE;
+    }
+
+  return FALSE;
+}
+
 /*
   Derived from XParseGeometry() in XFree86  
 
diff --git a/gtk/gtkwindow.h b/gtk/gtkwindow.h
index 22753fc..fec9d99 100644
--- a/gtk/gtkwindow.h
+++ b/gtk/gtkwindow.h
@@ -395,6 +395,9 @@ void             gtk_window_group_remove_window (GtkWindowGroup     *window_grou
 					         GtkWindow          *window);
 GList *          gtk_window_group_list_windows  (GtkWindowGroup     *window_group);
 
+GtkWidget *      gtk_window_group_get_current_device_grab (GtkWindowGroup *window_group,
+                                                           GdkDevice      *device);
+
 
 /* --- internal functions --- */
 void            _gtk_window_internal_set_focus (GtkWindow *window,
@@ -412,6 +415,17 @@ void            _gtk_window_constrain_size     (GtkWindow *window,
 						gint      *new_width,
 						gint      *new_height);
 GtkWidget      *_gtk_window_group_get_current_grab (GtkWindowGroup *window_group);
+void            _gtk_window_group_add_device_grab    (GtkWindowGroup   *window_group,
+                                                      GtkWidget        *widget,
+                                                      GdkDevice        *device,
+                                                      gboolean          block_others);
+void            _gtk_window_group_remove_device_grab (GtkWindowGroup   *window_group,
+                                                      GtkWidget        *widget,
+                                                      GdkDevice        *device);
+
+gboolean        _gtk_window_group_widget_is_blocked_for_device (GtkWindowGroup *window_group,
+                                                                GtkWidget      *widget,
+                                                                GdkDevice      *device);
 
 void            _gtk_window_set_has_toplevel_focus (GtkWindow *window,
 						    gboolean   has_toplevel_focus);



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]