Blender  V3.3
GHOST_TrackpadWin32.cpp
Go to the documentation of this file.
1 /* SPDX-License-Identifier: GPL-2.0-or-later */
2 
7 #include <cmath>
8 
9 #include "GHOST_Debug.h"
10 #include "GHOST_TrackpadWin32.h"
11 
12 GHOST_DirectManipulationHelper::GHOST_DirectManipulationHelper(
13  HWND hWnd,
14  Microsoft::WRL::ComPtr<IDirectManipulationManager> directManipulationManager,
15  Microsoft::WRL::ComPtr<IDirectManipulationUpdateManager> directManipulationUpdateManager,
16  Microsoft::WRL::ComPtr<IDirectManipulationViewport> directManipulationViewport,
17  Microsoft::WRL::ComPtr<GHOST_DirectManipulationViewportEventHandler>
18  directManipulationEventHandler,
19  DWORD directManipulationViewportHandlerCookie,
20  bool isScrollDirectionInverted)
21  : m_hWnd(hWnd),
22  m_scrollDirectionRegKey(NULL),
23  m_scrollDirectionChangeEvent(NULL),
24  m_directManipulationManager(directManipulationManager),
25  m_directManipulationUpdateManager(directManipulationUpdateManager),
26  m_directManipulationViewport(directManipulationViewport),
27  m_directManipulationEventHandler(directManipulationEventHandler),
28  m_directManipulationViewportHandlerCookie(directManipulationViewportHandlerCookie),
29  m_isScrollDirectionInverted(isScrollDirectionInverted)
30 {
31 }
32 
34 {
35 #define DM_CHECK_RESULT_AND_EXIT_EARLY(hr, failMessage) \
36  { \
37  if (!SUCCEEDED(hr)) { \
38  GHOST_PRINT(failMessage); \
39  return nullptr; \
40  } \
41  }
42 
43  Microsoft::WRL::ComPtr<IDirectManipulationManager> directManipulationManager;
44  HRESULT hr = ::CoCreateInstance(CLSID_DirectManipulationManager,
45  nullptr,
46  CLSCTX_INPROC_SERVER,
47  IID_PPV_ARGS(&directManipulationManager));
48  DM_CHECK_RESULT_AND_EXIT_EARLY(hr, "DirectManipulationManager create failed\n");
49 
50  /* Since we want to use fake viewport, we need to send fake updates to UpdateManager. */
51  Microsoft::WRL::ComPtr<IDirectManipulationUpdateManager> directManipulationUpdateManager;
52  hr = directManipulationManager->GetUpdateManager(IID_PPV_ARGS(&directManipulationUpdateManager));
53  DM_CHECK_RESULT_AND_EXIT_EARLY(hr, "Get UpdateManager failed\n");
54 
55  Microsoft::WRL::ComPtr<IDirectManipulationViewport> directManipulationViewport;
56  hr = directManipulationManager->CreateViewport(
57  nullptr, hWnd, IID_PPV_ARGS(&directManipulationViewport));
58  DM_CHECK_RESULT_AND_EXIT_EARLY(hr, "Viewport create failed\n");
59 
60  DIRECTMANIPULATION_CONFIGURATION configuration =
61  DIRECTMANIPULATION_CONFIGURATION_INTERACTION |
62  DIRECTMANIPULATION_CONFIGURATION_TRANSLATION_X |
63  DIRECTMANIPULATION_CONFIGURATION_TRANSLATION_Y |
64  DIRECTMANIPULATION_CONFIGURATION_TRANSLATION_INERTIA |
65  DIRECTMANIPULATION_CONFIGURATION_SCALING;
66 
67  hr = directManipulationViewport->ActivateConfiguration(configuration);
68  DM_CHECK_RESULT_AND_EXIT_EARLY(hr, "Viewport set ActivateConfiguration failed\n");
69 
70  /* Since we are using fake viewport and only want to use Direct Manipulation for touchpad, we
71  * need to use MANUALUPDATE option. */
72  hr = directManipulationViewport->SetViewportOptions(
73  DIRECTMANIPULATION_VIEWPORT_OPTIONS_MANUALUPDATE);
74  DM_CHECK_RESULT_AND_EXIT_EARLY(hr, "Viewport set ViewportOptions failed\n");
75 
76  /* We receive Direct Manipulation transform updates in IDirectManipulationViewportEventHandler
77  * callbacks. */
78  Microsoft::WRL::ComPtr<GHOST_DirectManipulationViewportEventHandler>
79  directManipulationEventHandler =
80  Microsoft::WRL::Make<GHOST_DirectManipulationViewportEventHandler>(dpi);
81  DWORD directManipulationViewportHandlerCookie;
82  directManipulationViewport->AddEventHandler(
83  hWnd, directManipulationEventHandler.Get(), &directManipulationViewportHandlerCookie);
84  DM_CHECK_RESULT_AND_EXIT_EARLY(hr, "Viewport add EventHandler failed\n");
85 
86  /* Set default rect for viewport before activating. */
87  RECT rect = {0, 0, 10000, 10000};
88  hr = directManipulationViewport->SetViewportRect(&rect);
89  DM_CHECK_RESULT_AND_EXIT_EARLY(hr, "Viewport set rect failed\n");
90 
91  hr = directManipulationManager->Activate(hWnd);
92  DM_CHECK_RESULT_AND_EXIT_EARLY(hr, "DirectManipulationManager activate failed\n");
93 
94  hr = directManipulationViewport->Enable();
95  DM_CHECK_RESULT_AND_EXIT_EARLY(hr, "Viewport enable failed\n");
96 
97  directManipulationEventHandler->resetViewport(directManipulationViewport.Get());
98 
99  bool isScrollDirectionInverted = getScrollDirectionFromReg();
100 
102  directManipulationManager,
103  directManipulationUpdateManager,
104  directManipulationViewport,
105  directManipulationEventHandler,
106  directManipulationViewportHandlerCookie,
107  isScrollDirectionInverted);
108 
109  instance->registerScrollDirectionChangeListener();
110 
111  return instance;
112 
113 #undef DM_CHECK_RESULT_AND_EXIT_EARLY
114 }
115 
116 bool GHOST_DirectManipulationHelper::getScrollDirectionFromReg()
117 {
118  DWORD scrollDirectionRegValue, pcbData;
119  HRESULT hr = HRESULT_FROM_WIN32(
120  RegGetValueW(HKEY_CURRENT_USER,
121  L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\PrecisionTouchPad\\",
122  L"ScrollDirection",
123  RRF_RT_REG_DWORD,
124  NULL,
125  &scrollDirectionRegValue,
126  &pcbData));
127  if (!SUCCEEDED(hr)) {
128  GHOST_PRINT("Failed to get scroll direction from registry\n");
129  return false;
130  }
131 
132  return scrollDirectionRegValue == 0;
133 }
134 
135 void GHOST_DirectManipulationHelper::registerScrollDirectionChangeListener()
136 {
137 
138  if (!m_scrollDirectionRegKey) {
139  HRESULT hr = HRESULT_FROM_WIN32(
140  RegOpenKeyExW(HKEY_CURRENT_USER,
141  L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\PrecisionTouchPad\\",
142  0,
143  KEY_NOTIFY,
144  &m_scrollDirectionRegKey));
145  if (!SUCCEEDED(hr)) {
146  GHOST_PRINT("Failed to open scroll direction registry key\n");
147  return;
148  }
149  }
150 
151  if (!m_scrollDirectionChangeEvent) {
152  m_scrollDirectionChangeEvent = CreateEventW(NULL, true, false, NULL);
153  }
154  else {
155  ResetEvent(m_scrollDirectionChangeEvent);
156  }
157  HRESULT hr = HRESULT_FROM_WIN32(RegNotifyChangeKeyValue(m_scrollDirectionRegKey,
158  true,
159  REG_NOTIFY_CHANGE_LAST_SET,
160  m_scrollDirectionChangeEvent,
161  true));
162  if (!SUCCEEDED(hr)) {
163  GHOST_PRINT("Failed to register scroll direction change listener\n");
164  return;
165  }
166 }
167 
169 {
170  [[maybe_unused]] HRESULT hr = m_directManipulationViewport->SetContact(pointerId);
171  GHOST_ASSERT(SUCCEEDED(hr), "Viewport set contact failed\n");
172 
173  if (WaitForSingleObject(m_scrollDirectionChangeEvent, 0) == WAIT_OBJECT_0) {
174  m_isScrollDirectionInverted = getScrollDirectionFromReg();
175  registerScrollDirectionChangeListener();
176  }
177 }
178 
180 {
181  if (m_directManipulationEventHandler->dm_status == DIRECTMANIPULATION_RUNNING ||
182  m_directManipulationEventHandler->dm_status == DIRECTMANIPULATION_INERTIA) {
183  [[maybe_unused]] HRESULT hr = m_directManipulationUpdateManager->Update(nullptr);
184  GHOST_ASSERT(SUCCEEDED(hr), "DirectManipulationUpdateManager update failed\n");
185  }
186 }
187 
189 {
190  m_directManipulationEventHandler->dpi = dpi;
191 }
192 
194 {
195  GHOST_TTrackpadInfo result = m_directManipulationEventHandler->accumulated_values;
196  result.isScrollDirectionInverted = m_isScrollDirectionInverted;
197 
198  m_directManipulationEventHandler->accumulated_values = {0, 0, 0};
199  return result;
200 }
201 
203 {
204  HRESULT hr;
205  hr = m_directManipulationViewport->Stop();
206  GHOST_ASSERT(SUCCEEDED(hr), "Viewport stop failed\n");
207 
208  hr = m_directManipulationViewport->RemoveEventHandler(m_directManipulationViewportHandlerCookie);
209  GHOST_ASSERT(SUCCEEDED(hr), "Viewport remove event handler failed\n");
210 
211  hr = m_directManipulationViewport->Abandon();
212  GHOST_ASSERT(SUCCEEDED(hr), "Viewport abandon failed\n");
213 
214  hr = m_directManipulationManager->Deactivate(m_hWnd);
215  GHOST_ASSERT(SUCCEEDED(hr), "DirectManipulationManager deactivate failed\n");
216 
217  if (m_scrollDirectionChangeEvent) {
218  CloseHandle(m_scrollDirectionChangeEvent);
219  m_scrollDirectionChangeEvent = NULL;
220  }
221  if (m_scrollDirectionRegKey) {
222  RegCloseKey(m_scrollDirectionRegKey);
223  m_scrollDirectionRegKey = NULL;
224  }
225 }
226 
228  uint16_t dpi)
229  : accumulated_values({0, 0, 0}), dpi(dpi), dm_status(DIRECTMANIPULATION_BUILDING)
230 {
231 }
232 
234  IDirectManipulationViewport *viewport)
235 {
236  if (gesture_state != GESTURE_NONE) {
237  [[maybe_unused]] HRESULT hr = viewport->ZoomToRect(0.0f, 0.0f, 10000.0f, 10000.0f, FALSE);
238  GHOST_ASSERT(SUCCEEDED(hr), "Viewport reset failed\n");
239  }
240 
241  gesture_state = GESTURE_NONE;
242 
243  last_scale = PINCH_SCALE_FACTOR;
244  last_x = 0.0f;
245  last_y = 0.0f;
246 }
247 
249  IDirectManipulationViewport *viewport,
250  DIRECTMANIPULATION_STATUS current,
251  DIRECTMANIPULATION_STATUS previous)
252 {
253  dm_status = current;
254 
255  if (current == previous) {
256  return S_OK;
257  }
258 
259  if (previous == DIRECTMANIPULATION_ENABLED || current == DIRECTMANIPULATION_READY ||
260  (previous == DIRECTMANIPULATION_INERTIA && current != DIRECTMANIPULATION_INERTIA)) {
261  resetViewport(viewport);
262  }
263 
264  return S_OK;
265 }
266 
268  IDirectManipulationViewport *viewport)
269 {
270  /* Nothing to do here. */
271  return S_OK;
272 }
273 
275  IDirectManipulationViewport *viewport, IDirectManipulationContent *content)
276 {
277  float transform[6];
278  HRESULT hr = content->GetContentTransform(transform, ARRAYSIZE(transform));
279  GHOST_ASSERT(SUCCEEDED(hr), "DirectManipulationContent get transform failed\n");
280 
281  const float device_scale_factor = dpi / 96.0f;
282 
283  const float scale = transform[0] * PINCH_SCALE_FACTOR;
284  const float x = transform[4] / device_scale_factor;
285  const float y = transform[5] / device_scale_factor;
286 
287  const float EPS = 3e-5;
288 
289  /* Ignore repeating or incorrect input. */
290  if ((fabs(scale - last_scale) <= EPS && fabs(x - last_x) <= EPS && fabs(y - last_y) <= EPS) ||
291  scale == 0.0f) {
292  GHOST_PRINT("Ignoring touchpad input\n");
293  return hr;
294  }
295 
296  /* Assume that every gesture is a pan in the beginning.
297  * If it's a pinch, the gesture will be changed below. */
298  if (gesture_state == GESTURE_NONE) {
299  gesture_state = GESTURE_PAN;
300  }
301 
302  /* DM doesn't always immediately recognize pinch gestures,
303  * so allow transition from pan to pinch. */
304  if (gesture_state == GESTURE_PAN) {
305  if (fabs(scale - PINCH_SCALE_FACTOR) > EPS) {
306  gesture_state = GESTURE_PINCH;
307  }
308  }
309 
310  /* This state machine is used here because:
311  * 1. Pinch and pan gestures must be differentiated and cannot be processed at the same time
312  * because XY transform values become nonsensical during pinch gesture.
313  * 2. GHOST requires delta values for events while DM provides transformation matrix of the
314  * current gesture.
315  * 3. GHOST events accept integer values while DM values are non-integer.
316  * Truncated fractional parts are accumulated and accounted for in following updates.
317  */
318  switch (gesture_state) {
319  case GESTURE_PINCH: {
320  int32_t dscale = roundf(scale - last_scale);
321 
322  last_scale += dscale;
323 
324  accumulated_values.scale += dscale;
325  break;
326  }
327  case GESTURE_PAN: {
328  int32_t dx = roundf(x - last_x);
329  int32_t dy = roundf(y - last_y);
330 
331  last_x += dx;
332  last_y += dy;
333 
334  accumulated_values.x += dx;
335  accumulated_values.y += dy;
336  break;
337  }
338  case GESTURE_NONE:
339  break;
340  }
341 
342  return hr;
343 }
#define FALSE
Definition: GHOST_C-Test.c:17
#define GHOST_ASSERT(x, info)
Definition: GHOST_Debug.h:54
#define GHOST_PRINT(x)
Definition: GHOST_Debug.h:35
#define DM_CHECK_RESULT_AND_EXIT_EARLY(hr, failMessage)
#define PINCH_SCALE_FACTOR
_GL_VOID GLfloat value _GL_VOID_RET _GL_VOID const GLuint GLboolean *residences _GL_BOOL_RET _GL_VOID GLsizei GLfloat GLfloat GLfloat GLfloat const GLubyte *bitmap _GL_VOID_RET _GL_VOID GLenum const void *lists _GL_VOID_RET _GL_VOID const GLdouble *equation _GL_VOID_RET _GL_VOID GLdouble GLdouble blue _GL_VOID_RET _GL_VOID GLfloat GLfloat blue _GL_VOID_RET _GL_VOID GLint GLint blue _GL_VOID_RET _GL_VOID GLshort GLshort blue _GL_VOID_RET _GL_VOID GLubyte GLubyte blue _GL_VOID_RET _GL_VOID GLuint GLuint blue _GL_VOID_RET _GL_VOID GLushort GLushort blue _GL_VOID_RET _GL_VOID GLbyte GLbyte GLbyte alpha _GL_VOID_RET _GL_VOID GLdouble GLdouble GLdouble alpha _GL_VOID_RET _GL_VOID GLfloat GLfloat GLfloat alpha _GL_VOID_RET _GL_VOID GLint GLint GLint alpha _GL_VOID_RET _GL_VOID GLshort GLshort GLshort alpha _GL_VOID_RET _GL_VOID GLubyte GLubyte GLubyte alpha _GL_VOID_RET _GL_VOID GLuint GLuint GLuint alpha _GL_VOID_RET _GL_VOID GLushort GLushort GLushort alpha _GL_VOID_RET _GL_VOID GLenum mode _GL_VOID_RET _GL_VOID GLint y
in reality light always falls off quadratically Particle Retrieve the data of the particle that spawned the object instance
ATTR_WARN_UNUSED_RESULT const BMVert const BMEdge * e
SIMD_FORCE_INLINE btVector3 transform(const btVector3 &point) const
void onPointerHitTest(UINT32 pointerId)
static GHOST_DirectManipulationHelper * create(HWND hWnd, uint16_t dpi)
void resetViewport(IDirectManipulationViewport *viewport)
HRESULT STDMETHODCALLTYPE OnContentUpdated(IDirectManipulationViewport *viewport, IDirectManipulationContent *content) override
HRESULT STDMETHODCALLTYPE OnViewportUpdated(IDirectManipulationViewport *viewport) override
HRESULT STDMETHODCALLTYPE OnViewportStatusChanged(IDirectManipulationViewport *viewport, DIRECTMANIPULATION_STATUS current, DIRECTMANIPULATION_STATUS previous) override
ccl_device_inline float2 fabs(const float2 &a)
Definition: math_float2.h:222
#define L
unsigned short uint16_t
Definition: stdint.h:79
signed int int32_t
Definition: stdint.h:77
#define EPS
Definition: unit.c:35