]> Pileus Git - ~andy/gtk/blobdiff - gdk/win32/gdkinput-win32.c
Avoid spurious core pointer events when the tablet pen is lifted.
[~andy/gtk] / gdk / win32 / gdkinput-win32.c
index a6eb0fbaf29d39d5ac06f8727aa53d303e38e8d7..c7bb9a3aa6ceb3ca276c949c3b2997c436ec6212 100644 (file)
@@ -1,6 +1,6 @@
 /* GDK - The GIMP Drawing Kit
  * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
- * Copyright (C) 1999 Tor Lillqvist
+ * Copyright (C) 1998-2002 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>
 #define PACKETMODE (PK_BUTTONS)
 #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 */
 
+#define PROXIMITY_OUT_DELAY 200 /* In milliseconds, see set_ignore_core */
+
 #endif
 
 #if defined(HAVE_WINTAB) || defined(HAVE_WHATEVER_OTHER)
 
 /* 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 GList     *wintab_contexts = NULL;
 
-static GdkWindow *wintab_window;
+static GdkWindow *wintab_window = NULL;
 
 #endif /* HAVE_WINTAB */
 
+#ifdef HAVE_SOME_XINPUT
+
+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;
+
+#endif /* HAVE_SOME_XINPUT */
+
 #ifdef HAVE_WINTAB
 
 static GdkDevicePrivate *
@@ -109,7 +109,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");
@@ -195,13 +194,13 @@ print_lc(LOGCONTEXT *lc)
 
 #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;
@@ -209,31 +208,25 @@ gdk_input_wintab_init (void)
   int i, k;
   int devix, cursorix;
   char devname[100], csrname[100];
+  BOOL defcontext_done;
 
-  _gdk_input_devices = NULL;
+  if (wintab_initialized)
+    return;
+  
+  wintab_initialized = TRUE;
+  
   wintab_contexts = NULL;
 
