[Bf-blender-cvs] [e58b18888c0] master: GHOST: Add support for precision touchpad gestures on Windows

Andrii Symkin noreply at git.blender.org
Fri May 6 09:41:11 CEST 2022


Commit: e58b18888c0e76a9ab8b0eeb16cdad9d9d9962cf
Author: Andrii Symkin
Date:   Wed Apr 6 18:17:33 2022 -0700
Branches: master
https://developer.blender.org/rBe58b18888c0e76a9ab8b0eeb16cdad9d9d9962cf

GHOST: Add support for precision touchpad gestures on Windows

This patch adds support for precision touchpad gestures on Windows 8.1
and newer using Direct Manipulation API. Gestures work exactly like on
macOS, with full support for pan/pinch and inertia. This works by
creating a viewport with a fake scrollable which is reset after every
gesture and converts any changes to the content's transform into GHOST
trackpad events (as explained [here](https://bugzilla.mozilla.org/show_bug.cgi?id=890878)).
The code is based on the implementation from the [Chromium project](https://chromium.googlesource.com/chromium/src/+/refs/heads/master/content/browser/renderer_host/direct_manipulation_helper_win.cc).

Tested on Windows 10.

Fixes {T70754}, {T69264}.

Demo:{F8520272}

Reviewed By: nicholas_rishel

Differential Revision: https://developer.blender.org/D7660

===================================================================

M	intern/ghost/CMakeLists.txt
M	intern/ghost/intern/GHOST_SystemWin32.cpp
M	intern/ghost/intern/GHOST_SystemWin32.h
A	intern/ghost/intern/GHOST_TrackpadWin32.cpp
A	intern/ghost/intern/GHOST_TrackpadWin32.h
M	intern/ghost/intern/GHOST_WindowWin32.cpp
M	intern/ghost/intern/GHOST_WindowWin32.h

===================================================================

diff --git a/intern/ghost/CMakeLists.txt b/intern/ghost/CMakeLists.txt
index 9421edecf12..dceb9ced803 100644
--- a/intern/ghost/CMakeLists.txt
+++ b/intern/ghost/CMakeLists.txt
@@ -376,6 +376,7 @@ elseif(WIN32)
     intern/GHOST_DisplayManagerWin32.cpp
     intern/GHOST_DropTargetWin32.cpp
     intern/GHOST_SystemWin32.cpp
+    intern/GHOST_TrackpadWin32.cpp
     intern/GHOST_WindowWin32.cpp
     intern/GHOST_Wintab.cpp
 
@@ -384,6 +385,7 @@ elseif(WIN32)
     intern/GHOST_DropTargetWin32.h
     intern/GHOST_SystemWin32.h
     intern/GHOST_TaskbarWin32.h
+    intern/GHOST_TrackpadWin32.h
     intern/GHOST_WindowWin32.h
     intern/GHOST_Wintab.h
   )
diff --git a/intern/ghost/intern/GHOST_SystemWin32.cpp b/intern/ghost/intern/GHOST_SystemWin32.cpp
index ca4bfa634c1..8e07bf4ea3d 100644
--- a/intern/ghost/intern/GHOST_SystemWin32.cpp
+++ b/intern/ghost/intern/GHOST_SystemWin32.cpp
@@ -8,6 +8,7 @@
 #include "GHOST_SystemWin32.h"
 #include "GHOST_ContextD3D.h"
 #include "GHOST_EventDragnDrop.h"
+#include "GHOST_EventTrackpad.h"
 
 #ifndef _WIN32_IE
 #  define _WIN32_IE 0x0501 /* shipped before XP, so doesn't impose additional requirements */
@@ -415,6 +416,8 @@ bool GHOST_SystemWin32::processEvents(bool waitForEvent)
       hasEventHandled = true;
     }
 
+    driveTrackpad();
+
     // Process all the events waiting for us
     while (::PeekMessageW(&msg, NULL, 0, 0, PM_REMOVE) != 0) {
       // TranslateMessage doesn't alter the message, and doesn't change our raw keyboard data.
@@ -424,6 +427,8 @@ bool GHOST_SystemWin32::processEvents(bool waitForEvent)
       hasEventHandled = true;
     }
 
+    processTrackpad();
+
     /* PeekMessage above is allowed to dispatch messages to the wndproc without us
      * noticing, so we need to check the event manager here to see if there are
      * events waiting in the queue.
@@ -1417,6 +1422,52 @@ bool GHOST_SystemWin32::processNDOF(RAWINPUT const &raw)
 }
 #endif  // WITH_INPUT_NDOF
 
+void GHOST_SystemWin32::driveTrackpad()
+{
+  GHOST_WindowWin32 *active_window = static_cast<GHOST_WindowWin32 *>(
+      getWindowManager()->getActiveWindow());
+  if (active_window) {
+    active_window->updateDirectManipulation();
+  }
+}
+
+void GHOST_SystemWin32::processTrackpad()
+{
+  GHOST_WindowWin32 *active_window = static_cast<GHOST_WindowWin32 *>(
+      getWindowManager()->getActiveWindow());
+
+  if (!active_window) {
+    return;
+  }
+
+  GHOST_TTrackpadInfo trackpad_info = active_window->getTrackpadInfo();
+  GHOST_SystemWin32 *system = (GHOST_SystemWin32 *)getSystem();
+
+  int32_t cursor_x, cursor_y;
+  system->getCursorPosition(cursor_x, cursor_y);
+
+  if (trackpad_info.x != 0 || trackpad_info.y != 0) {
+    system->pushEvent(new GHOST_EventTrackpad(system->getMilliSeconds(),
+                                              active_window,
+                                              GHOST_kTrackpadEventScroll,
+                                              cursor_x,
+                                              cursor_y,
+                                              trackpad_info.x,
+                                              trackpad_info.y,
+                                              trackpad_info.isScrollDirectionInverted));
+  }
+  if (trackpad_info.scale != 0) {
+    system->pushEvent(new GHOST_EventTrackpad(system->getMilliSeconds(),
+                                              active_window,
+                                              GHOST_kTrackpadEventMagnify,
+                                              cursor_x,
+                                              cursor_y,
+                                              trackpad_info.scale,
+                                              0,
+                                              false));
+  }
+}
+
 LRESULT WINAPI GHOST_SystemWin32::s_wndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
 {
   GHOST_Event *event = NULL;
@@ -1969,6 +2020,8 @@ LRESULT WINAPI GHOST_SystemWin32::s_wndProc(HWND hwnd, UINT msg, WPARAM wParam,
                          suggestedWindowRect->right - suggestedWindowRect->left,
                          suggestedWindowRect->bottom - suggestedWindowRect->top,
                          SWP_NOZORDER | SWP_NOACTIVATE);
+
+            window->updateDPI();
           }
           break;
         case WM_DISPLAYCHANGE: {
@@ -2063,6 +2116,12 @@ LRESULT WINAPI GHOST_SystemWin32::s_wndProc(HWND hwnd, UINT msg, WPARAM wParam,
            * In GHOST, we let DefWindowProc call the timer callback.
            */
           break;
+        case DM_POINTERHITTEST:
+          /* The DM_POINTERHITTEST message is sent to a window, when pointer input is first
+           * detected, in order to determine the most probable input target for Direct
+           * Manipulation. */
+          window->onPointerHitTest(wParam);
+          break;
       }
     }
     else {
diff --git a/intern/ghost/intern/GHOST_SystemWin32.h b/intern/ghost/intern/GHOST_SystemWin32.h
index 9f8d52f9ca3..689b78b0317 100644
--- a/intern/ghost/intern/GHOST_SystemWin32.h
+++ b/intern/ghost/intern/GHOST_SystemWin32.h
@@ -406,6 +406,16 @@ class GHOST_SystemWin32 : public GHOST_System {
   bool processNDOF(RAWINPUT const &raw);
 #endif
 
+  /**
+   * Drives Direct Manipulation update.
+   */
+  void driveTrackpad();
+
+  /**
+   * Creates trackpad events for the active window.
+   */
+  void processTrackpad();
+
   /**
    * Returns the local state of the modifier keys (from the message queue).
    * \param keys: The state of the keys.
diff --git a/intern/ghost/intern/GHOST_TrackpadWin32.cpp b/intern/ghost/intern/GHOST_TrackpadWin32.cpp
new file mode 100644
index 00000000000..69b590ee8de
--- /dev/null
+++ b/intern/ghost/intern/GHOST_TrackpadWin32.cpp
@@ -0,0 +1,343 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+/** \file
+ * \ingroup GHOST
+ */
+
+#include <cmath>
+
+#include "GHOST_Debug.h"
+#include "GHOST_TrackpadWin32.h"
+
+GHOST_DirectManipulationHelper::GHOST_DirectManipulationHelper(
+    HWND hWnd,
+    Microsoft::WRL::ComPtr<IDirectManipulationManager> directManipulationManager,
+    Microsoft::WRL::ComPtr<IDirectManipulationUpdateManager> directManipulationUpdateManager,
+    Microsoft::WRL::ComPtr<IDirectManipulationViewport> directManipulationViewport,
+    Microsoft::WRL::ComPtr<GHOST_DirectManipulationViewportEventHandler>
+        directManipulationEventHandler,
+    DWORD directManipulationViewportHandlerCookie,
+    bool isScrollDirectionInverted)
+    : m_hWnd(hWnd),
+      m_scrollDirectionRegKey(NULL),
+      m_scrollDirectionChangeEvent(NULL),
+      m_directManipulationManager(directManipulationManager),
+      m_directManipulationUpdateManager(directManipulationUpdateManager),
+      m_directManipulationViewport(directManipulationViewport),
+      m_directManipulationEventHandler(directManipulationEventHandler),
+      m_directManipulationViewportHandlerCookie(directManipulationViewportHandlerCookie),
+      m_isScrollDirectionInverted(isScrollDirectionInverted)
+{
+}
+
+GHOST_DirectManipulationHelper *GHOST_DirectManipulationHelper::create(HWND hWnd, uint16_t dpi)
+{
+#define DM_CHECK_RESULT_AND_EXIT_EARLY(hr, failMessage) \
+  { \
+    if (!SUCCEEDED(hr)) { \
+      GHOST_PRINT(failMessage); \
+      return nullptr; \
+    } \
+  }
+
+  Microsoft::WRL::ComPtr<IDirectManipulationManager> directManipulationManager;
+  HRESULT hr = ::CoCreateInstance(CLSID_DirectManipulationManager,
+                                  nullptr,
+                                  CLSCTX_INPROC_SERVER,
+                                  IID_PPV_ARGS(&directManipulationManager));
+  DM_CHECK_RESULT_AND_EXIT_EARLY(hr, "DirectManipulationManager create failed\n");
+
+  /* Since we want to use fake viewport, we need to send fake updates to UpdateManager. */
+  Microsoft::WRL::ComPtr<IDirectManipulationUpdateManager> directManipulationUpdateManager;
+  hr = directManipulationManager->GetUpdateManager(IID_PPV_ARGS(&directManipulationUpdateManager));
+  DM_CHECK_RESULT_AND_EXIT_EARLY(hr, "Get UpdateManager failed\n");
+
+  Microsoft::WRL::ComPtr<IDirectManipulationViewport> directManipulationViewport;
+  hr = directManipulationManager->CreateViewport(
+      nullptr, hWnd, IID_PPV_ARGS(&directManipulationViewport));
+  DM_CHECK_RESULT_AND_EXIT_EARLY(hr, "Viewport create failed\n");
+
+  DIRECTMANIPULATION_CONFIGURATION configuration =
+      DIRECTMANIPULATION_CONFIGURATION_INTERACTION |
+      DIRECTMANIPULATION_CONFIGURATION_TRANSLATION_X |
+      DIRECTMANIPULATION_CONFIGURATION_TRANSLATION_Y |
+      DIRECTMANIPULATION_CONFIGURATION_TRANSLATION_INERTIA |
+      DIRECTMANIPULATION_CONFIGURATION_SCALING;
+
+  hr = directManipulationViewport->ActivateConfiguration(configuration);
+  DM_CHECK_RESULT_AND_EXIT_EARLY(hr, "Viewport set ActivateConfiguration failed\n");
+
+  /* Since we are using fake viewport and only want to use Direct Manipulation for touchpad, we
+   * need to use MANUALUPDATE option. */
+  hr = directManipulationViewport->SetViewportOptions(
+      DIRECTMANIPULATION_VIEWPORT_OPTIONS_MANUALUPDATE);
+  DM_CHECK_RESULT_AND_EXIT_EARLY(hr, "Viewport set ViewportOptions failed\n");
+
+  /* We receive Direct Manipulation transform updates in IDirectManipulationViewportEventHandler
+   * callbacks. */
+  Microsoft::WRL::ComPtr<GHOST_DirectManipulationViewportEventHandler>
+      directManipulationEventHandler =
+          Microsoft::WRL::Make<GHOST_DirectManipulationViewportEventHandler>(dpi);
+  DWORD directManipulationViewportHandlerCookie;
+  directManipulationViewport->AddEventHandler(
+      hWnd, directManipulationEventHandler.Get(), &directManipulationViewportHandlerCookie);
+  DM_CHECK_RESULT_AND_EXIT_EARLY(hr, "Viewport add EventHandler failed\n");
+
+  /* Set default rect for viewport before activating. */
+  RECT rect = {0, 0, 10000, 10000};
+  hr = directManipulationViewport->SetViewportRect(&rect);
+  DM_CHECK_RESULT_AND_EXIT_EARLY(hr, "Viewport set rect failed\n");
+
+  hr = directManipulationManager->Activate(hWnd);
+  DM_CHECK_RESULT_AND_EXIT_EARLY(hr, "DirectManipulationManager activate failed\n");
+
+  hr = directManipulationViewport->Enable();
+  DM_CHECK_RESULT_AND_EXIT_EARLY(hr, "Viewport enable failed\n");
+
+  dire

@@ Diff output truncated at 10240 characters. @@



More information about the Bf-blender-cvs mailing list