[gtk/gtk-3-24: 1/2] Add support for Windows Pointer Input Stack




commit 712721b60aec8d334a6de93281634aa51f26210c
Author: Luca Bacci <luca bacci982 gmail com>
Date:   Wed Mar 10 11:20:25 2021 +0100

    Add support for Windows Pointer Input Stack
    
    https://gitlab.gnome.org/GNOME/gtk/-/merge_requests/1563

 README.win32                         |   77 ++-
 gdk/win32/Makefile.am                |    7 +-
 gdk/win32/gdkdevice-win32.c          |    4 +-
 gdk/win32/gdkdevice-winpointer.c     |  321 +++++++++
 gdk/win32/gdkdevice-winpointer.h     |   64 ++
 gdk/win32/gdkdevice-wintab.c         |    2 +-
 gdk/win32/gdkdevicemanager-win32.c   | 1257 +++++++++++++++++++++++++++++++++-
 gdk/win32/gdkdevicemanager-win32.h   |   32 +-
 gdk/win32/gdkevents-win32.c          |  374 ++++++++--
 gdk/win32/gdkglobals-win32.c         |    2 +-
 gdk/win32/gdkmain-win32.c            |   22 -
 gdk/win32/gdkprivate-win32.h         |   12 +-
 gdk/win32/gdkwindow-win32.c          |    3 +
 gdk/win32/makefile.msc               |    6 +-
 gdk/win32/meson.build                |    4 +-
 gdk/win32/winpointer.h               |  320 +++++++++
 win32/vs10/gtk3-build-defines.props  |    2 +-
 win32/vs9/gtk3-build-defines.vsprops |    2 +-
 18 files changed, 2398 insertions(+), 113 deletions(-)
---
diff --git a/README.win32 b/README.win32
index 3f63458394..b6601c48ba 100644
--- a/README.win32
+++ b/README.win32
@@ -15,6 +15,8 @@ the same compiler is used for at least GDK-Pixbuf, Pango, atk and glib
 so that crashes and errors caused by different CRTs can be avoided. Currently
 building with Visual Studio 2008 or later is supported, either via Visual Studio
 project files or via the Meson build system, as described in the below sections.
+For Visual Studio 2008, 2010, a special setup making use of the Windows 8.0 SDK
+is required, see at the bottom of this document for guidance.
 Interchanging between Visual Studio 2015, 2017 and 2019 builds should be fine
 as they use the same CRT (UCRT) DLLs.
 
@@ -286,6 +288,73 @@ If you are building with Visual Studio 2008, note the following items as well:
 - The more modern visual style for the print dialog is not applied for Visual
   Studio 2008 builds.  Any solutions to this is really appreciated.
 
+Support for pre-2012 Visual Studio
+==================================
+
+This release of GTK+ requires at least the Windows 8.0 SDK in order to be built
+successfully using Visual Studio, which means that building with Visual Studio
+2008 or 2010 is possible only with a special setup and must be done in the
+command line with Ninja.  Please see
+https://devblogs.microsoft.com/cppblog/using-the-windows-software-development-kit-sdk-for-windows-8-consumer-preview-with-visual-studio-2010/
+for references; basically, assuming that your Windows 8.0 SDK is installed in
+`C:\Program Files (x86)\Windows Kits\8.0` (`$(WIN8SDKDIR)` in short), you need
+to ensure the following before invoking Meson to configure the build:
+
+- Your `%INCLUDE%` must not include the Windows 7.0/7.1 SDK include directories,
+  and `$(WIN8SDKDIR)\include\um`, `$(WIN8SDKDIR)\include\um\share` and
+  `$(WIN8SDKDIR)\include\winrt` (in this order) must be before your stock
+  Visual Studio 2008/2010 header directories.  If you have the DirectX SDK installed,
+  you should remove its include directory from your `%INCLUDE%` as well.
+- You must replace the Windows 7.0/7.1 SDK library directory in `%LIB%` with the
+  Windows 8.0 SDK library directory, i.e. `$(WIN8SDKDIR)\lib\win8\um\[x86|x64]`.
+  If you have the DirectX SDK installed, you should remove its library directory
+  from your `%INCLUDE%` as well.
+- You must replace the Windows 7.0/7.1 SDK tools directory from your `%PATH%` with
+  the Windows 8.0 SDK tools directory, i.e. `$(WIN8SDKDIR)\bin\[x86|x64]`.
+  If you have the DirectX SDK installed, you should remove its utility directory
+  from your `%PATH%` as well.
+
+The Windows 8.0 SDK headers may contain an `roapi.h` that cannot be used under plain
+C, so to remedy that, change the following lines (around lines 55-57):
+
+  // RegisterActivationFactory/RevokeActivationFactory registration cookie
+  typedef struct {} *RO_REGISTRATION_COOKIE;
+  // RegisterActivationFactory/DllGetActivationFactory callback
+
+to
+
+  // RegisterActivationFactory/RevokeActivationFactory registration cookie
+  #ifdef __cplusplus
+  typedef struct {} *RO_REGISTRATION_COOKIE;
+  #else
+  typedef struct _RO_REGISTRATION_COOKIE *RO_REGISTRATION_COOKIE; /* make this header includable in C files 
*/
+  #endif
+  // RegisterActivationFactory/DllGetActivationFactory callback
+
+This follows what is done in the Windows 8.1 SDK, which contains an `roapi.h`
+that is usable under plain C.  Please note that you might need to copy that file
+into a location that is in your `%INCLUDE%` which precedes the include path for the
+Windows 8.0 SDK headers, if you do not have administrative privileges.
+
+Visual Studio 2008 hacks
+========================
+
+- You need to run the following lines from your build directory, to embed the
+  manifests that are generated during the build, assuming the built binaries
+  are installed to `$(PREFIX)`, after a successful build/installation:
+
+```cmd
+> for /r %f in (*.dll.manifest) do if exist $(PREFIX)\bin\%~nf mt /manifest %f (PREFIX)\bin\%~nf;2
+> for /r %f in (*.exe.manifest) do if exist $(PREFIX)\bin\%~nf mt /manifest %f (PREFIX)\bin\%~nf;1
+```
+
+
+- If building for amd64/x86_64/x64, sometimes the compilation of sources may seem to hang, which
+  is caused by an optimization issue in the 2008 x64 compiler.  You need to use Task Manager to
+  remove all running instances of `cl.exe`, which will cause the build process to terminate.  Update
+  the build flags of the sources that hang on compilation by changing its `"/O2"` flag to `"/O1"`
+  in `build.ninja`, and retry the build, where things should continue to build normally.
+
 Using GTK+ on Win32
 ===================
 
@@ -302,13 +371,5 @@ cases, but not in general. Sorry. If you have all GTK+ and GDK calls
 in the same thread, it might work. Otherwise, probably not at
 all. Possible ways to fix this are being investigated.
 
-Wintab
-======
-
-The tablet support uses the Wintab API. The Wintab development kit is
-no longer required. The wintab.h header file is bundled with GTK+
-sources. Unfortunately it seems that only Wacom tablets come with
-support for the Wintab API nowadays.
-
 --Tor Lillqvist <tml iki fi>, <tml novell com>
 --Updated by Fan, Chun-wei <fanc999 yahoo com tw>
diff --git a/gdk/win32/Makefile.am b/gdk/win32/Makefile.am
index 3081b69a4a..471c5d5da2 100644
--- a/gdk/win32/Makefile.am
+++ b/gdk/win32/Makefile.am
@@ -21,7 +21,10 @@ if WIN32_GLES
 AM_CPPFLAGS += "-DGDK_WIN32_ENABLE_EGL=1"
 endif #WIN32_GLES
 
-LDADDS = $(GDK_DEP_LIBS)
+libgdk_win32_DEP_LIBS = \
+       hid.lib
+
+LDADDS = $(libgdk_win32_DEP_LIBS) $(GDK_DEP_LIBS)
 
 noinst_LTLIBRARIES = libgdk-win32.la
 
@@ -40,6 +43,8 @@ libgdk_win32_la_SOURCES = \
        gdkdevice-virtual.h \
        gdkdevice-win32.c \
        gdkdevice-win32.h \
+       gdkdevice-winpointer.c \
+       gdkdevice-winpointer.h \
        gdkdevice-wintab.c \
        gdkdevice-wintab.h \
        gdkdisplay-win32.c \
diff --git a/gdk/win32/gdkdevice-win32.c b/gdk/win32/gdkdevice-win32.c
index 3ca8e03a51..8e107db981 100644
--- a/gdk/win32/gdkdevice-win32.c
+++ b/gdk/win32/gdkdevice-win32.c
@@ -117,7 +117,7 @@ gdk_device_win32_query_state (GdkDevice        *device,
   impl = GDK_WINDOW_IMPL_WIN32 (window->impl);
 
   hwnd = GDK_WINDOW_HWND (window);
-  GetCursorPos (&point);
+  _gdk_win32_get_cursor_pos (&point);
 
   if (root_x)
     *root_x = point.x / impl->window_scale;
@@ -215,7 +215,7 @@ _gdk_device_win32_window_at_position (GdkDevice       *device,
   HWND hwnd;
   RECT rect;
 
-  if (!GetCursorPos (&screen_pt))
+  if (!_gdk_win32_get_cursor_pos (&screen_pt))
     return NULL;
 
   hwnd = WindowFromPoint (screen_pt);
diff --git a/gdk/win32/gdkdevice-winpointer.c b/gdk/win32/gdkdevice-winpointer.c
new file mode 100644
index 0000000000..42cb5b8f03
--- /dev/null
+++ b/gdk/win32/gdkdevice-winpointer.c
@@ -0,0 +1,321 @@
+/* GDK - The GIMP Drawing Kit
+ * Copyright (C) 2020 the GTK team
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <gdk/gdkwindow.h>
+
+#include <windows.h>
+
+#include "gdkwin32.h"
+#include "gdkdevice-winpointer.h"
+
+G_DEFINE_TYPE (GdkDeviceWinpointer, gdk_device_winpointer, GDK_TYPE_DEVICE)
+
+static GdkModifierType
+get_keyboard_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;
+
+  return mask;
+}
+
+static gboolean
+gdk_device_winpointer_get_history (GdkDevice      *device,
+                                   GdkWindow      *window,
+                                   guint32         start,
+                                   guint32         stop,
+                                   GdkTimeCoord ***events,
+                                   gint           *n_events)
+{
+  return FALSE;
+}
+
+static void
+gdk_device_winpointer_get_state (GdkDevice       *device,
+                                 GdkWindow       *window,
+                                 gdouble         *axes,
+                                 GdkModifierType *mask)
+{
+  GdkDeviceWinpointer *device_winpointer = GDK_DEVICE_WINPOINTER (device);
+
+  if (mask)
+    {
+      *mask = get_keyboard_mask ();
+      *mask |= device_winpointer->last_button_mask;
+    }
+
+  if (axes)
+    {
+      gsize size = sizeof (double) * device_winpointer->num_axes;
+      memcpy (axes, device_winpointer->last_axis_data, size);
+    }
+}
+
+static void
+gdk_device_winpointer_set_window_cursor (GdkDevice *device,
+                                         GdkWindow *window,
+                                         GdkCursor *cursor)
+{
+}
+
+static void
+gdk_device_winpointer_warp (GdkDevice *device,
+                            GdkScreen *screen,
+                            gdouble    x,
+                            gdouble    y)
+{
+}
+
+static void
+gdk_device_winpointer_query_state (GdkDevice        *device,
+                                   GdkWindow        *window,
+                                   GdkWindow       **root_window,
+                                   GdkWindow       **child_window,
+                                   gdouble          *root_x,
+                                   gdouble          *root_y,
+                                   gdouble          *win_x,
+                                   gdouble          *win_y,
+                                   GdkModifierType  *mask)
+{
+  GdkDeviceWinpointer *device_winpointer = GDK_DEVICE_WINPOINTER (device);
+  GdkWindowImplWin32 *impl = GDK_WINDOW_IMPL_WIN32 (window->impl);
+  GdkScreen *screen = gdk_window_get_screen (window);
+  HWND hwnd = GDK_WINDOW_HWND (window);
+  HWND hwndc = NULL;
+  POINT point;
+
+  _gdk_win32_get_cursor_pos (&point);
+
+  if (root_x)
+    *root_x = point.x / impl->window_scale;
+
+  if (root_y)
+    *root_y = point.y / impl->window_scale;
+
+  ScreenToClient (hwnd, &point);
+
+  if (win_x)
+    *win_x = point.x / impl->window_scale;
+
+  if (win_y)
+    *win_y = point.y / impl->window_scale;
+
+  if (window == gdk_screen_get_root_window (screen))
+    {
+      if (win_x)
+        *win_x += _gdk_offset_x;
+
+      if (win_y)
+        *win_y += _gdk_offset_y;
+
+      if (root_x)
+        *root_x += _gdk_offset_x;
+
+      if (root_y)
+        *root_y += _gdk_offset_y;
+    }
+
+  if (child_window)
+    {
+      if (window == gdk_screen_get_root_window (screen))
+        {
+          /* Always use WindowFromPoint when searching from the root window.
+          *  Only WindowFromPoint is able to look through transparent
+          *  layered windows.
+          */
+          hwndc = GetAncestor (WindowFromPoint (point), GA_ROOT);
+        }
+      else
+        {
+          hwndc = ChildWindowFromPoint (hwnd, point);
+        }
+
+      if (hwndc && hwndc != hwnd)
+        *child_window = gdk_win32_handle_table_lookup (hwndc);
+      else
+        *child_window = NULL; /* Direct child unknown to gdk */
+    }
+
+  if (root_window)
+    *root_window = gdk_screen_get_root_window (screen);
+
+  if (mask)
+    {
+      *mask = get_keyboard_mask ();
+      *mask |= device_winpointer->last_button_mask;
+    }
+}
+
+static GdkGrabStatus
+gdk_device_winpointer_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_winpointer_ungrab (GdkDevice *device,
+                              guint32    time_)
+{
+}
+
+static void
+screen_to_client (HWND hwnd, POINT screen_pt, POINT *client_pt)
+{
+  *client_pt = screen_pt;
+  ScreenToClient (hwnd, client_pt);
+}
+
+GdkWindow *
+_gdk_device_winpointer_window_at_position (GdkDevice       *device,
+                                           gdouble         *win_x,
+                                           gdouble         *win_y,
+                                           GdkModifierType *mask,
+                                           gboolean         get_toplevel)
+{
+  GdkDeviceWinpointer *device_winpointer = GDK_DEVICE_WINPOINTER (device);
+  GdkWindow *window = NULL;
+  GdkWindowImplWin32 *impl = NULL;
+  POINT screen_pt, client_pt;
+  HWND hwnd;
+  RECT rect;
+
+  if (!_gdk_win32_get_cursor_pos (&screen_pt))
+    return NULL;
+
+  hwnd = WindowFromPoint (screen_pt);
+
+  if (get_toplevel)
+    {
+      /* Use WindowFromPoint instead of ChildWindowFromPoint(Ex).
+      *  Only WindowFromPoint is able to look through transparent
+      *  layered windows.
+      */
+      hwnd = GetAncestor (hwnd, GA_ROOT);
+    }
+
+  /* Verify that we're really inside the client area of the window */
+  GetClientRect (hwnd, &rect);
+  screen_to_client (hwnd, screen_pt, &client_pt);
+  if (!PtInRect (&rect, client_pt))
+    hwnd = NULL;
+
+  if (!get_toplevel && hwnd == NULL)
+    {
+      /* If we didn't hit any window, return the root window */
+      /* note that the root window ain't a toplevel window */
+      window = gdk_get_default_root_window ();
+      impl = GDK_WINDOW_IMPL_WIN32 (window->impl);
+
+      if (win_x)
+        *win_x = (screen_pt.x + _gdk_offset_x) / impl->window_scale;
+      if (win_y)
+        *win_y = (screen_pt.y + _gdk_offset_y) / impl->window_scale;
+
+      return window;
+    }
+
+  window = gdk_win32_handle_table_lookup (hwnd);
+
+  if (window && (win_x || win_y))
+    {
+      impl = GDK_WINDOW_IMPL_WIN32 (window->impl);
+
+      if (win_x)
+        *win_x = client_pt.x / impl->window_scale;
+      if (win_y)
+        *win_y = client_pt.y / impl->window_scale;
+    }
+
+  if (mask)
+    {
+      *mask = get_keyboard_mask ();
+      *mask |= device_winpointer->last_button_mask;
+    }
+
+  return window;
+}
+
+static void
+gdk_device_winpointer_select_window_events (GdkDevice    *device,
+                                            GdkWindow    *window,
+                                            GdkEventMask  event_mask)
+{
+}
+
+static void
+gdk_device_winpointer_init (GdkDeviceWinpointer *device_winpointer)
+{
+  device_winpointer->device_handle = NULL;
+  device_winpointer->start_cursor_id = 0;
+  device_winpointer->end_cursor_id = 0;
+
+  device_winpointer->origin_x = 0;
+  device_winpointer->origin_y = 0;
+  device_winpointer->scale_x = 0.0;
+  device_winpointer->scale_y = 0.0;
+
+  device_winpointer->last_axis_data = NULL;
+  device_winpointer->num_axes = 0;
+  device_winpointer->last_button_mask = 0;
+}
+
+static void
+gdk_device_winpointer_finalize (GObject *object)
+{
+  GdkDeviceWinpointer *device_winpointer = GDK_DEVICE_WINPOINTER (object);
+
+  g_free (device_winpointer->last_axis_data);
+
+  G_OBJECT_CLASS (gdk_device_winpointer_parent_class)->finalize (object);
+}
+
+static void
+gdk_device_winpointer_class_init (GdkDeviceWinpointerClass *klass)
+{
+  GdkDeviceClass *device_class = GDK_DEVICE_CLASS (klass);
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+  object_class->finalize = gdk_device_winpointer_finalize;
+  device_class->get_history = gdk_device_winpointer_get_history;
+  device_class->get_state = gdk_device_winpointer_get_state;
+  device_class->set_window_cursor = gdk_device_winpointer_set_window_cursor;
+  device_class->warp = gdk_device_winpointer_warp;
+  device_class->query_state = gdk_device_winpointer_query_state;
+  device_class->grab = gdk_device_winpointer_grab;
+  device_class->ungrab = gdk_device_winpointer_ungrab;
+  device_class->window_at_position = _gdk_device_winpointer_window_at_position;
+  device_class->select_window_events = gdk_device_winpointer_select_window_events;
+}
diff --git a/gdk/win32/gdkdevice-winpointer.h b/gdk/win32/gdkdevice-winpointer.h
new file mode 100644
index 0000000000..73146d7e4e
--- /dev/null
+++ b/gdk/win32/gdkdevice-winpointer.h
@@ -0,0 +1,64 @@
+/* GDK - The GIMP Drawing Kit
+ * Copyright (C) 2020 the GTK team
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __GDK_DEVICE_WINPOINTER_H__
+#define __GDK_DEVICE_WINPOINTER_H__
+
+#include <gdk/gdkdeviceprivate.h>
+
+#include <windows.h>
+
+G_BEGIN_DECLS
+
+#define GDK_TYPE_DEVICE_WINPOINTER         (gdk_device_winpointer_get_type ())
+#define GDK_DEVICE_WINPOINTER(o)           (G_TYPE_CHECK_INSTANCE_CAST ((o), GDK_TYPE_DEVICE_WINPOINTER, 
GdkDeviceWinpointer))
+#define GDK_DEVICE_WINPOINTER_CLASS(c)     (G_TYPE_CHECK_CLASS_CAST ((c), GDK_TYPE_DEVICE_WINPOINTER, 
GdkDeviceWinpointerClass))
+#define GDK_IS_DEVICE_WINPOINTER(o)        (G_TYPE_CHECK_INSTANCE_TYPE ((o), GDK_TYPE_DEVICE_WINPOINTER))
+#define GDK_IS_DEVICE_WINPOINTER_CLASS(c)  (G_TYPE_CHECK_CLASS_TYPE ((c), GDK_TYPE_DEVICE_WINPOINTER))
+#define GDK_DEVICE_WINPOINTER_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GDK_TYPE_DEVICE_WINPOINTER, 
GdkDeviceWinpointerClass))
+
+typedef struct _GdkDeviceWinpointer GdkDeviceWinpointer;
+typedef struct _GdkDeviceWinpointerClass GdkDeviceWinpointerClass;
+
+struct _GdkDeviceWinpointer
+{
+  GdkDevice parent_instance;
+
+  HANDLE device_handle;
+  UINT32 start_cursor_id;
+  UINT32 end_cursor_id;
+
+  int origin_x;
+  int origin_y;
+  double scale_x;
+  double scale_y;
+
+  double *last_axis_data;
+  unsigned num_axes;
+  GdkModifierType last_button_mask;
+};
+
+struct _GdkDeviceWinpointerClass
+{
+  GdkDeviceClass parent_class;
+};
+
+GType gdk_device_winpointer_get_type (void) G_GNUC_CONST;
+
+G_END_DECLS
+
+#endif /* __GDK_DEVICE_WINPOINTER_H__ */
diff --git a/gdk/win32/gdkdevice-wintab.c b/gdk/win32/gdkdevice-wintab.c
index 338479db43..1f9de43fa7 100644
--- a/gdk/win32/gdkdevice-wintab.c
+++ b/gdk/win32/gdkdevice-wintab.c
@@ -128,7 +128,7 @@ gdk_device_wintab_query_state (GdkDevice        *device,
   impl = GDK_WINDOW_IMPL_WIN32 (window->impl);
 
   hwnd = GDK_WINDOW_HWND (window);
-  GetCursorPos (&point);
+  _gdk_win32_get_cursor_pos (&point);
 
   if (root_x)
     *root_x = point.x / impl->window_scale;
diff --git a/gdk/win32/gdkdevicemanager-win32.c b/gdk/win32/gdkdevicemanager-win32.c
index 34c0083cb3..6eaab30a83 100644
--- a/gdk/win32/gdkdevicemanager-win32.c
+++ b/gdk/win32/gdkdevicemanager-win32.c
@@ -28,10 +28,17 @@
 #include "gdkdeviceprivate.h"
 #include "gdkdevice-win32.h"
 #include "gdkdevice-virtual.h"
+#include "gdkdevice-winpointer.h"
 #include "gdkdevice-wintab.h"
 #include "gdkdisplayprivate.h"
+#include "gdkdevicetoolprivate.h"
 #include "gdkseatdefaultprivate.h"
 
+#include <tchar.h>
+#include <tpcshrd.h>
+#include <hidsdi.h>
+#include "winpointer.h"
+
 #define WINTAB32_DLL "Wintab32.dll"
 
 #define PACKETDATA (PK_CONTEXT | PK_CURSOR | PK_BUTTONS | PK_X | PK_Y  | PK_NORMAL_PRESSURE | PK_ORIENTATION 
| PK_TANGENT_PRESSURE)
@@ -42,6 +49,49 @@
 #define DEBUG_WINTAB 1         /* Verbose debug messages enabled */
 #define TWOPI (2 * G_PI)
 