-  if (!gdk_input_ignore_wintab &&
+  if (!_gdk_input_ignore_wintab &&
       WTInfo (0, 0, NULL))
     {
       WTInfo (WTI_INTERFACE, IFC_SPECVERSION, &specversion);
-      GDK_NOTE (MISC, g_print ("Wintab interface version %d.%d\n",
+      GDK_NOTE (INPUT, g_print ("Wintab interface version %d.%d\n",
                               HIBYTE (specversion), LOBYTE (specversion)));
-#if USE_SYSCONTEXT
-      WTInfo (WTI_DEFSYSCTX, 0, &defcontext);
-#if DEBUG_WINTAB
-      GDK_NOTE (MISC, (g_print("DEFSYSCTX:\n"), print_lc(&defcontext)));
-#endif
-#else
-      WTInfo (WTI_DEFCONTEXT, 0, &defcontext);
-#if DEBUG_WINTAB
-      GDK_NOTE (MISC, (g_print("DEFCONTEXT:\n"), print_lc(&defcontext)));
-#endif
-#endif
       WTInfo (WTI_INTERFACE, IFC_NDEVICES, &ndevices);
       WTInfo (WTI_INTERFACE, IFC_NCURSORS, &ncursors);
 #if DEBUG_WINTAB
-      GDK_NOTE (MISC, g_print ("NDEVICES: %d, NCURSORS: %d\n",
+      GDK_NOTE (INPUT, g_print ("NDEVICES: %d, NCURSORS: %d\n",
                               ndevices, ncursors));
 #endif
       /* Create a dummy window to receive wintab events */
@@ -246,14 +239,19 @@ gdk_input_wintab_init (void)
       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_init: gdk_window_new failed");
+         g_warning ("gdk_input_wintab_init: gdk_window_new failed");
          return;
        }
-      gdk_drawable_ref (wintab_window);
+      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.
+          */
 
          WTInfo (WTI_DEVICES + devix, DVC_NAME, devname);
       
@@ -265,75 +263,50 @@ gdk_input_wintab_init (void)
          WTInfo (WTI_DEVICES + devix, DVC_NPRESSURE, &axis_npressure);
          WTInfo (WTI_DEVICES + devix, DVC_ORIENTATION, axis_or);
 
+         defcontext_done = FALSE;
          if (HIBYTE (specversion) > 1 || LOBYTE (specversion) >= 1)
            {
-             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;
+             /* Try to get device-specific default context */
+             /* Some drivers, e.g. Aiptek, don't provide this info */
+             if (WTInfo (WTI_DSCTXS + devix, 0, &lc) > 0)
+               defcontext_done = TRUE;
+#if DEBUG_WINTAB
+             if (defcontext_done)
+               GDK_NOTE (INPUT, (g_print("Using device-specific default context\n")));
+             else
+               GDK_NOTE (INPUT, (g_print("Note: Driver did not provide device specific default context info despite claiming to support version 1.1\n")));
 #endif
-             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);
            }
-         else
-           {
-             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 */
+
+         if (!defcontext_done)
+           WTInfo (WTI_DEFSYSCTX, 0, &lc);
+#if DEBUG_WINTAB
+         GDK_NOTE (INPUT, (g_print("Default context:\n"), print_lc(&lc)));
 #endif
-           }
+         lc.lcOptions |= CXO_MESSAGES;
+         lc.lcStatus = 0;
+         lc.lcMsgBase = WT_DEFBASE;
+         lc.lcPktRate = 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 (MISC, (g_print("context for device %d:\n", devix),
+         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)
            {
-             g_warning ("_gdk_input_init: WTOpen failed");
+             g_warning ("gdk_input_wintab_init: WTOpen failed");
              return;
            }
-         GDK_NOTE (MISC, g_print ("opened Wintab device %d %p\n",
+         GDK_NOTE (INPUT, g_print ("opened Wintab device %d %p\n",
                                   devix, *hctx));
 
          wintab_contexts = g_list_append (wintab_contexts, hctx);
@@ -343,25 +316,35 @@ gdk_input_wintab_init (void)
          WTOverlap (*hctx, TRUE);
 
 #if DEBUG_WINTAB
-         GDK_NOTE (MISC, (g_print("context for device %d after WTOpen:\n", devix),
+         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 = 128; i >= 1; i >>= 1)
+           {
+             if (WTQueueSizeSet(*hctx, i))
+               {
+                 GDK_NOTE (INPUT, g_print("Queue size set to %d\n", i));
+                 break;
+               }
+           }
+         if (!i)
+           GDK_NOTE (INPUT, g_print("Whoops, no queue size could be set\n"));
          for (cursorix = firstcsr; cursorix < firstcsr + ncsrtypes; cursorix++)
            {
              active = FALSE;
              WTInfo (WTI_CURSORS + cursorix, CSR_ACTIVE, &active);
              if (!active)
                continue;
-             gdkdev = g_new (GdkDevicePrivate, 1);
+             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);
@@ -399,7 +382,7 @@ gdk_input_wintab_init (void)
                    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;
+                 gdkdev->info.axes[k].max = axis_x.axMax;
                  k++;
                }
              if (gdkdev->pktdata & PK_Y)
@@ -412,7 +395,7 @@ gdk_input_wintab_init (void)
                    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->info.axes[k].max = axis_y.axMax;
                  k++;
                }
              if (gdkdev->pktdata & PK_NORMAL_PRESSURE)
@@ -424,8 +407,9 @@ gdk_input_wintab_init (void)
                  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;
+                 /* 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++;
                }
              if (gdkdev->pktdata & PK_ORIENTATION)
@@ -447,20 +431,20 @@ gdk_input_wintab_init (void)
                        gdkdev->axes[k].max_value = 1000;
                      gdkdev->info.axes[k].use = axis;
                      gdkdev->info.axes[k].min = -1000;
-                     gdkdev->info.axes[k].min = 1000;
+                     gdkdev->info.axes[k].max = 1000;
                      k++;
                    }
                }
              gdkdev->info.num_keys = 0;
              gdkdev->info.keys = NULL;
-             GDK_NOTE (EVENTS,
+             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 (EVENTS,
-                         g_print ("...axis %d: %d--%d@%d (%d--%d@%d)\n",
+               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, 
@@ -497,32 +481,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
@@ -533,7 +491,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 +524,9 @@ gdk_input_translate_coordinates (GdkDevicePrivate *gdkdev,
 
   if (gdkdev->info.mode == GDK_MODE_SCREEN) 
     {
-      x_scale = gdk_screen_width() / device_width;
-      y_scale = gdk_screen_height() / 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,34 +593,18 @@ 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
-_gdk_input_configure_event (GdkEventConfigure *event,
-                           GdkWindow         *window)
+_gdk_input_configure_event (GdkWindow         *window)
 {
   GdkInputWindow *input_window;
   int root_x, root_y;
 
-  input_window = gdk_input_window_find (window);
+  input_window = _gdk_input_window_find (window);
   g_return_if_fail (window != NULL);
 
   gdk_input_get_root_relative_geometry (GDK_WINDOW_HWND (window),
@@ -672,13 +615,12 @@ _gdk_input_configure_event (GdkEventConfigure *event,
 }
 
 void 
-_gdk_input_enter_event (GdkEventCrossing *event, 
-                       GdkWindow        *window)
+_gdk_input_enter_event (GdkWindow        *window)
 {
   GdkInputWindow *input_window;
   int root_x, root_y;
 
-  input_window = gdk_input_window_find (window);
+  input_window = _gdk_input_window_find (window);
   g_return_if_fail (window != NULL);
 
   gdk_input_get_root_relative_geometry (GDK_WINDOW_HWND (window), &root_x, &root_y);
@@ -687,20 +629,84 @@ _gdk_input_enter_event (GdkEventCrossing *event,
   input_window->root_y = root_y;
 }
 
-gint 
+/*
+ * 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;
+}
+
+#ifdef HAVE_WINTAB
+
+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 = g_timeout_add (PROXIMITY_OUT_DELAY,
+                                        ignore_core_timefunc, NULL);
+}
+#endif /* HAVE_WINTAB */
+
+gboolean 
 _gdk_input_other_event (GdkEvent  *event,
                        MSG       *msg,
                        GdkWindow *window)
 {
 #ifdef HAVE_WINTAB
-#if !USE_SYSCONTEXT
-  GdkWindow *current_window;
-#endif
-  GdkWindowObject *obj;
-  GdkWindowImplWin32 *impl;
+  GdkDisplay *display;
+  GdkWindowObject *obj, *grab_obj;
   GdkInputWindow *input_window;
-  GdkDevicePrivate *gdkdev;
+  GdkDevicePrivate *gdkdev = NULL;
   GdkEventMask masktest;
+  guint key_state;
   POINT pt;
 
   PACKET packet;
@@ -713,28 +719,17 @@ _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;
 
-  gdk_drawable_ref (window);
+  g_object_ref (window);
+  display = gdk_drawable_get_display (window);
 
-  GDK_NOTE (EVENTS,
-           g_print ("gdk_input_win32_other_event: window=%#x (%d,%d)\n",
-                    (guint) 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;
+  GDK_NOTE (EVENTS_OR_INPUT,
+           g_print ("gdk_input_win32_other_event: window=%p %+d%+d\n",
+                    GDK_WINDOW_HWND (window), x, y));
   
-  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))
@@ -742,14 +737,20 @@ _gdk_input_other_event (GdkEvent  *event,
     }
 
   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, g_print ("...is root\n"));
+         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"));
          return FALSE;
        }
 
@@ -806,34 +807,63 @@ _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
+      if (!GDK_WINDOW_IMPL_WIN32 (obj->impl)->extension_events_selected
          || !(obj->extension_events & masktest))
        {
-         GDK_NOTE (EVENTS, 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;
          ClientToScreen (GDK_WINDOW_HWND (window), &pt);
-         gdk_drawable_unref (window);
+         g_object_unref (window);
          window = (GdkWindow *) obj->parent;
          obj = GDK_WINDOW_OBJECT (window);
-         gdk_drawable_ref (window);
+         g_object_ref (window);
          ScreenToClient (GDK_WINDOW_HWND (window), &pt);
          x = pt.x;
          y = pt.y;
-         GDK_NOTE (EVENTS, g_print ("...propagating to %#x, (%d,%d)\n",
-                                    (guint) GDK_WINDOW_HWND (window), x, y));
+         GDK_NOTE (EVENTS_OR_INPUT, g_print ("... propagating to %p %+d%+d\n",
+                                             GDK_WINDOW_HWND (window), x, y));
          goto dijkstra;
        }
 
-      input_window = gdk_input_window_find (window);
+      input_window = _gdk_input_window_find (window);
 
       g_assert (input_window != NULL);
 
@@ -842,55 +872,66 @@ _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 = msg->time;
+         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_NOTE (EVENTS, g_print ("WINTAB button %s:%d %g,%g\n",
-                                    (event->button.type == GDK_BUTTON_PRESS ?
-                                     "press" : "release"),
-                                    event->button.button,
-                                    event->button.x, event->button.y));
+                                   | 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 ?
+                             "press" : "release"),
+                            event->button.button,
+                            event->button.x, event->button.y));
        }
       else
        {
-         event->motion.time = msg->time;
+         event->motion.time = _gdk_win32_get_next_tick (msg->time);
          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, g_print ("WINTAB motion: %g,%g\n",
-                                    event->motion.x, event->motion.y));
+         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
@@ -898,7 +939,7 @@ _gdk_input_other_event (GdkEvent  *event,
           */
          if ((gdkdev->pktdata & PK_NORMAL_PRESSURE
               && (event->motion.state & GDK_BUTTON1_MASK)
-              && packet.pkNormalPressure <= MAX (0, gdkdev->npbtnmarks[0] - 2))
+              && packet.pkNormalPressure <= MAX (0, (gint) gdkdev->npbtnmarks[0] - 2))
              || (gdkdev->pktdata & PK_NORMAL_PRESSURE
                  && !(event->motion.state & GDK_BUTTON1_MASK)
                  && packet.pkNormalPressure > gdkdev->npbtnmarks[1] + 2))
@@ -917,15 +958,17 @@ _gdk_input_other_event (GdkEvent  *event,
              event2->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;
              event2->button.button = 1;
-             GDK_NOTE (EVENTS, 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 (event2);
+             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;
@@ -934,23 +977,24 @@ _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 = msg->time;
+      event->proximity.time = _gdk_win32_get_next_tick (msg->time);
       event->proximity.device = &gdkdev->info;
 
-      GDK_NOTE (EVENTS, g_print ("WINTAB proximity %s\n",
-                                (event->proximity.type == GDK_PROXIMITY_IN ?
-                                 "in" : "out")));
+      GDK_NOTE (EVENTS_OR_INPUT,
+               g_print ("WINTAB proximity %s\n",
+                        (event->proximity.type == GDK_PROXIMITY_IN ?
+                         "in" : "out")));
       return TRUE;
     }
 #endif
-  return -1;
+  return FALSE;
 }
 
 gboolean
@@ -996,10 +1040,10 @@ _gdk_input_grab_pointer (GdkWindow    *window,
   new_window = NULL;
   need_ungrab = FALSE;
 
-  GDK_NOTE (MISC, g_print ("gdk_input_win32_grab_pointer: %#x %d %#x\n",
-                          (guint) GDK_WINDOW_HWND (window),
+  GDK_NOTE (INPUT, g_print ("gdk_input_win32_grab_pointer: %p %d %p\n",
+                          GDK_WINDOW_HWND (window),
                           owner_events,
-                          (confine_to ? (guint) GDK_WINDOW_HWND (confine_to) : 0)));
+                          (confine_to ? GDK_WINDOW_HWND (confine_to) : 0)));
 
   while (tmp_list)
     {
@@ -1019,6 +1063,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)
@@ -1048,6 +1097,7 @@ _gdk_input_grab_pointer (GdkWindow    *window,
     }
   else
     { 
+      x_grab_window = NULL;
       tmp_list = _gdk_input_devices;
       while (tmp_list)
        {
@@ -1078,7 +1128,7 @@ _gdk_input_ungrab_pointer (guint32 time)
   GdkDevicePrivate *gdkdev;
   GList *tmp_list;
 
-  GDK_NOTE (MISC, g_print ("gdk_input_win32_ungrab_pointer\n"));
+  GDK_NOTE (INPUT, g_print ("gdk_input_win32_ungrab_pointer\n"));
 
   tmp_list = _gdk_input_windows;
   while (tmp_list)
@@ -1105,16 +1155,10 @@ _gdk_input_ungrab_pointer (guint32 time)
          tmp_list = tmp_list->next;
        }
     }
+  x_grab_window = NULL;
 #endif
 }
 
-gint 
-_gdk_input_window_none_event (GdkEvent *event,
-                             MSG      *msg)
-{
-  return -1;
-}
-
 gboolean
 _gdk_device_get_history (GdkDevice         *device,
                         GdkWindow         *window,
@@ -1152,56 +1196,73 @@ gdk_device_get_state (GdkDevice       *device,
       GdkDevicePrivate *gdkdev;
       GdkInputWindow *input_window;
       
-      if (mask)
-       gdk_window_get_pointer (window, NULL, NULL, mask);
-      
       gdkdev = (GdkDevicePrivate *)device;
-      input_window = gdk_input_window_find (window);
-      g_return_if_fail (input_window != NULL);
-
-#if 0 /* FIXME */
-      state = XQueryDeviceState (gdk_display, gdkdev->xdevice);
-      input_class = state->data;
-      for (i = 0; i < state->num_classes; i++)
+      /* 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)
        {
-         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);
+         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));
        }
-      XFreeDeviceState (state);
-#endif
+      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);
     }
 }
 
+#ifdef HAVE_WINTAB
+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);
+      WTOverlap (*hctx, TRUE);
+      tmp_list = tmp_list->next;
+    }
+}
+#endif /* HAVE_WINTAB */
+
 void 
-_gdk_input_init (void)
+_gdk_input_init (GdkDisplay *display)
 {
   _gdk_input_ignore_core = FALSE;
   _gdk_input_devices = NULL;
 
-  _gdk_init_input_core ();
+  _gdk_init_input_core (display);
 #ifdef HAVE_WINTAB
-  gdk_input_wintab_init ();
+#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 */
 #endif /* HAVE_WINTAB */
 
-  _gdk_input_devices = g_list_append (_gdk_input_devices, _gdk_core_pointer);
+  _gdk_input_devices = g_list_append (_gdk_input_devices, display->core_pointer);
 }