[gtk: 11/19] Initialize WinPointer and enumerate devices




commit b54f4cf5d4d634be96910229b2807c5ca2f5d2b8
Author: Luca Bacci <luca bacci982 gmail com>
Date:   Fri Jul 2 11:13:06 2021 +0200

    Initialize WinPointer and enumerate devices

 gdk/win32/gdkdevice-winpointer.c   |   3 +
 gdk/win32/gdkdevice-winpointer.h   |   3 +
 gdk/win32/gdkdevicemanager-win32.c |  15 +-
 gdk/win32/gdkdevicemanager-win32.h |   2 +
 gdk/win32/gdkevents-win32.c        |  21 +-
 gdk/win32/gdkinput-winpointer.c    | 602 +++++++++++++++++++++++++++++++++++++
 gdk/win32/gdkinput-winpointer.h    |  28 ++
 gdk/win32/gdksurface-win32.c       |   4 +
 gdk/win32/meson.build              |   6 +-
 9 files changed, 680 insertions(+), 4 deletions(-)
---
diff --git a/gdk/win32/gdkdevice-winpointer.c b/gdk/win32/gdkdevice-winpointer.c
index b5639bf114..a2190ed4d5 100644
--- a/gdk/win32/gdkdevice-winpointer.c
+++ b/gdk/win32/gdkdevice-winpointer.c
@@ -204,6 +204,9 @@ gdk_device_winpointer_finalize (GObject *object)
 {
   GdkDeviceWinpointer *device_winpointer = GDK_DEVICE_WINPOINTER (object);
 
+  g_clear_object (&device_winpointer->tool_pen);
+  g_clear_object (&device_winpointer->tool_eraser);
+
   G_OBJECT_CLASS (gdk_device_winpointer_parent_class)->finalize (object);
 }
 
diff --git a/gdk/win32/gdkdevice-winpointer.h b/gdk/win32/gdkdevice-winpointer.h
index de13d51415..70d6f042c6 100644
--- a/gdk/win32/gdkdevice-winpointer.h
+++ b/gdk/win32/gdkdevice-winpointer.h
@@ -50,6 +50,9 @@ struct _GdkDeviceWinpointer
   double scale_y;
 
   GdkModifierType last_button_mask;
+
+  GdkDeviceTool *tool_pen;
+  GdkDeviceTool *tool_eraser;
 };
 
 struct _GdkDeviceWinpointerClass
diff --git a/gdk/win32/gdkdevicemanager-win32.c b/gdk/win32/gdkdevicemanager-win32.c
index 19203ab8fe..4ab0cc705d 100644
--- a/gdk/win32/gdkdevicemanager-win32.c
+++ b/gdk/win32/gdkdevicemanager-win32.c
@@ -29,6 +29,7 @@
 #include "gdkdevice-win32.h"
 #include "gdkdevice-virtual.h"
 #include "gdkdevice-wintab.h"
+#include "gdkinput-winpointer.h"
 #include "gdkdisplayprivate.h"
 #include "gdkseatdefaultprivate.h"
 
@@ -727,6 +728,8 @@ gdk_device_manager_win32_constructed (GObject *object)
   gdk_seat_default_add_physical_device (GDK_SEAT_DEFAULT (seat), device_manager->system_keyboard);
   g_object_unref (seat);
 
+  _gdk_device_manager = device_manager;
+
   tablet_input_api_user_preference = g_getenv ("GDK_WIN32_TABLET_INPUT_API");
   if (g_strcmp0 (tablet_input_api_user_preference, "none") == 0)
     {
@@ -738,12 +741,22 @@ gdk_device_manager_win32_constructed (GObject *object)
       have_tablet_input_api_preference = TRUE;
       _gdk_win32_tablet_input_api = GDK_WIN32_TABLET_INPUT_API_WINTAB;
     }
+  else if (g_strcmp0 (tablet_input_api_user_preference, "winpointer") == 0)
+    {
+      have_tablet_input_api_preference = TRUE;
+      _gdk_win32_tablet_input_api = GDK_WIN32_TABLET_INPUT_API_WINPOINTER;
+    }
   else
     {
       have_tablet_input_api_preference = FALSE;
-      _gdk_win32_tablet_input_api = GDK_WIN32_TABLET_INPUT_API_WINTAB;
+      _gdk_win32_tablet_input_api = GDK_WIN32_TABLET_INPUT_API_WINPOINTER;
     }
 
