Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--intern/ghost/CMakeLists.txt2
-rw-r--r--intern/ghost/intern/GHOST_SystemWin32.cpp59
-rw-r--r--intern/ghost/intern/GHOST_SystemWin32.h10
-rw-r--r--intern/ghost/intern/GHOST_TrackpadWin32.cpp343
-rw-r--r--intern/ghost/intern/GHOST_TrackpadWin32.h138
-rw-r--r--intern/ghost/intern/GHOST_WindowWin32.cpp47
-rw-r--r--intern/ghost/intern/GHOST_WindowWin32.h18
7 files changed, 617 insertions, 0 deletions
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
@@ -407,6 +407,16 @@ class GHOST_SystemWin32 : public GHOST_System {
#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");
+
+ directManipulationEventHandler->resetViewport(directManipulationViewport.Get());
+
+ bool isScrollDirectionInverted = getScrollDirectionFromReg();
+
+ auto instance = new GHOST_DirectManipulationHelper(hWnd,
+ directManipulationManager,
+ directManipulationUpdateManager,
+ directManipulationViewport,
+ directManipulationEventHandler,
+ directManipulationViewportHandlerCookie,
+ isScrollDirectionInverted);
+
+ instance->registerScrollDirectionChangeListener();
+
+ return instance;
+
+#undef DM_CHECK_RESULT_AND_EXIT_EARLY
+}
+
+bool GHOST_DirectManipulationHelper::getScrollDirectionFromReg()
+{
+ DWORD scrollDirectionRegValue, pcbData;
+ HRESULT hr = HRESULT_FROM_WIN32(
+ RegGetValueW(HKEY_CURRENT_USER,
+ L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\PrecisionTouchPad\\",
+ L"ScrollDirection",
+ RRF_RT_REG_DWORD,
+ NULL,
+ &scrollDirectionRegValue,
+ &pcbData));
+ if (!SUCCEEDED(hr)) {
+ GHOST_PRINT("Failed to get scroll direction from registry\n");
+ return false;
+ }
+
+ return scrollDirectionRegValue == 0;
+}
+
+void GHOST_DirectManipulationHelper::registerScrollDirectionChangeListener()
+{
+
+ if (!m_scrollDirectionRegKey) {
+ HRESULT hr = HRESULT_FROM_WIN32(
+ RegOpenKeyExW(HKEY_CURRENT_USER,
+ L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\PrecisionTouchPad\\",
+ 0,
+ KEY_NOTIFY,
+ &m_scrollDirectionRegKey));
+ if (!SUCCEEDED(hr)) {
+ GHOST_PRINT("Failed to open scroll direction registry key\n");
+ return;
+ }
+ }
+
+ if (!m_scrollDirectionChangeEvent) {
+ m_scrollDirectionChangeEvent = CreateEventW(NULL, true, false, NULL);
+ }
+ else {
+ ResetEvent(m_scrollDirectionChangeEvent);
+ }
+ HRESULT hr = HRESULT_FROM_WIN32(RegNotifyChangeKeyValue(m_scrollDirectionRegKey,
+ true,
+ REG_NOTIFY_CHANGE_LAST_SET,
+ m_scrollDirectionChangeEvent,
+ true));
+ if (!SUCCEEDED(hr)) {
+ GHOST_PRINT("Failed to register scroll direction change listener\n");
+ return;
+ }
+}
+
+void GHOST_DirectManipulationHelper::onPointerHitTest(UINT32 pointerId)
+{
+ [[maybe_unused]] HRESULT hr = m_directManipulationViewport->SetContact(pointerId);
+ GHOST_ASSERT(SUCCEEDED(hr), "Viewport set contact failed\n");
+
+ if (WaitForSingleObject(m_scrollDirectionChangeEvent, 0) == WAIT_OBJECT_0) {
+ m_isScrollDirectionInverted = getScrollDirectionFromReg();
+ registerScrollDirectionChangeListener();
+ }
+}
+
+void GHOST_DirectManipulationHelper::update()
+{
+ if (m_directManipulationEventHandler->dm_status == DIRECTMANIPULATION_RUNNING ||
+ m_directManipulationEventHandler->dm_status == DIRECTMANIPULATION_INERTIA) {
+ [[maybe_unused]] HRESULT hr = m_directManipulationUpdateManager->Update(nullptr);
+ GHOST_ASSERT(SUCCEEDED(hr), "DirectManipulationUpdateManager update failed\n");
+ }
+}
+
+void GHOST_DirectManipulationHelper::setDPI(uint16_t dpi)
+{
+ m_directManipulationEventHandler->dpi = dpi;
+}
+
+GHOST_TTrackpadInfo GHOST_DirectManipulationHelper::getTrackpadInfo()
+{
+ GHOST_TTrackpadInfo result = m_directManipulationEventHandler->accumulated_values;
+ result.isScrollDirectionInverted = m_isScrollDirectionInverted;
+
+ m_directManipulationEventHandler->accumulated_values = {0, 0, 0};
+ return result;
+}
+
+GHOST_DirectManipulationHelper::~GHOST_DirectManipulationHelper()
+{
+ HRESULT hr;
+ hr = m_directManipulationViewport->Stop();
+ GHOST_ASSERT(SUCCEEDED(hr), "Viewport stop failed\n");
+
+ hr = m_directManipulationViewport->RemoveEventHandler(m_directManipulationViewportHandlerCookie);
+ GHOST_ASSERT(SUCCEEDED(hr), "Viewport remove event handler failed\n");
+
+ hr = m_directManipulationViewport->Abandon();
+ GHOST_ASSERT(SUCCEEDED(hr), "Viewport abandon failed\n");
+
+ hr = m_directManipulationManager->Deactivate(m_hWnd);
+ GHOST_ASSERT(SUCCEEDED(hr), "DirectManipulationManager deactivate failed\n");
+
+ if (m_scrollDirectionChangeEvent) {
+ CloseHandle(m_scrollDirectionChangeEvent);
+ m_scrollDirectionChangeEvent = NULL;
+ }
+ if (m_scrollDirectionRegKey) {
+ RegCloseKey(m_scrollDirectionRegKey);
+ m_scrollDirectionRegKey = NULL;
+ }
+}
+
+GHOST_DirectManipulationViewportEventHandler::GHOST_DirectManipulationViewportEventHandler(
+ uint16_t dpi)
+ : accumulated_values({0, 0, 0}), dpi(dpi), dm_status(DIRECTMANIPULATION_BUILDING)
+{
+}
+
+void GHOST_DirectManipulationViewportEventHandler::resetViewport(
+ IDirectManipulationViewport *viewport)
+{
+ if (gesture_state != GESTURE_NONE) {
+ [[maybe_unused]] HRESULT hr = viewport->ZoomToRect(0.0f, 0.0f, 10000.0f, 10000.0f, FALSE);
+ GHOST_ASSERT(SUCCEEDED(hr), "Viewport reset failed\n");
+ }
+
+ gesture_state = GESTURE_NONE;
+
+ last_scale = PINCH_SCALE_FACTOR;
+ last_x = 0.0f;
+ last_y = 0.0f;
+}
+
+HRESULT GHOST_DirectManipulationViewportEventHandler::OnViewportStatusChanged(
+ IDirectManipulationViewport *viewport,
+ DIRECTMANIPULATION_STATUS current,
+ DIRECTMANIPULATION_STATUS previous)
+{
+ dm_status = current;
+
+ if (current == previous) {
+ return S_OK;
+ }
+
+ if (previous == DIRECTMANIPULATION_ENABLED || current == DIRECTMANIPULATION_READY ||
+ (previous == DIRECTMANIPULATION_INERTIA && current != DIRECTMANIPULATION_INERTIA)) {
+ resetViewport(viewport);
+ }
+
+ return S_OK;
+}
+
+HRESULT GHOST_DirectManipulationViewportEventHandler::OnViewportUpdated(
+ IDirectManipulationViewport *viewport)
+{
+ /* Nothing to do here. */
+ return S_OK;
+}
+
+HRESULT GHOST_DirectManipulationViewportEventHandler::OnContentUpdated(
+ IDirectManipulationViewport *viewport, IDirectManipulationContent *content)
+{
+ float transform[6];
+ HRESULT hr = content->GetContentTransform(transform, ARRAYSIZE(transform));
+ GHOST_ASSERT(SUCCEEDED(hr), "DirectManipulationContent get transform failed\n");
+
+ const float device_scale_factor = dpi / 96.0f;
+
+ const float scale = transform[0] * PINCH_SCALE_FACTOR;
+ const float x = transform[4] / device_scale_factor;
+ const float y = transform[5] / device_scale_factor;
+
+ const float EPS = 3e-5;
+
+ /* Ignore repeating or incorrect input. */
+ if ((fabs(scale - last_scale) <= EPS && fabs(x - last_x) <= EPS && fabs(y - last_y) <= EPS) ||
+ scale == 0.0f) {
+ GHOST_PRINT("Ignoring touchpad input\n");
+ return hr;
+ }
+
+ /* Assume that every gesture is a pan in the beginning.
+ * If it's a pinch, the gesture will be changed below. */
+ if (gesture_state == GESTURE_NONE) {
+ gesture_state = GESTURE_PAN;
+ }
+
+ /* DM doesn't always immediately recognize pinch gestures,
+ * so allow transition from pan to pinch. */
+ if (gesture_state == GESTURE_PAN) {
+ if (fabs(scale - PINCH_SCALE_FACTOR) > EPS) {
+ gesture_state = GESTURE_PINCH;
+ }
+ }
+
+ /* This state machine is used here because:
+ * 1. Pinch and pan gestures must be differentiated and cannot be proccessed at the same time
+ * because XY transform values become nonsensical during pinch gesture.
+ * 2. GHOST requires delta values for events while DM provides transformation matrix of the
+ * current gesture.
+ * 3. GHOST events accept integer values while DM values are non-integer.
+ * Truncated fractional parts are accumulated and accounted for in following updates.
+ */
+ switch (gesture_state) {
+ case GESTURE_PINCH: {
+ int32_t dscale = roundf(scale - last_scale);
+
+ last_scale += dscale;
+
+ accumulated_values.scale += dscale;
+ break;
+ }
+ case GESTURE_PAN: {
+ int32_t dx = roundf(x - last_x);
+ int32_t dy = roundf(y - last_y);
+
+ last_x += dx;
+ last_y += dy;
+
+ accumulated_values.x += dx;
+ accumulated_values.y += dy;
+ break;
+ }
+ case GESTURE_NONE:
+ break;
+ }
+
+ return hr;
+}
diff --git a/intern/ghost/intern/GHOST_TrackpadWin32.h b/intern/ghost/intern/GHOST_TrackpadWin32.h
new file mode 100644
index 00000000000..2e28f756965
--- /dev/null
+++ b/intern/ghost/intern/GHOST_TrackpadWin32.h
@@ -0,0 +1,138 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+/** \file
+ * \ingroup GHOST
+ * Declaration of GHOST DirectManipulation classes.
+ */
+
+#pragma once
+
+#ifndef WIN32
+# error WIN32 only!
+#endif // WIN32
+
+#include "GHOST_Types.h"
+
+#include <directmanipulation.h>
+#include <wrl.h>
+
+#define PINCH_SCALE_FACTOR 125.0f
+
+typedef struct {
+ int32_t x, y, scale;
+ bool isScrollDirectionInverted;
+} GHOST_TTrackpadInfo;
+
+class GHOST_DirectManipulationHelper;
+
+class GHOST_DirectManipulationViewportEventHandler
+ : public Microsoft::WRL::RuntimeClass<
+ Microsoft::WRL::RuntimeClassFlags<Microsoft::WRL::RuntimeClassType::ClassicCom>,
+ Microsoft::WRL::Implements<
+ Microsoft::WRL::RuntimeClassFlags<Microsoft::WRL::RuntimeClassType::ClassicCom>,
+ Microsoft::WRL::FtmBase,
+ IDirectManipulationViewportEventHandler>> {
+ public:
+ GHOST_DirectManipulationViewportEventHandler(uint16_t dpi);
+
+ /*
+ * Resets viewport and tracked touchpad state.
+ */
+ void resetViewport(IDirectManipulationViewport *viewport);
+
+ /* DirectManipulation callbacks. */
+ HRESULT STDMETHODCALLTYPE OnViewportStatusChanged(IDirectManipulationViewport *viewport,
+ DIRECTMANIPULATION_STATUS current,
+ DIRECTMANIPULATION_STATUS previous) override;
+
+ HRESULT STDMETHODCALLTYPE OnViewportUpdated(IDirectManipulationViewport *viewport) override;
+
+ HRESULT STDMETHODCALLTYPE OnContentUpdated(IDirectManipulationViewport *viewport,
+ IDirectManipulationContent *content) override;
+
+ private:
+ enum { GESTURE_NONE, GESTURE_PAN, GESTURE_PINCH } gesture_state;
+
+ int32_t last_x, last_y, last_scale;
+ GHOST_TTrackpadInfo accumulated_values;
+ uint16_t dpi;
+ DIRECTMANIPULATION_STATUS dm_status;
+
+ friend class GHOST_DirectManipulationHelper;
+};
+
+class GHOST_DirectManipulationHelper {
+ public:
+ /*
+ * Creates a GHOST_DirectManipulationHelper for the provided window.
+ * \param hWnd: The window receiving DirectManipulation events.
+ * \param dpi: The current DPI.
+ * \return Pointer to the new GHOST_DirectManipulationHelper if created, nullptr if there was an
+ * error.
+ */
+ static GHOST_DirectManipulationHelper *create(HWND hWnd, uint16_t dpi);
+
+ ~GHOST_DirectManipulationHelper();
+
+ /*
+ * Drives the DirectManipulation context.
+ * DirectManipulation's intended use is to tie user input into DirectComposition's compositor
+ * scaling and translating. We are not using DirectComposition and therefore must drive
+ * DirectManipulation manually.
+ */
+ void update();
+
+ /*
+ * Sets pointer in contact with the DirectManipulation context.
+ * \param pointerId: ID of the pointer in contact.
+ */
+ void onPointerHitTest(UINT32 pointerId);
+
+ /*
+ * Updates DPI information for touchpad scaling.
+ * \param dpi: The new DPI.
+ */
+ void setDPI(uint16_t dpi);
+
+ /*
+ * Retrieves trackpad input.
+ * \return The accumulated trackpad translation and scale since last call.
+ */
+ GHOST_TTrackpadInfo getTrackpadInfo();
+
+ private:
+ 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);
+
+ /*
+ * Retrieves the scroll direction from the registry.
+ * \return True if scroll direction is inverted.
+ */
+ static bool getScrollDirectionFromReg();
+
+ /*
+ * Registers listener for registry scroll direction entry changes.
+ */
+ void registerScrollDirectionChangeListener();
+
+ HWND m_hWnd;
+
+ HKEY m_scrollDirectionRegKey;
+ HANDLE m_scrollDirectionChangeEvent;
+
+ Microsoft::WRL::ComPtr<IDirectManipulationManager> m_directManipulationManager;
+ Microsoft::WRL::ComPtr<IDirectManipulationUpdateManager> m_directManipulationUpdateManager;
+ Microsoft::WRL::ComPtr<IDirectManipulationViewport> m_directManipulationViewport;
+ Microsoft::WRL::ComPtr<GHOST_DirectManipulationViewportEventHandler>
+ m_directManipulationEventHandler;
+ DWORD m_directManipulationViewportHandlerCookie;
+
+ bool m_isScrollDirectionInverted;
+};
diff --git a/intern/ghost/intern/GHOST_WindowWin32.cpp b/intern/ghost/intern/GHOST_WindowWin32.cpp
index f74a4b17a51..897e6c145da 100644
--- a/intern/ghost/intern/GHOST_WindowWin32.cpp
+++ b/intern/ghost/intern/GHOST_WindowWin32.cpp
@@ -68,6 +68,7 @@ GHOST_WindowWin32::GHOST_WindowWin32(GHOST_SystemWin32 *system,
m_normal_state(GHOST_kWindowStateNormal),
m_user32(::LoadLibrary("user32.dll")),
m_parentWindowHwnd(parentwindow ? parentwindow->m_hWnd : HWND_DESKTOP),
+ m_directManipulationHelper(NULL),
m_debug_context(is_debug)
{
DWORD style = parentwindow ?
@@ -204,6 +205,42 @@ GHOST_WindowWin32::GHOST_WindowWin32(GHOST_SystemWin32 *system,
/* Allow the showing of a progress bar on the taskbar. */
CoCreateInstance(
CLSID_TaskbarList, NULL, CLSCTX_INPROC_SERVER, IID_ITaskbarList3, (LPVOID *)&m_Bar);
+
+ /* Initialize Direct Manipulation. */
+ m_directManipulationHelper = GHOST_DirectManipulationHelper::create(m_hWnd, getDPIHint());
+}
+
+void GHOST_WindowWin32::updateDirectManipulation()
+{
+ if (!m_directManipulationHelper) {
+ return;
+ }
+
+ m_directManipulationHelper->update();
+}
+
+void GHOST_WindowWin32::onPointerHitTest(WPARAM wParam)
+{
+ /* Only DM_POINTERHITTEST can be the first message of input sequence of touchpad input. */
+
+ if (!m_directManipulationHelper) {
+ return;
+ }
+
+ UINT32 pointerId = GET_POINTERID_WPARAM(wParam);
+ POINTER_INPUT_TYPE pointerType;
+ if (GetPointerType(pointerId, &pointerType) && pointerType == PT_TOUCHPAD) {
+ m_directManipulationHelper->onPointerHitTest(pointerId);
+ }
+}
+
+GHOST_TTrackpadInfo GHOST_WindowWin32::getTrackpadInfo()
+{
+ if (!m_directManipulationHelper) {
+ return {0, 0, 0};
+ }
+
+ return m_directManipulationHelper->getTrackpadInfo();
}
GHOST_WindowWin32::~GHOST_WindowWin32()
@@ -253,6 +290,9 @@ GHOST_WindowWin32::~GHOST_WindowWin32()
::DestroyWindow(m_hWnd);
m_hWnd = 0;
}
+
+ delete m_directManipulationHelper;
+ m_directManipulationHelper = NULL;
}
void GHOST_WindowWin32::adjustWindowRectForClosestMonitor(LPRECT win_rect,
@@ -1035,6 +1075,13 @@ void GHOST_WindowWin32::ThemeRefresh()
}
}
+void GHOST_WindowWin32::updateDPI()
+{
+ if (m_directManipulationHelper) {
+ m_directManipulationHelper->setDPI(getDPIHint());
+ }
+}
+
uint16_t GHOST_WindowWin32::getDPIHint()
{
if (m_user32) {
diff --git a/intern/ghost/intern/GHOST_WindowWin32.h b/intern/ghost/intern/GHOST_WindowWin32.h
index bc678adff3c..c958a89ac48 100644
--- a/intern/ghost/intern/GHOST_WindowWin32.h
+++ b/intern/ghost/intern/GHOST_WindowWin32.h
@@ -13,6 +13,7 @@
#endif // WIN32
#include "GHOST_TaskbarWin32.h"
+#include "GHOST_TrackpadWin32.h"
#include "GHOST_Window.h"
#include "GHOST_Wintab.h"
#ifdef WITH_INPUT_IME
@@ -286,6 +287,8 @@ class GHOST_WindowWin32 : public GHOST_Window {
return GHOST_kFailure;
}
+ void updateDPI();
+
uint16_t getDPIHint() override;
/** True if the mouse is either over or captured by the window. */
@@ -308,6 +311,19 @@ class GHOST_WindowWin32 : public GHOST_Window {
void endIME();
#endif /* WITH_INPUT_IME */
+ /*
+ * Drive DirectManipulation context.
+ */
+ void updateDirectManipulation();
+
+ /*
+ * Handle DM_POINTERHITTEST events.
+ * \param wParam: wParam from the event.
+ */
+ void onPointerHitTest(WPARAM wParam);
+
+ GHOST_TTrackpadInfo getTrackpadInfo();
+
private:
/**
* \param type: The type of rendering context create.
@@ -391,6 +407,8 @@ class GHOST_WindowWin32 : public GHOST_Window {
HWND m_parentWindowHwnd;
+ GHOST_DirectManipulationHelper *m_directManipulationHelper;
+
#ifdef WITH_INPUT_IME
/** Handle input method editors event */
GHOST_ImeWin32 m_imeInput;