+typedef BOOL
+(WINAPI *registerPointerDeviceNotifications_t)(HWND window, BOOL notifyRange);
+typedef BOOL
+(WINAPI *getPointerDevices_t)(UINT32 *deviceCount, POINTER_DEVICE_INFO *pointerDevices);
+typedef BOOL
+(WINAPI *getPointerDeviceCursors_t)(HANDLE device, UINT32 *cursorCount, POINTER_DEVICE_CURSOR_INFO 
*deviceCursors);
+typedef BOOL
+(WINAPI *getPointerDeviceRects_t)(HANDLE device, RECT *pointerDeviceRect, RECT *displayRect);
+typedef BOOL
+(WINAPI *getPointerType_t)(UINT32 pointerId, POINTER_INPUT_TYPE *pointerType);
+typedef BOOL
+(WINAPI *getPointerCursorId_t)(UINT32 pointerId, UINT32 *cursorId);
+typedef BOOL
+(WINAPI *getPointerPenInfo_t)(UINT32 pointerId, POINTER_PEN_INFO *penInfo);
+typedef BOOL
+(WINAPI *getPointerTouchInfo_t)(UINT32 pointerId, POINTER_TOUCH_INFO *touchInfo);
+typedef BOOL
+(WINAPI *getPointerPenInfoHistory_t)(UINT32 pointerId, UINT32 *entriesCount, POINTER_PEN_INFO *penInfo);
+typedef BOOL
+(WINAPI *getPointerTouchInfoHistory_t)(UINT32 pointerId, UINT32 *entriesCount, POINTER_TOUCH_INFO 
*touchInfo);
+typedef BOOL
+(WINAPI *setGestureConfig_t)(HWND hwnd, DWORD dwReserved, UINT cIDs, PGESTURECONFIG pGestureConfig, UINT 
cbSize);
+typedef BOOL
+(WINAPI *setWindowFeedbackSetting_t)(HWND hwnd, FEEDBACK_TYPE feedback, DWORD dwFlags, UINT32 size, const 
VOID *configuration);
+
+static registerPointerDeviceNotifications_t registerPointerDeviceNotifications;
+static getPointerDevices_t getPointerDevices;
+static getPointerDeviceCursors_t getPointerDeviceCursors;
+static getPointerDeviceRects_t getPointerDeviceRects;
+static getPointerType_t getPointerType;
+static getPointerCursorId_t getPointerCursorId;
+static getPointerPenInfo_t getPointerPenInfo;
+static getPointerTouchInfo_t getPointerTouchInfo;
+static getPointerPenInfoHistory_t getPointerPenInfoHistory;
+static getPointerTouchInfoHistory_t getPointerTouchInfoHistory;
+static setGestureConfig_t setGestureConfig;
+static setWindowFeedbackSetting_t setWindowFeedbackSetting;
+
+static ATOM winpointer_notif_window_class;
+static HWND winpointer_notif_window_handle;
+
+static GPtrArray *winpointer_ignored_interactions;
+
 static GList     *wintab_contexts = NULL;
 static GdkWindow *wintab_window = NULL;
 extern gint       _gdk_input_ignore_core;
@@ -126,6 +176,586 @@ gdk_device_manager_win32_finalize (GObject *object)
   G_OBJECT_CLASS (gdk_device_manager_win32_parent_class)->finalize (object);
 }
 