+  if (_gdk_win32_tablet_input_api == GDK_WIN32_TABLET_INPUT_API_WINPOINTER)
+    {
+      if (!gdk_winpointer_initialize () && !have_tablet_input_api_preference)
+        _gdk_win32_tablet_input_api = GDK_WIN32_TABLET_INPUT_API_WINTAB;
+    }
   if (_gdk_win32_tablet_input_api == GDK_WIN32_TABLET_INPUT_API_WINTAB)
     {
       /* Only call Wintab init stuff after the default display
diff --git a/gdk/win32/gdkdevicemanager-win32.h b/gdk/win32/gdkdevicemanager-win32.h
index 5a93a2df41..26b5c761ac 100644
--- a/gdk/win32/gdkdevicemanager-win32.h
+++ b/gdk/win32/gdkdevicemanager-win32.h
@@ -40,6 +40,8 @@ struct _GdkDeviceManagerWin32
   /* Fake physical devices */
   GdkDevice *system_pointer;
   GdkDevice *system_keyboard;
+
+  GList *winpointer_devices;
   GList *wintab_devices;
 
   /* Bumped up every time a wintab device enters the proximity
diff --git a/gdk/win32/gdkevents-win32.c b/gdk/win32/gdkevents-win32.c
index db4f7d1aa7..44a6792169 100644
--- a/gdk/win32/gdkevents-win32.c
+++ b/gdk/win32/gdkevents-win32.c
@@ -57,6 +57,7 @@
 #include "gdkdeviceprivate.h"
 #include "gdkdevice-virtual.h"
 #include "gdkdevice-wintab.h"
+#include "gdkinput-winpointer.h"
 #include "gdkwin32dnd.h"
 #include "gdkwin32dnd-private.h"
 #include "gdkdisplay-win32.h"
@@ -72,8 +73,9 @@
 #endif
 
 #include <objbase.h>
-
 #include <imm.h>
+#include <tchar.h>
+#include <tpcshrd.h>
 
 #define GDK_MOD2_MASK (1 << 4)
 
@@ -2976,6 +2978,13 @@ gdk_event_translate (MSG *msg,
       *ret_valp = 0;
       break;
 
+    case WM_DESTROY:
+      if (_gdk_win32_tablet_input_api == GDK_WIN32_TABLET_INPUT_API_WINPOINTER)
+        gdk_winpointer_finalize_surface (window);
+
+      return_val = FALSE;
+      break;
+
     case WM_NCDESTROY:
       if ((pointer_grab != NULL && pointer_grab->surface == window) ||
           (keyboard_grab && keyboard_grab->surface == window))
@@ -3081,6 +3090,16 @@ gdk_event_translate (MSG *msg,
                                      GET_Y_LPARAM (msg->lParam), ret_valp);
       break;
 
+    case WM_TABLET_QUERYSYSTEMGESTURESTATUS:
+      *ret_valp = TABLET_DISABLE_PRESSANDHOLD |
+                  TABLET_DISABLE_PENTAPFEEDBACK |
+                  TABLET_DISABLE_PENBARRELFEEDBACK |
+                  TABLET_DISABLE_FLICKS |
+                  TABLET_DISABLE_FLICKFALLBACKKEYS;
+      return_val = TRUE;
+      break;
+
+
       /* Handle WINTAB events here, as we know that the device manager will
        * use the fixed WT_DEFBASE as lcMsgBase, and we thus can use the
        * constants as case labels.
diff --git a/gdk/win32/gdkinput-winpointer.c b/gdk/win32/gdkinput-winpointer.c
new file mode 100644
index 0000000000..5628cb5df9
--- /dev/null
+++ b/gdk/win32/gdkinput-winpointer.c
@@ -0,0 +1,602 @@
+/* GDK - The GIMP Drawing Kit
+ * Copyright (C) 2021 the GTK team
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <gdk/gdk.h>
+#include "gdkwin32.h"
+#include "gdkprivate-win32.h"
+#include "gdkdevicemanager-win32.h"
+#include "gdkdevice-winpointer.h"
+#include "gdkdeviceprivate.h"
+#include "gdkdisplayprivate.h"
+#include "gdkseatdefaultprivate.h"
+#include "gdkdevicetoolprivate.h"
+
+#include <windows.h>
+
+#include <tchar.h>
+#include <tpcshrd.h>
+#include <hidsdi.h>
+
+#define HID_STRING_BYTES_LIMIT 200
+#define VID_PID_CHARS 4
+
+typedef BOOL
+(WINAPI *registerPointerDeviceNotifications_t)(HWND window, BOOL notifyRange);
+typedef BOOL
+(WINAPI *getPointerDevices_t)(UINT32 *deviceCount, POINTER_DEVICE_INFO *pointerDevices);
+typedef BOOL
+(WINAPI *getPointerDeviceCursors_t)(HANDLE device, UINT32 *cursorCount, POINTER_DEVICE_CURSOR_INFO 
*deviceCursors);
+typedef BOOL
+(WINAPI *getPointerDeviceRects_t)(HANDLE device, RECT *pointerDeviceRect, RECT *displayRect);
+typedef BOOL
+(WINAPI *getPointerType_t)(UINT32 pointerId, POINTER_INPUT_TYPE *pointerType);
+typedef BOOL
+(WINAPI *getPointerCursorId_t)(UINT32 pointerId, UINT32 *cursorId);
+typedef BOOL
+(WINAPI *getPointerPenInfoHistory_t)(UINT32 pointerId, UINT32 *entriesCount, POINTER_PEN_INFO *penInfo);
+typedef BOOL
+(WINAPI *getPointerTouchInfoHistory_t)(UINT32 pointerId, UINT32 *entriesCount, POINTER_TOUCH_INFO 
*touchInfo);
+typedef BOOL
+(WINAPI *setGestureConfig_t)(HWND hwnd, DWORD dwReserved, UINT cIDs, PGESTURECONFIG pGestureConfig, UINT 
cbSize);
+
+static registerPointerDeviceNotifications_t registerPointerDeviceNotifications;
+static getPointerDevices_t getPointerDevices;
+static getPointerDeviceCursors_t getPointerDeviceCursors;
+static getPointerDeviceRects_t getPointerDeviceRects;
+static getPointerType_t getPointerType;
+static getPointerCursorId_t getPointerCursorId;
+static getPointerPenInfoHistory_t getPointerPenInfoHistory;
+static getPointerTouchInfoHistory_t getPointerTouchInfoHistory;
+static setGestureConfig_t setGestureConfig;
+
+static ATOM notifications_window_class;
+static HWND notifications_window_handle;
+
+static inline double
+utils_rect_width (RECT *rect)
+{
+  return rect->right - rect->left;
+}
+
+static inline double
+utils_rect_height (RECT *rect)
+{
+  return rect->bottom - rect->top;
+}
+
+static inline gboolean
+utils_rect_is_degenerate (RECT *rect)
+{
+  return utils_rect_width (rect) == 0 || utils_rect_height (rect) == 0;
+}
+
+static gboolean
+winpointer_device_update_scale_factors (GdkDeviceWinpointer *device)
+{
+  RECT device_rect;
+  RECT display_rect;
+
+  if (!getPointerDeviceRects (device->device_handle, &device_rect, &display_rect))
+    {
+      WIN32_API_FAILED ("GetPointerDeviceRects");
+      return FALSE;
+    }
+
+  if (utils_rect_is_degenerate (&device_rect))
+    {
+      g_warning ("Invalid coordinates from GetPointerDeviceRects");
+      return FALSE;
+    }
+
+  device->origin_x = display_rect.left;
+  device->origin_y = display_rect.top;
+  device->scale_x = utils_rect_width (&display_rect) / utils_rect_width (&device_rect);
+  device->scale_y = utils_rect_height (&display_rect) / utils_rect_height (&device_rect);
+
+  return TRUE;
+}
+
+static void
+winpointer_get_device_details (HANDLE device,
+                               char *vid,
+                               char *pid,
+                               char **manufacturer,
+                               char **product)
+{
+  RID_DEVICE_INFO info;
+  UINT wchars_count = 0;
+  UINT size = 0;
+
+  memset (&info, 0, sizeof (info));
+
+  info.cbSize = sizeof (info);
+  size = sizeof (info);
+
+  if (GetRawInputDeviceInfoW (device, RIDI_DEVICEINFO, &info, &size) > 0 &&
+      info.dwType == RIM_TYPEHID &&
+      info.hid.dwVendorId > 0 &&
+      info.hid.dwProductId > 0)
+    {
+      const char *format_string = "%0" G_STRINGIFY (VID_PID_CHARS) "x";
+
+      g_snprintf (vid, VID_PID_CHARS + 1, format_string, (unsigned) info.hid.dwVendorId);
+      g_snprintf (pid, VID_PID_CHARS + 1, format_string, (unsigned) info.hid.dwProductId);
+    }
+
+  if (GetRawInputDeviceInfoW (device, RIDI_DEVICENAME, NULL, &wchars_count) == 0)
+    {
+      gunichar2 *device_path = g_new0 (gunichar2, wchars_count);
+
+      if (GetRawInputDeviceInfoW (device, RIDI_DEVICENAME, device_path, &wchars_count) > 0)
+        {
+          HANDLE device_file = CreateFileW (device_path,
+                                            0,
+                                            FILE_SHARE_READ |
+                                            FILE_SHARE_WRITE |
+                                            FILE_SHARE_DELETE,
+                                            NULL,
+                                            OPEN_EXISTING,
+                                            FILE_FLAG_SESSION_AWARE,
+                                            NULL);
+
+          if (device_file != INVALID_HANDLE_VALUE)
+            {
+              gunichar2 *buffer = g_malloc0 (HID_STRING_BYTES_LIMIT);
+
+              if (HidD_GetManufacturerString (device_file, buffer, HID_STRING_BYTES_LIMIT))
+                if (buffer[0])
+                  *manufacturer = g_utf16_to_utf8 (buffer, -1, NULL, NULL, NULL);
+
+              if (HidD_GetProductString (device_file, buffer, HID_STRING_BYTES_LIMIT))
+                if (buffer[0])
+                  *product = g_utf16_to_utf8 (buffer, -1, NULL, NULL, NULL);
+
+              g_free (buffer);
+              CloseHandle (device_file);
+            }
+        }
+
+      g_free (device_path);
+    }
+}
+
+static void
+winpointer_create_device (POINTER_DEVICE_INFO *info,
+                          GdkInputSource source)
+{
+  GdkDeviceWinpointer *device = NULL;
+  GdkSeat *seat = NULL;
+  unsigned num_touches = 0;
+  char vid[VID_PID_CHARS + 1];
+  char pid[VID_PID_CHARS + 1];
+  char *manufacturer = NULL;
+  char *product = NULL;
+  char *base_name = NULL;
+  char *name = NULL;
+  UINT32 num_cursors = 0;
+  GdkAxisFlags axes_flags = 0;
+
+  seat = gdk_display_get_default_seat (_gdk_display);
+
+  memset (pid, 0, VID_PID_CHARS + 1);
+  memset (vid, 0, VID_PID_CHARS + 1);
+
+  if (!getPointerDeviceCursors (info->device, &num_cursors, NULL))
+    {
+      WIN32_API_FAILED ("GetPointerDeviceCursors");
+      return;
+    }
+
+  if (num_cursors == 0)
+    return;
+
+  winpointer_get_device_details (info->device, vid, pid, &manufacturer, &product);
+
+  /* build up the name */
+  if (!manufacturer && vid[0])
+    manufacturer = g_strdup (vid);
+
+  if (!product && pid[0])
+    product = g_strdup (pid);
+
+  if (manufacturer && product)
+    base_name = g_strconcat (manufacturer, " ", product, NULL);
+
+  if (!base_name && info->productString[0])
+    base_name = g_utf16_to_utf8 (info->productString, -1, NULL, NULL, NULL);
+
+  if (!base_name)
+    base_name = g_strdup ("Unnamed");
+
+  switch (source)
+    {
+    case GDK_SOURCE_PEN:
+      name = g_strconcat (base_name, " Pen", NULL);
+    break;
+
+    case GDK_SOURCE_TOUCHSCREEN:
+      num_touches = info->maxActiveContacts;
+      name = g_strconcat (base_name, " Finger touch", NULL);
+    break;
+
+    default:
+      name = g_strdup (base_name);
+    break;
+    }
+
+  device = g_object_new (GDK_TYPE_DEVICE_WINPOINTER,
+                         "display", _gdk_display,
+                         "seat", seat,
+                         "has-cursor", TRUE,
+                         "source", source,
+                         "name", name,
+                         "num-touches", num_touches,
+                         "vendor-id", vid[0] ? vid : NULL,
+                         "product-id", pid[0] ? pid : NULL,
+                         NULL);
+
+  switch (source)
+    {
+    case GDK_SOURCE_PEN:
+      _gdk_device_add_axis (GDK_DEVICE (device), GDK_AXIS_PRESSURE, 0.0, 1.0, 1.0 / 1024.0);
+      axes_flags |= GDK_AXIS_FLAG_PRESSURE;
+
+      _gdk_device_add_axis (GDK_DEVICE (device), GDK_AXIS_XTILT, -1.0, 1.0, 1.0 / 90.0);
+      axes_flags |= GDK_AXIS_FLAG_XTILT;
+
+      _gdk_device_add_axis (GDK_DEVICE (device), GDK_AXIS_YTILT, -1.0, 1.0, 1.0 / 90.0);
+      axes_flags |= GDK_AXIS_FLAG_YTILT;
+
+      _gdk_device_add_axis (GDK_DEVICE (device), GDK_AXIS_ROTATION, 0.0, 1.0, 1.0 / 360.0);
+      axes_flags |= GDK_AXIS_FLAG_ROTATION;
+    break;
+
+    case GDK_SOURCE_TOUCHSCREEN:
+      _gdk_device_add_axis (GDK_DEVICE (device), GDK_AXIS_PRESSURE, 0.0, 1.0, 1.0 / 1024.0);
+      axes_flags |= GDK_AXIS_FLAG_PRESSURE;
+    break;
+
+    default:
+      g_warn_if_reached ();
+    break;
+    }
+
+  device->device_handle = info->device;
+  device->start_cursor_id = info->startingCursorId;
+  device->end_cursor_id = info->startingCursorId + num_cursors - 1;
+
+  if (!winpointer_device_update_scale_factors (device))
+    {
+      g_set_object (&device, NULL);
+      goto cleanup;
+    }
+
+  switch (source)
+    {
+    case GDK_SOURCE_PEN:
+      {
+        device->tool_pen = gdk_device_tool_new (0, 0, GDK_DEVICE_TOOL_TYPE_PEN, axes_flags);
+        gdk_seat_default_add_tool (GDK_SEAT_DEFAULT (seat), device->tool_pen);
+
+        device->tool_eraser = gdk_device_tool_new (0, 0, GDK_DEVICE_TOOL_TYPE_ERASER, axes_flags);
+        gdk_seat_default_add_tool (GDK_SEAT_DEFAULT (seat), device->tool_eraser);
+      }
+    break;
+    case GDK_SOURCE_TOUCHSCREEN:
+    break;
+    default:
+      g_warn_if_reached ();
+    break;
+    }
+
+  _gdk_device_manager->winpointer_devices = g_list_append (_gdk_device_manager->winpointer_devices, device);
+
+  _gdk_device_set_associated_device (GDK_DEVICE (device), _gdk_device_manager->core_pointer);
+  _gdk_device_add_physical_device (_gdk_device_manager->core_pointer, GDK_DEVICE (device));
+
+  gdk_seat_default_add_physical_device (GDK_SEAT_DEFAULT (seat), GDK_DEVICE (device));
+
+cleanup:
+  g_free (name);
+  g_free (base_name);
+  g_free (product);
+  g_free (manufacturer);
+}
+
+static void
+winpointer_create_devices (POINTER_DEVICE_INFO *info)
+{
+  switch (info->pointerDeviceType)
+    {
+    case POINTER_DEVICE_TYPE_INTEGRATED_PEN:
+    case POINTER_DEVICE_TYPE_EXTERNAL_PEN:
+      winpointer_create_device (info, GDK_SOURCE_PEN);
+    break;
+    case POINTER_DEVICE_TYPE_TOUCH:
+      winpointer_create_device (info, GDK_SOURCE_TOUCHSCREEN);
+    break;
+    default:
+      g_warn_if_reached ();
+    break;
+    }
+}
+
+static gboolean
+winpointer_find_device_in_system_list (GdkDeviceWinpointer *device,
+                                       POINTER_DEVICE_INFO *infos,
+                                       UINT32 infos_count)
+{
+  for (UINT32 i = 0; i < infos_count; i++)
+    {
+      if (device->device_handle == infos[i].device &&
+          device->start_cursor_id == infos[i].startingCursorId)
+        {
+          return TRUE;
+        }
+    }
+
+  return FALSE;
+}
+
+static gboolean
+winpointer_find_system_device_in_device_manager (POINTER_DEVICE_INFO *info)
+{
+  for (GList *l = _gdk_device_manager->winpointer_devices; l != NULL; l = l->next)
+    {
+      GdkDeviceWinpointer *device = GDK_DEVICE_WINPOINTER (l->data);
+
+      if (device->device_handle == info->device &&
+          device->start_cursor_id == info->startingCursorId)
+        {
+          return TRUE;
+        }
+    }
+
+  return FALSE;
+}
+
+static void
+winpointer_enumerate_devices (void)
+{
+  POINTER_DEVICE_INFO *infos = NULL;
+  UINT32 infos_count = 0;
+  UINT32 i = 0;
+  GList *current = NULL;
+
+  do
+    {
+      infos = g_new0 (POINTER_DEVICE_INFO, infos_count);
+      if (!getPointerDevices (&infos_count, infos))
+        {
+          WIN32_API_FAILED ("GetPointerDevices");
+          g_free (infos);
+          return;
+        }
+    }
+  while (infos_count > 0 && !infos);
+
+  current = _gdk_device_manager->winpointer_devices;
+
+  while (current != NULL)
+    {
+      GdkDeviceWinpointer *device = GDK_DEVICE_WINPOINTER (current->data);
+      GList *next = current->next;
+
+      if (!winpointer_find_device_in_system_list (device, infos, infos_count))
+        {
+          GdkSeat *seat = gdk_device_get_seat (GDK_DEVICE (device));
+
+          _gdk_device_manager->winpointer_devices = g_list_delete_link 
(_gdk_device_manager->winpointer_devices,
+                                                                        current);
+
+          gdk_device_update_tool (GDK_DEVICE (device), NULL);
+
+          if (device->tool_pen)
+            gdk_seat_default_remove_tool (GDK_SEAT_DEFAULT (seat), device->tool_pen);
+
+          if (device->tool_eraser)
+            gdk_seat_default_remove_tool (GDK_SEAT_DEFAULT (seat), device->tool_eraser);
+
+          _gdk_device_set_associated_device (GDK_DEVICE (device), NULL);
+          _gdk_device_remove_physical_device (_gdk_device_manager->core_pointer, GDK_DEVICE (device));
+
+          gdk_seat_default_remove_physical_device (GDK_SEAT_DEFAULT (seat), GDK_DEVICE (device));
+
+          g_object_unref (device);
+        }
+      else
+        {
+          winpointer_device_update_scale_factors (device);
+        }
+
+      current = next;
+    }
+
+  /* create new gdk devices */
+  for (i = 0; i < infos_count; i++)
+    {
+      if (!winpointer_find_system_device_in_device_manager (&infos[i]))
+        {
+          winpointer_create_devices (&infos[i]);
+        }
+    }
+
+  g_free (infos);
+}
+
+static LRESULT CALLBACK
+winpointer_notifications_window_procedure (HWND hWnd,
+                                           UINT uMsg,
+                                           WPARAM wParam,
+                                           LPARAM lParam)
+{
+  switch (uMsg)
+    {
+    case WM_POINTERDEVICECHANGE:
+      winpointer_enumerate_devices ();
+      return 0;
+    }
+
+  return DefWindowProcW (hWnd, uMsg, wParam, lParam);
+}
+
+static gboolean
+winpointer_notif_window_create (void)
+{
+  WNDCLASSEXW wndclassex;
+
+  memset (&wndclassex, 0, sizeof (wndclassex));
+  wndclassex.cbSize = sizeof (wndclassex);
+  wndclassex.lpszClassName = L"GdkWin32WinpointerNotificationsWindowClass";
+  wndclassex.lpfnWndProc = winpointer_notifications_window_procedure;
+  wndclassex.hInstance = _gdk_dll_hinstance;
+
+  if ((notifications_window_class = RegisterClassExW (&wndclassex)) == 0)
+    {
+      WIN32_API_FAILED ("RegisterClassExW");
+      return FALSE;
+    }
+
+  if (!(notifications_window_handle = CreateWindowExW (0,
+                                                       (LPCWSTR)(guintptr)notifications_window_class,
+                                                       L"GdkWin32 Winpointer Notifications",
+                                                       0,
+                                                       0, 0, 0, 0,
+                                                       HWND_MESSAGE,
+                                                       NULL,
+                                                       _gdk_dll_hinstance,
+                                                       NULL)))
+    {
+      WIN32_API_FAILED ("CreateWindowExW");
+      return FALSE;
+    }
+
+  return TRUE;
+}
+
+static gboolean
+winpointer_ensure_procedures (void)
+{
+  static HMODULE user32_dll = NULL;
+
+  if (!user32_dll)
+    {
+      user32_dll = LoadLibraryW (L"user32.dll");
+      if (!user32_dll)
+        {
+          WIN32_API_FAILED ("LoadLibraryW");
+          return FALSE;
+        }
+
+      registerPointerDeviceNotifications = (registerPointerDeviceNotifications_t)
+        GetProcAddress (user32_dll, "RegisterPointerDeviceNotifications");
+      getPointerDevices = (getPointerDevices_t)
+        GetProcAddress (user32_dll, "GetPointerDevices");
+      getPointerDeviceCursors = (getPointerDeviceCursors_t)
+        GetProcAddress (user32_dll, "GetPointerDeviceCursors");
+      getPointerDeviceRects = (getPointerDeviceRects_t)
+        GetProcAddress (user32_dll, "GetPointerDeviceRects");
+      getPointerType = (getPointerType_t)
+        GetProcAddress (user32_dll, "GetPointerType");
+      getPointerCursorId = (getPointerCursorId_t)
+        GetProcAddress (user32_dll, "GetPointerCursorId");
+      getPointerPenInfoHistory = (getPointerPenInfoHistory_t)
+        GetProcAddress (user32_dll, "GetPointerPenInfoHistory");
+      getPointerTouchInfoHistory = (getPointerTouchInfoHistory_t)
+        GetProcAddress (user32_dll, "GetPointerTouchInfoHistory");
+      setGestureConfig = (setGestureConfig_t)
+        GetProcAddress (user32_dll, "SetGestureConfig");
+    }
+
+  return registerPointerDeviceNotifications &&
+         getPointerDevices &&
+         getPointerDeviceCursors &&
+         getPointerDeviceRects &&
+         getPointerType &&
+         getPointerCursorId &&
+         getPointerPenInfoHistory &&
+         getPointerTouchInfoHistory &&
+         setGestureConfig;
+}
+
+gboolean
+gdk_winpointer_initialize (void)
+{
+  if (!winpointer_ensure_procedures ())
+    return FALSE;
+
+  if (!winpointer_notif_window_create ())
+    return FALSE;
+
+  if (!registerPointerDeviceNotifications (notifications_window_handle, FALSE))
+    {
+      WIN32_API_FAILED ("RegisterPointerDeviceNotifications");
+      return FALSE;
+    }
+
+  winpointer_enumerate_devices ();
+
+  return TRUE;
+}
+
+#ifndef MICROSOFT_TABLETPENSERVICE_PROPERTY
+#define MICROSOFT_TABLETPENSERVICE_PROPERTY \
+_T("MicrosoftTabletPenServiceProperty")
+#endif
+
+void
+gdk_winpointer_initialize_surface (GdkSurface *surface)
+{
+  HWND hwnd = GDK_SURFACE_HWND (surface);
+  ATOM key = 0;
+  HANDLE val = (HANDLE)(TABLET_DISABLE_PRESSANDHOLD |
+                        TABLET_DISABLE_PENTAPFEEDBACK |
+                        TABLET_DISABLE_PENBARRELFEEDBACK |
+                        TABLET_DISABLE_FLICKS |
+                        TABLET_DISABLE_FLICKFALLBACKKEYS);
+
+  winpointer_ensure_procedures ();
+
+  key = GlobalAddAtom (MICROSOFT_TABLETPENSERVICE_PROPERTY);
+  API_CALL (SetPropW, (hwnd, (LPCWSTR)(guintptr)key, val));
+  GlobalDeleteAtom (key);
+
+  if (setGestureConfig != NULL)
+    {
+      GESTURECONFIG gesture_config;
+      memset (&gesture_config, 0, sizeof (gesture_config));
+
+      gesture_config.dwID = 0;
+      gesture_config.dwWant = 0;
+      gesture_config.dwBlock = GC_ALLGESTURES;
+
+      API_CALL (setGestureConfig, (hwnd, 0, 1, &gesture_config, sizeof (gesture_config)));
+    }
+}
+
+void
+gdk_winpointer_finalize_surface (GdkSurface *surface)
+{
+  HWND hwnd = GDK_SURFACE_HWND (surface);
+  ATOM key = 0;
+
+  key = GlobalAddAtom (MICROSOFT_TABLETPENSERVICE_PROPERTY);
+  RemovePropW (hwnd, (LPCWSTR)(guintptr)key);
+  GlobalDeleteAtom (key);
+}
diff --git a/gdk/win32/gdkinput-winpointer.h b/gdk/win32/gdkinput-winpointer.h
new file mode 100644
index 0000000000..d8a55c8f07
--- /dev/null
+++ b/gdk/win32/gdkinput-winpointer.h
@@ -0,0 +1,28 @@
+/* GDK - The GIMP Drawing Kit
+ * Copyright (C) 2021 the GTK team
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __GDK_INPUT_WINPOINTER_H__
+#define __GDK_INPUT_WINPOINTER_H__
+
+#include "winpointer.h"
+
+gboolean gdk_winpointer_initialize (void);
+
+void gdk_winpointer_initialize_surface (GdkSurface *surface);
+void gdk_winpointer_finalize_surface (GdkSurface *surface);
+
+#endif /* __GDK_INPUT_WINPOINTER_H__ */
diff --git a/gdk/win32/gdksurface-win32.c b/gdk/win32/gdksurface-win32.c
index 3570b28dfa..ee9c3e3477 100644
--- a/gdk/win32/gdksurface-win32.c
+++ b/gdk/win32/gdksurface-win32.c
@@ -43,6 +43,7 @@
 #include "gdkmonitorprivate.h"
 #include "gdkwin32surface.h"
 #include "gdkwin32cursor.h"
