]> Pileus Git - ~andy/gtk/blobdiff - gdk/win32/gdkinput-win32.c
New debugging function. (_gdk_input_wintab_init_check): Call it when
[~andy/gtk] / gdk / win32 / gdkinput-win32.c
index e7431859c8da41eb8a4533fcd98b29e40316824c..dbfe42fdba256d6ceb815e0c41f88db7f9d1a2d3 100644 (file)
@@ -1,6 +1,6 @@
 /* GDK - The GIMP Drawing Kit
  * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
- * Copyright (C) 1998-2002 Tor Lillqvist
+ * Copyright (C) 1998-2007 Tor Lillqvist
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
@@ -25,7 +25,7 @@
  * GTK+ at ftp://ftp.gtk.org/pub/gtk/. 
  */
 
-#include "config.h"
+#include <config.h>
 
 #include <stdlib.h>
 #include <stdio.h>
 #include "gdkprivate-win32.h"
 #include "gdkinput-win32.h"
 
-#ifdef HAVE_WINTAB
-
 #define PACKETDATA (PK_CONTEXT | PK_CURSOR | PK_BUTTONS | PK_X | PK_Y  | PK_NORMAL_PRESSURE | PK_ORIENTATION)
-#define PACKETMODE (PK_BUTTONS)
+/* We want everything in absolute mode */
+#define PACKETMODE (0)
 #include <pktdef.h>
 
-/* If USE_SYSCONTEXT is on, we open the Wintab device (hmm, what if
- * there are several?) as a system pointing device, i.e. it controls
- * the normal Windows cursor. This seems much more natural.
- */
-#define USE_SYSCONTEXT 1       /* The code for the other choice is not
-                                * good at all.
-                                */
-
 #define DEBUG_WINTAB 1         /* Verbose debug messages enabled */
 
-#endif
-
-#if defined(HAVE_WINTAB) || defined(HAVE_WHATEVER_OTHER)
-#define HAVE_SOME_XINPUT
-#endif
+#define PROXIMITY_OUT_DELAY 200 /* In milliseconds, see set_ignore_core */
 
 #define TWOPI (2.*G_PI)
 
 /* Forward declarations */
 
-#if !USE_SYSCONTEXT
-static GdkInputWindow *gdk_input_window_find_within (GdkWindow *window);
-#endif
-
-#ifdef HAVE_WINTAB
-
 static GdkDevicePrivate *gdk_input_find_dev_from_ctx (HCTX hctx,
                                                      UINT id);
-static GList     *wintab_contexts;
-
-static GdkWindow *wintab_window;
-
-#endif /* HAVE_WINTAB */
-
-#ifdef HAVE_WINTAB
+static GList     *wintab_contexts = NULL;
+
+static GdkWindow *wintab_window = NULL;
+
+static GdkWindow *x_grab_window = NULL; /* Window that currently holds
+                                        * the extended inputs grab
+                                        */
+static GdkEventMask x_grab_mask;
+static gboolean x_grab_owner_events;
+
+typedef UINT (WINAPI *t_WTInfoA) (UINT a, UINT b, LPVOID c);
+typedef BOOL (WINAPI *t_WTEnable) (HCTX a, BOOL b);
+typedef HCTX (WINAPI *t_WTOpenA) (HWND a, LPLOGCONTEXTA b, BOOL c);
+typedef BOOL (WINAPI *t_WTOverlap) (HCTX a, BOOL b);
+typedef BOOL (WINAPI *t_WTPacket) (HCTX a, UINT b, LPVOID c);
+typedef int (WINAPI *t_WTQueueSizeSet) (HCTX a, int b);
+
+static t_WTInfoA p_WTInfoA;
+static t_WTEnable p_WTEnable;
+static t_WTOpenA p_WTOpenA;
+static t_WTOverlap p_WTOverlap;
+static t_WTPacket p_WTPacket;
+static t_WTQueueSizeSet p_WTQueueSizeSet;
 
 static GdkDevicePrivate *
 gdk_input_find_dev_from_ctx (HCTX hctx,
@@ -109,7 +106,6 @@ print_lc(LOGCONTEXT *lc)
   if (lc->lcOptions & CXO_MARGIN) g_print (" CXO_MARGIN");
   if (lc->lcOptions & CXO_MGNINSIDE) g_print (" CXO_MGNINSIDE");
   if (lc->lcOptions & CXO_CSRMESSAGES) g_print (" CXO_CSRMESSAGES");
-  if (lc->lcOptions & CXO_CSRMESSAGES) g_print (" CXO_CSRMESSAGES");
   g_print ("\n");
   g_print ("lcStatus =");
   if (lc->lcStatus & CXS_DISABLED) g_print (" CXS_DISABLED");
@@ -193,284 +189,433 @@ print_lc(LOGCONTEXT *lc)
          lc->lcSysSensX / 65536., lc->lcSysSensY / 65536.);
 }
 
+static void
+print_cursor (int index)
+{
+  int size;
+  int i;
+  char *name;
+  BOOL active;
+  WTPKT wtpkt;
+  BYTE buttons;
+  BYTE buttonbits;
+  char *btnnames;
+  char *p;
+  BYTE buttonmap[32];
+  BYTE sysbtnmap[32];
+  BYTE npbutton;
+  UINT npbtnmarks[2];
+  UINT *npresponse;
+  BYTE tpbutton;
+  UINT tpbtnmarks[2];
+  UINT *tpresponse;
+  DWORD physid;
+  UINT mode;
+  UINT minpktdata;
+  UINT minbuttons;
+  UINT capabilities;
+
+  size = (*p_WTInfoA) (WTI_CURSORS + index, CSR_NAME, NULL);
+  name = g_malloc (size + 1);
+  (*p_WTInfoA) (WTI_CURSORS + index, CSR_NAME, name);
+  g_print ("NAME: %s\n", name);
+  (*p_WTInfoA) (WTI_CURSORS + index, CSR_ACTIVE, &active);
+  g_print ("ACTIVE: %s\n", active ? "YES" : "NO");
+  (*p_WTInfoA) (WTI_CURSORS + index, CSR_PKTDATA, &wtpkt);
+  g_print ("PKTDATA: %#x:", (guint) wtpkt);
+#define BIT(x) if (wtpkt & PK_##x) g_print (" " #x)
+  BIT (CONTEXT);
+  BIT (STATUS);
+  BIT (TIME);
+  BIT (CHANGED);
+  BIT (SERIAL_NUMBER);
+  BIT (BUTTONS);
+  BIT (X);
+  BIT (Y);
+  BIT (Z);
+  BIT (NORMAL_PRESSURE);
+  BIT (TANGENT_PRESSURE);
+  BIT (ORIENTATION);
+  BIT (ROTATION);
+#undef BIT
+  g_print ("\n");
+  (*p_WTInfoA) (WTI_CURSORS + index, CSR_BUTTONS, &buttons);
+  g_print ("BUTTONS: %d\n", buttons);
+  (*p_WTInfoA) (WTI_CURSORS + index, CSR_BUTTONBITS, &buttonbits);
+  g_print ("BUTTONBITS: %d\n", buttonbits);
+  size = (*p_WTInfoA) (WTI_CURSORS + index, CSR_BTNNAMES, NULL);
+  g_print ("BTNNAMES:");
+  if (size > 0)
+    {
+      btnnames = g_malloc (size + 1);
+      (*p_WTInfoA) (WTI_CURSORS + index, CSR_BTNNAMES, btnnames);
+      p = btnnames;
+      while (*p)
+       {
+         g_print (" %s", p);
+         p += strlen (p) + 1;
+       }
+    }
+  g_print ("\n");
+  (*p_WTInfoA) (WTI_CURSORS + index, CSR_BUTTONMAP, buttonmap);
+  g_print ("BUTTONMAP:");
+  for (i = 0; i < buttons; i++)
+    g_print (" %d", buttonmap[i]);
+  g_print ("\n");
+  (*p_WTInfoA) (WTI_CURSORS + index, CSR_SYSBTNMAP, sysbtnmap);
+  g_print ("SYSBTNMAP:");
+  for (i = 0; i < buttons; i++)
+    g_print (" %d", sysbtnmap[i]);
+  g_print ("\n");
+  (*p_WTInfoA) (WTI_CURSORS + index, CSR_NPBUTTON, &npbutton);
+  g_print ("NPBUTTON: %d\n", npbutton);
+  (*p_WTInfoA) (WTI_CURSORS + index, CSR_NPBTNMARKS, npbtnmarks);
+  g_print ("NPBTNMARKS: %d %d\n", npbtnmarks[0], npbtnmarks[1]);
+  size = (*p_WTInfoA) (WTI_CURSORS + index, CSR_NPRESPONSE, NULL);
+  g_print ("NPRESPONSE:");
+  if (size > 0)
+    {
+      npresponse = g_malloc (size);
+      (*p_WTInfoA) (WTI_CURSORS + index, CSR_NPRESPONSE, npresponse);
+      for (i = 0; i < size / sizeof (UINT); i++)
+       g_print (" %d", npresponse[i]);
+    }
+  g_print ("\n");
+  (*p_WTInfoA) (WTI_CURSORS + index, CSR_TPBUTTON, &tpbutton);
+  g_print ("TPBUTTON: %d\n", tpbutton);
+  (*p_WTInfoA) (WTI_CURSORS + index, CSR_TPBTNMARKS, tpbtnmarks);
+  g_print ("TPBTNMARKS: %d %d\n", tpbtnmarks[0], tpbtnmarks[1]);
+  size = (*p_WTInfoA) (WTI_CURSORS + index, CSR_TPRESPONSE, NULL);
+  g_print ("TPRESPONSE:");
+  if (size > 0)
+    {
+      tpresponse = g_malloc (size);
+      (*p_WTInfoA) (WTI_CURSORS + index, CSR_TPRESPONSE, tpresponse);
+      for (i = 0; i < size / sizeof (UINT); i++)
+       g_print (" %d", tpresponse[i]);
+    }
+  g_print ("\n");
+  (*p_WTInfoA) (WTI_CURSORS + index, CSR_PHYSID, &physid);
+  g_print ("PHYSID: %#x\n", (guint) physid);
+  (*p_WTInfoA) (WTI_CURSORS + index, CSR_CAPABILITIES, &capabilities);
+  g_print ("CAPABILITIES: %#x:", capabilities);
+#define BIT(x) if (capabilities & CRC_##x) g_print (" " #x)
+  BIT (MULTIMODE);
+  BIT (AGGREGATE);
+  BIT (INVERT);
+#undef BIT
+  g_print ("\n");
+  if (capabilities & CRC_MULTIMODE)
+    {
+      (*p_WTInfoA) (WTI_CURSORS + index, CSR_MODE, &mode);
+      g_print ("MODE: %d\n", mode);
+    }
+  if (capabilities & CRC_AGGREGATE)
+    {
+      (*p_WTInfoA) (WTI_CURSORS + index, CSR_MINPKTDATA, &minpktdata);
+      g_print ("MINPKTDATA: %d\n", minpktdata);
+      (*p_WTInfoA) (WTI_CURSORS + index, CSR_MINBUTTONS, &minbuttons);
+      g_print ("MINBUTTONS: %d\n", minbuttons);
+    }
+}
 #endif
 