+static inline double
+rect_width (RECT *rect)
+{
+  return rect->right - rect->left;
+}
+
+static inline double
+rect_height (RECT *rect)
+{
+  return rect->bottom - rect->top;
+}
+
+static inline gboolean
+rect_is_degenerate (RECT *rect)
+{
+  return rect_width (rect) == 0 || rect_height (rect) == 0;
+}
+
+static gboolean
+winpointer_device_update_scale_factors (GdkDeviceWinpointer *device)
+{
+  RECT device_rect;
+  RECT display_rect;
+
+  if (!getPointerDeviceRects (device->device_handle, &device_rect, &display_rect))
+    {
+      WIN32_API_FAILED ("GetPointerDeviceRects");
+      return FALSE;
+    }
+
+  if (rect_is_degenerate (&device_rect))
+    {
+      g_warning ("Invalid coordinates from GetPointerDeviceRects");
+      return FALSE;
+    }
+
+  device->origin_x = display_rect.left;
+  device->origin_y = display_rect.top;
+  device->scale_x = rect_width (&display_rect) / rect_width (&device_rect);
+  device->scale_y = rect_height (&display_rect) / rect_height (&device_rect);
+
+  return TRUE;
+}
+
+#define HID_STRING_BYTES_LIMIT 200
+#define VID_PID_CHARS 4
+
+static void
+winpointer_get_device_details (HANDLE device,
+                               char *vid,
+                               char *pid,
+                               char **manufacturer,
+                               char **product)
+{
+  RID_DEVICE_INFO info;
+  UINT wchars_count = 0;
+  UINT size = 0;
+
+  memset (&info, 0, sizeof (info));
+
+  info.cbSize = sizeof (info);
+  size = sizeof (info);
+
+  if (GetRawInputDeviceInfoW (device, RIDI_DEVICEINFO, &info, &size) > 0 &&
+      info.dwType == RIM_TYPEHID &&
+      info.hid.dwVendorId > 0 &&
+      info.hid.dwProductId > 0)
+    {
+      const char *format_string = "%0" G_STRINGIFY (VID_PID_CHARS) "x";
+
+      g_snprintf (vid, VID_PID_CHARS + 1, format_string, (unsigned) info.hid.dwVendorId);
+      g_snprintf (pid, VID_PID_CHARS + 1, format_string, (unsigned) info.hid.dwProductId);
+    }
+
+  if (GetRawInputDeviceInfoW (device, RIDI_DEVICENAME, NULL, &wchars_count) == 0)
+    {
+      gunichar2 *path = g_new0 (gunichar2, wchars_count);
+
+      if (GetRawInputDeviceInfoW (device, RIDI_DEVICENAME, path, &wchars_count) > 0)
+        {
+          HANDLE device_file = CreateFileW (path,
+                                            0,
+                                            FILE_SHARE_READ |
+                                            FILE_SHARE_WRITE |
+                                            FILE_SHARE_DELETE,
+                                            NULL,
+                                            OPEN_EXISTING,
+                                            FILE_FLAG_SESSION_AWARE,
+                                            NULL);
+
+          if (device_file != INVALID_HANDLE_VALUE)
+            {
+              gunichar2 *buffer = g_malloc0 (HID_STRING_BYTES_LIMIT);
+
+              if (HidD_GetManufacturerString (device_file, buffer, HID_STRING_BYTES_LIMIT))
+                if (buffer[0])
+                  *manufacturer = g_utf16_to_utf8 (buffer, -1, NULL, NULL, NULL);
+
+              if (HidD_GetProductString (device_file, buffer, HID_STRING_BYTES_LIMIT))
+                if (buffer[0])
+                  *product = g_utf16_to_utf8 (buffer, -1, NULL, NULL, NULL);
+
+              g_free (buffer);
+              CloseHandle (device_file);
+            }
+        }
+
+      g_free (path);
+    }
+}
+
+static void
+winpointer_create_device (GdkDeviceManagerWin32 *device_manager,
+                          POINTER_DEVICE_INFO *info,
+                          GdkInputSource source)
+{
+  GdkDisplay *display = NULL;
+  GdkSeat *seat = NULL;
+  GdkDeviceWinpointer *device = NULL;
+  unsigned num_touches = 0;
+  char vid[VID_PID_CHARS + 1];
+  char pid[VID_PID_CHARS + 1];
+  char *manufacturer = NULL;
+  char *product = NULL;
+  char *base_name = NULL;
+  char *name = NULL;
+  UINT32 num_cursors = 0;
+
+  memset (pid, 0, VID_PID_CHARS + 1);
+  memset (vid, 0, VID_PID_CHARS + 1);
+
+  g_object_get (device_manager, "display", &display, NULL);
+  seat = gdk_display_get_default_seat (display);
+
+  if (!getPointerDeviceCursors (info->device, &num_cursors, NULL))
+    {
+      WIN32_API_FAILED ("GetPointerDeviceCursors");
+      return;
+    }
+
+  if (num_cursors == 0)
+    return;
+
+  winpointer_get_device_details (info->device, vid, pid, &manufacturer, &product);
+
+  /* build up the name */
+  if (!manufacturer && vid[0])
+    manufacturer = g_strdup (vid);
+
+  if (!product && pid[0])
+    product = g_strdup (pid);
+
+  if (manufacturer && product)
+    base_name = g_strconcat (manufacturer, " ", product, NULL);
+
+  if (!base_name && info->productString[0])
+    base_name = g_utf16_to_utf8 (info->productString, -1, NULL, NULL, NULL);
+
+  if (!base_name)
+    base_name = g_strdup ("Unnamed");
+
+  switch (source)
+    {
+    case GDK_SOURCE_PEN:
+      name = g_strconcat (base_name, " Pen stylus", NULL);
+    break;
+    case GDK_SOURCE_ERASER:
+      name = g_strconcat (base_name, " Eraser", NULL);
+    break;
+    case GDK_SOURCE_TOUCHSCREEN:
+      num_touches = info->maxActiveContacts;
+      name = g_strconcat (base_name, " Finger touch", NULL);
+    break;
+    default:
+      name = g_strdup (base_name);
+    break;
+    }
+
+  device = g_object_new (GDK_TYPE_DEVICE_WINPOINTER,
+                         "display", display,
+                         "device-manager", device_manager,
+                         "seat", seat,
+                         "type", GDK_DEVICE_TYPE_SLAVE,
+                         "input-mode", GDK_MODE_SCREEN,
+                         "has-cursor", TRUE,
+                         "input-source", source,
+                         "name", name,
+                         "num-touches", num_touches,
+                         "vendor-id", vid[0] ? vid : NULL,
+                         "product-id", pid[0] ? pid : NULL,
+                         NULL);
+
+  switch (source)
+    {
+    case GDK_SOURCE_PEN:
+    case GDK_SOURCE_ERASER:
+      _gdk_device_add_axis (GDK_DEVICE (device), GDK_NONE, GDK_AXIS_PRESSURE, 0.0, 1.0, 1.0 / 1024.0);
+      _gdk_device_add_axis (GDK_DEVICE (device), GDK_NONE, GDK_AXIS_XTILT, -1.0, 1.0, 1.0 / 90.0);
+      _gdk_device_add_axis (GDK_DEVICE (device), GDK_NONE, GDK_AXIS_YTILT, -1.0, 1.0, 1.0 / 90.0);
+      _gdk_device_add_axis (GDK_DEVICE (device), GDK_NONE, GDK_AXIS_ROTATION, 0.0, 1.0, 1.0 / 360.0);
+
+      device->num_axes = 4;
+    break;
+    case GDK_SOURCE_TOUCHSCREEN:
+      _gdk_device_add_axis (GDK_DEVICE (device), GDK_NONE, GDK_AXIS_PRESSURE, 0.0, 1.0, 1.0 / 1024.0);
+
+      device->num_axes = 1;
+    break;
+    }
+
+  device->device_handle = info->device;
+  device->start_cursor_id = info->startingCursorId;
+  device->end_cursor_id = info->startingCursorId + num_cursors - 1;
+
+  device->last_axis_data = g_new0 (double, device->num_axes);
+
+  switch (source)
+    {
+    case GDK_SOURCE_PEN:
+      {
+        GdkAxisFlags axes = gdk_device_get_axes (GDK_DEVICE (device));
+        GdkDeviceTool *tool = gdk_device_tool_new (0, 0, GDK_DEVICE_TOOL_TYPE_PEN, axes);
+
+        gdk_seat_default_add_tool (GDK_SEAT_DEFAULT (seat), tool);
+        gdk_device_update_tool (GDK_DEVICE (device), tool);
+      }
+    break;
+    case GDK_SOURCE_ERASER:
+      {
+        GdkAxisFlags axes = gdk_device_get_axes (GDK_DEVICE (device));
+        GdkDeviceTool *tool = gdk_device_tool_new (0, 0, GDK_DEVICE_TOOL_TYPE_ERASER, axes);
+
+        gdk_seat_default_add_tool (GDK_SEAT_DEFAULT (seat), tool);
+        gdk_device_update_tool (GDK_DEVICE (device), tool);
+      }
+    break;
+    case GDK_SOURCE_TOUCHSCREEN:
+    break;
+    }
+
+  if (!winpointer_device_update_scale_factors (device))
+    {
+      g_set_object (&device, NULL);
+      goto cleanup;
+    }
+
+  device_manager->winpointer_devices = g_list_append (device_manager->winpointer_devices, device);
+
+  _gdk_device_set_associated_device (GDK_DEVICE (device), device_manager->core_pointer);
+  _gdk_device_add_slave (device_manager->core_pointer, GDK_DEVICE (device));
+
+  gdk_seat_default_add_slave (GDK_SEAT_DEFAULT (seat), GDK_DEVICE (device));
+
+  g_signal_emit_by_name (device_manager, "device-added", device);
+
+cleanup:
+  g_free (name);
+  g_free (base_name);
+  g_free (product);
+  g_free (manufacturer);
+}
+
+static void
+winpointer_create_devices (GdkDeviceManagerWin32 *device_manager,
+                           POINTER_DEVICE_INFO *info)
+{
+  switch (info->pointerDeviceType)
+    {
+    case POINTER_DEVICE_TYPE_INTEGRATED_PEN:
+    case POINTER_DEVICE_TYPE_EXTERNAL_PEN:
+      winpointer_create_device (device_manager, info, GDK_SOURCE_PEN);
+      winpointer_create_device (device_manager, info, GDK_SOURCE_ERASER);
+    break;
+    case POINTER_DEVICE_TYPE_TOUCH:
+      winpointer_create_device (device_manager, info, GDK_SOURCE_TOUCHSCREEN);
+    break;
+    }
+}
+
+static gboolean
+winpointer_match_device_in_system_list (GdkDeviceWinpointer *device,
+                                        POINTER_DEVICE_INFO *infos,
+                                        UINT32 infos_count)
+{
+  UINT32 i = 0;
+
+  for (i = 0; i < infos_count; i++)
+    {
+      if (device->device_handle == infos[i].device &&
+          device->start_cursor_id == infos[i].startingCursorId)
+        return TRUE;
+    }
+
+  return FALSE;
+}
+
+static gboolean
+winpointer_match_system_device_in_device_manager (GdkDeviceManagerWin32 *device_manager,
+                                                  POINTER_DEVICE_INFO *info)
+{
+  GList *l = NULL;
+
+  for (l = device_manager->winpointer_devices; l; l = l->next)
+    {
+      GdkDeviceWinpointer *device = GDK_DEVICE_WINPOINTER (l->data);
+
+      if (device->device_handle == info->device &&
+          device->start_cursor_id == info->startingCursorId)
+        return TRUE;
+    }
+
+  return FALSE;
+}
+
+static void
+winpointer_enumerate_devices (GdkDeviceManagerWin32 *device_manager)
+{
+  POINTER_DEVICE_INFO *infos = NULL;
+  UINT32 infos_count = 0;
+  UINT32 i = 0;
+  GList *current = NULL;
+
+  do
+    {
+      infos = g_new0 (POINTER_DEVICE_INFO, infos_count);
+      if (!getPointerDevices (&infos_count, infos))
+        {
+          WIN32_API_FAILED ("GetPointerDevices");
+          g_free (infos);
+          return;
+        }
+    }
+  while (infos_count > 0 && !infos);
+
+  /* remove any gdk device not present anymore or update info */
+  current = device_manager->winpointer_devices;
+  while (current != NULL)
+    {
+      GdkDeviceWinpointer *device = GDK_DEVICE_WINPOINTER (current->data);
+      GList *next = current->next;
+
+      if (!winpointer_match_device_in_system_list (device, infos, infos_count))
+        {
+          GdkSeat *seat = gdk_device_get_seat (GDK_DEVICE (device));
+
+          gdk_device_update_tool (GDK_DEVICE (device), NULL);
+          gdk_seat_default_remove_tool (GDK_SEAT_DEFAULT (seat), (GDK_DEVICE (device))->last_tool);
+
+          gdk_seat_default_remove_slave (GDK_SEAT_DEFAULT (seat), GDK_DEVICE (device));
+          device_manager->winpointer_devices = g_list_delete_link (device_manager->winpointer_devices,
+                                                                   current);
+          g_signal_emit_by_name (device_manager, "device-removed", device);
+
+          _gdk_device_set_associated_device (GDK_DEVICE (device), NULL);
+          _gdk_device_remove_slave (device_manager->core_pointer, GDK_DEVICE (device));
+
+          g_object_unref (device);
+        }
+      else
+        winpointer_device_update_scale_factors (device);
+
+      current = next;
+    }
+
+  /* create new gdk devices */
+  for (i = 0; i < infos_count; i++)
+    if (!winpointer_match_system_device_in_device_manager (device_manager,
+                                                           &infos[i]))
+      winpointer_create_devices (device_manager, &infos[i]);
+
+  g_free (infos);
+}
+
+static LRESULT CALLBACK
+winpointer_notif_window_proc (HWND hWnd,
+                              UINT uMsg,
+                              WPARAM wParam,
+                              LPARAM lParam)
+{
+  switch (uMsg)
+    {
+    case WM_POINTERDEVICECHANGE:
+      {
+        LONG_PTR user_data = GetWindowLongPtrW (hWnd, GWLP_USERDATA);
+        GdkDeviceManagerWin32 *device_manager = GDK_DEVICE_MANAGER_WIN32 (user_data);
+
+        winpointer_enumerate_devices (device_manager);
+      }
+    return 0;
+    }
+
+  return DefWindowProcW (hWnd, uMsg, wParam, lParam);
+}
+
+static gboolean
+winpointer_notif_window_create ()
+{
+  WNDCLASSEXW wndclass;
+
+  memset (&wndclass, 0, sizeof (wndclass));
+  wndclass.cbSize = sizeof (wndclass);
+  wndclass.lpszClassName = L"GdkWin32WinPointerNotificationsWindowClass";
+  wndclass.lpfnWndProc = winpointer_notif_window_proc;
+  wndclass.hInstance = _gdk_dll_hinstance;
+
+  if ((winpointer_notif_window_class = RegisterClassExW (&wndclass)) == 0)
+    {
+      WIN32_API_FAILED ("RegisterClassExW");
+      return FALSE;
+    }
+
+  if (!(winpointer_notif_window_handle = CreateWindowExW (0,
+                                                          (LPCWSTR) winpointer_notif_window_class,
+                                                          L"GdkWin32 WinPointer Notifications",
+                                                          0,
+                                                          0, 0, 0, 0,
+                                                          HWND_MESSAGE,
+                                                          NULL,
+                                                          _gdk_dll_hinstance,
+                                                          NULL)))
+    {
+      WIN32_API_FAILED ("CreateWindowExW");
+      return FALSE;
+    }
+
+  return TRUE;
+}
+
+static gboolean
+winpointer_ensure_procedures ()
+{
+  static HMODULE user32_dll = NULL;
+
+  if (!user32_dll)
+    {
+      user32_dll = LoadLibraryW (L"user32.dll");
+      if (!user32_dll)
+        {
+          WIN32_API_FAILED ("LoadLibraryW");
+          return FALSE;
+        }
+
+      registerPointerDeviceNotifications = (registerPointerDeviceNotifications_t)
+        GetProcAddress (user32_dll, "RegisterPointerDeviceNotifications");
+      getPointerDevices = (getPointerDevices_t)
+        GetProcAddress (user32_dll, "GetPointerDevices");
+      getPointerDeviceCursors = (getPointerDeviceCursors_t)
+        GetProcAddress (user32_dll, "GetPointerDeviceCursors");
+      getPointerDeviceRects = (getPointerDeviceRects_t)
+        GetProcAddress (user32_dll, "GetPointerDeviceRects");
+      getPointerType = (getPointerType_t)
+        GetProcAddress (user32_dll, "GetPointerType");
+      getPointerCursorId = (getPointerCursorId_t)
+        GetProcAddress (user32_dll, "GetPointerCursorId");
+      getPointerPenInfo = (getPointerPenInfo_t)
+        GetProcAddress (user32_dll, "GetPointerPenInfo");
+      getPointerTouchInfo = (getPointerTouchInfo_t)
+        GetProcAddress (user32_dll, "GetPointerTouchInfo");
+      getPointerPenInfoHistory = (getPointerPenInfoHistory_t)
+        GetProcAddress (user32_dll, "GetPointerPenInfoHistory");
+      getPointerTouchInfoHistory = (getPointerTouchInfoHistory_t)
+        GetProcAddress (user32_dll, "GetPointerTouchInfoHistory");
+      setGestureConfig = (setGestureConfig_t)
+        GetProcAddress (user32_dll, "SetGestureConfig");
+      setWindowFeedbackSetting = (setWindowFeedbackSetting_t)
+        GetProcAddress (user32_dll, "SetWindowFeedbackSetting");
+    }
+
+  return registerPointerDeviceNotifications &&
+         getPointerDevices &&
+         getPointerDeviceCursors &&
+         getPointerDeviceRects &&
+         getPointerType &&
+         getPointerCursorId &&
+         getPointerPenInfo &&
+         getPointerTouchInfo &&
+         getPointerPenInfoHistory &&
+         getPointerTouchInfoHistory &&
+         setGestureConfig;
+}
+
+static gboolean
+winpointer_initialize (GdkDeviceManagerWin32 *device_manager)
+{
+  if (!winpointer_ensure_procedures ())
+    return FALSE;
+
+  if (!winpointer_notif_window_create ())
+    return FALSE;
+
+  /* associate device_manager with the window */
+  SetLastError (0);
+  if (SetWindowLongPtrW (winpointer_notif_window_handle,
+                         GWLP_USERDATA,
+                         (LONG_PTR) device_manager) == 0
+      && GetLastError () != 0)
+    {
+      WIN32_API_FAILED ("SetWindowLongPtrW");
+      return FALSE;
+    }
+
+  if (!registerPointerDeviceNotifications (winpointer_notif_window_handle, FALSE))
+    {
+      WIN32_API_FAILED ("RegisterPointerDeviceNotifications");
+      return FALSE;
+    }
+
+  winpointer_ignored_interactions = g_ptr_array_new ();
+
+  winpointer_enumerate_devices (device_manager);
+
+  return TRUE;
+}
+
+void
+gdk_winpointer_initialize_window (GdkWindow *window)
+{
+  HWND hwnd = GDK_WINDOW_HWND (window);
+  ATOM key = 0;
+  HANDLE val = (HANDLE)(TABLET_DISABLE_PRESSANDHOLD |
+                        TABLET_DISABLE_PENTAPFEEDBACK |
+                        TABLET_DISABLE_PENBARRELFEEDBACK |
+                        TABLET_DISABLE_FLICKS |
+                        TABLET_DISABLE_FLICKFALLBACKKEYS);
+
+  winpointer_ensure_procedures ();
+
+  key = GlobalAddAtom (MICROSOFT_TABLETPENSERVICE_PROPERTY);
+  API_CALL (SetPropW, (hwnd, (LPCWSTR) key, val));
+  GlobalDeleteAtom (key);
+
+  if (setGestureConfig != NULL)
+    {
+      GESTURECONFIG gesture_config;
+      memset (&gesture_config, 0, sizeof (gesture_config));
+
+      gesture_config.dwID = 0;
+      gesture_config.dwWant = 0;
+      gesture_config.dwBlock = GC_ALLGESTURES;
+
+      API_CALL (setGestureConfig, (hwnd, 0, 1, &gesture_config, sizeof (gesture_config)));
+    }
+
+  if (setWindowFeedbackSetting != NULL)
+    {
+      FEEDBACK_TYPE feedbacks[] = {
+        FEEDBACK_TOUCH_CONTACTVISUALIZATION,
+        FEEDBACK_PEN_BARRELVISUALIZATION,
+        FEEDBACK_PEN_TAP,
+        FEEDBACK_PEN_DOUBLETAP,
+        FEEDBACK_PEN_PRESSANDHOLD,
+        FEEDBACK_PEN_RIGHTTAP,
+        FEEDBACK_TOUCH_TAP,
+        FEEDBACK_TOUCH_DOUBLETAP,
+        FEEDBACK_TOUCH_PRESSANDHOLD,
+        FEEDBACK_TOUCH_RIGHTTAP,
+        FEEDBACK_GESTURE_PRESSANDTAP
+      };
+      gsize i = 0;
+
+      for (i = 0; i < G_N_ELEMENTS (feedbacks); i++)
+        {
+          BOOL setting = FALSE;
+
+          API_CALL (setWindowFeedbackSetting, (hwnd, feedbacks[i], 0, sizeof (BOOL), &setting));
+        }
+    }
+}
+
+void
+gdk_winpointer_finalize_window (GdkWindow *window)
+{
+  HWND hwnd = GDK_WINDOW_HWND (window);
+  ATOM key = 0;
+
+  key = GlobalAddAtom (MICROSOFT_TABLETPENSERVICE_PROPERTY);
+  RemovePropW (hwnd, (LPCWSTR) key);
+  GlobalDeleteAtom (key);
+}
+
 #if DEBUG_WINTAB
 
 static void
@@ -381,9 +1011,6 @@ wintab_init_check (GdkDeviceManagerWin32 *device_manager)
 
   wintab_contexts = NULL;
 
-  if (_gdk_input_ignore_wintab)
-    return;
-
   n = GetSystemDirectory (&dummy, 0);
 
   if (n <= 0)
@@ -791,6 +1418,8 @@ gdk_device_manager_win32_constructed (GObject *object)
   GdkSeat *seat;
   GdkDisplayManager *display_manager = NULL;
   GdkDisplay *default_display = NULL;
+  const char *tablet_input_api_user_preference = NULL;
+  gboolean have_tablet_input_api_preference = FALSE;
 
   device_manager = GDK_DEVICE_MANAGER_WIN32 (object);
   device_manager->core_pointer =
