summaryrefslogtreecommitdiff
path: root/gdk/win32/gdkdevicemanager-win32.c
diff options
context:
space:
mode:
Diffstat (limited to 'gdk/win32/gdkdevicemanager-win32.c')
-rw-r--r--gdk/win32/gdkdevicemanager-win32.c1089
1 files changed, 1089 insertions, 0 deletions
diff --git a/gdk/win32/gdkdevicemanager-win32.c b/gdk/win32/gdkdevicemanager-win32.c
new file mode 100644
index 0000000000..eea814296b
--- /dev/null
+++ b/gdk/win32/gdkdevicemanager-win32.c
@@ -0,0 +1,1089 @@
+/* GDK - The GIMP Drawing Kit
+ * Copyright (C) 2009 Carlos Garnacho <carlosg@gnome.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include "config.h"
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <math.h>
+
+#include <gdk/gdk.h>
+#include "gdkprivate-win32.h"
+#include "gdkdevicemanager-win32.h"
+#include "gdkdeviceprivate.h"
+#include "gdkdevice-win32.h"
+#include "gdkdevice-wintab.h"
+
+#include <windows.h>
+#include <wintab.h>
+
+#define PACKETDATA (PK_CONTEXT | PK_CURSOR | PK_BUTTONS | PK_X | PK_Y | PK_NORMAL_PRESSURE | PK_ORIENTATION)
+/* We want everything in absolute mode */
+#define PACKETMODE (0)
+#include <pktdef.h>
+
+#define DEBUG_WINTAB 1 /* Verbose debug messages enabled */
+#define PROXIMITY_OUT_DELAY 200 /* In milliseconds, see set_ignore_core */
+#define TWOPI (2 * G_PI)
+
+static GList *wintab_contexts = NULL;
+static GdkWindow *wintab_window = NULL;
+static guint ignore_core_timer = 0;
+extern gint _gdk_input_ignore_core;
+
+typedef UINT (WINAPI *t_WTInfoA) (UINT a, UINT b, LPVOID c);
+typedef UINT (WINAPI *t_WTInfoW) (UINT a, UINT b, LPVOID c);
+typedef BOOL (WINAPI *t_WTEnable) (HCTX a, BOOL b);
+typedef HCTX (WINAPI *t_WTOpenA) (HWND a, LPLOGCONTEXTA b, BOOL c);
+typedef BOOL (WINAPI *t_WTOverlap) (HCTX a, BOOL b);
+typedef BOOL (WINAPI *t_WTPacket) (HCTX a, UINT b, LPVOID c);
+typedef int (WINAPI *t_WTQueueSizeSet) (HCTX a, int b);
+
+static t_WTInfoA p_WTInfoA;
+static t_WTInfoW p_WTInfoW;
+static t_WTEnable p_WTEnable;
+static t_WTOpenA p_WTOpenA;
+static t_WTOverlap p_WTOverlap;
+static t_WTPacket p_WTPacket;
+static t_WTQueueSizeSet p_WTQueueSizeSet;
+
+
+static void gdk_device_manager_win32_finalize (GObject *object);
+static void gdk_device_manager_win32_constructed (GObject *object);
+
+static GList * gdk_device_manager_win32_list_devices (GdkDeviceManager *device_manager,
+ GdkDeviceType type);
+
+
+G_DEFINE_TYPE (GdkDeviceManagerWin32, gdk_device_manager_win32, GDK_TYPE_DEVICE_MANAGER)
+
+static void
+gdk_device_manager_win32_class_init (GdkDeviceManagerWin32Class *klass)
+{
+ GdkDeviceManagerClass *device_manager_class = GDK_DEVICE_MANAGER_CLASS (klass);
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ object_class->finalize = gdk_device_manager_win32_finalize;
+ object_class->constructed = gdk_device_manager_win32_constructed;
+ device_manager_class->list_devices = gdk_device_manager_win32_list_devices;
+}
+
+static GdkDevice *
+create_core_pointer (GdkDeviceManager *device_manager)
+{
+ return g_object_new (GDK_TYPE_DEVICE_WIN32,
+ "name", "Core Pointer",
+ "type", GDK_DEVICE_TYPE_MASTER,
+ "input-source", GDK_SOURCE_MOUSE,
+ "input-mode", GDK_MODE_SCREEN,
+ "has-cursor", TRUE,
+ "display", _gdk_display,
+ "device-manager", device_manager,
+ NULL);
+}
+
+static GdkDevice *
+create_core_keyboard (GdkDeviceManager *device_manager)
+{
+ return g_object_new (GDK_TYPE_DEVICE_WIN32,
+ "name", "Core Keyboard",
+ "type", GDK_DEVICE_TYPE_MASTER,
+ "input-source", GDK_SOURCE_KEYBOARD,
+ "input-mode", GDK_MODE_SCREEN,
+ "has-cursor", FALSE,
+ "display", _gdk_display,
+ "device-manager", device_manager,
+ NULL);
+}
+
+static void
+gdk_device_manager_win32_init (GdkDeviceManagerWin32 *device_manager_win32)
+{
+}
+
+static void
+gdk_device_manager_win32_finalize (GObject *object)
+{
+ GdkDeviceManagerWin32 *device_manager_win32;
+
+ device_manager_win32 = GDK_DEVICE_MANAGER_WIN32 (object);
+
+ g_object_unref (device_manager_win32->core_pointer);
+ g_object_unref (device_manager_win32->core_keyboard);
+
+ G_OBJECT_CLASS (gdk_device_manager_win32_parent_class)->finalize (object);
+}
+
+#if DEBUG_WINTAB
+
+static void
+print_lc(LOGCONTEXT *lc)
+{
+ g_print ("lcName = %s\n", lc->lcName);
+ g_print ("lcOptions =");
+ if (lc->lcOptions & CXO_SYSTEM) g_print (" CXO_SYSTEM");
+ if (lc->lcOptions & CXO_PEN) g_print (" CXO_PEN");
+ if (lc->lcOptions & CXO_MESSAGES) g_print (" CXO_MESSAGES");
+ if (lc->lcOptions & CXO_MARGIN) g_print (" CXO_MARGIN");
+ if (lc->lcOptions & CXO_MGNINSIDE) g_print (" CXO_MGNINSIDE");
+ if (lc->lcOptions & CXO_CSRMESSAGES) g_print (" CXO_CSRMESSAGES");
+ g_print ("\n");
+ g_print ("lcStatus =");
+ if (lc->lcStatus & CXS_DISABLED) g_print (" CXS_DISABLED");
+ if (lc->lcStatus & CXS_OBSCURED) g_print (" CXS_OBSCURED");
+ if (lc->lcStatus & CXS_ONTOP) g_print (" CXS_ONTOP");
+ g_print ("\n");
+ g_print ("lcLocks =");
+ if (lc->lcLocks & CXL_INSIZE) g_print (" CXL_INSIZE");
+ if (lc->lcLocks & CXL_INASPECT) g_print (" CXL_INASPECT");
+ if (lc->lcLocks & CXL_SENSITIVITY) g_print (" CXL_SENSITIVITY");
+ if (lc->lcLocks & CXL_MARGIN) g_print (" CXL_MARGIN");
+ g_print ("\n");
+ g_print ("lcMsgBase = %#x, lcDevice = %#x, lcPktRate = %d\n",
+ lc->lcMsgBase, lc->lcDevice, lc->lcPktRate);
+ g_print ("lcPktData =");
+ if (lc->lcPktData & PK_CONTEXT) g_print (" PK_CONTEXT");
+ if (lc->lcPktData & PK_STATUS) g_print (" PK_STATUS");
+ if (lc->lcPktData & PK_TIME) g_print (" PK_TIME");
+ if (lc->lcPktData & PK_CHANGED) g_print (" PK_CHANGED");
+ if (lc->lcPktData & PK_SERIAL_NUMBER) g_print (" PK_SERIAL_NUMBER");
+ if (lc->lcPktData & PK_CURSOR) g_print (" PK_CURSOR");
+ if (lc->lcPktData & PK_BUTTONS) g_print (" PK_BUTTONS");
+ if (lc->lcPktData & PK_X) g_print (" PK_X");
+ if (lc->lcPktData & PK_Y) g_print (" PK_Y");
+ if (lc->lcPktData & PK_Z) g_print (" PK_Z");
+ if (lc->lcPktData & PK_NORMAL_PRESSURE) g_print (" PK_NORMAL_PRESSURE");
+ if (lc->lcPktData & PK_TANGENT_PRESSURE) g_print (" PK_TANGENT_PRESSURE");
+ if (lc->lcPktData & PK_ORIENTATION) g_print (" PK_ORIENTATION");
+ if (lc->lcPktData & PK_ROTATION) g_print (" PK_ROTATION");
+ g_print ("\n");
+ g_print ("lcPktMode =");
+ if (lc->lcPktMode & PK_CONTEXT) g_print (" PK_CONTEXT");
+ if (lc->lcPktMode & PK_STATUS) g_print (" PK_STATUS");
+ if (lc->lcPktMode & PK_TIME) g_print (" PK_TIME");
+ if (lc->lcPktMode & PK_CHANGED) g_print (" PK_CHANGED");
+ if (lc->lcPktMode & PK_SERIAL_NUMBER) g_print (" PK_SERIAL_NUMBER");
+ if (lc->lcPktMode & PK_CURSOR) g_print (" PK_CURSOR");
+ if (lc->lcPktMode & PK_BUTTONS) g_print (" PK_BUTTONS");
+ if (lc->lcPktMode & PK_X) g_print (" PK_X");
+ if (lc->lcPktMode & PK_Y) g_print (" PK_Y");
+ if (lc->lcPktMode & PK_Z) g_print (" PK_Z");
+ if (lc->lcPktMode & PK_NORMAL_PRESSURE) g_print (" PK_NORMAL_PRESSURE");
+ if (lc->lcPktMode & PK_TANGENT_PRESSURE) g_print (" PK_TANGENT_PRESSURE");
+ if (lc->lcPktMode & PK_ORIENTATION) g_print (" PK_ORIENTATION");
+ if (lc->lcPktMode & PK_ROTATION) g_print (" PK_ROTATION");
+ g_print ("\n");
+ g_print ("lcMoveMask =");
+ if (lc->lcMoveMask & PK_CONTEXT) g_print (" PK_CONTEXT");
+ if (lc->lcMoveMask & PK_STATUS) g_print (" PK_STATUS");
+ if (lc->lcMoveMask & PK_TIME) g_print (" PK_TIME");
+ if (lc->lcMoveMask & PK_CHANGED) g_print (" PK_CHANGED");
+ if (lc->lcMoveMask & PK_SERIAL_NUMBER) g_print (" PK_SERIAL_NUMBER");
+ if (lc->lcMoveMask & PK_CURSOR) g_print (" PK_CURSOR");
+ if (lc->lcMoveMask & PK_BUTTONS) g_print (" PK_BUTTONS");
+ if (lc->lcMoveMask & PK_X) g_print (" PK_X");
+ if (lc->lcMoveMask & PK_Y) g_print (" PK_Y");
+ if (lc->lcMoveMask & PK_Z) g_print (" PK_Z");
+ if (lc->lcMoveMask & PK_NORMAL_PRESSURE) g_print (" PK_NORMAL_PRESSURE");
+ if (lc->lcMoveMask & PK_TANGENT_PRESSURE) g_print (" PK_TANGENT_PRESSURE");
+ if (lc->lcMoveMask & PK_ORIENTATION) g_print (" PK_ORIENTATION");
+ if (lc->lcMoveMask & PK_ROTATION) g_print (" PK_ROTATION");
+ g_print ("\n");
+ g_print ("lcBtnDnMask = %#x, lcBtnUpMask = %#x\n",
+ (guint) lc->lcBtnDnMask, (guint) lc->lcBtnUpMask);
+ g_print ("lcInOrgX = %ld, lcInOrgY = %ld, lcInOrgZ = %ld\n",
+ lc->lcInOrgX, lc->lcInOrgY, lc->lcInOrgZ);
+ g_print ("lcInExtX = %ld, lcInExtY = %ld, lcInExtZ = %ld\n",
+ lc->lcInExtX, lc->lcInExtY, lc->lcInExtZ);
+ g_print ("lcOutOrgX = %ld, lcOutOrgY = %ld, lcOutOrgZ = %ld\n",
+ lc->lcOutOrgX, lc->lcOutOrgY, lc->lcOutOrgZ);
+ g_print ("lcOutExtX = %ld, lcOutExtY = %ld, lcOutExtZ = %ld\n",
+ lc->lcOutExtX, lc->lcOutExtY, lc->lcOutExtZ);
+ g_print ("lcSensX = %g, lcSensY = %g, lcSensZ = %g\n",
+ lc->lcSensX / 65536., lc->lcSensY / 65536., lc->lcSensZ / 65536.);
+ g_print ("lcSysMode = %d\n", lc->lcSysMode);
+ g_print ("lcSysOrgX = %d, lcSysOrgY = %d\n",
+ lc->lcSysOrgX, lc->lcSysOrgY);
+ g_print ("lcSysExtX = %d, lcSysExtY = %d\n",
+ lc->lcSysExtX, lc->lcSysExtY);
+ g_print ("lcSysSensX = %g, lcSysSensY = %g\n",
+ lc->lcSysSensX / 65536., lc->lcSysSensY / 65536.);
+}
+
+static void
+print_cursor (int index)
+{
+ int size;
+ int i;
+ char *name;
+ BOOL active;
+ WTPKT wtpkt;
+ BYTE buttons;
+ BYTE buttonbits;
+ char *btnnames;
+ char *p;
+ BYTE buttonmap[32];
+ BYTE sysbtnmap[32];
+ BYTE npbutton;
+ UINT npbtnmarks[2];
+ UINT *npresponse;
+ BYTE tpbutton;
+ UINT tpbtnmarks[2];
+ UINT *tpresponse;
+ DWORD physid;
+ UINT mode;
+ UINT minpktdata;
+ UINT minbuttons;
+ UINT capabilities;
+
+ size = (*p_WTInfoA) (WTI_CURSORS + index, CSR_NAME, NULL);
+ name = g_malloc (size + 1);
+ (*p_WTInfoA) (WTI_CURSORS + index, CSR_NAME, name);
+ g_print ("NAME: %s\n", name);
+ (*p_WTInfoA) (WTI_CURSORS + index, CSR_ACTIVE, &active);
+ g_print ("ACTIVE: %s\n", active ? "YES" : "NO");
+ (*p_WTInfoA) (WTI_CURSORS + index, CSR_PKTDATA, &wtpkt);
+ g_print ("PKTDATA: %#x:", (guint) wtpkt);
+#define BIT(x) if (wtpkt & PK_##x) g_print (" " #x)
+ BIT (CONTEXT);
+ BIT (STATUS);
+ BIT (TIME);
+ BIT (CHANGED);
+ BIT (SERIAL_NUMBER);
+ BIT (BUTTONS);
+ BIT (X);
+ BIT (Y);
+ BIT (Z);
+ BIT (NORMAL_PRESSURE);
+ BIT (TANGENT_PRESSURE);
+ BIT (ORIENTATION);
+ BIT (ROTATION);
+#undef BIT
+ g_print ("\n");
+ (*p_WTInfoA) (WTI_CURSORS + index, CSR_BUTTONS, &buttons);
+ g_print ("BUTTONS: %d\n", buttons);
+ (*p_WTInfoA) (WTI_CURSORS + index, CSR_BUTTONBITS, &buttonbits);
+ g_print ("BUTTONBITS: %d\n", buttonbits);
+ size = (*p_WTInfoA) (WTI_CURSORS + index, CSR_BTNNAMES, NULL);
+ g_print ("BTNNAMES:");
+ if (size > 0)
+ {
+ btnnames = g_malloc (size + 1);
+ (*p_WTInfoA) (WTI_CURSORS + index, CSR_BTNNAMES, btnnames);
+ p = btnnames;
+ while (*p)
+ {
+ g_print (" %s", p);
+ p += strlen (p) + 1;
+ }
+ }
+ g_print ("\n");
+ (*p_WTInfoA) (WTI_CURSORS + index, CSR_BUTTONMAP, buttonmap);
+ g_print ("BUTTONMAP:");
+ for (i = 0; i < buttons; i++)
+ g_print (" %d", buttonmap[i]);
+ g_print ("\n");
+ (*p_WTInfoA) (WTI_CURSORS + index, CSR_SYSBTNMAP, sysbtnmap);
+ g_print ("SYSBTNMAP:");
+ for (i = 0; i < buttons; i++)
+ g_print (" %d", sysbtnmap[i]);
+ g_print ("\n");
+ (*p_WTInfoA) (WTI_CURSORS + index, CSR_NPBUTTON, &npbutton);
+ g_print ("NPBUTTON: %d\n", npbutton);
+ (*p_WTInfoA) (WTI_CURSORS + index, CSR_NPBTNMARKS, npbtnmarks);
+ g_print ("NPBTNMARKS: %d %d\n", npbtnmarks[0], npbtnmarks[1]);
+ size = (*p_WTInfoA) (WTI_CURSORS + index, CSR_NPRESPONSE, NULL);
+ g_print ("NPRESPONSE:");
+ if (size > 0)
+ {
+ npresponse = g_malloc (size);
+ (*p_WTInfoA) (WTI_CURSORS + index, CSR_NPRESPONSE, npresponse);
+ for (i = 0; i < size / sizeof (UINT); i++)
+ g_print (" %d", npresponse[i]);
+ }
+ g_print ("\n");
+ (*p_WTInfoA) (WTI_CURSORS + index, CSR_TPBUTTON, &tpbutton);
+ g_print ("TPBUTTON: %d\n", tpbutton);
+ (*p_WTInfoA) (WTI_CURSORS + index, CSR_TPBTNMARKS, tpbtnmarks);
+ g_print ("TPBTNMARKS: %d %d\n", tpbtnmarks[0], tpbtnmarks[1]);
+ size = (*p_WTInfoA) (WTI_CURSORS + index, CSR_TPRESPONSE, NULL);
+ g_print ("TPRESPONSE:");
+ if (size > 0)
+ {
+ tpresponse = g_malloc (size);
+ (*p_WTInfoA) (WTI_CURSORS + index, CSR_TPRESPONSE, tpresponse);
+ for (i = 0; i < size / sizeof (UINT); i++)
+ g_print (" %d", tpresponse[i]);
+ }
+ g_print ("\n");
+ (*p_WTInfoA) (WTI_CURSORS + index, CSR_PHYSID, &physid);
+ g_print ("PHYSID: %#x\n", (guint) physid);
+ (*p_WTInfoA) (WTI_CURSORS + index, CSR_CAPABILITIES, &capabilities);
+ g_print ("CAPABILITIES: %#x:", capabilities);
+#define BIT(x) if (capabilities & CRC_##x) g_print (" " #x)
+ BIT (MULTIMODE);
+ BIT (AGGREGATE);
+ BIT (INVERT);
+#undef BIT
+ g_print ("\n");
+ if (capabilities & CRC_MULTIMODE)
+ {
+ (*p_WTInfoA) (WTI_CURSORS + index, CSR_MODE, &mode);
+ g_print ("MODE: %d\n", mode);
+ }
+ if (capabilities & CRC_AGGREGATE)
+ {
+ (*p_WTInfoA) (WTI_CURSORS + index, CSR_MINPKTDATA, &minpktdata);
+ g_print ("MINPKTDATA: %d\n", minpktdata);
+ (*p_WTInfoA) (WTI_CURSORS + index, CSR_MINBUTTONS, &minbuttons);
+ g_print ("MINBUTTONS: %d\n", minbuttons);
+ }
+}
+#endif
+
+static void
+_gdk_input_wintab_init_check (GdkDeviceManagerWin32 *device_manager)
+{
+ static gboolean wintab_initialized = FALSE;
+ GdkDeviceWintab *device;
+ GdkWindowAttr wa;
+ WORD specversion;
+ HCTX *hctx;
+ UINT ndevices, ncursors, ncsrtypes, firstcsr, hardware;
+ BOOL active;
+ DWORD physid;
+ AXIS axis_x, axis_y, axis_npressure, axis_or[3];
+ int i, devix, cursorix, num_axes = 0;
+ wchar_t devname[100], csrname[100];
+ gchar *devname_utf8, *csrname_utf8, *device_name;
+ BOOL defcontext_done;
+ HMODULE wintab32;
+
+ if (wintab_initialized)
+ return;
+
+ wintab_initialized = TRUE;
+
+ wintab_contexts = NULL;
+
+ if (_gdk_input_ignore_wintab)
+ return;
+
+ if ((wintab32 = LoadLibrary ("wintab32.dll")) == NULL)
+ return;
+
+ if ((p_WTInfoA = (t_WTInfoA) GetProcAddress (wintab32, "WTInfoA")) == NULL)
+ return;
+ if ((p_WTInfoW = (t_WTInfoW) GetProcAddress (wintab32, "WTInfoW")) == NULL)
+ return;
+ if ((p_WTEnable = (t_WTEnable) GetProcAddress (wintab32, "WTEnable")) == NULL)
+ return;
+ if ((p_WTOpenA = (t_WTOpenA) GetProcAddress (wintab32, "WTOpenA")) == NULL)
+ return;
+ if ((p_WTOverlap = (t_WTOverlap) GetProcAddress (wintab32, "WTOverlap")) == NULL)
+ return;
+ if ((p_WTPacket = (t_WTPacket) GetProcAddress (wintab32, "WTPacket")) == NULL)
+ return;
+ if ((p_WTQueueSizeSet = (t_WTQueueSizeSet) GetProcAddress (wintab32, "WTQueueSizeSet")) == NULL)
+ return;
+
+ if (!(*p_WTInfoA) (0, 0, NULL))
+ return;
+
+ (*p_WTInfoA) (WTI_INTERFACE, IFC_SPECVERSION, &specversion);
+ GDK_NOTE (INPUT, g_print ("Wintab interface version %d.%d\n",
+ HIBYTE (specversion), LOBYTE (specversion)));
+ (*p_WTInfoA) (WTI_INTERFACE, IFC_NDEVICES, &ndevices);
+ (*p_WTInfoA) (WTI_INTERFACE, IFC_NCURSORS, &ncursors);
+#if DEBUG_WINTAB
+ GDK_NOTE (INPUT, g_print ("NDEVICES: %d, NCURSORS: %d\n",
+ ndevices, ncursors));
+#endif
+ /* Create a dummy window to receive wintab events */
+ wa.wclass = GDK_INPUT_OUTPUT;
+ wa.event_mask = GDK_ALL_EVENTS_MASK;
+ wa.width = 2;
+ wa.height = 2;
+ wa.x = -100;
+ wa.y = -100;
+ wa.window_type = GDK_WINDOW_TOPLEVEL;
+ if ((wintab_window = gdk_window_new (NULL, &wa, GDK_WA_X|GDK_WA_Y)) == NULL)
+ {
+ g_warning ("gdk_input_wintab_init: gdk_window_new failed");
+ return;
+ }
+ g_object_ref (wintab_window);
+
+ for (devix = 0; devix < ndevices; devix++)
+ {
+ LOGCONTEXT lc;
+
+ /* We open the Wintab device (hmm, what if there are several, or
+ * can there even be several, probably not?) as a system
+ * pointing device, i.e. it controls the normal Windows
+ * cursor. This seems much more natural.
+ */
+
+ (*p_WTInfoW) (WTI_DEVICES + devix, DVC_NAME, devname);
+ devname_utf8 = g_utf16_to_utf8 (devname, -1, NULL, NULL, NULL);
+#ifdef DEBUG_WINTAB
+ GDK_NOTE (INPUT, (g_print("Device %d: %s\n", devix, devname_utf8)));
+#endif
+ (*p_WTInfoA) (WTI_DEVICES + devix, DVC_NCSRTYPES, &ncsrtypes);
+ (*p_WTInfoA) (WTI_DEVICES + devix, DVC_FIRSTCSR, &firstcsr);
+ (*p_WTInfoA) (WTI_DEVICES + devix, DVC_HARDWARE, &hardware);
+ (*p_WTInfoA) (WTI_DEVICES + devix, DVC_X, &axis_x);
+ (*p_WTInfoA) (WTI_DEVICES + devix, DVC_Y, &axis_y);
+ (*p_WTInfoA) (WTI_DEVICES + devix, DVC_NPRESSURE, &axis_npressure);
+ (*p_WTInfoA) (WTI_DEVICES + devix, DVC_ORIENTATION, axis_or);
+
+ defcontext_done = FALSE;
+ if (HIBYTE (specversion) > 1 || LOBYTE (specversion) >= 1)
+ {
+ /* Try to get device-specific default context */
+ /* Some drivers, e.g. Aiptek, don't provide this info */
+ if ((*p_WTInfoA) (WTI_DSCTXS + devix, 0, &lc) > 0)
+ defcontext_done = TRUE;
+#if DEBUG_WINTAB
+ if (defcontext_done)
+ GDK_NOTE (INPUT, (g_print("Using device-specific default context\n")));
+ else
+ GDK_NOTE (INPUT, (g_print("Note: Driver did not provide device specific default context info despite claiming to support version 1.1\n")));
+#endif
+ }
+
+ if (!defcontext_done)
+ (*p_WTInfoA) (WTI_DEFSYSCTX, 0, &lc);
+#if DEBUG_WINTAB
+ GDK_NOTE (INPUT, (g_print("Default context:\n"), print_lc(&lc)));
+#endif
+ lc.lcOptions |= CXO_MESSAGES;
+ lc.lcStatus = 0;
+ lc.lcMsgBase = WT_DEFBASE;
+ lc.lcPktRate = 0;
+ lc.lcPktData = PACKETDATA;
+ lc.lcPktMode = PACKETMODE;
+ lc.lcMoveMask = PACKETDATA;
+ lc.lcBtnUpMask = lc.lcBtnDnMask = ~0;
+ lc.lcOutOrgX = axis_x.axMin;
+ lc.lcOutOrgY = axis_y.axMin;
+ lc.lcOutExtX = axis_x.axMax - axis_x.axMin;
+ lc.lcOutExtY = axis_y.axMax - axis_y.axMin;
+ lc.lcOutExtY = -lc.lcOutExtY; /* We want Y growing downward */
+#if DEBUG_WINTAB
+ GDK_NOTE (INPUT, (g_print("context for device %d:\n", devix),
+ print_lc(&lc)));
+#endif
+ hctx = g_new (HCTX, 1);
+ if ((*hctx = (*p_WTOpenA) (GDK_WINDOW_HWND (wintab_window), &lc, TRUE)) == NULL)
+ {
+ g_warning ("gdk_input_wintab_init: WTOpen failed");
+ return;
+ }
+ GDK_NOTE (INPUT, g_print ("opened Wintab device %d %p\n",
+ devix, *hctx));
+
+ wintab_contexts = g_list_append (wintab_contexts, hctx);
+#if 0
+ (*p_WTEnable) (*hctx, TRUE);
+#endif
+ (*p_WTOverlap) (*hctx, TRUE);
+
+#if DEBUG_WINTAB
+ GDK_NOTE (INPUT, (g_print("context for device %d after WTOpen:\n", devix),
+ print_lc(&lc)));
+#endif
+ /* Increase packet queue size to reduce the risk of lost packets.
+ * According to the specs, if the function fails we must try again
+ * with a smaller queue size.
+ */
+ GDK_NOTE (INPUT, g_print("Attempting to increase queue size\n"));
+ for (i = 32; i >= 1; i >>= 1)
+ {
+ if ((*p_WTQueueSizeSet) (*hctx, i))
+ {
+ GDK_NOTE (INPUT, g_print("Queue size set to %d\n", i));
+ break;
+ }
+ }
+ if (!i)
+ GDK_NOTE (INPUT, g_print("Whoops, no queue size could be set\n"));
+ for (cursorix = firstcsr; cursorix < firstcsr + ncsrtypes; cursorix++)
+ {
+#ifdef DEBUG_WINTAB
+ GDK_NOTE (INPUT, (g_print("Cursor %d:\n", cursorix), print_cursor (cursorix)));
+#endif
+ active = FALSE;
+ (*p_WTInfoA) (WTI_CURSORS + cursorix, CSR_ACTIVE, &active);
+ if (!active)
+ continue;
+
+ /* Wacom tablets seem to report cursors corresponding to
+ * nonexistent pens or pucks. At least my ArtPad II reports
+ * six cursors: a puck, pressure stylus and eraser stylus,
+ * and then the same three again. I only have a
+ * pressure-sensitive pen. The puck instances, and the
+ * second instances of the styluses report physid zero. So
+ * at least for Wacom, skip cursors with physid zero.
+ */
+ (*p_WTInfoA) (WTI_CURSORS + cursorix, CSR_PHYSID, &physid);
+ if (wcscmp (devname, L"WACOM Tablet") == 0 && physid == 0)
+ continue;
+
+ (*p_WTInfoW) (WTI_CURSORS + cursorix, CSR_NAME, csrname);
+ csrname_utf8 = g_utf16_to_utf8 (csrname, -1, NULL, NULL, NULL);
+ device_name = g_strconcat (devname_utf8, " ", csrname_utf8, NULL);
+
+ device = g_object_new (GDK_TYPE_DEVICE_WINTAB,
+ "name", device_name,
+ "type", GDK_DEVICE_TYPE_SLAVE,
+ "source", GDK_SOURCE_PEN,
+ "mode", GDK_MODE_SCREEN,
+ "has-cursor", FALSE,
+ "display", _gdk_display,
+ "device-manager", device_manager,
+ NULL);
+
+ g_free (csrname_utf8);
+
+ device->hctx = *hctx;
+ device->cursor = cursorix;
+ (*p_WTInfoA) (WTI_CURSORS + cursorix, CSR_PKTDATA, &device->pktdata);
+
+ if (device->pktdata & PK_X)
+ {
+ _gdk_device_add_axis (GDK_DEVICE (device),
+ GDK_NONE,
+ GDK_AXIS_X,
+ axis_x.axMin,
+ axis_x.axMax,
+ axis_x.axResolution / 65535);
+ num_axes++;
+ }
+
+ if (device->pktdata & PK_Y)
+ {
+ _gdk_device_add_axis (GDK_DEVICE (device),
+ GDK_NONE,
+ GDK_AXIS_Y,
+ axis_y.axMin,
+ axis_y.axMax,
+ axis_y.axResolution / 65535);
+ num_axes++;
+ }
+
+
+ if (device->pktdata & PK_NORMAL_PRESSURE)
+ {
+ _gdk_device_add_axis (GDK_DEVICE (device),
+ GDK_NONE,
+ GDK_AXIS_PRESSURE,
+ axis_npressure.axMin,
+ axis_npressure.axMax,
+ axis_npressure.axResolution / 65535);
+ num_axes++;
+ }
+
+ /* The wintab driver for the Wacom ArtPad II reports
+ * PK_ORIENTATION in CSR_PKTDATA, but the tablet doesn't
+ * actually sense tilt. Catch this by noticing that the
+ * orientation axis's azimuth resolution is zero.
+ */
+ if ((device->pktdata & PK_ORIENTATION) && axis_or[0].axResolution == 0)
+ {
+ device->orientation_axes[0] = axis_or[0];
+ device->orientation_axes[1] = axis_or[1];
+
+ /* Wintab gives us aximuth and altitude, which
+ * we convert to x and y tilt in the -1000..1000 range
+ */
+ _gdk_device_add_axis (GDK_DEVICE (device),
+ GDK_NONE,
+ GDK_AXIS_XTILT,
+ -1000,
+ 1000,
+ 1000);
+
+ _gdk_device_add_axis (GDK_DEVICE (device),
+ GDK_NONE,
+ GDK_AXIS_YTILT,
+ -1000,
+ 1000,
+ 1000);
+ num_axes += 2;
+ }
+
+ device->last_axis_data = g_new (gint, num_axes);
+
+ GDK_NOTE (INPUT, g_print ("device: (%d) %s axes: %d\n",
+ cursorix,
+ device_name,
+ num_axes));
+
+#if 0
+ for (i = 0; i < gdkdev->info.num_axes; i++)
+ GDK_NOTE (INPUT, g_print ("... axis %d: %d--%d@%d\n",
+ i,
+ gdkdev->axes[i].min_value,
+ gdkdev->axes[i].max_value,
+ gdkdev->axes[i].resolution));
+#endif
+
+ device_manager->wintab_devices = g_list_append (device_manager->wintab_devices,
+ device);
+
+ g_free (device_name);
+ }
+
+ g_free (devname_utf8);
+ }
+}
+
+static void
+gdk_device_manager_win32_constructed (GObject *object)
+{
+ GdkDeviceManagerWin32 *device_manager;
+
+ device_manager = GDK_DEVICE_MANAGER_WIN32 (object);
+ device_manager->core_pointer = create_core_pointer (GDK_DEVICE_MANAGER (device_manager));
+ device_manager->core_keyboard = create_core_keyboard (GDK_DEVICE_MANAGER (device_manager));
+
+ _gdk_device_set_associated_device (device_manager->core_pointer, device_manager->core_keyboard);
+ _gdk_device_set_associated_device (device_manager->core_keyboard, device_manager->core_pointer);
+
+ _gdk_input_wintab_init_check (device_manager);
+}
+
+static GList *
+gdk_device_manager_win32_list_devices (GdkDeviceManager *device_manager,
+ GdkDeviceType type)
+{
+ GdkDeviceManagerWin32 *device_manager_win32;
+ GList *devices = NULL;
+
+ device_manager_win32 = (GdkDeviceManagerWin32 *) device_manager;
+
+ if (type == GDK_DEVICE_TYPE_MASTER)
+ {
+ devices = g_list_prepend (devices, device_manager_win32->core_keyboard);
+ devices = g_list_prepend (devices, device_manager_win32->core_pointer);
+ }
+ else if (type == GDK_DEVICE_TYPE_FLOATING)
+ devices = g_list_copy (device_manager_win32->wintab_devices);
+
+ return devices;
+}
+
+void
+_gdk_input_set_tablet_active (void)
+{
+ GList *tmp_list;
+ HCTX *hctx;
+
+ /* Bring the contexts to the top of the overlap order when one of the
+ * application's windows is activated */
+
+ if (!wintab_contexts)
+ return; /* No tablet devices found, or Wintab not initialized yet */
+
+ GDK_NOTE (INPUT, g_print ("_gdk_input_set_tablet_active: "
+ "Bringing Wintab contexts to the top of the overlap order\n"));
+
+ tmp_list = wintab_contexts;
+
+ while (tmp_list)
+ {
+ hctx = (HCTX *) (tmp_list->data);
+ (*p_WTOverlap) (*hctx, TRUE);
+ tmp_list = tmp_list->next;
+ }
+}
+
+static void
+decode_tilt (gint *axis_data,
+ AXIS *axes,
+ PACKET *packet)
+{
+ double az, el;
+
+ /* As I don't have a tilt-sensing tablet,
+ * I cannot test this code.
+ */
+ az = TWOPI * packet->pkOrientation.orAzimuth /
+ (axes[0].axResolution / 65536.);
+ el = TWOPI * packet->pkOrientation.orAltitude /
+ (axes[1].axResolution / 65536.);
+
+ /* X tilt */
+ axis_data[0] = cos (az) * cos (el) * 1000;
+ /* Y tilt */
+ axis_data[1] = sin (az) * cos (el) * 1000;
+}
+
+/*
+ * Get the currently active keyboard modifiers (ignoring the mouse buttons)
+ * We could use gdk_window_get_pointer but that function does a lot of other
+ * expensive things besides getting the modifiers. This code is somewhat based
+ * on build_pointer_event_state from gdkevents-win32.c
+ */
+static guint
+get_modifier_key_state (void)
+{
+ guint state;
+
+ state = 0;
+ /* High-order bit is up/down, low order bit is toggled/untoggled */
+ if (GetKeyState (VK_CONTROL) < 0)
+ state |= GDK_CONTROL_MASK;
+ if (GetKeyState (VK_SHIFT) < 0)
+ state |= GDK_SHIFT_MASK;
+ if (GetKeyState (VK_MENU) < 0)
+ state |= GDK_MOD1_MASK;
+ if (GetKeyState (VK_CAPITAL) & 0x1)
+ state |= GDK_LOCK_MASK;
+
+ return state;
+}
+
+static gboolean
+ignore_core_timefunc (gpointer data)
+{
+ /* The delay has passed */
+ _gdk_input_ignore_core = FALSE;
+ ignore_core_timer = 0;
+
+ return FALSE; /* remove timeout */
+}
+
+/*
+ * Set or unset the _gdk_input_ignore_core variable that tells GDK
+ * to ignore events for the core pointer when the tablet is in proximity
+ * The unsetting is delayed slightly so that if a tablet event arrives
+ * just after proximity out, it does not cause a core pointer event
+ * which e.g. causes GIMP to switch tools.
+ */
+static void
+set_ignore_core (gboolean ignore)
+{
+ if (ignore)
+ {
+ _gdk_input_ignore_core = TRUE;
+ /* Remove any pending clear */
+ if (ignore_core_timer)
+ {
+ g_source_remove (ignore_core_timer);
+ ignore_core_timer = 0;
+ }
+ }
+ else if (!ignore_core_timer)
+ ignore_core_timer = gdk_threads_add_timeout (PROXIMITY_OUT_DELAY,
+ ignore_core_timefunc, NULL);
+}
+
+static GdkDeviceWintab *
+_gdk_device_manager_find_wintab_device (HCTX hctx,
+ UINT cursor)
+{
+ GdkDeviceManagerWin32 *device_manager;
+ GdkDeviceWintab *device;
+ GList *tmp_list;
+
+ device_manager = GDK_DEVICE_MANAGER_WIN32 (gdk_display_get_device_manager (_gdk_display));
+ tmp_list = device_manager->wintab_devices;
+
+ while (tmp_list)
+ {
+ device = tmp_list->data;
+ tmp_list = tmp_list->next;
+
+ if (device->hctx == hctx &&
+ device->cursor == cursor)
+ return device;
+ }
+
+ return NULL;
+}
+
+gboolean
+_gdk_input_other_event (GdkEvent *event,
+ MSG *msg,
+ GdkWindow *window)
+{
+ GdkDisplay *display;
+ GdkWindowObject *obj;
+ GdkDeviceWintab *device = NULL;
+ GdkDeviceGrabInfo *last_grab;
+ GdkEventMask masktest;
+ guint key_state;
+ POINT pt;
+
+ PACKET packet;
+ gdouble root_x, root_y;
+ gint num_axes;
+ gint x, y;
+ guint translated_buttons, button_diff, button_mask;
+ /* Translation from tablet button state to GDK button state for
+ * buttons 1-3 - swap button 2 and 3.
+ */
+ static guint button_map[8] = {0, 1, 4, 5, 2, 3, 6, 7};
+
+ if (event->any.window != wintab_window)
+ {
+ g_warning ("_gdk_input_other_event: not wintab_window?");
+ return FALSE;
+ }
+
+ window = gdk_window_at_pointer (&x, &y);
+ if (window == NULL)
+ window = _gdk_root;
+
+ g_object_ref (window);
+ display = gdk_drawable_get_display (window);
+
+ GDK_NOTE (EVENTS_OR_INPUT,
+ g_print ("_gdk_input_other_event: window=%p %+d%+d\n",
+ GDK_WINDOW_HWND (window), x, y));
+
+ if (msg->message == WT_PACKET)
+ {
+ if (!(*p_WTPacket) ((HCTX) msg->lParam, msg->wParam, &packet))
+ return FALSE;
+ }
+
+ obj = GDK_WINDOW_OBJECT (window);
+
+ switch (msg->message)
+ {
+ case WT_PACKET:
+ /* Don't produce any button or motion events while a window is being
+ * moved or resized, see bug #151090.
+ */
+ if (_modal_operation_in_progress)
+ {
+ GDK_NOTE (EVENTS_OR_INPUT, g_print ("... ignored when moving/sizing\n"));
+ return FALSE;
+ }
+
+ if ((device = _gdk_device_manager_find_wintab_device ((HCTX) msg->lParam,
+ packet.pkCursor)) == NULL)
+ return FALSE;
+
+ if (gdk_device_get_mode (GDK_DEVICE (device)) == GDK_MODE_DISABLED)
+ return FALSE;
+
+ last_grab = _gdk_display_get_last_device_grab (_gdk_display, GDK_DEVICE (device));
+
+ if (last_grab && last_grab->window)
+ {
+ g_object_unref (window);
+
+ window = g_object_ref (last_grab->window);
+ obj = GDK_WINDOW_OBJECT (window);
+ }
+
+ if (window == _gdk_root)
+ {
+ GDK_NOTE (EVENTS_OR_INPUT, g_print ("... is root\n"));
+ return FALSE;
+ }
+
+ num_axes = 0;
+ if (device->pktdata & PK_X)
+ device->last_axis_data[num_axes++] = packet.pkX;
+ if (device->pktdata & PK_Y)
+ device->last_axis_data[num_axes++] = packet.pkY;
+ if (device->pktdata & PK_NORMAL_PRESSURE)
+ device->last_axis_data[num_axes++] = packet.pkNormalPressure;
+ if (device->pktdata & PK_ORIENTATION)
+ {
+ decode_tilt (device->last_axis_data + num_axes,
+ device->orientation_axes, &packet);
+ num_axes += 2;
+ }
+
+ translated_buttons = button_map[packet.pkButtons & 0x07] | (packet.pkButtons & ~0x07);
+
+ if (translated_buttons != device->button_state)
+ {
+ /* At least one button has changed state so produce a button event
+ * If more than one button has changed state (unlikely),
+ * just care about the first and act on the next the next time
+ * we get a packet
+ */
+ button_diff = translated_buttons ^ device->button_state;
+
+ /* Gdk buttons are numbered 1.. */
+ event->button.button = 1;
+
+ for (button_mask = 1; button_mask != 0x80000000;
+ button_mask <<= 1, event->button.button++)
+ {
+ if (button_diff & button_mask)
+ {
+ /* Found a button that has changed state */
+ break;
+ }
+ }
+
+ if (!(translated_buttons & button_mask))
+ {
+ event->any.type = GDK_BUTTON_RELEASE;
+ masktest = GDK_BUTTON_RELEASE_MASK;
+ }
+ else
+ {
+ event->any.type = GDK_BUTTON_PRESS;
+ masktest = GDK_BUTTON_PRESS_MASK;
+ }
+ device->button_state ^= button_mask;
+ }
+ else
+ {
+ event->any.type = GDK_MOTION_NOTIFY;
+ masktest = GDK_POINTER_MOTION_MASK;
+ if (device->button_state & (1 << 0))
+ masktest |= GDK_BUTTON_MOTION_MASK | GDK_BUTTON1_MOTION_MASK;
+ if (device->button_state & (1 << 1))
+ masktest |= GDK_BUTTON_MOTION_MASK | GDK_BUTTON2_MOTION_MASK;
+ if (device->button_state & (1 << 2))
+ masktest |= GDK_BUTTON_MOTION_MASK | GDK_BUTTON3_MOTION_MASK;
+ }
+
+ /* Now we can check if the window wants the event, and
+ * propagate if necessary.
+ */
+ while (gdk_window_get_device_events (window, GDK_DEVICE (device)) == 0)
+ {
+ GDK_NOTE (EVENTS_OR_INPUT, g_print ("... not selected\n"));
+
+ if (obj->parent == GDK_WINDOW_OBJECT (_gdk_root))
+ return FALSE;
+
+ /* It is not good to propagate the extended events up to the parent
+ * if this window wants normal (not extended) motion/button events */
+ if (obj->event_mask & masktest)
+ {
+ GDK_NOTE (EVENTS_OR_INPUT,
+ g_print ("... wants ordinary event, ignoring this\n"));
+ return FALSE;
+ }
+
+ pt.x = x;
+ pt.y = y;
+ ClientToScreen (GDK_WINDOW_HWND (window), &pt);
+ g_object_unref (window);
+ window = (GdkWindow *) obj->parent;
+ obj = GDK_WINDOW_OBJECT (window);
+ g_object_ref (window);
+ ScreenToClient (GDK_WINDOW_HWND (window), &pt);
+ x = pt.x;
+ y = pt.y;
+ GDK_NOTE (EVENTS_OR_INPUT, g_print ("... propagating to %p %+d%+d\n",
+ GDK_WINDOW_HWND (window), x, y));
+ }
+
+ if (gdk_window_get_device_events (window, GDK_DEVICE (device)) == 0)
+ return FALSE;
+
+ event->any.window = window;
+ key_state = get_modifier_key_state ();
+ if (event->any.type == GDK_BUTTON_PRESS ||
+ event->any.type == GDK_BUTTON_RELEASE)
+ {
+ event->button.time = _gdk_win32_get_next_tick (msg->time);
+ gdk_event_set_device (event, GDK_DEVICE (device));
+
+ event->button.axes = g_new (gdouble, num_axes);
+ _gdk_device_wintab_get_window_coords (window, &root_x, &root_y);
+
+ _gdk_device_wintab_translate_axes (device,
+ window,
+ event->button.axes,
+ &event->button.x,
+ &event->button.y);
+
+ event->button.x_root = event->button.x + root_x;
+ event->button.y_root = event->button.y + root_y;
+
+ event->button.state =
+ key_state | ((device->button_state << 8)
+ & (GDK_BUTTON1_MASK | GDK_BUTTON2_MASK
+ | GDK_BUTTON3_MASK | GDK_BUTTON4_MASK
+ | GDK_BUTTON5_MASK));
+
+ GDK_NOTE (EVENTS_OR_INPUT,
+ g_print ("WINTAB button %s:%d %g,%g\n",
+ (event->button.type == GDK_BUTTON_PRESS ?
+ "press" : "release"),
+ event->button.button,
+ event->button.x, event->button.y));
+ }
+ else
+ {
+ event->motion.time = _gdk_win32_get_next_tick (msg->time);
+ event->motion.is_hint = FALSE;
+ gdk_event_set_device (event, GDK_DEVICE (device));
+
+ event->motion.axes = g_new (gdouble, num_axes);
+ _gdk_device_wintab_get_window_coords (window, &root_x, &root_y);
+
+ _gdk_device_wintab_translate_axes (device,
+ window,
+ event->motion.axes,
+ &event->motion.x,
+ &event->motion.y);
+
+ event->motion.x_root = event->motion.x + root_x;
+ event->motion.y_root = event->motion.y + root_y;
+
+ event->motion.state =
+ key_state | ((device->button_state << 8)
+ & (GDK_BUTTON1_MASK | GDK_BUTTON2_MASK
+ | GDK_BUTTON3_MASK | GDK_BUTTON4_MASK
+ | GDK_BUTTON5_MASK));
+
+ GDK_NOTE (EVENTS_OR_INPUT,
+ g_print ("WINTAB motion: %g,%g\n",
+ event->motion.x, event->motion.y));
+ }
+ return TRUE;
+
+ case WT_PROXIMITY:
+ if (LOWORD (msg->lParam) == 0)
+ {
+ event->proximity.type = GDK_PROXIMITY_OUT;
+ set_ignore_core (FALSE);
+ }
+ else
+ {
+ event->proximity.type = GDK_PROXIMITY_IN;
+ set_ignore_core (TRUE);
+ }
+ event->proximity.time = _gdk_win32_get_next_tick (msg->time);
+ gdk_event_set_device (event, GDK_DEVICE (device));
+
+ GDK_NOTE (EVENTS_OR_INPUT,
+ g_print ("WINTAB proximity %s\n",
+ (event->proximity.type == GDK_PROXIMITY_IN ?
+ "in" : "out")));
+ return TRUE;
+ }
+
+ return FALSE;
+}