+#include "gdkinput-winpointer.h"
 #include "gdkglcontext-win32.h"
 #include "gdkdisplay-win32.h"
 #include "gdkdevice-win32.h"
@@ -645,6 +646,9 @@ _gdk_win32_display_create_surface (GdkDisplay     *display,
       return NULL;
     }
 
+  if (_gdk_win32_tablet_input_api == GDK_WIN32_TABLET_INPUT_API_WINPOINTER)
+    gdk_winpointer_initialize_surface (surface);
+
   _gdk_win32_surface_enable_transparency (surface);
   _gdk_win32_surface_register_dnd (surface);
 
diff --git a/gdk/win32/meson.build b/gdk/win32/meson.build
index b46699ee4a..93c55fa6d8 100644
--- a/gdk/win32/meson.build
+++ b/gdk/win32/meson.build
@@ -17,6 +17,7 @@ gdk_win32_sources = files([
   'gdkglcontext-win32-wgl.c',
   'gdkglobals-win32.c',
   'gdkhdataoutputstream-win32.c',
+  'gdkinput-winpointer.c',
   'gdkkeys-win32.c',
   'gdkwin32langnotification.c',
   'gdkmain-win32.c',
@@ -52,8 +53,9 @@ if win32_has_egl
   gdk_win32_sources += ['gdkglcontext-win32-egl.c']
 endif
 
-gdk_win32_deps = [ # FIXME
-  pangowin32_dep
+gdk_win32_deps = [
+  pangowin32_dep, # FIXME
+  cc.find_library('hid'),
 ]
 
 libgdk_win32 = static_library('gdk-win32',


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