[gtk: 1/2] GdkWin32: Add support for DirectManipulation




commit 8f19099f1ae87591802a4b0aafb12ab49bd011da
Author: Luca Bacci <luca bacci982 gmail com>
Date:   Wed May 4 14:56:48 2022 +0200

    GdkWin32: Add support for DirectManipulation
    
    Adds support for Precision TouchPads (PTPs) gestures
    and scroll events.

 gdk/win32/gdkevents-win32.c        |  10 +
 gdk/win32/gdkinput-dmanipulation.c | 586 +++++++++++++++++++++++++++++++++++++
 gdk/win32/gdkinput-dmanipulation.h |  32 ++
 gdk/win32/gdkmain-win32.c          |  83 ++++++
 gdk/win32/gdkprivate-win32.h       |  10 +
 gdk/win32/gdksurface-win32.c       |  11 +-
 gdk/win32/gdksurface-win32.h       |   4 +
 gdk/win32/meson.build              |   1 +
 8 files changed, 735 insertions(+), 2 deletions(-)
---
diff --git a/gdk/win32/gdkevents-win32.c b/gdk/win32/gdkevents-win32.c
index c928a14142..23f99c1ab7 100644
--- a/gdk/win32/gdkevents-win32.c
+++ b/gdk/win32/gdkevents-win32.c
@@ -58,6 +58,7 @@
 #include "gdkdeviceprivate.h"
 #include "gdkdevice-virtual.h"
 #include "gdkdevice-wintab.h"
+#include "gdkinput-dmanipulation.h"
 #include "gdkinput-winpointer.h"
 #include "gdkwin32dnd.h"
 #include "gdkwin32dnd-private.h"