@@ -833,18 +1462,50 @@ gdk_device_manager_win32_constructed (GObject *object)
   gdk_seat_default_add_slave (GDK_SEAT_DEFAULT (seat), device_manager->system_keyboard);
   g_object_unref (seat);
 
-  /* Only call Wintab init stuff after the default display
-   * is globally known and accessible through the display manager
-   * singleton. Approach lifted from gtkmodules.c.
-   */
-  display_manager = gdk_display_manager_get();
-  g_assert (display_manager != NULL);
-  default_display = gdk_display_manager_get_default_display (display_manager);
-  g_assert (default_display == NULL);
+  tablet_input_api_user_preference = g_getenv ("GDK_WIN32_TABLET_INPUT_API");
+  if (g_strcmp0 (tablet_input_api_user_preference, "none") == 0)
+    {
+      have_tablet_input_api_preference = TRUE;
+      _gdk_win32_tablet_input_api = GDK_WIN32_TABLET_INPUT_API_NONE;
+    }
+  else if (g_strcmp0 (tablet_input_api_user_preference, "wintab") == 0)
+    {
+      have_tablet_input_api_preference = TRUE;
+      _gdk_win32_tablet_input_api = GDK_WIN32_TABLET_INPUT_API_WINTAB;
+    }
+  else if (g_strcmp0 (tablet_input_api_user_preference, "winpointer") == 0)
+    {
+      have_tablet_input_api_preference = TRUE;
+      _gdk_win32_tablet_input_api = GDK_WIN32_TABLET_INPUT_API_WINPOINTER;
+    }
+  else
+    {
+      have_tablet_input_api_preference = FALSE;
+      _gdk_win32_tablet_input_api = GDK_WIN32_TABLET_INPUT_API_WINPOINTER;
+    }
 
-  g_signal_connect (display_manager, "notify::default-display",
-                    G_CALLBACK (wintab_default_display_notify_cb),
-                    NULL);
+  if (_gdk_win32_tablet_input_api == GDK_WIN32_TABLET_INPUT_API_WINPOINTER)
+    {
+      if (!winpointer_initialize (device_manager) &&
+          !have_tablet_input_api_preference)
+        _gdk_win32_tablet_input_api = GDK_WIN32_TABLET_INPUT_API_WINTAB;
+    }
+
+  if (_gdk_win32_tablet_input_api == GDK_WIN32_TABLET_INPUT_API_WINTAB)
+    {
+      /* Only call Wintab init stuff after the default display
+       * is globally known and accessible through the display manager
+       * singleton. Approach lifted from gtkmodules.c.
+       */
+      display_manager = gdk_display_manager_get();
+      g_assert (display_manager != NULL);
+      default_display = gdk_display_manager_get_default_display (display_manager);
+      g_assert (default_display == NULL);
+
+      g_signal_connect (display_manager, "notify::default-display",
+                        G_CALLBACK (wintab_default_display_notify_cb),
+                        NULL);
+    }
 }
 
 static GList *
@@ -869,6 +1530,14 @@ gdk_device_manager_win32_list_devices (GdkDeviceManager *device_manager,
          devices = g_list_prepend (devices, device_manager_win32->system_pointer);
        }
 
+      for (l = device_manager_win32->winpointer_devices; l != NULL; l = l->next)
+        {
+          GdkDevice *device = l->data;
+
+          if (gdk_device_get_device_type (device) == type)
+            devices = g_list_prepend (devices, device);
+        }
+
       for (l = device_manager_win32->wintab_devices; l != NULL; l = l->next)
        {
          GdkDevice *device = l->data;
@@ -902,8 +1571,552 @@ gdk_device_manager_win32_class_init (GdkDeviceManagerWin32Class *klass)
   device_manager_class->get_client_pointer = gdk_device_manager_win32_get_client_pointer;
 }
 
+static inline void
+winpointer_ignore_interaction (UINT32 pointer_id)
+{
+  g_ptr_array_add (winpointer_ignored_interactions, GUINT_TO_POINTER (pointer_id));
+}
+
+static inline void
+winpointer_remove_ignored_interaction (UINT32 pointer_id)
+{
+  g_ptr_array_remove_fast (winpointer_ignored_interactions, GUINT_TO_POINTER (pointer_id));
+}
+
+static inline gboolean
+winpointer_should_ignore_interaction (UINT32 pointer_id)
+{
+  return g_ptr_array_find (winpointer_ignored_interactions, GUINT_TO_POINTER (pointer_id), NULL);
+}
+
+static inline guint32
+winpointer_get_time (MSG *msg, POINTER_INFO *info)
+{
+  return info->dwTime != 0 ? info->dwTime : msg->time;
+}
+
+static inline gboolean
+winpointer_is_eraser (POINTER_PEN_INFO *pen_info)
+{
+  return (pen_info->penFlags & (PEN_FLAG_INVERTED | PEN_FLAG_ERASER)) != 0;
+}
+
+static inline gboolean
+winpointer_should_filter_message (MSG *msg,
+                                  POINTER_INPUT_TYPE type)
+{
+  switch (type)
+    {
+    case PT_TOUCH:
+      return msg->message == WM_POINTERENTER ||
+             msg->message == WM_POINTERLEAVE;
+    break;
+    }
+
+  return FALSE;
+}
+
+static GdkDeviceWinpointer*
+winpointer_find_device_with_source (GdkDeviceManagerWin32 *device_manager,
+                                    HANDLE device_handle,
+                                    UINT32 cursor_id,
+                                    GdkInputSource input_source)
+{
+  GList *l;
+
+  for (l = device_manager->winpointer_devices; l; l = l->next)
+    {
+      GdkDeviceWinpointer *device = (GdkDeviceWinpointer*) l->data;
+
+      if (device->device_handle == device_handle &&
+          device->start_cursor_id <= cursor_id &&
+          device->end_cursor_id >= cursor_id &&
+          gdk_device_get_source (GDK_DEVICE (device)) == input_source)
+        return device;
+    }
+
+  return NULL;
+}
+
+static GdkEvent*
+winpointer_allocate_event (MSG *msg,
+                           POINTER_INFO *info)
+{
+  switch (info->pointerType)
+    {
+    case PT_PEN:
+      switch (msg->message)
+        {
+        case WM_POINTERENTER:
+          g_return_val_if_fail (IS_POINTER_NEW_WPARAM (msg->wParam), NULL);
+          return gdk_event_new (GDK_PROXIMITY_IN);
+        break;
+        case WM_POINTERLEAVE:
+          g_return_val_if_fail (!IS_POINTER_INRANGE_WPARAM (msg->wParam), NULL);
+          return gdk_event_new (GDK_PROXIMITY_OUT);
+        break;
+        case WM_POINTERDOWN:
+          return gdk_event_new (GDK_BUTTON_PRESS);
+        break;
+        case WM_POINTERUP:
+          return gdk_event_new (GDK_BUTTON_RELEASE);
+        break;
+        case WM_POINTERUPDATE:
+          return gdk_event_new (GDK_MOTION_NOTIFY);
+        break;
+        }
+    break;
+    case PT_TOUCH:
+      if (IS_POINTER_CANCELED_WPARAM (msg->wParam) ||
+          !HAS_POINTER_CONFIDENCE_WPARAM (msg->wParam))
+        {
+          winpointer_ignore_interaction (GET_POINTERID_WPARAM (msg->wParam));
+
+          if (((info->pointerFlags & POINTER_FLAG_INCONTACT) &&
+               (info->pointerFlags & POINTER_FLAG_UPDATE))
+              || (info->pointerFlags & POINTER_FLAG_UP))
+            return gdk_event_new (GDK_TOUCH_CANCEL);
+          else
+            return NULL;
+        }
+
+      g_return_val_if_fail (msg->message != WM_POINTERENTER &&
+                            msg->message != WM_POINTERLEAVE, NULL);
+
+      switch (msg->message)
+        {
+          case WM_POINTERDOWN:
+            return gdk_event_new (GDK_TOUCH_BEGIN);
+          break;
+          case WM_POINTERUP:
+            return gdk_event_new (GDK_TOUCH_END);
+          break;
+          case WM_POINTERUPDATE:
+            if (IS_POINTER_INCONTACT_WPARAM (msg->wParam))
+              return gdk_event_new (GDK_TOUCH_UPDATE);
+            else if (IS_POINTER_PRIMARY_WPARAM (msg->wParam))
+              return gdk_event_new (GDK_MOTION_NOTIFY);
+            else
+              return NULL;
+          break;
+        }
+    break;
+    }
+
+  g_warn_if_reached ();
+  return NULL;
+}
+
+static void
+winpointer_make_event (GdkDisplay *display,
+                       GdkDeviceManagerWin32 *device_manager,
+                       GdkDeviceWinpointer *device,
+                       GdkWindow *window,
+                       MSG *msg,
+                       POINTER_INFO *info)
+{
+  guint32 time = 0;
+  double x_root = 0.0;
+  double y_root = 0.0;
+  double x = 0.0;
+  double y = 0.0;
+  unsigned int state = 0;
+  double *axes = NULL;
+  unsigned int button = 0;
+  GdkEventSequence *sequence = NULL;
+  gboolean emulating_pointer = FALSE;
+  POINT client_area_coordinates;
+  GdkWindowImplWin32 *impl = NULL;
+  GdkEvent *evt = NULL;
+
+  evt = winpointer_allocate_event (msg, info);
+  if (!evt)
+    return;
+
+  time = winpointer_get_time (msg, info);
+
+  x_root = device->origin_x + info->ptHimetricLocation.x * device->scale_x;
+  y_root = device->origin_y + info->ptHimetricLocation.y * device->scale_y;
+
+  client_area_coordinates.x = 0;
+  client_area_coordinates.y = 0;
+  ClientToScreen (GDK_WINDOW_HWND (window), &client_area_coordinates);
+  x = x_root - client_area_coordinates.x;
+  y = y_root - client_area_coordinates.y;
+
+  /* bring potential win32 negative screen coordinates to
+     the non-negative screen coordinates that GDK expects. */
+  x_root += _gdk_offset_x;
+  y_root += _gdk_offset_y;
+
+  /* handle DPI scaling */
+  impl = GDK_WINDOW_IMPL_WIN32 (window->impl);
+  x_root /= impl->window_scale;
+  y_root /= impl->window_scale;
+  x /= impl->window_scale;
+  y /= impl->window_scale;
+
+  state = 0;
+  if (info->dwKeyStates & POINTER_MOD_CTRL)
+    state |= GDK_CONTROL_MASK;
+  if (info->dwKeyStates & POINTER_MOD_SHIFT)
+    state |= GDK_SHIFT_MASK;
+  if (GetKeyState (VK_MENU) < 0)
+    state |= GDK_MOD1_MASK;
+  if (GetKeyState (VK_CAPITAL) & 0x1)
+    state |= GDK_LOCK_MASK;
+
+  device->last_button_mask = 0;
+  if (((info->pointerFlags & POINTER_FLAG_FIRSTBUTTON) &&
+       (info->ButtonChangeType != POINTER_CHANGE_FIRSTBUTTON_DOWN))
+      || info->ButtonChangeType == POINTER_CHANGE_FIRSTBUTTON_UP)
+    device->last_button_mask |= GDK_BUTTON1_MASK;
+  if (((info->pointerFlags & POINTER_FLAG_SECONDBUTTON) &&
+       (info->ButtonChangeType != POINTER_CHANGE_SECONDBUTTON_DOWN))
+      || info->ButtonChangeType == POINTER_CHANGE_SECONDBUTTON_UP)
+    device->last_button_mask |= GDK_BUTTON3_MASK;
+  state |= device->last_button_mask;
+
+  switch (info->pointerType)
+    {
+    case PT_PEN:
+      {
+        POINTER_PEN_INFO *pen_info = (POINTER_PEN_INFO*) info;
+
+        axes = g_new (double, device->num_axes);
+        axes[0] = (pen_info->penMask & PEN_MASK_PRESSURE) ? pen_info->pressure / 1024.0 :
+                  (pen_info->pointerInfo.pointerFlags & POINTER_FLAG_INCONTACT) ? 1.0 : 0.0;
+        axes[1] = (pen_info->penMask & PEN_MASK_TILT_X) ? pen_info->tiltX / 90.0 : 0.0;
+        axes[2] = (pen_info->penMask & PEN_MASK_TILT_Y) ? pen_info->tiltY / 90.0 : 0.0;
+        axes[3] = (pen_info->penMask & PEN_MASK_ROTATION) ? pen_info->rotation / 360.0 : 0.0;
+      }
+    break;
+    case PT_TOUCH:
+      {
+        POINTER_TOUCH_INFO *touch_info = (POINTER_TOUCH_INFO*) info;
+
+        axes = g_new (double, device->num_axes);
+        axes[0] = (touch_info->touchMask & TOUCH_MASK_PRESSURE) ? touch_info->pressure / 1024.0 :
+                  (touch_info->pointerInfo.pointerFlags & POINTER_FLAG_INCONTACT) ? 1.0 : 0.0;
+      }
+    break;
+    }
+
+  if (axes)
+    {
+      memcpy (device->last_axis_data, axes, sizeof (double) * device->num_axes);
+    }
+
+  sequence = (GdkEventSequence*) GUINT_TO_POINTER (info->pointerId);
+  emulating_pointer = (info->pointerFlags & POINTER_FLAG_PRIMARY) != 0;
+  button = (info->pointerFlags & POINTER_FLAG_FIRSTBUTTON) ||
+           (info->ButtonChangeType == POINTER_CHANGE_FIRSTBUTTON_UP) ? 1 : 3;
+
+  switch (evt->any.type)
+    {
+    case GDK_PROXIMITY_IN:
+    case GDK_PROXIMITY_OUT:
+      evt->proximity.time = time;
+    break;
+    case GDK_BUTTON_PRESS:
+    case GDK_BUTTON_RELEASE:
+      evt->button.time = time;
+      evt->button.x_root = x_root;
+      evt->button.y_root = y_root;
+      evt->button.x = x;
+      evt->button.y = y;
+      evt->button.state = state;
+      evt->button.axes = axes;
+      evt->button.button = button;
+    break;
+    case GDK_MOTION_NOTIFY:
+      evt->motion.time = time;
+      evt->motion.x_root = x_root;
+      evt->motion.y_root = y_root;
+      evt->motion.x = x;
+      evt->motion.y = y;
+      evt->motion.state = state;
+      evt->motion.axes = axes;
+    break;
+    case GDK_TOUCH_BEGIN:
+    case GDK_TOUCH_UPDATE:
+    case GDK_TOUCH_CANCEL:
+    case GDK_TOUCH_END:
+      evt->touch.time = time;
+      evt->touch.x_root = x_root;
+      evt->touch.y_root = y_root;
+      evt->touch.x = x;
+      evt->touch.y = y;
+      evt->touch.state = state;
+      evt->touch.axes = axes;
+      evt->touch.sequence = sequence;
+      evt->touch.emulating_pointer = emulating_pointer;
+      gdk_event_set_pointer_emulated (evt, emulating_pointer);
+    break;
+    }
+
+  evt->any.send_event = FALSE;
+  evt->any.window = window;
+  gdk_event_set_device (evt, device_manager->core_pointer);
+  gdk_event_set_source_device (evt, GDK_DEVICE (device));
+  gdk_event_set_device_tool (evt, ((GdkDevice*)device)->last_tool);
+  gdk_event_set_seat (evt, gdk_device_get_seat (device_manager->core_pointer));
+  gdk_event_set_screen (evt, gdk_display_get_default_screen (display));
+
+  _gdk_device_virtual_set_active (device_manager->core_pointer, GDK_DEVICE (device));
+
+  _gdk_win32_append_event (evt);
+}
+
+void
+gdk_winpointer_input_events (GdkDisplay *display,
+                             GdkWindow *window,
+                             crossing_cb_t crossing_cb,
+                             MSG *msg)
+{
+  GdkDeviceManagerWin32 *device_manager = NULL;
+  UINT32 pointer_id = GET_POINTERID_WPARAM (msg->wParam);
+  POINTER_INPUT_TYPE type = PT_POINTER;
+  UINT32 cursor_id = 0;
+
+  G_GNUC_BEGIN_IGNORE_DEPRECATIONS;
+  device_manager = GDK_DEVICE_MANAGER_WIN32 (gdk_display_get_device_manager (display));
+  G_GNUC_END_IGNORE_DEPRECATIONS;
+
+  if (!getPointerType (pointer_id, &type))
+    {
+      WIN32_API_FAILED_LOG_ONCE ("GetPointerType");
+      return;
+    }
+
+  if (!getPointerCursorId (pointer_id, &cursor_id))
+    {
+      WIN32_API_FAILED_LOG_ONCE ("GetPointerCursorId");
+      return;
+    }
+
+  if (winpointer_should_filter_message (msg, type))
+    {
+      return;
+    }
+
+  if (winpointer_should_ignore_interaction (pointer_id))
+    {
+      return;
+    }
+
+  switch (type)
+    {
+    case PT_PEN:
+      {
+        POINTER_PEN_INFO *infos = NULL;
+        UINT32 history_count = 0;
+        GdkDeviceWinpointer *device = NULL;
+        UINT32 h = 0;
+
+        do
+          {
+            infos = g_new0 (POINTER_PEN_INFO, history_count);
+            if (!getPointerPenInfoHistory (pointer_id, &history_count, infos))
+              {
+                WIN32_API_FAILED_LOG_ONCE ("GetPointerPenInfoHistory");
+                g_free (infos);
+                return;
+              }
+          }
+        while (!infos && history_count > 0);
+
+        if (history_count == 0)
+          return;
+
+        device = winpointer_find_device_with_source (device_manager,
+                                                     infos->pointerInfo.sourceDevice,
+                                                     cursor_id,
+                                                     winpointer_is_eraser (infos) ?
+                                                     GDK_SOURCE_ERASER : GDK_SOURCE_PEN);
+        if (!device)
+          {
+            g_free (infos);
+            return;
+          }
+
+        h = history_count - 1;
+
+        if (crossing_cb)
+          {
+            POINT screen_pt = infos[h].pointerInfo.ptPixelLocation;
+            guint32 event_time = winpointer_get_time (msg, &infos[h].pointerInfo);
+
+            crossing_cb(display, GDK_DEVICE (device), window, &screen_pt, event_time);
+          }
+
+        do
+          winpointer_make_event (display,
+                                 device_manager,
+                                 device,
+                                 window,
+                                 msg,
+                                 (POINTER_INFO*) &infos[h]);
+        while (h-- > 0);
+
+        g_free (infos);
+      }
+    break;
+    case PT_TOUCH:
+      {
+        POINTER_TOUCH_INFO *infos = NULL;
+        UINT32 history_count = 0;
+        GdkDeviceWinpointer *device = NULL;
+        UINT32 h = 0;
+
+        do
+          {
+            infos = g_new0 (POINTER_TOUCH_INFO, history_count);
+            if (!getPointerTouchInfoHistory (pointer_id, &history_count, infos))
+              {
+                WIN32_API_FAILED_LOG_ONCE ("GetPointerTouchInfoHistory");
+                g_free (infos);
+                return;
+              }
+          }
+        while (!infos && history_count > 0);
+
+        if (history_count == 0)
+          return;
+
+        device = winpointer_find_device_with_source (device_manager,
+                                                     infos->pointerInfo.sourceDevice,
+                                                     cursor_id,
+                                                     GDK_SOURCE_TOUCHSCREEN);
+        if (!device)
+          {
+            g_free (infos);
+            return;
+          }
+
+        h = history_count - 1;
+
+        if (crossing_cb)
+          {
+            POINT screen_pt = infos[h].pointerInfo.ptPixelLocation;
+            guint32 event_time = winpointer_get_time (msg, &infos[h].pointerInfo);
+
+            crossing_cb(display, GDK_DEVICE (device), window, &screen_pt, event_time);
+          }
+
+        do
+          winpointer_make_event (display,
+                                 device_manager,
+                                 device,
+                                 window,
+                                 msg,
+                                 (POINTER_INFO*) &infos[h]);
+        while (h-- > 0);
+
+        g_free (infos);
+      }
+    break;
+    }
+}
+
+gboolean
+gdk_winpointer_get_message_info (GdkDisplay *display,
+                                 MSG *msg,
+                                 GdkDevice **device,
+                                 guint32 *time_)
+{
+  GdkDeviceManagerWin32 *device_manager = NULL;
+  UINT32 pointer_id = GET_POINTERID_WPARAM (msg->wParam);
+  POINTER_INPUT_TYPE type = PT_POINTER;
+  UINT32 cursor_id = 0;
+
+  G_GNUC_BEGIN_IGNORE_DEPRECATIONS;
+  device_manager = GDK_DEVICE_MANAGER_WIN32 (gdk_display_get_device_manager (display));
+  G_GNUC_END_IGNORE_DEPRECATIONS;
+
+  if (!getPointerType (pointer_id, &type))
+    {
+      WIN32_API_FAILED_LOG_ONCE ("GetPointerType");
+      return FALSE;
+    }
+
+  if (!getPointerCursorId (pointer_id, &cursor_id))
+    {
+      WIN32_API_FAILED_LOG_ONCE ("GetPointerCursorId");
+      return FALSE;
+    }
+
+  switch (type)
+    {
+    case PT_PEN:
+      {
+        POINTER_PEN_INFO pen_info;
+
+        if (!getPointerPenInfo (pointer_id, &pen_info))
+          {
+            WIN32_API_FAILED_LOG_ONCE ("GetPointerPenInfo");
+            return FALSE;
+          }
+
+        *device = GDK_DEVICE (winpointer_find_device_with_source (device_manager,
+                                                                  pen_info.pointerInfo.sourceDevice,
+                                                                  cursor_id,
+                                                                  winpointer_is_eraser (&pen_info) ?
+                                                                  GDK_SOURCE_ERASER : GDK_SOURCE_PEN));
+
+        *time_ = winpointer_get_time (msg, &pen_info.pointerInfo);
+      }
+    break;
+    case PT_TOUCH:
+      {
+        POINTER_TOUCH_INFO touch_info;
+
+        if (!getPointerTouchInfo (pointer_id, &touch_info))
+            {
+              WIN32_API_FAILED_LOG_ONCE ("GetPointerTouchInfo");
+              return FALSE;
+            }
+
+        *device = GDK_DEVICE (winpointer_find_device_with_source (device_manager,
+                                                                  touch_info.pointerInfo.sourceDevice,
+                                                                  cursor_id,
+                                                                  GDK_SOURCE_TOUCHSCREEN));
+
+        *time_ = winpointer_get_time (msg, &touch_info.pointerInfo);
+      }
+    break;
+    default:
+      g_warn_if_reached ();
+      return FALSE;
+    break;
+    }
+
+  return *device ? TRUE : FALSE;
+}
+
+gboolean
+gdk_winpointer_should_forward_message (MSG *msg)
+{
+  UINT32 pointer_id = GET_POINTERID_WPARAM (msg->wParam);
+  POINTER_INPUT_TYPE type = PT_POINTER;
+
+  if (!getPointerType (pointer_id, &type))
+    {
+      WIN32_API_FAILED_LOG_ONCE ("GetPointerType");
+      return TRUE;
+    }
+
+  return !(type == PT_PEN || type == PT_TOUCH);
+}
+
+void
+gdk_winpointer_interaction_ended (MSG *msg)
+{
+  winpointer_remove_ignored_interaction (GET_POINTERID_WPARAM (msg->wParam));
+}
+
 void