-static void
-gdk_input_wintab_init (void)
+void
+_gdk_input_wintab_init_check (void)
 {
+  static gboolean wintab_initialized = FALSE;
   GdkDevicePrivate *gdkdev;
   GdkWindowAttr wa;
   WORD specversion;
-  LOGCONTEXT defcontext;
   HCTX *hctx;
   UINT ndevices, ncursors, ncsrtypes, firstcsr, hardware;
   BOOL active;
+  DWORD physid;
   AXIS axis_x, axis_y, axis_npressure, axis_or[3];
   int i, k;
   int devix, cursorix;
   char devname[100], csrname[100];
+  BOOL defcontext_done;
+  HMODULE wintab32;
 
-  _gdk_input_devices = NULL;
+  if (wintab_initialized)
+    return;
+  
+  wintab_initialized = TRUE;
+  
   wintab_contexts = NULL;
 
-  if (!_gdk_input_ignore_wintab &&
-      WTInfo (0, 0, NULL))
-    {
-      WTInfo (WTI_INTERFACE, IFC_SPECVERSION, &specversion);
-      GDK_NOTE (INPUT, g_print ("Wintab interface version %d.%d\n",
-                              HIBYTE (specversion), LOBYTE (specversion)));
-#if USE_SYSCONTEXT
-      WTInfo (WTI_DEFSYSCTX, 0, &defcontext);
+  if (_gdk_input_ignore_wintab)
+    return;
+
+  if ((wintab32 = LoadLibrary ("wintab32.dll")) == NULL)
+    return;
+
+  if ((p_WTInfoA = (t_WTInfoA) GetProcAddress (wintab32, "WTInfoA")) == NULL)
+    return;
+  if ((p_WTEnable = (t_WTEnable) GetProcAddress (wintab32, "WTEnable")) == NULL)
+    return;
+  if ((p_WTOpenA = (t_WTOpenA) GetProcAddress (wintab32, "WTOpenA")) == NULL)
+    return;
+  if ((p_WTOverlap = (t_WTOverlap) GetProcAddress (wintab32, "WTOverlap")) == NULL)
+    return;
+  if ((p_WTPacket = (t_WTPacket) GetProcAddress (wintab32, "WTPacket")) == NULL)
+    return;
+  if ((p_WTQueueSizeSet = (t_WTQueueSizeSet) GetProcAddress (wintab32, "WTQueueSizeSet")) == NULL)
+    return;
+    
+  if (!(*p_WTInfoA) (0, 0, NULL))
+    return;
+
+  (*p_WTInfoA) (WTI_INTERFACE, IFC_SPECVERSION, &specversion);
+  GDK_NOTE (INPUT, g_print ("Wintab interface version %d.%d\n",
+                           HIBYTE (specversion), LOBYTE (specversion)));
+  (*p_WTInfoA) (WTI_INTERFACE, IFC_NDEVICES, &ndevices);
+  (*p_WTInfoA) (WTI_INTERFACE, IFC_NCURSORS, &ncursors);
 #if DEBUG_WINTAB
-      GDK_NOTE (INPUT, (g_print("DEFSYSCTX:\n"), print_lc(&defcontext)));
+  GDK_NOTE (INPUT, g_print ("NDEVICES: %d, NCURSORS: %d\n",
+                           ndevices, ncursors));
 #endif
-#else
-      WTInfo (WTI_DEFCONTEXT, 0, &defcontext);
+  /* Create a dummy window to receive wintab events */
+  wa.wclass = GDK_INPUT_OUTPUT;
+  wa.event_mask = GDK_ALL_EVENTS_MASK;
+  wa.width = 2;
+  wa.height = 2;
+  wa.x = -100;
+  wa.y = -100;
+  wa.window_type = GDK_WINDOW_TOPLEVEL;
+  if ((wintab_window = gdk_window_new (NULL, &wa, GDK_WA_X|GDK_WA_Y)) == NULL)
+    {
+      g_warning ("gdk_input_wintab_init: gdk_window_new failed");
+      return;
+    }
+  g_object_ref (wintab_window);
+      
+  for (devix = 0; devix < ndevices; devix++)
+    {
+      LOGCONTEXT lc;
+      
+      /* We open the Wintab device (hmm, what if there are several?) as a
+       * system pointing device, i.e. it controls the normal Windows
+       * cursor. This seems much more natural.
+       */
+
+      (*p_WTInfoA) (WTI_DEVICES + devix, DVC_NAME, devname);
+      
+      (*p_WTInfoA) (WTI_DEVICES + devix, DVC_NCSRTYPES, &ncsrtypes);
+      (*p_WTInfoA) (WTI_DEVICES + devix, DVC_FIRSTCSR, &firstcsr);
+      (*p_WTInfoA) (WTI_DEVICES + devix, DVC_HARDWARE, &hardware);
+      (*p_WTInfoA) (WTI_DEVICES + devix, DVC_X, &axis_x);
+      (*p_WTInfoA) (WTI_DEVICES + devix, DVC_Y, &axis_y);
+      (*p_WTInfoA) (WTI_DEVICES + devix, DVC_NPRESSURE, &axis_npressure);
+      (*p_WTInfoA) (WTI_DEVICES + devix, DVC_ORIENTATION, axis_or);
+
+      defcontext_done = FALSE;
+      if (HIBYTE (specversion) > 1 || LOBYTE (specversion) >= 1)
+       {
+         /* Try to get device-specific default context */
+         /* Some drivers, e.g. Aiptek, don't provide this info */
+         if ((*p_WTInfoA) (WTI_DSCTXS + devix, 0, &lc) > 0)
+           defcontext_done = TRUE;
 #if DEBUG_WINTAB
-      GDK_NOTE (INPUT, (g_print("DEFCONTEXT:\n"), print_lc(&defcontext)));
+         if (defcontext_done)
+           GDK_NOTE (INPUT, (g_print("Using device-specific default context\n")));
+         else
+           GDK_NOTE (INPUT, (g_print("Note: Driver did not provide device specific default context info despite claiming to support version 1.1\n")));
 #endif
+       }
+
+      if (!defcontext_done)
+       (*p_WTInfoA) (WTI_DEFSYSCTX, 0, &lc);
+#if DEBUG_WINTAB
+      GDK_NOTE (INPUT, (g_print("Default context:\n"), print_lc(&lc)));
 #endif
-      WTInfo (WTI_INTERFACE, IFC_NDEVICES, &ndevices);
-      WTInfo (WTI_INTERFACE, IFC_NCURSORS, &ncursors);
+      lc.lcOptions |= CXO_MESSAGES;
+      lc.lcStatus = 0;
+      lc.lcMsgBase = WT_DEFBASE;
+      lc.lcPktRate = 50;
+      lc.lcPktData = PACKETDATA;
+      lc.lcPktMode = PACKETMODE;
+      lc.lcMoveMask = PACKETDATA;
+      lc.lcBtnUpMask = lc.lcBtnDnMask = ~0;
+      lc.lcOutOrgX = axis_x.axMin;
+      lc.lcOutOrgY = axis_y.axMin;
+      lc.lcOutExtX = axis_x.axMax - axis_x.axMin;
+      lc.lcOutExtY = axis_y.axMax - axis_y.axMin;
+      lc.lcOutExtY = -lc.lcOutExtY; /* We want Y growing downward */
 #if DEBUG_WINTAB
-      GDK_NOTE (INPUT, g_print ("NDEVICES: %d, NCURSORS: %d\n",
-                              ndevices, ncursors));
+      GDK_NOTE (INPUT, (g_print("context for device %d:\n", devix),
+                       print_lc(&lc)));
 #endif
-      /* Create a dummy window to receive wintab events */
-      wa.wclass = GDK_INPUT_OUTPUT;
-      wa.event_mask = GDK_ALL_EVENTS_MASK;
-      wa.width = 2;
-      wa.height = 2;
-      wa.x = -100;
-      wa.y = -100;
-      wa.window_type = GDK_WINDOW_TOPLEVEL;
-      if ((wintab_window = gdk_window_new (NULL, &wa, GDK_WA_X|GDK_WA_Y)) == NULL)
+      hctx = g_new (HCTX, 1);
+      if ((*hctx = (*p_WTOpenA) (GDK_WINDOW_HWND (wintab_window), &lc, TRUE)) == NULL)
        {
-         g_warning ("gdk_input_wintab_init: gdk_window_new failed");
+         g_warning ("gdk_input_wintab_init: WTOpen failed");
          return;
        }
-      g_object_ref (wintab_window);
+      GDK_NOTE (INPUT, g_print ("opened Wintab device %d %p\n",
+                               devix, *hctx));
       
-      for (devix = 0; devix < ndevices; devix++)
-       {
-         LOGCONTEXT lc;
+      wintab_contexts = g_list_append (wintab_contexts, hctx);
+#if 0
+      (*p_WTEnable) (*hctx, TRUE);
+#endif
+      (*p_WTOverlap) (*hctx, TRUE);
 
-         WTInfo (WTI_DEVICES + devix, DVC_NAME, devname);
-      
-         WTInfo (WTI_DEVICES + devix, DVC_NCSRTYPES, &ncsrtypes);
-         WTInfo (WTI_DEVICES + devix, DVC_FIRSTCSR, &firstcsr);
-         WTInfo (WTI_DEVICES + devix, DVC_HARDWARE, &hardware);
-         WTInfo (WTI_DEVICES + devix, DVC_X, &axis_x);
-         WTInfo (WTI_DEVICES + devix, DVC_Y, &axis_y);
-         WTInfo (WTI_DEVICES + devix, DVC_NPRESSURE, &axis_npressure);
-         WTInfo (WTI_DEVICES + devix, DVC_ORIENTATION, axis_or);
-
-         if (HIBYTE (specversion) > 1 || LOBYTE (specversion) >= 1)
+#if DEBUG_WINTAB
+      GDK_NOTE (INPUT, (g_print("context for device %d after WTOpen:\n", devix),
+                       print_lc(&lc)));
+#endif
+      /* Increase packet queue size to reduce the risk of lost packets.
+       * According to the specs, if the function fails we must try again
+       * with a smaller queue size.
+       */
+      GDK_NOTE (INPUT, g_print("Attempting to increase queue size\n"));
+      for (i = 32; i >= 1; i >>= 1)
+       {
+         if ((*p_WTQueueSizeSet) (*hctx, i))
            {
-             WTInfo (WTI_DDCTXS + devix, CTX_NAME, lc.lcName);
-             WTInfo (WTI_DDCTXS + devix, CTX_OPTIONS, &lc.lcOptions);
-             lc.lcOptions |= CXO_MESSAGES;
-#if USE_SYSCONTEXT
-             lc.lcOptions |= CXO_SYSTEM;
+             GDK_NOTE (INPUT, g_print("Queue size set to %d\n", i));
+             break;
+           }
+       }
+      if (!i)
+       GDK_NOTE (INPUT, g_print("Whoops, no queue size could be set\n"));
+      for (cursorix = firstcsr; cursorix < firstcsr + ncsrtypes; cursorix++)
+       {
+#ifdef DEBUG_WINTAB
+             GDK_NOTE (INPUT, (g_print("Cursor %d:\n", cursorix), print_cursor (cursorix)));
 #endif
-             lc.lcStatus = 0;
-             WTInfo (WTI_DDCTXS + devix, CTX_LOCKS, &lc.lcLocks);
-             lc.lcMsgBase = WT_DEFBASE;
-             lc.lcDevice = devix;
-             lc.lcPktRate = 50;
-             lc.lcPktData = PACKETDATA;
-             lc.lcPktMode = PK_BUTTONS; /* We want buttons in relative mode */
-             lc.lcMoveMask = PACKETDATA;
-             lc.lcBtnDnMask = lc.lcBtnUpMask = ~0;
-             WTInfo (WTI_DDCTXS + devix, CTX_INORGX, &lc.lcInOrgX);
-             WTInfo (WTI_DDCTXS + devix, CTX_INORGY, &lc.lcInOrgY);
-             WTInfo (WTI_DDCTXS + devix, CTX_INORGZ, &lc.lcInOrgZ);
-             WTInfo (WTI_DDCTXS + devix, CTX_INEXTX, &lc.lcInExtX);
-             WTInfo (WTI_DDCTXS + devix, CTX_INEXTY, &lc.lcInExtY);
-             WTInfo (WTI_DDCTXS + devix, CTX_INEXTZ, &lc.lcInExtZ);
-             lc.lcOutOrgX = axis_x.axMin;
-             lc.lcOutOrgY = axis_y.axMin;
-             lc.lcOutExtX = axis_x.axMax - axis_x.axMin;
-             lc.lcOutExtY = axis_y.axMax - axis_y.axMin;
-             lc.lcOutExtY = -lc.lcOutExtY; /* We want Y growing downward */
-             WTInfo (WTI_DDCTXS + devix, CTX_SENSX, &lc.lcSensX);
-             WTInfo (WTI_DDCTXS + devix, CTX_SENSY, &lc.lcSensY);
-             WTInfo (WTI_DDCTXS + devix, CTX_SENSZ, &lc.lcSensZ);
-             WTInfo (WTI_DDCTXS + devix, CTX_SYSMODE, &lc.lcSysMode);
-             lc.lcSysOrgX = lc.lcSysOrgY = 0;
-             WTInfo (WTI_DDCTXS + devix, CTX_SYSEXTX, &lc.lcSysExtX);
-             WTInfo (WTI_DDCTXS + devix, CTX_SYSEXTY, &lc.lcSysExtY);
-             WTInfo (WTI_DDCTXS + devix, CTX_SYSSENSX, &lc.lcSysSensX);
-             WTInfo (WTI_DDCTXS + devix, CTX_SYSSENSY, &lc.lcSysSensY);
+         active = FALSE;
+         (*p_WTInfoA) (WTI_CURSORS + cursorix, CSR_ACTIVE, &active);
+         if (!active)
+           continue;
+
+         /* Wacom tablets seem to report cursors corresponding to
+          * nonexistent pens or pucks. At least my ArtPad II reports
+          * six cursors: a puck, pressure stylus and eraser stylus,
+          * and then the same three again. I only have a
+          * pressure-sensitive pen. The puck instances, and the
+          * second instances of the styluses report physid zero. So
+          * at least for Wacom, skip cursors with physid zero.
+          */
+         (*p_WTInfoA) (WTI_CURSORS + cursorix, CSR_PHYSID, &physid);
+         if (strcmp (devname, "WACOM Tablet") == 0 && physid == 0)
+           continue;
+
+         gdkdev = g_object_new (GDK_TYPE_DEVICE, NULL);
+         (*p_WTInfoA) (WTI_CURSORS + cursorix, CSR_NAME, csrname);
+         gdkdev->info.name = g_strconcat (devname, " ", csrname, NULL);
+         gdkdev->info.source = GDK_SOURCE_PEN;
+         gdkdev->info.mode = GDK_MODE_SCREEN;
+         gdkdev->info.has_cursor = TRUE;
+         gdkdev->hctx = *hctx;
+         gdkdev->cursor = cursorix;
+         (*p_WTInfoA) (WTI_CURSORS + cursorix, CSR_PKTDATA, &gdkdev->pktdata);
+         gdkdev->info.num_axes = 0;
+         if (gdkdev->pktdata & PK_X)
+           gdkdev->info.num_axes++;
+         if (gdkdev->pktdata & PK_Y)
+           gdkdev->info.num_axes++;
+         if (gdkdev->pktdata & PK_NORMAL_PRESSURE)
+           gdkdev->info.num_axes++;
+         /* The wintab driver for the Wacom ArtPad II reports
+          * PK_ORIENTATION in CSR_PKTDATA, but the tablet doesn't
+          * actually sense tilt. Catch this by noticing that the
+          * orientation axis's azimuth resolution is zero.
+          */
+         if ((gdkdev->pktdata & PK_ORIENTATION)
+             && axis_or[0].axResolution == 0)
+           gdkdev->pktdata &= ~PK_ORIENTATION;
+         
+         if (gdkdev->pktdata & PK_ORIENTATION)
+           gdkdev->info.num_axes += 2; /* x and y tilt */
+         (*p_WTInfoA) (WTI_CURSORS + cursorix, CSR_NPBTNMARKS, &gdkdev->npbtnmarks);
+         gdkdev->info.axes = g_new (GdkDeviceAxis, gdkdev->info.num_axes);
+         gdkdev->axes = g_new (GdkAxisInfo, gdkdev->info.num_axes);
+         gdkdev->last_axis_data = g_new (gint, gdkdev->info.num_axes);
+         
+         k = 0;
+         if (gdkdev->pktdata & PK_X)
+           {
+             gdkdev->axes[k].xresolution =
+               gdkdev->axes[k].resolution = axis_x.axResolution / 65535.;
+             gdkdev->axes[k].xmin_value =
+               gdkdev->axes[k].min_value = axis_x.axMin;
+             gdkdev->axes[k].xmax_value =
+               gdkdev->axes[k].max_value = axis_x.axMax;
+             gdkdev->info.axes[k].use = GDK_AXIS_X;
+             gdkdev->info.axes[k].min = axis_x.axMin;
+             gdkdev->info.axes[k].max = axis_x.axMax;
+             k++;
            }
-         else
+         if (gdkdev->pktdata & PK_Y)
            {
-             lc = defcontext;
-             lc.lcOptions |= CXO_MESSAGES;
-             lc.lcMsgBase = WT_DEFBASE;
-             lc.lcPktRate = 50;
-             lc.lcPktData = PACKETDATA;
-             lc.lcPktMode = PACKETMODE;
-             lc.lcMoveMask = PACKETDATA;
-             lc.lcBtnUpMask = lc.lcBtnDnMask = ~0;
-#if 0
-             lc.lcOutExtY = -lc.lcOutExtY; /* Y grows downward */
-#else
-             lc.lcOutOrgX = axis_x.axMin;
-             lc.lcOutOrgY = axis_y.axMin;
-             lc.lcOutExtX = axis_x.axMax - axis_x.axMin;
-             lc.lcOutExtY = axis_y.axMax - axis_y.axMin;
-             lc.lcOutExtY = -lc.lcOutExtY; /* We want Y growing downward */
-#endif
+             gdkdev->axes[k].xresolution =
+               gdkdev->axes[k].resolution = axis_y.axResolution / 65535.;
+             gdkdev->axes[k].xmin_value =
+               gdkdev->axes[k].min_value = axis_y.axMin;
+             gdkdev->axes[k].xmax_value =
+               gdkdev->axes[k].max_value = axis_y.axMax;
+             gdkdev->info.axes[k].use = GDK_AXIS_Y;
+             gdkdev->info.axes[k].min = axis_y.axMin;
+             gdkdev->info.axes[k].max = axis_y.axMax;
+             k++;
            }
-#if DEBUG_WINTAB
-         GDK_NOTE (INPUT, (g_print("context for device %d:\n", devix),
-                          print_lc(&lc)));
-#endif
-         hctx = g_new (HCTX, 1);
-          if ((*hctx = WTOpen (GDK_WINDOW_HWND (wintab_window), &lc, TRUE)) == NULL)
+         if (gdkdev->pktdata & PK_NORMAL_PRESSURE)
            {
-             g_warning ("gdk_input_wintab_init: WTOpen failed");
-             return;
+             gdkdev->axes[k].xresolution =
+               gdkdev->axes[k].resolution = axis_npressure.axResolution / 65535.;
+             gdkdev->axes[k].xmin_value =
+               gdkdev->axes[k].min_value = axis_npressure.axMin;
+             gdkdev->axes[k].xmax_value =
+               gdkdev->axes[k].max_value = axis_npressure.axMax;
+             gdkdev->info.axes[k].use = GDK_AXIS_PRESSURE;
+             /* GIMP seems to expect values in the range 0-1 */
+             gdkdev->info.axes[k].min = 0.0; /*axis_npressure.axMin;*/
+             gdkdev->info.axes[k].max = 1.0; /*axis_npressure.axMax;*/
+             k++;
            }
-         GDK_NOTE (INPUT, g_print ("opened Wintab device %d %p\n",
-                                  devix, *hctx));
-
-         wintab_contexts = g_list_append (wintab_contexts, hctx);
-#if 0
-         WTEnable (*hctx, TRUE);
-#endif
-         WTOverlap (*hctx, TRUE);
-
-#if DEBUG_WINTAB
-         GDK_NOTE (INPUT, (g_print("context for device %d after WTOpen:\n", devix),
-                          print_lc(&lc)));
-#endif
-         for (cursorix = firstcsr; cursorix < firstcsr + ncsrtypes; cursorix++)
+         if (gdkdev->pktdata & PK_ORIENTATION)
            {
-             active = FALSE;
-             WTInfo (WTI_CURSORS + cursorix, CSR_ACTIVE, &active);
-             if (!active)
-               continue;
-             gdkdev = g_object_new (GDK_TYPE_DEVICE, NULL);
-             WTInfo (WTI_CURSORS + cursorix, CSR_NAME, csrname);
-             gdkdev->info.name = g_strconcat (devname, " ", csrname, NULL);
-             gdkdev->info.source = GDK_SOURCE_PEN;
-             gdkdev->info.mode = GDK_MODE_SCREEN;
-#if USE_SYSCONTEXT
-             gdkdev->info.has_cursor = TRUE;
-#else
-             gdkdev->info.has_cursor = FALSE;
-#endif
-             gdkdev->hctx = *hctx;
-             gdkdev->cursor = cursorix;
-             WTInfo (WTI_CURSORS + cursorix, CSR_PKTDATA, &gdkdev->pktdata);
-             gdkdev->info.num_axes = 0;
-             if (gdkdev->pktdata & PK_X)
-               gdkdev->info.num_axes++;
-             if (gdkdev->pktdata & PK_Y)
-               gdkdev->info.num_axes++;
-             if (gdkdev->pktdata & PK_NORMAL_PRESSURE)
-               gdkdev->info.num_axes++;
-             /* The wintab driver for the Wacom ArtPad II reports
-              * PK_ORIENTATION in CSR_PKTDATA, but the tablet doesn't
-              * actually sense tilt. Catch this by noticing that the
-              * orientation axis's azimuth resolution is zero.
-              */
-             if ((gdkdev->pktdata & PK_ORIENTATION)
-                 && axis_or[0].axResolution == 0)
-               gdkdev->pktdata &= ~PK_ORIENTATION;
-
-             if (gdkdev->pktdata & PK_ORIENTATION)
-               gdkdev->info.num_axes += 2; /* x and y tilt */
-             WTInfo (WTI_CURSORS + cursorix, CSR_NPBTNMARKS, &gdkdev->npbtnmarks);
-             gdkdev->info.axes = g_new (GdkDeviceAxis, gdkdev->info.num_axes);
-             gdkdev->axes = g_new (GdkAxisInfo, gdkdev->info.num_axes);
-             gdkdev->last_axis_data = g_new (gint, gdkdev->info.num_axes);
+             GdkAxisUse axis;
              
-             k = 0;
-             if (gdkdev->pktdata & PK_X)
-               {
-                 gdkdev->axes[k].xresolution =
-                   gdkdev->axes[k].resolution = axis_x.axResolution / 65535.;
-                 gdkdev->axes[k].xmin_value =
-                   gdkdev->axes[k].min_value = axis_x.axMin;
-                 gdkdev->axes[k].xmax_value =
-                   gdkdev->axes[k].max_value = axis_x.axMax;
-                 gdkdev->info.axes[k].use = GDK_AXIS_X;
-                 gdkdev->info.axes[k].min = axis_x.axMin;
-                 gdkdev->info.axes[k].min = axis_x.axMax;
-                 k++;
-               }
-             if (gdkdev->pktdata & PK_Y)
+             gdkdev->orientation_axes[0] = axis_or[0];
+             gdkdev->orientation_axes[1] = axis_or[1];
+             for (axis = GDK_AXIS_XTILT; axis <= GDK_AXIS_YTILT; axis++)
                {
+                 /* Wintab gives us aximuth and altitude, which
+                  * we convert to x and y tilt in the -1000..1000 range
+                  */
                  gdkdev->axes[k].xresolution =
-                   gdkdev->axes[k].resolution = axis_y.axResolution / 65535.;
+                   gdkdev->axes[k].resolution = 1000;
                  gdkdev->axes[k].xmin_value =
-                   gdkdev->axes[k].min_value = axis_y.axMin;
+                   gdkdev->axes[k].min_value = -1000;
                  gdkdev->axes[k].xmax_value =
-                   gdkdev->axes[k].max_value = axis_y.axMax;
-                 gdkdev->info.axes[k].use = GDK_AXIS_Y;
-                 gdkdev->info.axes[k].min = axis_y.axMin;
-                 gdkdev->info.axes[k].min = axis_y.axMax;
+                   gdkdev->axes[k].max_value = 1000;
+                 gdkdev->info.axes[k].use = axis;
+                 gdkdev->info.axes[k].min = -1000;
+                 gdkdev->info.axes[k].max = 1000;
                  k++;
                }
-             if (gdkdev->pktdata & PK_NORMAL_PRESSURE)
-               {
-                 gdkdev->axes[k].xresolution =
-                   gdkdev->axes[k].resolution = axis_npressure.axResolution / 65535.;
-                 gdkdev->axes[k].xmin_value =
-                   gdkdev->axes[k].min_value = axis_npressure.axMin;
-                 gdkdev->axes[k].xmax_value =
-                   gdkdev->axes[k].max_value = axis_npressure.axMax;
-                 gdkdev->info.axes[k].use = GDK_AXIS_PRESSURE;
-                 gdkdev->info.axes[k].min = axis_npressure.axMin;
-                 gdkdev->info.axes[k].min = axis_npressure.axMax;
-                 k++;
-               }
-             if (gdkdev->pktdata & PK_ORIENTATION)
-               {
-                 GdkAxisUse axis;
-
-                 gdkdev->orientation_axes[0] = axis_or[0];
-                 gdkdev->orientation_axes[1] = axis_or[1];
-                 for (axis = GDK_AXIS_XTILT; axis <= GDK_AXIS_YTILT; axis++)
-                   {
-                     /* Wintab gives us aximuth and altitude, which
-                      * we convert to x and y tilt in the -1000..1000 range
-                      */
-                     gdkdev->axes[k].xresolution =
-                       gdkdev->axes[k].resolution = 1000;
-                     gdkdev->axes[k].xmin_value =
-                       gdkdev->axes[k].min_value = -1000;
-                     gdkdev->axes[k].xmax_value =
-                       gdkdev->axes[k].max_value = 1000;
-                     gdkdev->info.axes[k].use = axis;
-                     gdkdev->info.axes[k].min = -1000;
-                     gdkdev->info.axes[k].min = 1000;
-                     k++;
-                   }
-               }
-             gdkdev->info.num_keys = 0;
-             gdkdev->info.keys = NULL;
-             GDK_NOTE (INPUT,
-                       g_print ("device: (%d) %s axes: %d\n",
-                                cursorix,
-                                gdkdev->info.name,
-                                gdkdev->info.num_axes));
-             for (i = 0; i < gdkdev->info.num_axes; i++)
-               GDK_NOTE (INPUT,
-                         g_print ("...axis %d: %d--%d@%d (%d--%d@%d)\n",
-                                  i,
-                                  gdkdev->axes[i].xmin_value, 
-                                  gdkdev->axes[i].xmax_value, 
-                                  gdkdev->axes[i].xresolution, 
-                                  gdkdev->axes[i].min_value, 
-                                  gdkdev->axes[i].max_value, 
-                                  gdkdev->axes[i].resolution));
-             _gdk_input_devices = g_list_append (_gdk_input_devices,
-                                                gdkdev);
            }
+         gdkdev->info.num_keys = 0;
+         gdkdev->info.keys = NULL;
+         GDK_NOTE (INPUT, g_print ("device: (%d) %s axes: %d\n",
+                                   cursorix,
+                                   gdkdev->info.name,
+                                   gdkdev->info.num_axes));
+         for (i = 0; i < gdkdev->info.num_axes; i++)
+           GDK_NOTE (INPUT, g_print ("... axis %d: %d--%d@%d (%d--%d@%d)\n",
+                                     i,
+                                     gdkdev->axes[i].xmin_value, 
+                                     gdkdev->axes[i].xmax_value, 
+                                     gdkdev->axes[i].xresolution, 
+                                     gdkdev->axes[i].min_value, 
+                                     gdkdev->axes[i].max_value, 
+                                     gdkdev->axes[i].resolution));
+         _gdk_input_devices = g_list_append (_gdk_input_devices,
+                                             gdkdev);
        }
     }
 }
@@ -497,34 +642,6 @@ decode_tilt (gint   *axis_data,
   axis_data[1] = sin (az) * cos (el) * 1000;
 }
 
-#if !USE_SYSCONTEXT
-
-static GdkInputWindow *
-gdk_input_window_find_within (GdkWindow *window)
-{
-  GList *list;
-  GdkWindow *tmpw;
-  GdkInputWindow *candidate = NULL;
-
-  for (list = _gdk_input_windows; list != NULL; list = list->next)
-    {
-      tmpw = ((GdkInputWindow *) (tmp_list->data))->window;
-      if (tmpw == window
-         || IsChild (GDK_WINDOW_HWND (window), GDK_WINDOW_HWND (tmpw)))
-       {
-         if (candidate)
-           return NULL;                /* Multiple hits */
-         candidate = (GdkInputWindow *) (list->data);
-       }
-    }
-
-  return candidate;
-}
-
-#endif /* USE_SYSCONTEXT */
-
-#endif /* HAVE_WINTAB */
-
 static void
 gdk_input_translate_coordinates (GdkDevicePrivate *gdkdev,
                                 GdkInputWindow   *input_window,
@@ -533,7 +650,7 @@ gdk_input_translate_coordinates (GdkDevicePrivate *gdkdev,
                                 gdouble          *x_out,
                                 gdouble          *y_out)
 {
-  GdkWindowImplWin32 *impl;
+  GdkWindowImplWin32 *impl, *root_impl;
 
   int i;
   int x_axis = 0;
@@ -566,8 +683,9 @@ gdk_input_translate_coordinates (GdkDevicePrivate *gdkdev,
 
   if (gdkdev->info.mode == GDK_MODE_SCREEN) 
     {
-      x_scale = GetSystemMetrics (SM_CXSCREEN) / device_width;
-      y_scale = GetSystemMetrics (SM_CYSCREEN) / device_height;
+      root_impl = GDK_WINDOW_IMPL_WIN32 (GDK_WINDOW_OBJECT (_gdk_root)->impl);
+      x_scale = root_impl->width / device_width;
+      y_scale = root_impl->height / device_height;
 
       x_offset = - input_window->root_x;
       y_offset = - input_window->root_y;
@@ -634,24 +752,9 @@ gdk_input_get_root_relative_geometry (HWND w,
   GetWindowRect (w, &rect);
 
   if (x_ret)
-    *x_ret = rect.left;
+    *x_ret = rect.left + _gdk_offset_x;
   if (y_ret)
-    *y_ret = rect.top;
-}
-
-GdkTimeCoord *
-gdk_input_motion_events (GdkWindow *window,
-                        guint32    deviceid,
-                        guint32    start,
-                        guint32    stop,
-                        gint      *nevents_return)
-{
-  g_return_val_if_fail (window != NULL, NULL);
-  if (GDK_WINDOW_DESTROYED (window))
-    return NULL;
-
-  *nevents_return = 0;
-  return NULL;         /* ??? */
+    *y_ret = rect.top + _gdk_offset_y;
 }
 
 void
@@ -685,26 +788,90 @@ _gdk_input_enter_event (GdkWindow        *window)
   input_window->root_y = root_y;
 }
 
+/*
+ * Get the currently active keyboard modifiers (ignoring the mouse buttons)
+ * We could use gdk_window_get_pointer but that function does a lot of other
+ * expensive things besides getting the modifiers. This code is somewhat based
+ * on build_pointer_event_state from gdkevents-win32.c
+ */
+static guint
+get_modifier_key_state (void)
+{
+  guint state;
+  
+  state = 0;
+  /* High-order bit is up/down, low order bit is toggled/untoggled */
+  if (GetKeyState (VK_CONTROL) < 0)
+    state |= GDK_CONTROL_MASK;
+  if (GetKeyState (VK_SHIFT) < 0)
+    state |= GDK_SHIFT_MASK;
+  if (GetKeyState (VK_MENU) < 0)
+    state |= GDK_MOD1_MASK;
+  if (GetKeyState (VK_CAPITAL) & 0x1)
+    state |= GDK_LOCK_MASK;
+
+  return state;
+}
+
+static guint ignore_core_timer = 0;
+
+static gboolean
+ignore_core_timefunc (gpointer data)
+{
+  /* The delay has passed */
+  _gdk_input_ignore_core = FALSE;
+  ignore_core_timer = 0;
+
+  return FALSE; /* remove timeout */
+}
+
+/*
+ * Set or unset the _gdk_input_ignore_core variable that tells GDK
+ * to ignore events for the core pointer when the tablet is in proximity
+ * The unsetting is delayed slightly so that if a tablet event arrives
+ * just after proximity out, it does not cause a core pointer event
+ * which e.g. causes GIMP to switch tools.
+ */
+static void
+set_ignore_core (gboolean ignore)
+{
+  if (ignore)
+    {
+      _gdk_input_ignore_core = TRUE;
+      /* Remove any pending clear */
+      if (ignore_core_timer)
+        {
+         g_source_remove (ignore_core_timer);
+         ignore_core_timer = 0;
+       }
+    }
+  else
+    if (!ignore_core_timer)
+      ignore_core_timer = gdk_threads_add_timeout (PROXIMITY_OUT_DELAY,
+                                        ignore_core_timefunc, NULL);
+}
+
 gboolean 
 _gdk_input_other_event (GdkEvent  *event,
                        MSG       *msg,
                        GdkWindow *window)
 {
-#ifdef HAVE_WINTAB
-#if !USE_SYSCONTEXT
-  GdkWindow *current_window;
-#endif
   GdkDisplay *display;
-  GdkWindowObject *obj;
-  GdkWindowImplWin32 *impl;
+  GdkWindowObject *obj, *grab_obj;
   GdkInputWindow *input_window;
   GdkDevicePrivate *gdkdev = NULL;
   GdkEventMask masktest;
+  guint key_state;
   POINT pt;
 
   PACKET packet;
   gint k;
   gint x, y;
+  guint translated_buttons, button_diff, button_mask;
+  /* Translation from tablet button state to GDK button state for
+   * buttons 1-3 - swap button 2 and 3.
+   */
+  static guint button_map[8] = {0, 1, 4, 5, 2, 3, 6, 7};
 
   if (event->any.window != wintab_window)
     {
@@ -712,44 +879,39 @@ _gdk_input_other_event (GdkEvent  *event,
       return FALSE;
     }
 
-#if USE_SYSCONTEXT
   window = gdk_window_at_pointer (&x, &y);
   if (window == NULL)
-    window = _gdk_parent_root;
+    window = _gdk_root;
 
   g_object_ref (window);
   display = gdk_drawable_get_display (window);
 
   GDK_NOTE (EVENTS_OR_INPUT,
-           g_print ("gdk_input_win32_other_event: window=%p (%d,%d)\n",
+           g_print ("gdk_input_win32_other_event: window=%p %+d%+d\n",
                     GDK_WINDOW_HWND (window), x, y));
   
-#else
-  /* ??? This code is pretty bogus */
-  current_window = gdk_win32_handle_table_lookup (GetActiveWindow ());
-  if (current_window == NULL)
-    return FALSE;
-  
-  input_window = gdk_input_window_find_within (current_window);
-  if (input_window == NULL)
-    return FALSE;
-#endif
-
   if (msg->message == WT_PACKET)
     {
-      if (!WTPacket ((HCTX) msg->lParam, msg->wParam, &packet))
+      if (!(*p_WTPacket) ((HCTX) msg->lParam, msg->wParam, &packet))
        return FALSE;
     }
 
   obj = GDK_WINDOW_OBJECT (window);
-  impl = GDK_WINDOW_IMPL_WIN32 (obj->impl);
 
   switch (msg->message)
     {
     case WT_PACKET:
-      if (window == _gdk_parent_root)
+      /* Don't produce any button or motion events while a window is being
+       * moved or resized, see bug #151090.
+       */
+      if (_sizemove_in_progress)
+       {
+         GDK_NOTE (EVENTS_OR_INPUT, g_print ("... ignored when moving/sizing\n"));
+         return FALSE;
+       }
+      if (window == _gdk_root && x_grab_window == NULL)
        {
-         GDK_NOTE (EVENTS_OR_INPUT, g_print ("...is root\n"));
+         GDK_NOTE (EVENTS_OR_INPUT, g_print ("... is root\n"));
          return FALSE;
        }
 
@@ -776,23 +938,41 @@ _gdk_input_other_event (GdkEvent  *event,
 
       g_assert (k == gdkdev->info.num_axes);
 
-      if (HIWORD (packet.pkButtons) != TBN_NONE)
+      translated_buttons = button_map[packet.pkButtons & 0x07] | (packet.pkButtons & ~0x07);
+
+      if (translated_buttons != gdkdev->button_state)
        {
+         /* At least one button has changed state so produce a button event
+          * If more than one button has changed state (unlikely),
+          * just care about the first and act on the next the next time
+          * we get a packet
+          */
+         button_diff = translated_buttons ^ gdkdev->button_state;
+         
          /* Gdk buttons are numbered 1.. */
-         event->button.button = 1 + LOWORD (packet.pkButtons);
+         event->button.button = 1;
 
-         if (HIWORD (packet.pkButtons) == TBN_UP)
+         for (button_mask = 1; button_mask != 0x80000000;
+              button_mask <<= 1, event->button.button++)
+           {
+             if (button_diff & button_mask)
+               {
+                 /* Found a button that has changed state */
+                 break;
+               }
+           }
+
+         if (!(translated_buttons & button_mask))
            {
              event->any.type = GDK_BUTTON_RELEASE;
              masktest = GDK_BUTTON_RELEASE_MASK;
-             gdkdev->button_state &= ~(1 << LOWORD (packet.pkButtons));
            }
          else
            {
              event->any.type = GDK_BUTTON_PRESS;
              masktest = GDK_BUTTON_PRESS_MASK;
-             gdkdev->button_state |= 1 << LOWORD (packet.pkButtons);
            }
+         gdkdev->button_state ^= button_mask;
        }
       else
        {
@@ -806,17 +986,46 @@ _gdk_input_other_event (GdkEvent  *event,
            masktest |= GDK_BUTTON_MOTION_MASK | GDK_BUTTON3_MOTION_MASK;
        }
 
+      /* See if input is grabbed */
+      /* FIXME: x_grab_owner_events should probably be handled somehow */
+      if (x_grab_window != NULL)
+       {
+         grab_obj = GDK_WINDOW_OBJECT (x_grab_window);
+         if (!GDK_WINDOW_IMPL_WIN32 (grab_obj->impl)->extension_events_selected
+             || !(grab_obj->extension_events & masktest)
+             || !(x_grab_mask && masktest))
+           {
+             GDK_NOTE (EVENTS_OR_INPUT, 
+                       g_print ("... grabber doesn't want it\n"));
+             return FALSE;
+           }
+         GDK_NOTE (EVENTS_OR_INPUT, g_print ("... to grabber\n"));
+
+         g_object_ref(x_grab_window);
+         g_object_unref(window);
+         window = x_grab_window;
+         obj = grab_obj;
+       }
       /* Now we can check if the window wants the event, and
        * propagate if necessary.
        */
-    dijkstra:
-      if (!impl->extension_events_selected
+    loop:
+      if (!GDK_WINDOW_IMPL_WIN32 (obj->impl)->extension_events_selected
          || !(obj->extension_events & masktest))
        {
-         GDK_NOTE (EVENTS_OR_INPUT, g_print ("...not selected\n"));
+         GDK_NOTE (EVENTS_OR_INPUT, g_print ("... not selected\n"));
 
-         if (obj->parent == GDK_WINDOW_OBJECT (_gdk_parent_root))
+         if (obj->parent == GDK_WINDOW_OBJECT (_gdk_root))
            return FALSE;
+
+         /* It is not good to propagate the extended events up to the parent
+          * if this window wants normal (not extended) motion/button events */
+         if (obj->event_mask & masktest)
+           {
+             GDK_NOTE (EVENTS_OR_INPUT, 
+                       g_print ("... wants ordinary event, ignoring this\n"));
+             return FALSE;
+           }
          
          pt.x = x;
          pt.y = y;
@@ -828,9 +1037,9 @@ _gdk_input_other_event (GdkEvent  *event,
          ScreenToClient (GDK_WINDOW_HWND (window), &pt);
          x = pt.x;
          y = pt.y;
-         GDK_NOTE (EVENTS_OR_INPUT, g_print ("...propagating to %p (%d,%d)\n",
+         GDK_NOTE (EVENTS_OR_INPUT, g_print ("... propagating to %p %+d%+d\n",
                                              GDK_WINDOW_HWND (window), x, y));
-         goto dijkstra;
+         goto loop;
        }
 
       input_window = _gdk_input_window_find (window);
@@ -842,30 +1051,31 @@ _gdk_input_other_event (GdkEvent  *event,
        return FALSE;
 
       event->any.window = window;
-
+      key_state = get_modifier_key_state ();
       if (event->any.type == GDK_BUTTON_PRESS
          || event->any.type == GDK_BUTTON_RELEASE)
        {
          event->button.time = _gdk_win32_get_next_tick (msg->time);
          event->button.device = &gdkdev->info;
          
-#if 0
-#if USE_SYSCONTEXT
-         /* Buttons 1 to 3 will come in as WM_[LMR]BUTTON{DOWN,UP} */
-         if (event->button.button <= 3)
-           return FALSE;
-#endif
-#endif
+         event->button.axes = g_new(gdouble, gdkdev->info.num_axes);
+
          gdk_input_translate_coordinates (gdkdev, input_window,
                                           gdkdev->last_axis_data,
                                           event->button.axes,
                                           &event->button.x, 
                                           &event->button.y);
 
+         /* Also calculate root coordinates. Note that input_window->root_x
+            is in GDK root coordinates. */
+         event->button.x_root = event->button.x + input_window->root_x;
+         event->button.y_root = event->button.y + input_window->root_y;
+
          event->button.state = ((gdkdev->button_state << 8)
                                 & (GDK_BUTTON1_MASK | GDK_BUTTON2_MASK
                                    | GDK_BUTTON3_MASK | GDK_BUTTON4_MASK
-                                   | GDK_BUTTON5_MASK));
+                                   | GDK_BUTTON5_MASK))
+                               | key_state;
          GDK_NOTE (EVENTS_OR_INPUT,
                    g_print ("WINTAB button %s:%d %g,%g\n",
                             (event->button.type == GDK_BUTTON_PRESS ?
@@ -879,57 +1089,28 @@ _gdk_input_other_event (GdkEvent  *event,
          event->motion.is_hint = FALSE;
          event->motion.device = &gdkdev->info;
 
+         event->motion.axes = g_new(gdouble, gdkdev->info.num_axes);
+
          gdk_input_translate_coordinates (gdkdev, input_window,
                                           gdkdev->last_axis_data,
                                           event->motion.axes,
                                           &event->motion.x, 
                                           &event->motion.y);
 
+         /* Also calculate root coordinates. Note that input_window->root_x
+            is in GDK root coordinates. */
+         event->motion.x_root = event->motion.x + input_window->root_x;
+         event->motion.y_root = event->motion.y + input_window->root_y;
+
          event->motion.state = ((gdkdev->button_state << 8)
                                 & (GDK_BUTTON1_MASK | GDK_BUTTON2_MASK
                                    | GDK_BUTTON3_MASK | GDK_BUTTON4_MASK
-                                   | GDK_BUTTON5_MASK));
+                                   | GDK_BUTTON5_MASK))
+                               | key_state;
 
          GDK_NOTE (EVENTS_OR_INPUT,
                    g_print ("WINTAB motion: %g,%g\n",
                             event->motion.x, event->motion.y));
-
-         /* Check for missing release or press events for the normal
-          * pressure button. At least on my ArtPadII I sometimes miss a
-          * release event?
-          */
-         if ((gdkdev->pktdata & PK_NORMAL_PRESSURE
-              && (event->motion.state & GDK_BUTTON1_MASK)
-              && packet.pkNormalPressure <= MAX (0, gdkdev->npbtnmarks[0] - 2))
-             || (gdkdev->pktdata & PK_NORMAL_PRESSURE
-                 && !(event->motion.state & GDK_BUTTON1_MASK)
-                 && packet.pkNormalPressure > gdkdev->npbtnmarks[1] + 2))
-           {
-             GdkEvent *event2 = gdk_event_copy (event);
-             if (event->motion.state & GDK_BUTTON1_MASK)
-               {
-                 event2->button.type = GDK_BUTTON_RELEASE;
-                 gdkdev->button_state &= ~1;
-               }
-             else
-               {
-                 event2->button.type = GDK_BUTTON_PRESS;
-                 gdkdev->button_state |= 1;
-               }
-             event2->button.state = ((gdkdev->button_state << 8)
-                                     & (GDK_BUTTON1_MASK | GDK_BUTTON2_MASK
-                                        | GDK_BUTTON3_MASK | GDK_BUTTON4_MASK
-                                        | GDK_BUTTON5_MASK));
-             event2->button.button = 1;
-             GDK_NOTE (EVENTS_OR_INPUT,
-                       g_print ("WINTAB synthesized button %s: %d %g,%gg\n",
-                                (event2->button.type == GDK_BUTTON_PRESS ?
-                                 "press" : "release"),
-                                event2->button.button,
-                                event2->button.x,
-                                event2->button.y));
-             _gdk_event_queue_append (display, event2);
-           }
        }
       return TRUE;
 
@@ -937,12 +1118,12 @@ _gdk_input_other_event (GdkEvent  *event,
       if (LOWORD (msg->lParam) == 0)
        {
          event->proximity.type = GDK_PROXIMITY_OUT;
-         _gdk_input_ignore_core = FALSE;
+         set_ignore_core (FALSE);
        }
       else
        {
          event->proximity.type = GDK_PROXIMITY_IN;
-         _gdk_input_ignore_core = TRUE;
+         set_ignore_core (TRUE);
        }
       event->proximity.time = _gdk_win32_get_next_tick (msg->time);
       event->proximity.device = &gdkdev->info;
@@ -953,7 +1134,6 @@ _gdk_input_other_event (GdkEvent  *event,
                          "in" : "out")));
       return TRUE;
     }
-#endif
   return FALSE;
 }
 
@@ -961,11 +1141,9 @@ gboolean
 _gdk_input_enable_window (GdkWindow        *window,
                          GdkDevicePrivate *gdkdev)
 {
-#ifdef HAVE_SOME_XINPUT
   GdkWindowImplWin32 *impl = GDK_WINDOW_IMPL_WIN32 (GDK_WINDOW_OBJECT (window)->impl);
 
   impl->extension_events_selected = TRUE;
-#endif
 
   return TRUE;
 }
@@ -974,11 +1152,9 @@ gboolean
 _gdk_input_disable_window (GdkWindow        *window,
                           GdkDevicePrivate *gdkdev)
 {
-#ifdef HAVE_SOME_XINPUT
   GdkWindowImplWin32 *impl = GDK_WINDOW_IMPL_WIN32 (GDK_WINDOW_OBJECT (window)->impl);
 
   impl->extension_events_selected = FALSE;
-#endif
 
   return TRUE;
 }
@@ -990,7 +1166,6 @@ _gdk_input_grab_pointer (GdkWindow    *window,
                         GdkWindow    *confine_to,
                         guint32       time)
 {
-#ifdef HAVE_SOME_XINPUT
   GdkInputWindow *input_window, *new_window;
   gboolean need_ungrab;
   GdkDevicePrivate *gdkdev;
@@ -1023,6 +1198,11 @@ _gdk_input_grab_pointer (GdkWindow    *window,
   if (new_window)
     {
       new_window->grabbed = TRUE;
+      x_grab_window = window;
+      x_grab_mask = event_mask;
+      x_grab_owner_events = owner_events;
+
+      /* FIXME: Do we need to handle confine_to and time? */
       
       tmp_list = _gdk_input_devices;
       while (tmp_list)
@@ -1052,6 +1232,8 @@ _gdk_input_grab_pointer (GdkWindow    *window,
     }
   else
     { 
+      x_grab_window = NULL;
+#if 0
       tmp_list = _gdk_input_devices;
       while (tmp_list)
        {
@@ -1068,8 +1250,8 @@ _gdk_input_grab_pointer (GdkWindow    *window,
          
          tmp_list = tmp_list->next;
        }
-    }
 #endif
+    }
 
   return GDK_GRAB_SUCCESS;
 }
@@ -1077,7 +1259,6 @@ _gdk_input_grab_pointer (GdkWindow    *window,
 void 
 _gdk_input_ungrab_pointer (guint32 time)
 {
-#ifdef HAVE_SOME_XINPUT
   GdkInputWindow *input_window;
   GdkDevicePrivate *gdkdev;
   GList *tmp_list;
@@ -1109,7 +1290,7 @@ _gdk_input_ungrab_pointer (guint32 time)
          tmp_list = tmp_list->next;
        }
     }
-#endif
+  x_grab_window = NULL;
 }
 
 gboolean
@@ -1149,42 +1330,51 @@ gdk_device_get_state (GdkDevice       *device,
       GdkDevicePrivate *gdkdev;
       GdkInputWindow *input_window;
       
-      if (mask)
-       gdk_window_get_pointer (window, NULL, NULL, mask);
-      
       gdkdev = (GdkDevicePrivate *)device;
+      /* For now just use the last known button and axis state of the device.
+       * Since graphical tablets send an insane amount of motion events each
+       * second, the info should be fairly up to date */
+      if (mask)
+       {
+         gdk_window_get_pointer (window, NULL, NULL, mask);
+         *mask &= 0xFF; /* Mask away core pointer buttons */
+         *mask |= ((gdkdev->button_state << 8)
+                   & (GDK_BUTTON1_MASK | GDK_BUTTON2_MASK
+                      | GDK_BUTTON3_MASK | GDK_BUTTON4_MASK
+                      | GDK_BUTTON5_MASK));
+       }
       input_window = _gdk_input_window_find (window);
       g_return_if_fail (input_window != NULL);
+      /* For some reason, input_window is sometimes NULL when I use The GIMP 2
+       * (bug #141543?). Avoid crashing if debugging is disabled. */
+      if (axes && gdkdev->last_axis_data && input_window)
+       gdk_input_translate_coordinates (gdkdev, input_window,
+                                        gdkdev->last_axis_data,
+                                        axes, NULL, NULL);
+    }
+}
 
-#if 0 /* FIXME */
-      state = XQueryDeviceState (gdk_display, gdkdev->xdevice);
-      input_class = state->data;
-      for (i = 0; i < state->num_classes; i++)
-       {
-         switch (input_class->class)
-           {
-           case ValuatorClass:
-             if (axes)
-               gdk_input_translate_coordinates (gdkdev, input_window,
-                                                ((XValuatorState *)input_class)->valuators,
-                                                axes, NULL, NULL);
-             break;
-             
-           case ButtonClass:
-             if (mask)
-               {
-                 *mask &= 0xFF;
-                 if (((XButtonState *)input_class)->num_buttons > 0)
-                   *mask |= ((XButtonState *)input_class)->buttons[0] << 7;
-                 /* GDK_BUTTON1_MASK = 1 << 8, and button n is stored
-                  * in bit 1<<(n%8) in byte n/8. n = 1,2,... */
-               }
-             break;
-           }
-         input_class = (XInputClass *)(((char *)input_class)+input_class->length);
-       }
-      XFreeDeviceState (state);
-#endif
+void
+_gdk_input_set_tablet_active (void)
+{
+  GList *tmp_list;
+  HCTX *hctx;
+
+  /* Bring the contexts to the top of the overlap order when one of the
+   * application's windows is activated */
+  
+  if (!wintab_contexts)
+    return; /* No tablet devices found, or Wintab not initialized yet */
+  
+  GDK_NOTE (INPUT, g_print ("_gdk_input_set_tablet_active: "
+       "Bringing Wintab contexts to the top of the overlap order\n"));
+
+  tmp_list = wintab_contexts;
+  while (tmp_list)
+    {
+      hctx = (HCTX *) (tmp_list->data);
+      (*p_WTOverlap) (*hctx, TRUE);
+      tmp_list = tmp_list->next;
     }
 }
 
@@ -1195,9 +1385,13 @@ _gdk_input_init (GdkDisplay *display)
   _gdk_input_devices = NULL;
 
   _gdk_init_input_core (display);
-#ifdef HAVE_WINTAB
-  gdk_input_wintab_init ();
-#endif /* HAVE_WINTAB */
+#ifdef WINTAB_NO_LAZY_INIT
+  /* Normally, Wintab is only initialized when the application performs
+   * an action that requires it, such as enabling extended input events
+   * for a window or enumerating the devices.
+   */
+  _gdk_input_wintab_init_check ();
+#endif /* WINTAB_NO_LAZY_INIT */
 
   _gdk_input_devices = g_list_append (_gdk_input_devices, display->core_pointer);
 }