@@ -2656,6 +2657,13 @@ gdk_event_translate (MSG *msg,
       return_val = TRUE;
       break;
 
+    case DM_POINTERHITTEST:
+      gdk_dmanipulation_maybe_add_contact (window, msg);
+
+      *ret_valp = 0;
+      return_val = TRUE;
+      break;
+
     case WM_MOUSEWHEEL:
     case WM_MOUSEHWHEEL:
     {
@@ -3156,6 +3164,8 @@ gdk_event_translate (MSG *msg,
       if (win32_display->tablet_input_api == GDK_WIN32_TABLET_INPUT_API_WINPOINTER)
         gdk_winpointer_finalize_surface (window);
 
+      gdk_dmanipulation_finalize_surface (window);
+
       return_val = FALSE;
       break;
 
diff --git a/gdk/win32/gdkinput-dmanipulation.c b/gdk/win32/gdkinput-dmanipulation.c
new file mode 100644
index 0000000000..49244f02c2
--- /dev/null
+++ b/gdk/win32/gdkinput-dmanipulation.c
@@ -0,0 +1,586 @@
+/* gdkinput-dmanipulation.c
+ *
+ * Copyright © 2022 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/>.
+ *
+ * SPDX-License-Identifier: LGPL-2.1-or-later
+ */
+
+/* {{{ */
+
+#ifdef WINVER
+#undef WINVER
+#endif
+#ifdef _WIN32_WINNT
+#undef _WIN32_WINNT
+#endif
+#define WINVER 0x0603
+#define _WIN32_WINNT 0x0603
+#define COBJMACROS
+#include "config.h"
+
+#include <gdk/gdk.h>
+#include "gdkwin32.h"
+#include "gdkprivate-win32.h"
+#include "gdkdevicemanager-win32.h"
+#include "gdkdevice-virtual.h"
+#include "gdkdeviceprivate.h"
+#include "gdkdisplayprivate.h"
+#include "gdkeventsprivate.h"
+#include "gdkseatdefaultprivate.h"
+#include "gdkinput-dmanipulation.h"
+#include "winpointer.h"
+
+#include <windows.h>
+#include <directmanipulation.h>
+
+typedef BOOL
+(WINAPI *getPointerType_t)(UINT32 pointerId, POINTER_INPUT_TYPE *pointerType);
+static getPointerType_t getPointerType;
+
+static IDirectManipulationManager *dmanipulation_manager;
+
+typedef struct
+{
+  IDirectManipulationViewportEventHandlerVtbl *vtable;
+  LONG reference_count;
+
+  enum {
+    GESTURE_PAN,
+    GESTURE_ZOOM,
+  } gesture;
+
+  GdkTouchpadGesturePhase phase;
+  gpointer sequence;
+
+  float scale;
+  float pan_x;
+  float pan_y;
+
+  GdkSurface *surface;
+  GdkDevice *device;
+}
+DManipEventHandler;
+
+static void dmanip_event_handler_running_state_clear (DManipEventHandler *handler);
+static void dmanip_event_handler_free (DManipEventHandler *handler);
+
+static void reset_viewport (IDirectManipulationViewport *viewport);
+
+static gpointer util_get_next_sequence (void);
+static GdkModifierType util_get_modifier_state (void);
+static gboolean util_handler_free (gpointer);
+
+/* }}} */
+/* {{{ ViewportEventHandler */
+
+static STDMETHODIMP_ (ULONG)
+DManipEventHandler_AddRef (IDirectManipulationViewportEventHandler *self_)
+{
+  DManipEventHandler *self = (DManipEventHandler*) self_;
+
+  return (ULONG) InterlockedIncrement (&self->reference_count);
+}
+
+static STDMETHODIMP_ (ULONG)
+DManipEventHandler_Release (IDirectManipulationViewportEventHandler *self_)
+{
+  DManipEventHandler *self = (DManipEventHandler*) self_;
+
+  /* NOTE: This may run from a worker thread */
+
+  LONG new_reference_count = InterlockedDecrement (&self->reference_count);
+
+  if (new_reference_count <= 0)
+    {
+      /* For safety, schedule the cleanup to be executed
+       * on the main thread */
+      g_idle_add (util_handler_free, self);
+
+      return 0;
+    }
+
+  return (ULONG) new_reference_count;
+}
+
+static STDMETHODIMP
+DManipEventHandler_QueryInterface (IDirectManipulationViewportEventHandler *self_,
+                                   REFIID riid,
+                                   void **ppvObject)
+{
+  DManipEventHandler *self = (DManipEventHandler*) self_;
+
+  if G_UNLIKELY (!self || !ppvObject)
+    return E_POINTER;
+
+  *ppvObject = NULL;
+
+  if (IsEqualGUID (riid, &IID_IUnknown))
+    *ppvObject = self;
+  else if (IsEqualGUID (riid, &IID_IDirectManipulationViewportEventHandler))
+    *ppvObject = self;
+
+  if (*ppvObject == NULL)
+    return E_NOINTERFACE;
+
+  DManipEventHandler_AddRef (self_);
+  return S_OK;
+}
+
+/* NOTE:
+ *
+ * All DManipEventHandler callbacks are fired from the main thread */
+
+static STDMETHODIMP
+DManipEventHandler_OnViewportUpdated (IDirectManipulationViewportEventHandler *self_,
+                                      IDirectManipulationViewport *viewport)
+{
+  return S_OK;
+}
+
+static STDMETHODIMP
+DManipEventHandler_OnContentUpdated (IDirectManipulationViewportEventHandler *self_,
+                                     IDirectManipulationViewport *viewport,
+                                     IDirectManipulationContent *content)
+{
+  DManipEventHandler *self = (DManipEventHandler*) self_;
+  float transform[6] = {1., 0., 0., 1., 0., 0.};
+  HRESULT hr;
+
+  hr = IDirectManipulationContent_GetContentTransform (content, transform,
+                                                       G_N_ELEMENTS (transform));
+  HR_CHECK_RETURN_VAL (hr, E_FAIL);
+
+  switch (self->gesture)
+    {
+    case GESTURE_PAN:
+      {
+        GdkModifierType state;
+        uint32_t time;
+        float pan_x;
+        float pan_y;
+        GdkEvent *event;
+
+        pan_x = transform[4];
+        pan_y = transform[5];
+
+        state = util_get_modifier_state ();
+        time = (uint32_t) GetMessageTime ();
+
+        event = gdk_scroll_event_new (self->surface,
+                                      self->device,
+                                      NULL, time, state,
+                                      self->pan_x - pan_x,
+                                      self->pan_y - pan_y,
+                                      FALSE,
+                                      GDK_SCROLL_UNIT_SURFACE);
+        _gdk_win32_append_event (event);
+
+        self->pan_x = pan_x;
+        self->pan_y = pan_y;
+      }
+    break;
+    case GESTURE_ZOOM:
+      {
+        GdkModifierType state;
+        uint32_t time;
+        POINT cursor = {0, 0};
+        float scale;
+        GdkEvent *event;
+
+        scale = transform[0];
+
+        state = util_get_modifier_state ();
+        time = (uint32_t) GetMessageTime ();
+        _gdk_win32_get_cursor_pos (&cursor);
+
+        ScreenToClient (GDK_SURFACE_HWND (self->surface), &cursor);
+
+        if (!self->sequence)
+          self->sequence = util_get_next_sequence ();
+
+        event = gdk_touchpad_event_new_pinch (self->surface, self->sequence, self->device,
+                                              time, state, self->phase, cursor.x, cursor.y,
+                                              2, 0.0, 0.0, scale, 0.0);
+        _gdk_win32_append_event (event);
+
+        self->scale = scale;
+        self->phase = GDK_TOUCHPAD_GESTURE_PHASE_UPDATE;
+      }
+    break;
+    default:
+      g_assert_not_reached ();
+    break;
+    }
+
+  return S_OK;
+}
+
+static STDMETHODIMP
+DManipEventHandler_OnViewportStatusChanged (IDirectManipulationViewportEventHandler *self_,
+                                            IDirectManipulationViewport *viewport,
+                                            DIRECTMANIPULATION_STATUS current,
+                                            DIRECTMANIPULATION_STATUS previous)
+{
+  DManipEventHandler *self = (DManipEventHandler*) self_;
+
+  if (previous == DIRECTMANIPULATION_RUNNING)
+    {
+      switch (self->gesture)
+        {
+        case GESTURE_PAN:
+          {
+            GdkModifierType state;
+            uint32_t time;
+            GdkEvent *event;
+
+            state = util_get_modifier_state ();
+            time = (uint32_t) GetMessageTime ();
+
+            event = gdk_scroll_event_new (self->surface, self->device,
+                                          NULL, time, state,
+                                          0.0, 0.0, TRUE,
+                                          GDK_SCROLL_UNIT_SURFACE);
+            _gdk_win32_append_event (event);
+          }
+        break;
+        case GESTURE_ZOOM:
+          {
+            GdkModifierType state;
+            uint32_t time;
+            POINT cursor = {0, 0};
+            GdkEvent *event;
+
+            if (self->phase == GDK_TOUCHPAD_GESTURE_PHASE_BEGIN)
+              break;
+
+            state = util_get_modifier_state ();
+            time = (uint32_t) GetMessageTime ();
+            _gdk_win32_get_cursor_pos (&cursor);
+
+            ScreenToClient (GDK_SURFACE_HWND (self->surface), &cursor);
+
+            event = gdk_touchpad_event_new_pinch (self->surface, self->sequence, self->device,
+                                                  time, state, GDK_TOUCHPAD_GESTURE_PHASE_END,
+                                                  cursor.x, cursor.y, 2, 0., 0., self->scale,
+                                                  0.);
+            _gdk_win32_append_event (event);
+          }
+        break;
+        default:
+          g_assert_not_reached ();
+        break;
+        }
+
+      dmanip_event_handler_running_state_clear (self);
+      reset_viewport (viewport);
+    }
+
+  return S_OK;
+}
+
+static void
+dmanip_event_handler_running_state_clear (DManipEventHandler *handler)
+{
+  handler->scale = 1.0;
+  handler->pan_x = 0.0;
+  handler->pan_y = 0.0;
+
+  handler->phase = GDK_TOUCHPAD_GESTURE_PHASE_BEGIN;
+  handler->sequence = NULL;
+}
+
+static DManipEventHandler*
+dmanip_event_handler_new (GdkSurface *surface,
+                          int gesture)
+{
+  static IDirectManipulationViewportEventHandlerVtbl vtable = {
+    DManipEventHandler_QueryInterface,
+    DManipEventHandler_AddRef,
+    DManipEventHandler_Release,
+    DManipEventHandler_OnViewportStatusChanged,
+    DManipEventHandler_OnViewportUpdated,
+    DManipEventHandler_OnContentUpdated,
+  };
+  DManipEventHandler *handler;
+
+  handler = g_new0 (DManipEventHandler, 1);
+  handler->vtable = &vtable;
+  handler->reference_count = 1;
+
+  handler->gesture = gesture;
+
+  handler->surface = surface;
+  handler->device = _gdk_device_manager->core_pointer;
+
+  dmanip_event_handler_running_state_clear (handler);
+
+  return handler;
+}
+
+static void
+dmanip_event_handler_free (DManipEventHandler *handler)
+{
+  g_free (handler);
+}
+
+
+/* }}} */
+/* {{{ Viewport utils */
+
+
+static void
+reset_viewport (IDirectManipulationViewport *viewport)
+{
+  IDirectManipulationContent *content = NULL;
+  REFIID iid = &IID_IDirectManipulationContent;
+  float identity[6] = {1., 0., 0., 1., 0., 0.};
+  HRESULT hr;
+
+  hr = IDirectManipulationViewport_GetPrimaryContent (viewport, iid, (void**)&content);
+  HR_CHECK (hr);
+
+  hr = IDirectManipulationContent_SyncContentTransform (content, identity,
+                                                        G_N_ELEMENTS (identity));
+  HR_CHECK (hr);
+}
+
+static void
+close_viewport (IDirectManipulationViewport **p_viewport)
+{
+  IDirectManipulationViewport *viewport = *p_viewport;
+
+  if (viewport)
+    {
+      IDirectManipulationViewport_Abandon (viewport);
+      IUnknown_Release (viewport);
+
+      *p_viewport = NULL;
+    }
+}
+
+static void
+create_viewport (GdkSurface *surface,
+                 int gesture,
+                 IDirectManipulationViewport **pViewport)
+{
+  DIRECTMANIPULATION_CONFIGURATION configuration = 0;
+  HWND hwnd = GDK_SURFACE_HWND (surface);
+  IDirectManipulationViewportEventHandler *handler;
+  DWORD cookie = 0;
+  HRESULT hr;
+
+  hr = IDirectManipulationManager_CreateViewport (dmanipulation_manager, NULL, hwnd,
+                                                  &IID_IDirectManipulationViewport,
+                                                  (void**) pViewport);
+  HR_CHECK_GOTO (hr, failed);
+
+  switch (gesture)
+    {
+    case GESTURE_PAN:
+      configuration = DIRECTMANIPULATION_CONFIGURATION_INTERACTION |
+                      DIRECTMANIPULATION_CONFIGURATION_TRANSLATION_X |
+                      DIRECTMANIPULATION_CONFIGURATION_TRANSLATION_Y;
+    break;
+    case GESTURE_ZOOM:
+      configuration = DIRECTMANIPULATION_CONFIGURATION_INTERACTION |
+                      DIRECTMANIPULATION_CONFIGURATION_SCALING;
+    break;
+    default:
+      g_assert_not_reached ();
+    break;
+    }
+
+  handler = (IDirectManipulationViewportEventHandler*)
+    dmanip_event_handler_new (surface, gesture);
+
+  hr = IDirectManipulationViewport_AddEventHandler (*pViewport, hwnd, handler, &cookie);
+  HR_CHECK_GOTO (hr, failed);
+
+  hr = IDirectManipulationViewport_ActivateConfiguration (*pViewport, configuration);
+  HR_CHECK_GOTO (hr, failed);
+
+  hr = IDirectManipulationViewport_SetViewportOptions (*pViewport,
+         DIRECTMANIPULATION_VIEWPORT_OPTIONS_DISABLEPIXELSNAPPING);
+
+  hr = IDirectManipulationViewport_Enable (*pViewport);
+  HR_CHECK_GOTO (hr, failed);
+
+  // drop our initial reference
+  IUnknown_Release (handler);
+  return;
+
+failed:
+  if (handler)
+    IUnknown_Release (handler);
+
+  close_viewport (pViewport);
+}
+
+
+/* }}} */
+/* {{{ Public */
+
+
+void gdk_dmanipulation_initialize (void)
+{
+  if (!getPointerType)
+    {
+      HMODULE user32_mod;
+
+      user32_mod = LoadLibraryW (L"user32.dll");
+      if (!user32_mod)
+        {
+          WIN32_API_FAILED ("LoadLibraryW");
+          return;
+        }
+
+      getPointerType = (getPointerType_t)
+        GetProcAddress (user32_mod, "GetPointerType");
+
+      if (!getPointerType)
+        return;
+    }
+
+  if (!gdk_win32_ensure_com ())
+        return;
+
+  if (dmanipulation_manager == NULL)
+    {
+      HRESULT hr;
+
+      hr = CoCreateInstance (&CLSID_DirectManipulationManager,
+                             NULL,
+                             CLSCTX_INPROC_SERVER,
+                             &IID_IDirectManipulationManager,
+                             (LPVOID*)&dmanipulation_manager);
+      if (FAILED (hr))
+        {
+          if (hr == REGDB_E_CLASSNOTREG || hr == E_NOINTERFACE);
+            /* Not an error,
+             * DirectManipulation is not available */
+          else HR_LOG (hr);
+        }
+    }
+}
+
+void gdk_dmanipulation_initialize_surface (GdkSurface *surface)
+{
+  GdkWin32Surface *surface_win32;
+  HRESULT hr;
+
+  if (!dmanipulation_manager)
+    return;
+
+  surface_win32 = GDK_WIN32_SURFACE (surface);
+
+  create_viewport (surface, GESTURE_PAN,
+                   &surface_win32->dmanipulation_viewport_pan);
+
+  create_viewport (surface, GESTURE_ZOOM,
+                   &surface_win32->dmanipulation_viewport_zoom);
+
+  hr = IDirectManipulationManager_Activate (dmanipulation_manager,
+                                            GDK_SURFACE_HWND (surface));
+  HR_CHECK (hr);
+}
+
+void gdk_dmanipulation_finalize_surface (GdkSurface *surface)
+{
+  GdkWin32Surface *surface_win32 = GDK_WIN32_SURFACE (surface);
+
+  close_viewport (&surface_win32->dmanipulation_viewport_zoom);
+  close_viewport (&surface_win32->dmanipulation_viewport_pan);
+}
+
+void gdk_dmanipulation_maybe_add_contact (GdkSurface *surface,
+                                          MSG *msg)
+{
+  POINTER_INPUT_TYPE type = PT_POINTER;
+  UINT32 pointer_id = GET_POINTERID_WPARAM (msg->wParam);
+
+  if (!dmanipulation_manager)
+    return;
+
+  if (!getPointerType)
+    return;
+
+  if G_UNLIKELY (!getPointerType (pointer_id, &type))
+    {
+      WIN32_API_FAILED_LOG_ONCE ("GetPointerType");
+      return;
+    }
+
+  if (type == PT_TOUCHPAD)
+    {
+      GdkWin32Surface *surface_win32 = GDK_WIN32_SURFACE (surface);
+      HRESULT hr;
+
+      hr = IDirectManipulationViewport_SetContact (surface_win32->dmanipulation_viewport_pan,
+                                                   pointer_id);
+      HR_CHECK (hr);
+
+      hr = IDirectManipulationViewport_SetContact (surface_win32->dmanipulation_viewport_zoom,
+                                                   pointer_id);
+      HR_CHECK (hr);
+    }
+}
+
+
+/* }}} */
+/* {{{ Utils */
+
+
+static gpointer
+util_get_next_sequence (void)
+{
+  //TODO: sequence of other input types?
+  static unsigned char *sequence_counter = 0;
+
+  if (++sequence_counter == 0)
+    sequence_counter++;
+
+  return sequence_counter;
+}
+
+static GdkModifierType
+util_get_modifier_state (void)
+{
+  GdkModifierType mask = 0;
+  BYTE kbd[256];
+
+  GetKeyboardState (kbd);
+  if (kbd[VK_SHIFT] & 0x80)
+    mask |= GDK_SHIFT_MASK;
+  if (kbd[VK_CAPITAL] & 0x80)
+    mask |= GDK_LOCK_MASK;
+  if (kbd[VK_CONTROL] & 0x80)
+    mask |= GDK_CONTROL_MASK;
+  if (kbd[VK_MENU] & 0x80)
+    mask |= GDK_ALT_MASK;
+
+  return mask;
+}
+
+static gboolean
+util_handler_free (gpointer handler)
+{
+  dmanip_event_handler_free ((DManipEventHandler*)handler);
+
+  return G_SOURCE_REMOVE;
+}
+
+
+/* }}} */
diff --git a/gdk/win32/gdkinput-dmanipulation.h b/gdk/win32/gdkinput-dmanipulation.h
new file mode 100644
index 0000000000..77d603c0cf
--- /dev/null
+++ b/gdk/win32/gdkinput-dmanipulation.h
@@ -0,0 +1,32 @@
+/* gdkinput-dmanipulation.h
+ *
+ * Copyright © 2022 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/>.
+ *
+ * SPDX-License-Identifier: LGPL-2.1-or-later
+ */
+
+#ifndef __GDK_INPUT_DMANIPULATION_H__
+#define __GDK_INPUT_DMANIPULATION_H__
+
+void gdk_dmanipulation_initialize (void);
+
+void gdk_dmanipulation_initialize_surface (GdkSurface *surface);
+void gdk_dmanipulation_finalize_surface   (GdkSurface *surface);
+
+void gdk_dmanipulation_maybe_add_contact  (GdkSurface *surface,
+                                           MSG        *msg);
+
+#endif /* __GDK_INPUT_DMANIPULATION_H__ */
diff --git a/gdk/win32/gdkmain-win32.c b/gdk/win32/gdkmain-win32.c
index d140d8e644..d6f8dc82ed 100644
--- a/gdk/win32/gdkmain-win32.c
+++ b/gdk/win32/gdkmain-win32.c
@@ -36,6 +36,7 @@
 #include "gdkkeysyms.h"
 #include "gdkintl.h"
 #include "gdkprivate-win32.h"
+#include "gdkinput-dmanipulation.h"
 #include "gdkwin32.h"
 
 #include <objbase.h>
@@ -49,6 +50,12 @@
 
 static gboolean gdk_synchronize = FALSE;
 
+/* Whether GDK initialized COM */
+static gboolean co_initialized = FALSE;
+
+/* Whether GDK initialized OLE */
+static gboolean ole_initialized = FALSE;
+
 void
 _gdk_win32_surfaceing_init (void)
 {
@@ -67,6 +74,82 @@ _gdk_win32_surfaceing_init (void)
   GDK_NOTE (EVENTS, g_print ("input_locale: %p\n", _gdk_input_locale));
 
   _gdk_win32_clipdrop_init ();
+
+  gdk_dmanipulation_initialize ();
+}
+
+gboolean
+gdk_win32_ensure_com (void)
+{
+  if (!co_initialized)
+    {
+      /* UI thread should only use STA model. See
+       * -> https://devblogs.microsoft.com/oldnewthing/20080424-00/?p=22603
+       * -> https://devblogs.microsoft.com/oldnewthing/20071018-00/?p=24743
+       */
+      const DWORD flags = COINIT_APARTMENTTHREADED |
+                          COINIT_DISABLE_OLE1DDE;
+      HRESULT hr;
+
+      hr = CoInitializeEx (NULL, flags);
+      if (SUCCEEDED (hr))
+        co_initialized = TRUE;
+      else switch (hr)
+        {
+        case RPC_E_CHANGED_MODE:
+          g_warning ("COM runtime already initialized on the main "
+                     "thread with an incompatible apartment model");
+        break;
+        default:
+          HR_LOG (hr);
+        break;
+        }
+    }
+
+  return co_initialized;
+}
+
+static void
+gdk_win32_finalize_com (void)
+{
+  if (co_initialized)
+    {
+      CoUninitialize ();
+      co_initialized = FALSE;
+    }
+}
+
+gboolean
+gdk_win32_ensure_ole (void)
+{
+  if (!ole_initialized)
+    {
+      HRESULT hr = OleInitialize (NULL);
+      if (SUCCEEDED (hr))
+        ole_initialized = TRUE;
+      else switch (hr)
+        {
+        case RPC_E_CHANGED_MODE:
+          g_warning ("Failed to initialize the OLE2 runtime because "
+                     "the thread has an incompatible apartment model");
+        break;
+        default:
+          HR_LOG (hr);
+        break;
+        }
+    }
+
+  return ole_initialized;
+}
+
+static void
+gdk_win32_finalize_ole (void)
+{
+  if (ole_initialized)
+    {
+      OleUninitialize ();
+      ole_initialized = FALSE;
+    }
 }
 
 void
diff --git a/gdk/win32/gdkprivate-win32.h b/gdk/win32/gdkprivate-win32.h
index 652272c5cc..3ba99dce83 100644
--- a/gdk/win32/gdkprivate-win32.h
+++ b/gdk/win32/gdkprivate-win32.h
@@ -196,6 +196,9 @@ void       _gdk_remove_modal_window (GdkSurface *window);
 GdkSurface *_gdk_modal_current       (void);
 gboolean   _gdk_modal_blocked       (GdkSurface *window);
 
+gboolean gdk_win32_ensure_com (void);
+gboolean gdk_win32_ensure_ole (void);
+
 #ifdef G_ENABLE_DEBUG
 void   _gdk_win32_print_paletteentries (const PALETTEENTRY *pep,
                                         const int           nentries);
@@ -252,6 +255,13 @@ void    _gdk_other_api_failed        (const char *where,
 #define GDI_CALL(api, arglist) (api arglist ? 1 : (WIN32_GDI_FAILED (#api), 0))
 #define API_CALL(api, arglist) (api arglist ? 1 : (WIN32_API_FAILED (#api), 0))
 
+#define HR_LOG(hr)
+
+#define HR_CHECK_RETURN(hr) { if G_UNLIKELY (FAILED (hr)) return; }
+#define HR_CHECK_RETURN_VAL(hr, val) { if G_UNLIKELY (FAILED (hr)) return val; }
+#define HR_CHECK(hr)
+#define HR_CHECK_GOTO(hr, label) { if G_UNLIKELY (FAILED (hr)) goto label; }
+
 extern LRESULT CALLBACK _gdk_win32_surface_procedure (HWND, UINT, WPARAM, LPARAM);
 
 extern GdkDisplay       *_gdk_display;
diff --git a/gdk/win32/gdksurface-win32.c b/gdk/win32/gdksurface-win32.c
index 938737e86b..cef3d4c8f5 100644
--- a/gdk/win32/gdksurface-win32.c
+++ b/gdk/win32/gdksurface-win32.c
@@ -44,6 +44,7 @@
 #include "gdktoplevelprivate.h"
 #include "gdkwin32surface.h"
 #include "gdkwin32cursor.h"
+#include "gdkinput-dmanipulation.h"
 #include "gdkinput-winpointer.h"
 #include "gdkglcontext-win32.h"
 #include "gdkdisplay-win32.h"
@@ -639,8 +640,14 @@ _gdk_win32_display_create_surface (GdkDisplay     *display,
     }
 
   gdk_surface_set_egl_native_window (surface, (void *) impl->handle);
-  if (display_win32->tablet_input_api == GDK_WIN32_TABLET_INPUT_API_WINPOINTER)
-    gdk_winpointer_initialize_surface (surface);
+
+  if (surface_type != GDK_SURFACE_TEMP)
+    {
+      if (display_win32->tablet_input_api == GDK_WIN32_TABLET_INPUT_API_WINPOINTER)
+        gdk_winpointer_initialize_surface (surface);
+
+      gdk_dmanipulation_initialize_surface (surface);
+    }
 
   _gdk_win32_surface_enable_transparency (surface);
   _gdk_win32_surface_register_dnd (surface);
diff --git a/gdk/win32/gdksurface-win32.h b/gdk/win32/gdksurface-win32.h
index 08bd0a20b7..cff41117ef 100644
--- a/gdk/win32/gdksurface-win32.h
+++ b/gdk/win32/gdksurface-win32.h
@@ -32,6 +32,7 @@
 #include "gdk/gdkcursor.h"
 
 #include <windows.h>
+#include <directmanipulation.h>
 
 #ifdef HAVE_EGL
 # include <epoxy/egl.h>
@@ -339,6 +340,9 @@ struct _GdkWin32Surface
   } next_layout;
   gboolean force_recompute_size;
 
+  IDirectManipulationViewport *dmanipulation_viewport_pan;
+  IDirectManipulationViewport *dmanipulation_viewport_zoom;
+
 #ifdef HAVE_EGL
   guint egl_force_redraw_all : 1;
 #endif
diff --git a/gdk/win32/meson.build b/gdk/win32/meson.build
index d368648624..156c9ab730 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-dmanipulation.c',
   'gdkinput-winpointer.c',
   'gdkkeys-win32.c',
   'gdkkeys-win32-impl.c',


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