-_gdk_input_set_tablet_active (void)
+_gdk_wintab_set_tablet_active (void)
 {
   GList *tmp_list;
   HCTX *hctx;
@@ -914,7 +2127,7 @@ _gdk_input_set_tablet_active (void)
   if (!wintab_contexts)
     return; /* No tablet devices found, or Wintab not initialized yet */
 
-  GDK_NOTE (INPUT, g_print ("_gdk_input_set_tablet_active: "
+  GDK_NOTE (INPUT, g_print ("_gdk_wintab_set_tablet_active: "
                             "Bringing Wintab contexts to the top of the overlap order\n"));
 
   tmp_list = wintab_contexts;
@@ -1049,10 +2262,10 @@ gdk_device_manager_find_wintab_device (GdkDeviceManagerWin32 *device_manager,
 }
 
 gboolean
-gdk_input_other_event (GdkDisplay *display,
-                       GdkEvent   *event,
-                       MSG        *msg,
-                       GdkWindow  *window)
+gdk_wintab_input_events (GdkDisplay *display,
+                         GdkEvent   *event,
+                         MSG        *msg,
+                         GdkWindow  *window)
 {
   GdkDeviceManagerWin32 *device_manager;
   GdkDeviceWintab *source_device = NULL;
@@ -1074,7 +2287,7 @@ gdk_input_other_event (GdkDisplay *display,
 
   if (event->any.window != wintab_window)
     {
-      g_warning ("gdk_input_other_event: not wintab_window?");
+      g_warning ("gdk_wintab_input_events: not wintab_window?");
       return FALSE;
     }
 
@@ -1088,7 +2301,7 @@ G_GNUC_END_IGNORE_DEPRECATIONS;
   g_object_ref (window);
 
   GDK_NOTE (EVENTS_OR_INPUT,
-           g_print ("gdk_input_other_event: window=%p %+d%+d\n",
+           g_print ("gdk_wintab_input_events: window=%p %+d%+d\n",
                GDK_WINDOW_HWND (window), x, y));
 
   if (msg->message == WT_PACKET || msg->message == WT_CSRCHANGE)
diff --git a/gdk/win32/gdkdevicemanager-win32.h b/gdk/win32/gdkdevicemanager-win32.h
index 36a7cefb1f..435c77368b 100644
--- a/gdk/win32/gdkdevicemanager-win32.h
+++ b/gdk/win32/gdkdevicemanager-win32.h
@@ -41,6 +41,8 @@ struct _GdkDeviceManagerWin32
   /* Fake slave devices */
   GdkDevice *system_pointer;
   GdkDevice *system_keyboard;
+
+  GList *winpointer_devices;
   GList *wintab_devices;
 
   /* Bumped up every time a wintab device enters the proximity
@@ -57,11 +59,31 @@ struct _GdkDeviceManagerWin32Class
 
 GType gdk_device_manager_win32_get_type (void) G_GNUC_CONST;
 
-void     _gdk_input_set_tablet_active (void);
-gboolean gdk_input_other_event        (GdkDisplay *display,
-                                       GdkEvent   *event,
-                                       MSG        *msg,
-                                       GdkWindow  *window);
+typedef void
+(*crossing_cb_t)(GdkDisplay *display,
+                 GdkDevice *device,
+                 GdkWindow *window,
+                 POINT *screen_pt,
+                 guint32 time_);
+
+void     gdk_winpointer_initialize_window (GdkWindow *window);
+gboolean gdk_winpointer_should_forward_message (MSG *msg);
+void     gdk_winpointer_input_events (GdkDisplay *display,
+                                      GdkWindow *window,
+                                      crossing_cb_t crossing_cb,
+                                      MSG *msg);
+gboolean gdk_winpointer_get_message_info (GdkDisplay *display,
+                                          MSG *msg,
+                                          GdkDevice **device,
+                                          guint32 *time_);
+void     gdk_winpointer_interaction_ended (MSG *msg);
+void     gdk_winpointer_finalize_window (GdkWindow *window);
+
+void     _gdk_wintab_set_tablet_active (void);
+gboolean gdk_wintab_input_events       (GdkDisplay *display,
+                                        GdkEvent   *event,
+                                        MSG        *msg,
+                                        GdkWindow  *window);
 
 G_END_DECLS
 
diff --git a/gdk/win32/gdkevents-win32.c b/gdk/win32/gdkevents-win32.c
index e3c7f824ac..a7cc3e2394 100644
--- a/gdk/win32/gdkevents-win32.c
+++ b/gdk/win32/gdkevents-win32.c
@@ -54,6 +54,7 @@
 #include "gdkglcontext-win32.h"
 #include "gdkdevicemanager-win32.h"
 #include "gdkdeviceprivate.h"
+#include "gdkdevice-virtual.h"
 #include "gdkdevice-wintab.h"
 #include "gdkwin32dnd.h"
 #include "gdkdisplay-win32.h"
@@ -61,6 +62,8 @@
 #include "gdkdndprivate.h"
 
 #include <windowsx.h>
+#include <tpcshrd.h>
+#include "winpointer.h"
 
 #ifdef G_WITH_CYGWIN
 #include <fcntl.h>
@@ -153,6 +156,10 @@ static int both_shift_pressed[2]; /* to store keycodes for shift keys */
 static HHOOK keyboard_hook = NULL;
 static UINT aerosnap_message;
 
+static gboolean pen_touch_input;
+static POINT pen_touch_cursor_position;
+static LONG last_digitizer_time;
+
 static void
 track_mouse_event (DWORD dwFlags,
                   HWND  hwnd)
@@ -187,6 +194,18 @@ _gdk_win32_get_next_tick (gulong suggested_tick)
     return cur_tick = suggested_tick;
 }
 
+BOOL
+_gdk_win32_get_cursor_pos (LPPOINT lpPoint)
+{
+  if (pen_touch_input)
+    {
+      *lpPoint = pen_touch_cursor_position;
+      return TRUE;
+    }
+  else
+    return GetCursorPos (lpPoint);
+}
+
 static void
 generate_focus_event (GdkDeviceManager *device_manager,
                       GdkWindow        *window,
@@ -228,6 +247,7 @@ generate_grab_broken_event (GdkDeviceManager *device_manager,
     {
       device = GDK_DEVICE_MANAGER_WIN32 (device_manager)->core_pointer;
       source_device = GDK_DEVICE_MANAGER_WIN32 (device_manager)->system_pointer;
+      _gdk_device_virtual_set_active (device, source_device);
     }
 
   event->grab_broken.window = window;
@@ -1237,6 +1257,7 @@ do_show_window (GdkWindow *window, gboolean hide_window)
 
 static void
 send_crossing_event (GdkDisplay                 *display,
+                     GdkDevice                  *source_device,
                     GdkWindow                  *window,
                     GdkEventType                type,
                     GdkCrossingMode             mode,
@@ -1271,7 +1292,7 @@ send_crossing_event (GdkDisplay                 *display,
   event = gdk_event_new (type);
   event->crossing.window = window;
   event->crossing.subwindow = subwindow;
-  event->crossing.time = _gdk_win32_get_next_tick (time_);
+  event->crossing.time = time_;
   event->crossing.x = pt.x / impl->window_scale;
   event->crossing.y = pt.y / impl->window_scale;
   event->crossing.x_root = (screen_pt->x + _gdk_offset_x) / impl->window_scale;
@@ -1283,9 +1304,11 @@ send_crossing_event (GdkDisplay                 *display,
   event->crossing.focus = FALSE;
   event->crossing.state = mask;
   gdk_event_set_device (event, device_manager->core_pointer);
-  gdk_event_set_source_device (event, device_manager->system_pointer);
+  gdk_event_set_source_device (event, source_device);
   gdk_event_set_seat (event, gdk_device_get_seat (device_manager->core_pointer));
 
+  _gdk_device_virtual_set_active (device_manager->core_pointer, source_device);
+
   _gdk_win32_append_event (event);
 }
 
@@ -1336,6 +1359,7 @@ find_common_ancestor (GdkWindow *win1,
 
 void
 synthesize_crossing_events (GdkDisplay                 *display,
+                            GdkDevice                  *source_device,
                            GdkWindow                  *src,
                            GdkWindow                  *dest,
                            GdkCrossingMode             mode,
@@ -1370,6 +1394,7 @@ synthesize_crossing_events (GdkDisplay                 *display,
       else
        notify_type = GDK_NOTIFY_ANCESTOR;
       send_crossing_event (display,
+                           source_device,
                           a, GDK_LEAVE_NOTIFY,
                           mode,
                           notify_type,
@@ -1389,6 +1414,7 @@ synthesize_crossing_events (GdkDisplay                 *display,
          while (win != c && win->window_type != GDK_WINDOW_ROOT)
            {
              send_crossing_event (display,
+                                   source_device,
                                   win, GDK_LEAVE_NOTIFY,
                                   mode,
                                   notify_type,
@@ -1431,6 +1457,7 @@ synthesize_crossing_events (GdkDisplay                 *display,
                next = b;
 
              send_crossing_event (display,
+                                   source_device,
                                   win, GDK_ENTER_NOTIFY,
                                   mode,
                                   notify_type,
@@ -1450,6 +1477,7 @@ synthesize_crossing_events (GdkDisplay                 *display,
        notify_type = GDK_NOTIFY_INFERIOR;
 
       send_crossing_event (display,
+                           source_device,
                           b, GDK_ENTER_NOTIFY,
                           mode,
                           notify_type,
@@ -1459,6 +1487,27 @@ synthesize_crossing_events (GdkDisplay                 *display,
     }
 }
 
+static void
+make_crossing_event (GdkDisplay *display,
+                     GdkDevice *device,
+                     GdkWindow *window,
+                     POINT *screen_pt,
+                     guint32 time_)
+{
+  GDK_NOTE (EVENTS, g_print (" mouse_window %p -> %p",
+                             mouse_window ? GDK_WINDOW_HWND (mouse_window) : NULL,
+                             window ? GDK_WINDOW_HWND (window) : NULL));
+  synthesize_crossing_events (display,
+                              device,
+                              mouse_window, window,
+                              GDK_CROSSING_NORMAL,
+                              screen_pt,
+                              0, /* TODO: Set right mask */
+                              time_,
+                              FALSE);
+  g_set_object (&mouse_window, window);
+}
+
 /* The check_extended flag controls whether to check if the windows want
  * events from extended input devices and if the message should be skipped
  * because an extended input device is active
@@ -1861,6 +1910,8 @@ generate_button_event (GdkEventType      type,
   gdk_event_set_source_device (event, device_manager->system_pointer);
   gdk_event_set_seat (event, gdk_device_get_seat (device_manager->core_pointer));
 
+  _gdk_device_virtual_set_active (device_manager->core_pointer, device_manager->system_pointer);
+
   _gdk_win32_append_event (event);
 }
 
@@ -2104,6 +2155,8 @@ gdk_event_translate (MSG  *msg,
   GdkDeviceGrabInfo *pointer_grab = NULL;
   GdkWindow *grab_window = NULL;
 
+  crossing_cb_t crossing_cb = NULL;
+
   gint button;
   GdkAtom target;
 
@@ -2600,6 +2653,8 @@ gdk_event_translate (MSG  *msg,
                g_print (" (%d,%d)",
                         GET_X_LPARAM (msg->lParam), GET_Y_LPARAM (msg->lParam)));
 
+      pen_touch_input = FALSE;
+
       g_set_object (&window, find_window_for_mouse_event (window, msg));
       /* TODO_CSW?: there used to some synthesize and propagate */
       if (GDK_WINDOW_DESTROYED (window))
@@ -2639,6 +2694,8 @@ gdk_event_translate (MSG  *msg,
                g_print (" (%d,%d)",
                         GET_X_LPARAM (msg->lParam), GET_Y_LPARAM (msg->lParam)));
 
+      pen_touch_input = FALSE;
+
       g_set_object (&window, find_window_for_mouse_event (window, msg));
 
       if (pointer_grab != NULL && pointer_grab->implicit)
@@ -2665,11 +2722,12 @@ gdk_event_translate (MSG  *msg,
                }
 
              synthesize_crossing_events (display,
+                                          device_manager_win32->system_pointer,
                                          native_window, new_window,
                                          GDK_CROSSING_UNGRAB,
                                          &msg->pt,
                                          0, /* TODO: Set right mask */
-                                         msg->time,
+                                         _gdk_win32_get_next_tick (msg->time),
                                          FALSE);
              g_set_object (&mouse_window, new_window);
              mouse_window_ignored_leave = NULL;
@@ -2695,6 +2753,13 @@ gdk_event_translate (MSG  *msg,
                         (gpointer) msg->wParam,
                         GET_X_LPARAM (msg->lParam), GET_Y_LPARAM (msg->lParam)));
 
+      if (_gdk_win32_tablet_input_api == GDK_WIN32_TABLET_INPUT_API_WINPOINTER &&
+          ( (msg->time - last_digitizer_time) < 200 ||
+           -(msg->time - last_digitizer_time) < 200 ))
+        break;
+
+      pen_touch_input = FALSE;
+
       new_window = window;
 
       if (pointer_grab != NULL)
@@ -2722,15 +2787,16 @@ gdk_event_translate (MSG  *msg,
 
       if (mouse_window != new_window)
        {
-         GDK_NOTE (EVENTS, g_print (" mouse_sinwod %p -> %p",
+         GDK_NOTE (EVENTS, g_print (" mouse_window %p -> %p",
                                     mouse_window ? GDK_WINDOW_HWND (mouse_window) : NULL,
                                     new_window ? GDK_WINDOW_HWND (new_window) : NULL));
          synthesize_crossing_events (display,
+                                      device_manager_win32->system_pointer,
                                      mouse_window, new_window,
                                      GDK_CROSSING_NORMAL,
                                      &msg->pt,
                                      0, /* TODO: Set right mask */
-                                     msg->time,
+                                     _gdk_win32_get_next_tick (msg->time),
                                      FALSE);
          g_set_object (&mouse_window, new_window);
          mouse_window_ignored_leave = NULL;
@@ -2782,6 +2848,8 @@ gdk_event_translate (MSG  *msg,
          gdk_event_set_source_device (event, device_manager_win32->system_pointer);
           gdk_event_set_seat (event, gdk_device_get_seat (device_manager_win32->core_pointer));
 
+          _gdk_device_virtual_set_active (device_manager_win32->core_pointer, 
device_manager_win32->system_pointer);
+
          _gdk_win32_append_event (event);
        }
 
@@ -2792,12 +2860,17 @@ gdk_event_translate (MSG  *msg,
       GDK_NOTE (EVENTS,
                g_print (" (%d,%d)",
                         GET_X_LPARAM (msg->lParam), GET_Y_LPARAM (msg->lParam)));
+
+      pen_touch_input = FALSE;
+
       break;
 
     case WM_MOUSELEAVE:
       GDK_NOTE (EVENTS, g_print (" %d (%ld,%ld)",
                                 HIWORD (msg->wParam), msg->pt.x, msg->pt.y));
 
+      pen_touch_input = FALSE;
+
       new_window = NULL;
       hwnd = WindowFromPoint (msg->pt);
       ignore_leave = FALSE;
@@ -2823,11 +2896,12 @@ gdk_event_translate (MSG  *msg,
 
       if (!ignore_leave)
        synthesize_crossing_events (display,
+                                    device_manager_win32->system_pointer,
                                    mouse_window, new_window,
                                    GDK_CROSSING_NORMAL,
                                    &msg->pt,
                                    0, /* TODO: Set right mask */
-                                   msg->time,
+                                   _gdk_win32_get_next_tick (msg->time),
                                    FALSE);
       g_set_object (&mouse_window, new_window);
       mouse_window_ignored_leave = ignore_leave ? new_window : NULL;
@@ -2836,6 +2910,215 @@ gdk_event_translate (MSG  *msg,
       return_val = TRUE;
       break;
 
+    case WM_POINTERDOWN:
+      if (_gdk_win32_tablet_input_api != GDK_WIN32_TABLET_INPUT_API_WINPOINTER ||
+          gdk_winpointer_should_forward_message (msg))
+        {
+          return_val = FALSE;
+          break;
+        }
+
+      if (IS_POINTER_PRIMARY_WPARAM (msg->wParam))
+        {
+          current_root_x = pen_touch_cursor_position.x = GET_X_LPARAM (msg->lParam);
+          current_root_y = pen_touch_cursor_position.y = GET_Y_LPARAM (msg->lParam);
+          pen_touch_input = TRUE;
+          last_digitizer_time = msg->time;
+        }
+
+      if (pointer_grab != NULL &&
+          !pointer_grab->implicit &&
+          !pointer_grab->owner_events)
+        g_set_object (&window, pointer_grab->native_window);
+
+      if (IS_POINTER_PRIMARY_WPARAM (msg->wParam) && mouse_window != window)
+        crossing_cb = make_crossing_event;
+
+      gdk_winpointer_input_events (display, window, crossing_cb, msg);
+
+      *ret_valp = 0;
+      return_val = TRUE;
+      break;
+
+    case WM_POINTERUP:
+      if (_gdk_win32_tablet_input_api != GDK_WIN32_TABLET_INPUT_API_WINPOINTER ||
+          gdk_winpointer_should_forward_message (msg))
+        {
+          return_val = FALSE;
+          break;
+        }
+
+      if (IS_POINTER_PRIMARY_WPARAM (msg->wParam))
+        {
+          current_root_x = pen_touch_cursor_position.x = GET_X_LPARAM (msg->lParam);
+          current_root_y = pen_touch_cursor_position.y = GET_Y_LPARAM (msg->lParam);
+          pen_touch_input = TRUE;
+          last_digitizer_time = msg->time;
+        }
+
+      if (pointer_grab != NULL &&
+          !pointer_grab->implicit &&
+          !pointer_grab->owner_events)
+        g_set_object (&window, pointer_grab->native_window);
+
+      gdk_winpointer_input_events (display, window, NULL, msg);
+
+      impl = GDK_WINDOW_IMPL_WIN32 (window->impl);
+      if (impl->drag_move_resize_context.op != GDK_WIN32_DRAGOP_NONE)
+        {
+          gdk_win32_window_end_move_resize_drag (window);
+        }
+
+      *ret_valp = 0;
+      return_val = TRUE;
+      break;
+
+    case WM_POINTERUPDATE:
+      if (_gdk_win32_tablet_input_api != GDK_WIN32_TABLET_INPUT_API_WINPOINTER ||
+          gdk_winpointer_should_forward_message (msg))
+        {
+          return_val = FALSE;
+          break;
+        }
+
+      if (IS_POINTER_PRIMARY_WPARAM (msg->wParam))
+        {
+          current_root_x = pen_touch_cursor_position.x = GET_X_LPARAM (msg->lParam);
+          current_root_y = pen_touch_cursor_position.y = GET_Y_LPARAM (msg->lParam);
+          pen_touch_input = TRUE;
+          last_digitizer_time = msg->time;
+        }
+
+      if (pointer_grab != NULL &&
+          !pointer_grab->implicit &&
+          !pointer_grab->owner_events)
+        g_set_object (&window, pointer_grab->native_window);
+
+      if (IS_POINTER_PRIMARY_WPARAM (msg->wParam) && mouse_window != window)
+        crossing_cb = make_crossing_event;
+
+      impl = GDK_WINDOW_IMPL_WIN32 (window->impl);
+
+      if (impl->drag_move_resize_context.op != GDK_WIN32_DRAGOP_NONE)
+        {
+          gdk_win32_window_do_move_resize_drag (window, current_root_x, current_root_y);
+        }
+      else
+        {
+          gdk_winpointer_input_events (display, window, crossing_cb, msg);
+        }
+
+      *ret_valp = 0;
+      return_val = TRUE;
+      break;
+
+    case WM_NCPOINTERUPDATE:
+      if (_gdk_win32_tablet_input_api != GDK_WIN32_TABLET_INPUT_API_WINPOINTER ||
+          gdk_winpointer_should_forward_message (msg))
+        {
+          return_val = FALSE;
+          break;
+        }
+
+      if (IS_POINTER_PRIMARY_WPARAM (msg->wParam))
+        {
+          current_root_x = pen_touch_cursor_position.x = GET_X_LPARAM (msg->lParam);
+          current_root_y = pen_touch_cursor_position.y = GET_Y_LPARAM (msg->lParam);
+          pen_touch_input = TRUE;
+          last_digitizer_time = msg->time;
+        }
+
+      if (IS_POINTER_PRIMARY_WPARAM (msg->wParam) &&
+          !IS_POINTER_INCONTACT_WPARAM (msg->wParam) &&
+          mouse_window != NULL)
+        {
+          GdkDevice *event_device = NULL;
+          guint32 event_time = 0;
+
+          if (gdk_winpointer_get_message_info (display, msg, &event_device, &event_time))
+            {
+              make_crossing_event(display,
+                                  event_device,
+                                  NULL,
+                                  &pen_touch_cursor_position,
+                                  event_time);
+            }
+        }
+
+      return_val = FALSE; /* forward to DefWindowProc */
+      break;
+
+    case WM_POINTERENTER:
+      if (_gdk_win32_tablet_input_api != GDK_WIN32_TABLET_INPUT_API_WINPOINTER ||
+          gdk_winpointer_should_forward_message (msg))
+        {
+          return_val = FALSE;
+          break;
+        }
+
+      if (IS_POINTER_PRIMARY_WPARAM (msg->wParam))
+        {
+          current_root_x = pen_touch_cursor_position.x = GET_X_LPARAM (msg->lParam);
+          current_root_y = pen_touch_cursor_position.y = GET_Y_LPARAM (msg->lParam);
+          pen_touch_input = TRUE;
+          last_digitizer_time = msg->time;
+        }
+
+      if (pointer_grab != NULL &&
+          !pointer_grab->implicit &&
+          !pointer_grab->owner_events)
+        g_set_object (&window, pointer_grab->native_window);
+
+      if (IS_POINTER_NEW_WPARAM (msg->wParam))
+        {
+          gdk_winpointer_input_events (display, window, NULL, msg);
+        }
+
+      *ret_valp = 0;
+      return_val = TRUE;
+      break;
+
+    case WM_POINTERLEAVE:
+      if (_gdk_win32_tablet_input_api != GDK_WIN32_TABLET_INPUT_API_WINPOINTER ||
+          gdk_winpointer_should_forward_message (msg))
+        {
+          return_val = FALSE;
+          break;
+        }
+
+      if (IS_POINTER_PRIMARY_WPARAM (msg->wParam))
+        {
+          current_root_x = pen_touch_cursor_position.x = GET_X_LPARAM (msg->lParam);
+          current_root_y = pen_touch_cursor_position.y = GET_Y_LPARAM (msg->lParam);
+          pen_touch_input = TRUE;
+          last_digitizer_time = msg->time;
+        }
+
+      if (!IS_POINTER_INRANGE_WPARAM (msg->wParam))
+        {
+          gdk_winpointer_input_events (display, window, NULL, msg);
+        }
+      else if (IS_POINTER_PRIMARY_WPARAM (msg->wParam) && mouse_window != NULL)
+        {
+          GdkDevice *event_device = NULL;
+          guint32 event_time = 0;
+
+          if (gdk_winpointer_get_message_info (display, msg, &event_device, &event_time))
+            {
+              make_crossing_event(display,
+                                  event_device,
+                                  NULL,
+                                  &pen_touch_cursor_position,
+                                  event_time);
+            }
+        }
+
+      gdk_winpointer_interaction_ended (msg);
+
+      *ret_valp = 0;
+      return_val = TRUE;
+      break;
+
     case WM_MOUSEWHEEL:
     case WM_MOUSEHWHEEL:
       GDK_NOTE (EVENTS, g_print (" %d", (short) HIWORD (msg->wParam)));
@@ -2914,6 +3197,8 @@ gdk_event_translate (MSG  *msg,
       gdk_event_set_seat (event, gdk_device_get_seat (device_manager_win32->core_pointer));
       gdk_event_set_pointer_emulated (event, FALSE);
 
+      _gdk_device_virtual_set_active (device_manager_win32->core_pointer, 
device_manager_win32->system_pointer);
+
       _gdk_win32_append_event (gdk_event_copy (event));
 
       /* Append the discrete version too */
@@ -2932,44 +3217,6 @@ gdk_event_translate (MSG  *msg,
       return_val = TRUE;
       break;
 
-    case WM_HSCROLL:
-      /* Just print more debugging information, don't actually handle it. */
-      GDK_NOTE (EVENTS,
-               (g_print (" %s",
-                         (LOWORD (msg->wParam) == SB_ENDSCROLL ? "ENDSCROLL" :
-                          (LOWORD (msg->wParam) == SB_LEFT ? "LEFT" :
-                           (LOWORD (msg->wParam) == SB_RIGHT ? "RIGHT" :
-                            (LOWORD (msg->wParam) == SB_LINELEFT ? "LINELEFT" :
-                             (LOWORD (msg->wParam) == SB_LINERIGHT ? "LINERIGHT" :
-                              (LOWORD (msg->wParam) == SB_PAGELEFT ? "PAGELEFT" :
-                               (LOWORD (msg->wParam) == SB_PAGERIGHT ? "PAGERIGHT" :
-                                (LOWORD (msg->wParam) == SB_THUMBPOSITION ? "THUMBPOSITION" :
-                                 (LOWORD (msg->wParam) == SB_THUMBTRACK ? "THUMBTRACK" :
-                                  "???")))))))))),
-                (LOWORD (msg->wParam) == SB_THUMBPOSITION ||
-                 LOWORD (msg->wParam) == SB_THUMBTRACK) ?
-                (g_print (" %d", HIWORD (msg->wParam)), 0) : 0));
-      break;
-
-    case WM_VSCROLL:
-      /* Just print more debugging information, don't actually handle it. */
-      GDK_NOTE (EVENTS,
-               (g_print (" %s",
-                         (LOWORD (msg->wParam) == SB_ENDSCROLL ? "ENDSCROLL" :
-                          (LOWORD (msg->wParam) == SB_BOTTOM ? "BOTTOM" :
-                           (LOWORD (msg->wParam) == SB_TOP ? "TOP" :
-                            (LOWORD (msg->wParam) == SB_LINEDOWN ? "LINDOWN" :
-                             (LOWORD (msg->wParam) == SB_LINEUP ? "LINEUP" :
-                              (LOWORD (msg->wParam) == SB_PAGEDOWN ? "PAGEDOWN" :
-                               (LOWORD (msg->wParam) == SB_PAGEUP ? "PAGEUP" :
-                                (LOWORD (msg->wParam) == SB_THUMBPOSITION ? "THUMBPOSITION" :
-                                 (LOWORD (msg->wParam) == SB_THUMBTRACK ? "THUMBTRACK" :
-                                  "???")))))))))),
-                (LOWORD (msg->wParam) == SB_THUMBPOSITION ||
-                 LOWORD (msg->wParam) == SB_THUMBTRACK) ?
-                (g_print (" %d", HIWORD (msg->wParam)), 0) : 0));
-      break;
-
      case WM_MOUSEACTIVATE:
        {
         if (gdk_window_get_window_type (window) == GDK_WINDOW_TEMP
@@ -2988,6 +3235,17 @@ gdk_event_translate (MSG  *msg,
 
        break;
 
+    case WM_POINTERACTIVATE:
+      if (gdk_window_get_window_type (window) == GDK_WINDOW_TEMP ||
+          !window->accept_focus ||
+          _gdk_modal_blocked (gdk_window_get_toplevel (window)))
+        {
+          *ret_valp = PA_NOACTIVATE;
+          return_val = TRUE;
+        }
+
+      break;
+
     case WM_KILLFOCUS:
       if (keyboard_grab != NULL &&
          !GDK_WINDOW_DESTROYED (keyboard_grab->window) &&
@@ -3566,6 +3824,14 @@ gdk_event_translate (MSG  *msg,
       return_val = TRUE;
       break;
 
+    case WM_DESTROY:
+      /* we have to call RemoveProp before the window is destroyed */
+      if (_gdk_win32_tablet_input_api == GDK_WIN32_TABLET_INPUT_API_WINPOINTER)
+        gdk_winpointer_finalize_window (window);
+
+      return_val = FALSE;
+      break;
+
     case WM_NCDESTROY:
       if ((pointer_grab != NULL && pointer_grab -> window == window) ||
           (keyboard_grab && keyboard_grab -> window == window))
@@ -3735,7 +4001,10 @@ gdk_event_translate (MSG  *msg,
        * instead
        */
       if (LOWORD(msg->wParam) != WA_INACTIVE)
-       _gdk_input_set_tablet_active ();
+        {
+          if (_gdk_win32_tablet_input_api == GDK_WIN32_TABLET_INPUT_API_WINTAB)
+            _gdk_wintab_set_tablet_active ();
+        }
       break;
 
     case WM_ACTIVATEAPP:
@@ -3775,17 +4044,30 @@ gdk_event_translate (MSG  *msg,
                                 HIWORD (msg->lParam)));
       /* Fall through */
     wintab:
+      if (_gdk_win32_tablet_input_api != GDK_WIN32_TABLET_INPUT_API_WINTAB)
+        break;
 
       event = gdk_event_new (GDK_NOTHING);
       event->any.window = window;
       g_object_ref (window);
 
-      if (gdk_input_other_event (display, event, msg, window))
+      if (gdk_wintab_input_events (display, event, msg, window))
        _gdk_win32_append_event (event);
       else
        gdk_event_free (event);
 
       break;
+
+    case WM_TABLET_QUERYSYSTEMGESTURESTATUS:
+
+      *ret_valp = TABLET_DISABLE_PRESSANDHOLD |
+                  TABLET_DISABLE_PENTAPFEEDBACK |
+                  TABLET_DISABLE_PENBARRELFEEDBACK |
+                  TABLET_DISABLE_FLICKS |
+                  TABLET_DISABLE_FLICKFALLBACKKEYS;
+
+      return_val = TRUE;
+      break;
     }
 
 done:
diff --git a/gdk/win32/gdkglobals-win32.c b/gdk/win32/gdkglobals-win32.c
index 78a43782f3..59af6168f4 100644
--- a/gdk/win32/gdkglobals-win32.c
+++ b/gdk/win32/gdkglobals-win32.c
@@ -36,12 +36,12 @@ HINSTANCE     _gdk_dll_hinstance;
 HINSTANCE        _gdk_app_hmodule;
 
 gint             _gdk_input_ignore_core;
+GdkWin32TabletInputAPI _gdk_win32_tablet_input_api;
 
 HKL              _gdk_input_locale;
 gboolean         _gdk_input_locale_is_ime = FALSE;
 UINT             _gdk_input_codepage;
 
-gint             _gdk_input_ignore_wintab = FALSE;
 gint             _gdk_max_colors = 0;
 
 GdkWin32ModalOpKind      _modal_operation_in_progress = GDK_WIN32_MODAL_OP_NONE;
diff --git a/gdk/win32/gdkmain-win32.c b/gdk/win32/gdkmain-win32.c
index 3c03e5f5db..23f76b4e25 100644
--- a/gdk/win32/gdkmain-win32.c
+++ b/gdk/win32/gdkmain-win32.c
@@ -49,23 +49,6 @@
 
 static gboolean gdk_synchronize = FALSE;
 
-static gboolean dummy;
-
-const GOptionEntry _gdk_windowing_args[] = {
-  { "sync", 0, 0, G_OPTION_ARG_NONE, &gdk_synchronize,
-    /* Description of --sync in --help output */              N_("Don't batch GDI requests"), NULL },
-  { "no-wintab", 0, 0, G_OPTION_ARG_NONE, &_gdk_input_ignore_wintab,
-    /* Description of --no-wintab in --help output */         N_("Don't use the Wintab API for tablet 
support"), NULL },
-  { "ignore-wintab", 0, 0, G_OPTION_ARG_NONE, &_gdk_input_ignore_wintab,
-    /* Description of --ignore-wintab in --help output */     N_("Same as --no-wintab"), NULL },
-  { "use-wintab", 0, 0, G_OPTION_ARG_NONE, &dummy,
-    /* Description of --use-wintab in --help output */     N_("Do use the Wintab API [default]"), NULL },
-  { "max-colors", 0, 0, G_OPTION_ARG_INT, &_gdk_max_colors,
-    /* Description of --max-colors=COLORS in --help output */ N_("Size of the palette in 8 bit mode"),
-    /* Placeholder in --max-colors=COLORS in --help output */ N_("COLORS") },
-  { NULL }
-};
-
 BOOL WINAPI
 DllMain (HINSTANCE hinstDLL,
         DWORD     dwReason,
@@ -81,11 +64,6 @@ _gdk_win32_windowing_init (void)
 {
   gchar buf[10];
 
-  if (getenv ("GDK_IGNORE_WINTAB") != NULL)
-    _gdk_input_ignore_wintab = TRUE;
-  else if (getenv ("GDK_USE_WINTAB") != NULL)
-    _gdk_input_ignore_wintab = FALSE;
-
   if (gdk_synchronize)
     GdiSetBatchLimit (1);
 
diff --git a/gdk/win32/gdkprivate-win32.h b/gdk/win32/gdkprivate-win32.h
index 99616e170d..c9c6a98249 100644
--- a/gdk/win32/gdkprivate-win32.h
+++ b/gdk/win32/gdkprivate-win32.h
@@ -163,10 +163,18 @@ struct _GdkColormapPrivateWin32
   GdkColorInfo *info;
 };
 
+typedef enum {
+  GDK_WIN32_TABLET_INPUT_API_NONE = 0,
+  GDK_WIN32_TABLET_INPUT_API_WINTAB,
+  GDK_WIN32_TABLET_INPUT_API_WINPOINTER
+} GdkWin32TabletInputAPI;
+
 GType _gdk_gc_win32_get_type (void);
 
 gulong _gdk_win32_get_next_tick (gulong suggested_tick);
 
+BOOL _gdk_win32_get_cursor_pos (LPPOINT lpPoint);
+
 void _gdk_window_init_position     (GdkWindow *window);
 void _gdk_window_move_resize_child (GdkWindow *window,
                                    gint       x,
@@ -262,6 +270,8 @@ void    _gdk_other_api_failed        (const gchar *where,
 #define WIN32_GDI_FAILED(api) WIN32_API_FAILED (api)
 #define OTHER_API_FAILED(api) _gdk_other_api_failed (G_STRLOC, api)
 
+#define WIN32_API_FAILED_LOG_ONCE(api) G_STMT_START { static gboolean logged = 0; if (!logged) { 
_gdk_win32_api_failed (G_STRLOC , api); logged = 1; }} G_STMT_END
+
 /* These two macros call a GDI or other Win32 API and if the return
  * value is zero or NULL, print a warning message. The majority of GDI
  * calls return zero or NULL on failure. The value of the macros is nonzero
@@ -287,6 +297,7 @@ extern HINSTANCE     _gdk_dll_hinstance;
 extern HINSTANCE        _gdk_app_hmodule;
 
 extern gint             _gdk_input_ignore_core;
+extern GdkWin32TabletInputAPI _gdk_win32_tablet_input_api;
 
 /* These are thread specific, but GDK/win32 works OK only when invoked
  * from a single thread anyway.
@@ -326,7 +337,6 @@ void  _gdk_win32_end_modal_call (GdkWin32ModalOpKind kind);
 
 
 /* Options */
-extern gboolean                 _gdk_input_ignore_wintab;
 extern gint             _gdk_max_colors;
 
 #define GDK_WIN32_COLORMAP_DATA(cmap) ((GdkColormapPrivateWin32 *) GDK_COLORMAP (cmap)->windowing_data)
diff --git a/gdk/win32/gdkwindow-win32.c b/gdk/win32/gdkwindow-win32.c
index 7fe3e98af9..2bccf59736 100644
--- a/gdk/win32/gdkwindow-win32.c
+++ b/gdk/win32/gdkwindow-win32.c
@@ -975,6 +975,9 @@ _gdk_win32_display_create_window_impl (GdkDisplay    *display,
   if (attributes_mask & GDK_WA_CURSOR)
     gdk_window_set_cursor (window, attributes->cursor);
 
+  if (_gdk_win32_tablet_input_api == GDK_WIN32_TABLET_INPUT_API_WINPOINTER)
+    gdk_winpointer_initialize_window (window);
+
   _gdk_win32_window_enable_transparency (window);
 }
 
diff --git a/gdk/win32/makefile.msc b/gdk/win32/makefile.msc
index 49e8d5e8bb..a2e125c297 100644
--- a/gdk/win32/makefile.msc
+++ b/gdk/win32/makefile.msc
@@ -30,9 +30,13 @@ all: \
        gdk-win32.lib \
        gdk.res
 
+gdk_win32_DEPS = \
+       hid.lib
+
 gdk_win32_OBJECTS = \
        gdkcursor-win32.obj \
        gdkdevice-win32.obj \
+       gdkdevice-winpointer.obj \
        gdkdevice-wintab.obj \
        gdkdevicemanager-win32.obj \
        gdkdnd-win32.obj \
@@ -61,7 +65,7 @@ gdk.res : rc\gdk.rc
        rc -DBUILDNUMBER=0 -r -fo gdk.res rc\gdk.rc
 
 gdk-win32.lib : $(gdk_win32_OBJECTS)
-       lib -out:gdk-win32.lib $(gdk_win32_OBJECTS)
+       lib -out:gdk-win32.lib $(gdk_win32_DEPS) $(gdk_win32_OBJECTS)
 
 clean::
        del *.obj
diff --git a/gdk/win32/meson.build b/gdk/win32/meson.build
index f28bd92b99..3c40f894b5 100644
--- a/gdk/win32/meson.build
+++ b/gdk/win32/meson.build
@@ -3,6 +3,7 @@ gdk_win32_sources = files(
   'gdkdevicemanager-win32.c',
   'gdkdevice-virtual.c',
   'gdkdevice-win32.c',
+  'gdkdevice-winpointer.c',
   'gdkdevice-wintab.c',
   'gdkdisplay-win32.c',
   'gdkdisplaymanager-win32.c',
@@ -47,7 +48,8 @@ install_headers(gdk_win32_public_headers, subdir: 'gtk-3.0/gdk/win32')
 install_headers('gdkwin32.h', subdir: 'gtk-3.0/gdk')
 
 gdk_win32_deps = [ # FIXME
-    pangowin32_dep
+    pangowin32_dep,
+    meson.get_compiler('c').find_library('hid')
 ]
 
 libgdk_win32 = static_library('gdk-win32',
diff --git a/gdk/win32/winpointer.h b/gdk/win32/winpointer.h
new file mode 100644
index 0000000000..ee3e42e063
--- /dev/null
+++ b/gdk/win32/winpointer.h
@@ -0,0 +1,320 @@
+/* GDK - The GIMP Drawing Kit
+ * Copyright (C) 2021 the GTK team
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
+
+/* This code is derived from portions provided by the mingw-w64 project
+ * (mingw-w64.org), originally licensed under the Zope Public License
+ * (ZPL) version 2.1, with modifications made on May 12, 2021.
+ * Legal notice of the Zope Public License version 2.1 follows:
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *    1. Redistributions in source code must retain the accompanying copyright
+ *       notice, this list of conditions, and the following disclaimer.
+ *    2. Redistributions in binary form must reproduce the accompanying
+ *       copyright notice, this list of conditions, and the following disclaimer
+ *       in the documentation and/or other materials provided with the
+ *       distribution.
+ *    3. Names of the copyright holders must not be used to endorse or promote
+ *       products derived from this software without prior written permission
+ *       from the copyright holders.
+ *    4. The right to distribute this software or to use it for any purpose does
+ *       not give you the right to use Servicemarks (sm) or Trademarks (tm) of
+ *       the copyright holders.  Use of them is covered by separate agreement
+ *       with the copyright holders.
+ *    5. If any files are modified, you must cause the modified files to carry
+ *       prominent notices stating that you changed the files and the date of
+ *       any change.
+ */
+
+#ifndef POINTER_FLAG_NONE
+
+#include <windows.h>
+#include <tchar.h>
+
+#define WM_POINTERDEVICECHANGE 0x238
+#define WM_POINTERDEVICEINRANGE 0x239
+#define WM_POINTERDEVICEOUTOFRANGE 0x23a
+
+#define WM_NCPOINTERUPDATE 0x0241
+#define WM_NCPOINTERDOWN 0x0242
+#define WM_NCPOINTERUP 0x0243
+#define WM_POINTERUPDATE 0x0245
+#define WM_POINTERDOWN 0x0246
+#define WM_POINTERUP 0x0247
+#define WM_POINTERENTER 0x0249
+#define WM_POINTERLEAVE 0x024a
+#define WM_POINTERACTIVATE 0x024b
+#define WM_POINTERCAPTURECHANGED 0x024c
+#define WM_TOUCHHITTESTING 0x024d
+#define WM_POINTERWHEEL 0x024e
+#define WM_POINTERHWHEEL 0x024f
+#define DM_POINTERHITTEST 0x0250
+#define WM_POINTERROUTEDTO 0x0251
+#define WM_POINTERROUTEDAWAY 0x0252
+#define WM_POINTERROUTEDRELEASED 0x0253
+
+#define POINTER_FLAG_NONE 0x00000000
+#define POINTER_FLAG_NEW 0x00000001
+#define POINTER_FLAG_INRANGE 0x00000002
+#define POINTER_FLAG_INCONTACT 0x00000004
+#define POINTER_FLAG_FIRSTBUTTON 0x00000010
+#define POINTER_FLAG_SECONDBUTTON 0x00000020
+#define POINTER_FLAG_THIRDBUTTON 0x00000040
+#define POINTER_FLAG_FOURTHBUTTON 0x00000080
+#define POINTER_FLAG_FIFTHBUTTON 0x00000100
+#define POINTER_FLAG_PRIMARY 0x00002000
+#define POINTER_FLAG_CONFIDENCE 0x00004000
+#define POINTER_FLAG_CANCELED 0x00008000
+#define POINTER_FLAG_DOWN 0x00010000
+#define POINTER_FLAG_UPDATE 0x00020000
+#define POINTER_FLAG_UP 0x00040000
+#define POINTER_FLAG_WHEEL 0x00080000
+#define POINTER_FLAG_HWHEEL 0x00100000
+#define POINTER_FLAG_CAPTURECHANGED 0x00200000
+#define POINTER_FLAG_HASTRANSFORM 0x00400000
+
+#define POINTER_MOD_SHIFT (0x0004)
+#define POINTER_MOD_CTRL (0x0008)
+
+#define TOUCH_FLAG_NONE 0x00000000
+
+#define TOUCH_MASK_NONE 0x00000000
+#define TOUCH_MASK_CONTACTAREA 0x00000001
+#define TOUCH_MASK_ORIENTATION 0x00000002
+#define TOUCH_MASK_PRESSURE 0x00000004
+
+#define PEN_FLAG_NONE 0x00000000
+#define PEN_FLAG_BARREL 0x00000001
+#define PEN_FLAG_INVERTED 0x00000002
+#define PEN_FLAG_ERASER 0x00000004
+
+#define PEN_MASK_NONE 0x00000000
+#define PEN_MASK_PRESSURE 0x00000001
+#define PEN_MASK_ROTATION 0x00000002
+#define PEN_MASK_TILT_X 0x00000004
+#define PEN_MASK_TILT_Y 0x00000008
+
+#define POINTER_MESSAGE_FLAG_NEW 0x00000001
+#define POINTER_MESSAGE_FLAG_INRANGE 0x00000002
+#define POINTER_MESSAGE_FLAG_INCONTACT 0x00000004
+#define POINTER_MESSAGE_FLAG_FIRSTBUTTON 0x00000010
+#define POINTER_MESSAGE_FLAG_SECONDBUTTON 0x00000020
+#define POINTER_MESSAGE_FLAG_THIRDBUTTON 0x00000040
+#define POINTER_MESSAGE_FLAG_FOURTHBUTTON 0x00000080
+#define POINTER_MESSAGE_FLAG_FIFTHBUTTON 0x00000100
+#define POINTER_MESSAGE_FLAG_PRIMARY 0x00002000
+#define POINTER_MESSAGE_FLAG_CONFIDENCE 0x00004000
+#define POINTER_MESSAGE_FLAG_CANCELED 0x00008000
+
+#define GET_POINTERID_WPARAM(wParam) (LOWORD (wParam))
+#define IS_POINTER_FLAG_SET_WPARAM(wParam, flag) (((DWORD)HIWORD (wParam) &(flag)) == (flag))
+#define IS_POINTER_NEW_WPARAM(wParam) IS_POINTER_FLAG_SET_WPARAM (wParam, POINTER_MESSAGE_FLAG_NEW)
+#define IS_POINTER_INRANGE_WPARAM(wParam) IS_POINTER_FLAG_SET_WPARAM (wParam, POINTER_MESSAGE_FLAG_INRANGE)
+#define IS_POINTER_INCONTACT_WPARAM(wParam) IS_POINTER_FLAG_SET_WPARAM (wParam, 
POINTER_MESSAGE_FLAG_INCONTACT)
+#define IS_POINTER_FIRSTBUTTON_WPARAM(wParam) IS_POINTER_FLAG_SET_WPARAM (wParam, 
POINTER_MESSAGE_FLAG_FIRSTBUTTON)
+#define IS_POINTER_SECONDBUTTON_WPARAM(wParam) IS_POINTER_FLAG_SET_WPARAM (wParam, 
POINTER_MESSAGE_FLAG_SECONDBUTTON)
+#define IS_POINTER_THIRDBUTTON_WPARAM(wParam) IS_POINTER_FLAG_SET_WPARAM (wParam, 
POINTER_MESSAGE_FLAG_THIRDBUTTON)
+#define IS_POINTER_FOURTHBUTTON_WPARAM(wParam) IS_POINTER_FLAG_SET_WPARAM (wParam, 
POINTER_MESSAGE_FLAG_FOURTHBUTTON)
+#define IS_POINTER_FIFTHBUTTON_WPARAM(wParam) IS_POINTER_FLAG_SET_WPARAM (wParam, 
POINTER_MESSAGE_FLAG_FIFTHBUTTON)
+#define IS_POINTER_PRIMARY_WPARAM(wParam) IS_POINTER_FLAG_SET_WPARAM (wParam, POINTER_MESSAGE_FLAG_PRIMARY)
+#define HAS_POINTER_CONFIDENCE_WPARAM(wParam) IS_POINTER_FLAG_SET_WPARAM (wParam, 
POINTER_MESSAGE_FLAG_CONFIDENCE)
+#define IS_POINTER_CANCELED_WPARAM(wParam) IS_POINTER_FLAG_SET_WPARAM (wParam, POINTER_MESSAGE_FLAG_CANCELED)
+
+#define PA_ACTIVATE MA_ACTIVATE
+#define PA_NOACTIVATE MA_NOACTIVATE
+
+typedef DWORD POINTER_INPUT_TYPE;
+typedef UINT32 POINTER_FLAGS;
+typedef UINT32 TOUCH_FLAGS;
+typedef UINT32 TOUCH_MASK;
+typedef UINT32 PEN_FLAGS;
+typedef UINT32 PEN_MASK;
+
+enum tagPOINTER_INPUT_TYPE {
+  PT_POINTER = 0x00000001,
+  PT_TOUCH = 0x00000002,
+  PT_PEN = 0x00000003,
+  PT_MOUSE = 0x00000004,
+  PT_TOUCHPAD = 0x00000005
+};
+
+typedef enum tagFEEDBACK_TYPE {
+  FEEDBACK_TOUCH_CONTACTVISUALIZATION = 1,
+  FEEDBACK_PEN_BARRELVISUALIZATION = 2,
+  FEEDBACK_PEN_TAP = 3,
+  FEEDBACK_PEN_DOUBLETAP = 4,
+  FEEDBACK_PEN_PRESSANDHOLD = 5,
+  FEEDBACK_PEN_RIGHTTAP = 6,
+  FEEDBACK_TOUCH_TAP = 7,
+  FEEDBACK_TOUCH_DOUBLETAP = 8,
+  FEEDBACK_TOUCH_PRESSANDHOLD = 9,
+  FEEDBACK_TOUCH_RIGHTTAP = 10,
+  FEEDBACK_GESTURE_PRESSANDTAP = 11,
+  FEEDBACK_MAX = 0xffffffff
+} FEEDBACK_TYPE;
+
+typedef enum tagPOINTER_BUTTON_CHANGE_TYPE {
+  POINTER_CHANGE_NONE,
+  POINTER_CHANGE_FIRSTBUTTON_DOWN,
+  POINTER_CHANGE_FIRSTBUTTON_UP,
+  POINTER_CHANGE_SECONDBUTTON_DOWN,
+  POINTER_CHANGE_SECONDBUTTON_UP,
+  POINTER_CHANGE_THIRDBUTTON_DOWN,
+  POINTER_CHANGE_THIRDBUTTON_UP,
+  POINTER_CHANGE_FOURTHBUTTON_DOWN,
+  POINTER_CHANGE_FOURTHBUTTON_UP,
+  POINTER_CHANGE_FIFTHBUTTON_DOWN,
+  POINTER_CHANGE_FIFTHBUTTON_UP,
+} POINTER_BUTTON_CHANGE_TYPE;
+
+typedef struct tagPOINTER_INFO {
+  POINTER_INPUT_TYPE pointerType;
+  UINT32 pointerId;
+  UINT32 frameId;
+  POINTER_FLAGS pointerFlags;
+  HANDLE sourceDevice;
+  HWND hwndTarget;
+  POINT ptPixelLocation;
+  POINT ptHimetricLocation;
+  POINT ptPixelLocationRaw;
+  POINT ptHimetricLocationRaw;
+  DWORD dwTime;
+  UINT32 historyCount;
+  INT32 InputData;
+  DWORD dwKeyStates;
+  UINT64 PerformanceCount;
+  POINTER_BUTTON_CHANGE_TYPE ButtonChangeType;
+} POINTER_INFO;
+
+typedef struct tagPOINTER_TOUCH_INFO {
+  POINTER_INFO pointerInfo;
+  TOUCH_FLAGS touchFlags;
+  TOUCH_MASK touchMask;
+  RECT rcContact;
+  RECT rcContactRaw;
+  UINT32 orientation;
+  UINT32 pressure;
+} POINTER_TOUCH_INFO;
+
+typedef struct tagPOINTER_PEN_INFO {
+  POINTER_INFO pointerInfo;
+  PEN_FLAGS penFlags;
+  PEN_MASK penMask;
+  UINT32 pressure;
+  UINT32 rotation;
+  INT32 tiltX;
+  INT32 tiltY;
+} POINTER_PEN_INFO;
+
+typedef enum {
+  POINTER_FEEDBACK_DEFAULT = 1,
+  POINTER_FEEDBACK_INDIRECT = 2,
+  POINTER_FEEDBACK_NONE = 3
+} POINTER_FEEDBACK_MODE;
+
+typedef struct tagUSAGE_PROPERTIES {
+  USHORT level;
+  USHORT page;
+  USHORT usage;
+  INT32 logicalMinimum;
+  INT32 logicalMaximum;
+  USHORT unit;
+  USHORT exponent;
+  BYTE count;
+  INT32 physicalMinimum;
+  INT32 physicalMaximum;
+} USAGE_PROPERTIES, *PUSAGE_PROPERTIES;
+
+typedef struct tagPOINTER_TYPE_INFO {
+  POINTER_INPUT_TYPE  type;
+  union {
+      POINTER_TOUCH_INFO touchInfo;
+      POINTER_PEN_INFO penInfo;
+  } DUMMYUNIONNAME;
+} POINTER_TYPE_INFO, *PPOINTER_TYPE_INFO;
+
+#define POINTER_DEVICE_PRODUCT_STRING_MAX 520
+#define PDC_ARRIVAL 0x001
+#define PDC_REMOVAL 0x002
+#define PDC_ORIENTATION_0 0x004
+#define PDC_ORIENTATION_90 0x008
+#define PDC_ORIENTATION_180 0x010
+#define PDC_ORIENTATION_270 0x020
+#define PDC_MODE_DEFAULT 0x040
+#define PDC_MODE_CENTERED 0x080
+#define PDC_MAPPING_CHANGE 0x100
+#define PDC_RESOLUTION 0x200
+#define PDC_ORIGIN 0x400
+#define PDC_MODE_ASPECTRATIOPRESERVED 0x800
+
+typedef enum tagPOINTER_DEVICE_TYPE {
+  POINTER_DEVICE_TYPE_INTEGRATED_PEN = 0x00000001,
+  POINTER_DEVICE_TYPE_EXTERNAL_PEN = 0x00000002,
+  POINTER_DEVICE_TYPE_TOUCH = 0x00000003,
+  POINTER_DEVICE_TYPE_TOUCH_PAD = 0x00000004,
+  POINTER_DEVICE_TYPE_MAX = 0xffffffff
+} POINTER_DEVICE_TYPE;
+
+typedef struct tagPOINTER_DEVICE_INFO {
+  DWORD displayOrientation;
+  HANDLE device;
+  POINTER_DEVICE_TYPE pointerDeviceType;
+  HMONITOR monitor;
+  ULONG startingCursorId;
+  USHORT maxActiveContacts;
+  WCHAR productString[POINTER_DEVICE_PRODUCT_STRING_MAX];
+} POINTER_DEVICE_INFO;
+
+typedef struct tagPOINTER_DEVICE_PROPERTY {
+  INT32 logicalMin;
+  INT32 logicalMax;
+  INT32 physicalMin;
+  INT32 physicalMax;
+  UINT32 unit;
+  UINT32 unitExponent;
+  USHORT usagePageId;
+  USHORT usageId;
+} POINTER_DEVICE_PROPERTY;
+
+typedef enum tagPOINTER_DEVICE_CURSOR_TYPE {
+  POINTER_DEVICE_CURSOR_TYPE_UNKNOWN = 0x00000000,
+  POINTER_DEVICE_CURSOR_TYPE_TIP = 0x00000001,
+  POINTER_DEVICE_CURSOR_TYPE_ERASER = 0x00000002,
+  POINTER_DEVICE_CURSOR_TYPE_MAX = 0xffffffff
+} POINTER_DEVICE_CURSOR_TYPE;
+
+typedef struct tagPOINTER_DEVICE_CURSOR_INFO {
+  UINT32 cursorId;
+  POINTER_DEVICE_CURSOR_TYPE cursor;
+} POINTER_DEVICE_CURSOR_INFO;
+
+#endif /* POINTER_FLAG_NONE */
+
+#if WINVER < 0x0601
+
+typedef struct tagGESTURECONFIG {
+  DWORD dwID;
+  DWORD dwWant;
+  DWORD dwBlock;
+} GESTURECONFIG,*PGESTURECONFIG;
+
+#endif /* WINVER < 0x0601 */
+
+#ifndef MICROSOFT_TABLETPENSERVICE_PROPERTY
+#define MICROSOFT_TABLETPENSERVICE_PROPERTY _T("MicrosoftTabletPenServiceProperty")
+#endif
diff --git a/win32/vs10/gtk3-build-defines.props b/win32/vs10/gtk3-build-defines.props
index 490c508daa..50c8cf6fbe 100644
--- a/win32/vs10/gtk3-build-defines.props
+++ b/win32/vs10/gtk3-build-defines.props
@@ -15,7 +15,7 @@
     
<GtkDefines>GTK_COMPILATION;G_LOG_DOMAIN="Gtk";GTK_HOST="$(GtkHostMachine)-pc-vs$(VSVer)";GTK_PRINT_BACKENDS="file";GTK_PRINT_BACKEND_ENABLE_UNSUPPORTED;$(GtkIncludedImmodulesDefines);GTK_LIBDIR="$(GtkDummyPrefix)/lib";GTK_DATADIR="$(GtkDummyPrefix)/share";GTK_DATA_PREFIX="$(GtkDummyPrefix)";GTK_SYSCONFDIR="$(GtkDummyPrefix)/etc";MULTIPRESS_CONFDIR="$(GtkDummyPrefix)/etc/gtk-$(ApiVersion)";MULTIPRESS_LOCALEDIR="$(GtkDummyPrefix)/share/locale";GTK_VERSION="$(GtkVersion)/etc";GTK_BINARY_VERSION="$(GtkBinaryVersion)/etc";GDK_DISABLE_DEPRECATED;ISOLATION_AWARE_ENABLED</GtkDefines>
     <CommonARM64SystemLibs Condition="'$(VisualStudioVersion)|$(Platform)' == 
'15.0|arm64'">ole32.lib;advapi32.lib;shell32.lib;gdi32.lib</CommonARM64SystemLibs>
     <GtkGdkCommonLibs>pangowin32-1.0.lib;fribidi.lib;imm32.lib;$(CommonARM64SystemLibs)</GtkGdkCommonLibs>
-    <GdkAdditionalLibs>winmm.lib;dwmapi.lib;setupapi.lib;$(GtkGdkCommonLibs)</GdkAdditionalLibs>
+    <GdkAdditionalLibs>winmm.lib;dwmapi.lib;setupapi.lib;hid.lib;$(GtkGdkCommonLibs)</GdkAdditionalLibs>
     <GdkBroadwayAdditionalLibs>ws2_32.lib</GdkBroadwayAdditionalLibs>
     <GtkAdditionalLibs>atk-1.0.lib;winspool.lib;comctl32.lib;$(GtkGdkCommonLibs)</GtkAdditionalLibs>
     <GtkIntrospectNMakeCmd>cd ..
diff --git a/win32/vs9/gtk3-build-defines.vsprops b/win32/vs9/gtk3-build-defines.vsprops
index be5223c84e..e47c348063 100644
--- a/win32/vs9/gtk3-build-defines.vsprops
+++ b/win32/vs9/gtk3-build-defines.vsprops
@@ -57,7 +57,7 @@
        />
        <UserMacro
                Name="GdkAdditionalLibs"
-               Value="winmm.lib dwmapi.lib setupapi.lib $(GtkGdkCommonLibs)"
+               Value="winmm.lib dwmapi.lib setupapi.lib hid.lib $(GtkGdkCommonLibs)"
        />
        <UserMacro
                Name="GtkAdditionalLibs"


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