1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 #include "DirectManipulationOwner.h"
8 #include "WinModifierKeyState.h"
10 #include "mozilla/StaticPrefs_apz.h"
11 #include "mozilla/SwipeTracker.h"
12 #include "mozilla/TimeStamp.h"
13 #include "mozilla/VsyncDispatcher.h"
15 // Direct Manipulation is only defined for Win8 and newer.
16 #if defined(_WIN32_WINNT)
18 # define _WIN32_WINNT _WIN32_WINNT_WIN8
19 #endif // defined(_WIN32_WINNT)
20 #if defined(NTDDI_VERSION)
22 # define NTDDI_VERSION NTDDI_WIN8
23 #endif // defined(NTDDI_VERSION)
25 #include "directmanipulation.h"
30 class DManipEventHandler
: public IDirectManipulationViewportEventHandler
,
31 public IDirectManipulationInteractionEventHandler
{
33 typedef mozilla::LayoutDeviceIntRect LayoutDeviceIntRect
;
35 friend class DirectManipulationOwner
;
37 NS_INLINE_DECL_THREADSAFE_REFCOUNTING(DManipEventHandler
)
39 STDMETHODIMP
QueryInterface(REFIID
, void**) override
;
41 friend class DirectManipulationOwner
;
43 explicit DManipEventHandler(nsWindow
* aWindow
,
44 DirectManipulationOwner
* aOwner
,
45 const LayoutDeviceIntRect
& aBounds
);
47 HRESULT STDMETHODCALLTYPE
OnViewportStatusChanged(
48 IDirectManipulationViewport
* viewport
, DIRECTMANIPULATION_STATUS current
,
49 DIRECTMANIPULATION_STATUS previous
) override
;
51 HRESULT STDMETHODCALLTYPE
52 OnViewportUpdated(IDirectManipulationViewport
* viewport
) override
;
54 HRESULT STDMETHODCALLTYPE
55 OnContentUpdated(IDirectManipulationViewport
* viewport
,
56 IDirectManipulationContent
* content
) override
;
58 HRESULT STDMETHODCALLTYPE
59 OnInteraction(IDirectManipulationViewport2
* viewport
,
60 DIRECTMANIPULATION_INTERACTION_TYPE interaction
) override
;
64 class VObserver final
: public mozilla::VsyncObserver
{
65 NS_INLINE_DECL_THREADSAFE_REFCOUNTING(DManipEventHandler::VObserver
,
69 void NotifyVsync(const mozilla::VsyncEvent
& aVsync
) override
{
74 explicit VObserver(DManipEventHandler
* aOwner
) : mOwner(aOwner
) {}
76 void ClearOwner() { mOwner
= nullptr; }
79 virtual ~VObserver() {}
80 DManipEventHandler
* mOwner
;
83 enum class State
{ eNone
, ePanning
, eInertia
, ePinching
};
84 void TransitionToState(State aNewState
);
86 enum class Phase
{ eStart
, eMiddle
, eEnd
};
87 // Return value indicates if we sent an event or not and hence if we should
88 // update mLastScale. (We only want to send pinch events if the computed
89 // deltaY for the corresponding WidgetWheelEvent would be non-zero.)
90 bool SendPinch(Phase aPhase
, float aScale
);
91 void SendPan(Phase aPhase
, float x
, float y
, bool aIsInertia
);
92 static void SendPanCommon(nsWindow
* aWindow
, Phase aPhase
,
93 ScreenPoint aPosition
, double aDeltaX
,
94 double aDeltaY
, Modifiers aMods
, bool aIsInertia
);
96 static void SynthesizeNativeTouchpadPan(
97 nsWindow
* aWindow
, nsIWidget::TouchpadGesturePhase aEventPhase
,
98 LayoutDeviceIntPoint aPoint
, double aDeltaX
, double aDeltaY
,
99 int32_t aModifierFlags
);
102 virtual ~DManipEventHandler() = default;
105 DirectManipulationOwner
* mOwner
;
106 RefPtr
<VObserver
> mObserver
;
110 LayoutDeviceIntRect mBounds
;
111 bool mShouldSendPanStart
;
112 bool mShouldSendPinchStart
;
113 State mState
= State::eNone
;
116 DManipEventHandler::DManipEventHandler(nsWindow
* aWindow
,
117 DirectManipulationOwner
* aOwner
,
118 const LayoutDeviceIntRect
& aBounds
)
125 mShouldSendPanStart(false),
126 mShouldSendPinchStart(false) {}
129 DManipEventHandler::QueryInterface(REFIID iid
, void** ppv
) {
130 const IID IID_IDirectManipulationViewportEventHandler
=
131 __uuidof(IDirectManipulationViewportEventHandler
);
132 const IID IID_IDirectManipulationInteractionEventHandler
=
133 __uuidof(IDirectManipulationInteractionEventHandler
);
135 if ((IID_IUnknown
== iid
) ||
136 (IID_IDirectManipulationViewportEventHandler
== iid
)) {
137 *ppv
= static_cast<IDirectManipulationViewportEventHandler
*>(this);
141 if (IID_IDirectManipulationInteractionEventHandler
== iid
) {
142 *ppv
= static_cast<IDirectManipulationInteractionEventHandler
*>(this);
147 return E_NOINTERFACE
;
151 DManipEventHandler::OnViewportStatusChanged(
152 IDirectManipulationViewport
* viewport
, DIRECTMANIPULATION_STATUS current
,
153 DIRECTMANIPULATION_STATUS previous
) {
154 if (current
== previous
) {
158 if (current
== DIRECTMANIPULATION_INERTIA
) {
159 if (previous
!= DIRECTMANIPULATION_RUNNING
|| mState
!= State::ePanning
) {
160 // xxx transition to none?
164 TransitionToState(State::eInertia
);
167 if (current
== DIRECTMANIPULATION_RUNNING
) {
168 // INERTIA -> RUNNING, should start a new sequence.
169 if (previous
== DIRECTMANIPULATION_INERTIA
) {
170 TransitionToState(State::eNone
);
174 if (current
!= DIRECTMANIPULATION_ENABLED
&&
175 current
!= DIRECTMANIPULATION_READY
) {
179 // A session has ended, reset the transform.
180 if (mLastScale
!= 1.f
|| mLastXOffset
!= 0.f
|| mLastYOffset
!= 0.f
) {
182 viewport
->ZoomToRect(0, 0, mBounds
.width
, mBounds
.height
, false);
183 if (!SUCCEEDED(hr
)) {
184 NS_WARNING("ZoomToRect failed");
191 TransitionToState(State::eNone
);
197 DManipEventHandler::OnViewportUpdated(IDirectManipulationViewport
* viewport
) {
201 void DManipEventHandler::TransitionToState(State aNewState
) {
202 if (mState
== aNewState
) {
206 State prevState
= mState
;
209 // End the previous sequence.
211 case State::ePanning
: {
212 // ePanning -> *: PanEnd
213 SendPan(Phase::eEnd
, 0.f
, 0.f
, false);
216 case State::eInertia
: {
217 // eInertia -> *: MomentumEnd
218 SendPan(Phase::eEnd
, 0.f
, 0.f
, true);
221 case State::ePinching
: {
222 MOZ_ASSERT(aNewState
== State::eNone
);
223 // ePinching -> eNone: PinchEnd. ePinching should only transition to
225 // Only send a pinch end if we sent a pinch start.
226 if (!mShouldSendPinchStart
) {
227 SendPinch(Phase::eEnd
, 0.f
);
229 mShouldSendPinchStart
= false;
233 // eNone -> *: no cleanup is needed.
240 // Start the new sequence.
242 case State::ePanning
: {
243 // eInertia, eNone -> ePanning: PanStart.
244 // We're being called from OnContentUpdated, it has the coords we need to
245 // pass to SendPan(Phase::eStart), so set mShouldSendPanStart and when we
246 // return OnContentUpdated will check it and call SendPan(Phase::eStart).
247 mShouldSendPanStart
= true;
250 case State::eInertia
: {
251 // Only ePanning can transition to eInertia.
252 MOZ_ASSERT(prevState
== State::ePanning
);
253 SendPan(Phase::eStart
, 0.f
, 0.f
, true);
256 case State::ePinching
: {
257 // * -> ePinching: PinchStart.
258 // Pinch gesture may begin with some scroll events.
259 // We're being called from OnContentUpdated, it has the scale we need to
260 // pass to SendPinch(Phase::eStart), so set mShouldSendPinchStart and when
261 // we return OnContentUpdated will check it and call
262 // SendPinch(Phase::eStart).
263 mShouldSendPinchStart
= true;
267 // * -> eNone: only cleanup is needed.
276 DManipEventHandler::OnContentUpdated(IDirectManipulationViewport
* viewport
,
277 IDirectManipulationContent
* content
) {
279 HRESULT hr
= content
->GetContentTransform(transform
, ARRAYSIZE(transform
));
280 if (!SUCCEEDED(hr
)) {
281 NS_WARNING("GetContentTransform failed");
285 float scale
= transform
[0];
286 float xoffset
= transform
[4];
287 float yoffset
= transform
[5];
289 // Not different from last time.
290 if (FuzzyEqualsMultiplicative(scale
, mLastScale
) && xoffset
== mLastXOffset
&&
291 yoffset
== mLastYOffset
) {
295 // Consider this is a Scroll when scale factor equals 1.0.
296 if (FuzzyEqualsMultiplicative(scale
, 1.f
)) {
297 if (mState
== State::eNone
) {
298 TransitionToState(State::ePanning
);
301 // Pinch gesture may begin with some scroll events.
302 TransitionToState(State::ePinching
);
305 if (mState
== State::ePanning
|| mState
== State::eInertia
) {
306 // Accumulate the offset (by not updating mLastX/YOffset) until we have at
308 float dx
= std::abs(mLastXOffset
- xoffset
);
309 float dy
= std::abs(mLastYOffset
- yoffset
);
310 if (dx
< 1.f
&& dy
< 1.f
) {
315 bool updateLastScale
= true;
316 if (mState
== State::ePanning
) {
317 if (mShouldSendPanStart
) {
318 SendPan(Phase::eStart
, mLastXOffset
- xoffset
, mLastYOffset
- yoffset
,
320 mShouldSendPanStart
= false;
322 SendPan(Phase::eMiddle
, mLastXOffset
- xoffset
, mLastYOffset
- yoffset
,
325 } else if (mState
== State::eInertia
) {
326 SendPan(Phase::eMiddle
, mLastXOffset
- xoffset
, mLastYOffset
- yoffset
,
328 } else if (mState
== State::ePinching
) {
329 if (mShouldSendPinchStart
) {
330 updateLastScale
= SendPinch(Phase::eStart
, scale
);
331 // Only clear mShouldSendPinchStart if we actually sent the event
332 // (updateLastScale tells us if we sent an event).
333 if (updateLastScale
) {
334 mShouldSendPinchStart
= false;
337 updateLastScale
= SendPinch(Phase::eMiddle
, scale
);
341 if (updateLastScale
) {
344 mLastXOffset
= xoffset
;
345 mLastYOffset
= yoffset
;
351 DManipEventHandler::OnInteraction(
352 IDirectManipulationViewport2
* viewport
,
353 DIRECTMANIPULATION_INTERACTION_TYPE interaction
) {
354 if (interaction
== DIRECTMANIPULATION_INTERACTION_BEGIN
) {
356 mObserver
= new VObserver(this);
359 gfxWindowsPlatform::GetPlatform()
360 ->GetGlobalVsyncDispatcher()
361 ->AddMainThreadObserver(mObserver
);
364 if (mObserver
&& interaction
== DIRECTMANIPULATION_INTERACTION_END
) {
365 gfxWindowsPlatform::GetPlatform()
366 ->GetGlobalVsyncDispatcher()
367 ->RemoveMainThreadObserver(mObserver
);
373 void DManipEventHandler::Update() {
379 void DirectManipulationOwner::Update() {
380 if (mDmUpdateManager
) {
381 mDmUpdateManager
->Update(nullptr);
385 DirectManipulationOwner::DirectManipulationOwner(nsWindow
* aWindow
)
386 : mWindow(aWindow
) {}
388 DirectManipulationOwner::~DirectManipulationOwner() { Destroy(); }
390 bool DManipEventHandler::SendPinch(Phase aPhase
, float aScale
) {
395 if (aScale
== mLastScale
&& aPhase
!= Phase::eEnd
) {
399 PinchGestureInput::PinchGestureType pinchGestureType
=
400 PinchGestureInput::PINCHGESTURE_SCALE
;
403 pinchGestureType
= PinchGestureInput::PINCHGESTURE_START
;
406 pinchGestureType
= PinchGestureInput::PINCHGESTURE_SCALE
;
409 pinchGestureType
= PinchGestureInput::PINCHGESTURE_END
;
412 MOZ_ASSERT_UNREACHABLE("handle all enum values");
415 TimeStamp eventTimeStamp
= TimeStamp::Now();
417 ModifierKeyState modifierKeyState
;
418 Modifiers mods
= modifierKeyState
.GetModifiers();
420 ExternalPoint screenOffset
= ViewAs
<ExternalPixel
>(
421 mWindow
->WidgetToScreenOffset(),
422 PixelCastJustification::LayoutDeviceIsScreenForUntransformedEvent
);
425 ::GetCursorPos(&cursor_pos
);
426 HWND wnd
= static_cast<HWND
>(mWindow
->GetNativeData(NS_NATIVE_WINDOW
));
427 ::ScreenToClient(wnd
, &cursor_pos
);
428 ScreenPoint position
= {(float)cursor_pos
.x
, (float)cursor_pos
.y
};
430 PinchGestureInput event
{pinchGestureType
,
431 PinchGestureInput::TRACKPAD
,
435 100.0 * ((aPhase
== Phase::eEnd
) ? 1.f
: aScale
),
436 100.0 * ((aPhase
== Phase::eEnd
) ? 1.f
: mLastScale
),
439 if (!event
.SetLineOrPageDeltaY(mWindow
)) {
443 mWindow
->SendAnAPZEvent(event
);
448 void DManipEventHandler::SendPan(Phase aPhase
, float x
, float y
,
454 ModifierKeyState modifierKeyState
;
455 Modifiers mods
= modifierKeyState
.GetModifiers();
458 ::GetCursorPos(&cursor_pos
);
459 HWND wnd
= static_cast<HWND
>(mWindow
->GetNativeData(NS_NATIVE_WINDOW
));
460 ::ScreenToClient(wnd
, &cursor_pos
);
461 ScreenPoint position
= {(float)cursor_pos
.x
, (float)cursor_pos
.y
};
463 SendPanCommon(mWindow
, aPhase
, position
, x
, y
, mods
, aIsInertia
);
467 void DManipEventHandler::SendPanCommon(nsWindow
* aWindow
, Phase aPhase
,
468 ScreenPoint aPosition
, double aDeltaX
,
469 double aDeltaY
, Modifiers aMods
,
475 PanGestureInput::PanGestureType panGestureType
=
476 PanGestureInput::PANGESTURE_PAN
;
480 panGestureType
= PanGestureInput::PANGESTURE_MOMENTUMSTART
;
483 panGestureType
= PanGestureInput::PANGESTURE_MOMENTUMPAN
;
486 panGestureType
= PanGestureInput::PANGESTURE_MOMENTUMEND
;
489 MOZ_ASSERT_UNREACHABLE("handle all enum values");
494 panGestureType
= PanGestureInput::PANGESTURE_START
;
497 panGestureType
= PanGestureInput::PANGESTURE_PAN
;
500 panGestureType
= PanGestureInput::PANGESTURE_END
;
503 MOZ_ASSERT_UNREACHABLE("handle all enum values");
507 TimeStamp eventTimeStamp
= TimeStamp::Now();
509 PanGestureInput event
{panGestureType
, eventTimeStamp
, aPosition
,
510 ScreenPoint(aDeltaX
, aDeltaY
), aMods
};
512 aWindow
->SendAnAPZEvent(event
);
515 void DirectManipulationOwner::Init(const LayoutDeviceIntRect
& aBounds
) {
516 HRESULT hr
= CoCreateInstance(
517 CLSID_DirectManipulationManager
, nullptr, CLSCTX_INPROC_SERVER
,
518 IID_IDirectManipulationManager
, getter_AddRefs(mDmManager
));
519 if (!SUCCEEDED(hr
)) {
520 NS_WARNING("CoCreateInstance(CLSID_DirectManipulationManager failed");
521 mDmManager
= nullptr;
525 hr
= mDmManager
->GetUpdateManager(IID_IDirectManipulationUpdateManager
,
526 getter_AddRefs(mDmUpdateManager
));
527 if (!SUCCEEDED(hr
)) {
528 NS_WARNING("GetUpdateManager failed");
529 mDmManager
= nullptr;
530 mDmUpdateManager
= nullptr;
534 HWND wnd
= static_cast<HWND
>(mWindow
->GetNativeData(NS_NATIVE_WINDOW
));
536 hr
= mDmManager
->CreateViewport(nullptr, wnd
, IID_IDirectManipulationViewport
,
537 getter_AddRefs(mDmViewport
));
538 if (!SUCCEEDED(hr
)) {
539 NS_WARNING("CreateViewport failed");
540 mDmManager
= nullptr;
541 mDmUpdateManager
= nullptr;
542 mDmViewport
= nullptr;
546 DIRECTMANIPULATION_CONFIGURATION configuration
=
547 DIRECTMANIPULATION_CONFIGURATION_INTERACTION
|
548 DIRECTMANIPULATION_CONFIGURATION_TRANSLATION_X
|
549 DIRECTMANIPULATION_CONFIGURATION_TRANSLATION_Y
|
550 DIRECTMANIPULATION_CONFIGURATION_TRANSLATION_INERTIA
|
551 DIRECTMANIPULATION_CONFIGURATION_RAILS_X
|
552 DIRECTMANIPULATION_CONFIGURATION_RAILS_Y
;
553 if (StaticPrefs::apz_allow_zooming()) {
554 configuration
|= DIRECTMANIPULATION_CONFIGURATION_SCALING
;
557 hr
= mDmViewport
->ActivateConfiguration(configuration
);
558 if (!SUCCEEDED(hr
)) {
559 NS_WARNING("ActivateConfiguration failed");
560 mDmManager
= nullptr;
561 mDmUpdateManager
= nullptr;
562 mDmViewport
= nullptr;
566 hr
= mDmViewport
->SetViewportOptions(
567 DIRECTMANIPULATION_VIEWPORT_OPTIONS_MANUALUPDATE
);
568 if (!SUCCEEDED(hr
)) {
569 NS_WARNING("SetViewportOptions failed");
570 mDmManager
= nullptr;
571 mDmUpdateManager
= nullptr;
572 mDmViewport
= nullptr;
576 mDmHandler
= new DManipEventHandler(mWindow
, this, aBounds
);
578 hr
= mDmViewport
->AddEventHandler(wnd
, mDmHandler
.get(),
579 &mDmViewportHandlerCookie
);
580 if (!SUCCEEDED(hr
)) {
581 NS_WARNING("AddEventHandler failed");
582 mDmManager
= nullptr;
583 mDmUpdateManager
= nullptr;
584 mDmViewport
= nullptr;
585 mDmHandler
= nullptr;
589 RECT rect
= {0, 0, aBounds
.Width(), aBounds
.Height()};
590 hr
= mDmViewport
->SetViewportRect(&rect
);
591 if (!SUCCEEDED(hr
)) {
592 NS_WARNING("SetViewportRect failed");
593 mDmManager
= nullptr;
594 mDmUpdateManager
= nullptr;
595 mDmViewport
= nullptr;
596 mDmHandler
= nullptr;
600 hr
= mDmManager
->Activate(wnd
);
601 if (!SUCCEEDED(hr
)) {
602 NS_WARNING("manager Activate failed");
603 mDmManager
= nullptr;
604 mDmUpdateManager
= nullptr;
605 mDmViewport
= nullptr;
606 mDmHandler
= nullptr;
610 hr
= mDmViewport
->Enable();
611 if (!SUCCEEDED(hr
)) {
612 NS_WARNING("mDmViewport->Enable failed");
613 mDmManager
= nullptr;
614 mDmUpdateManager
= nullptr;
615 mDmViewport
= nullptr;
616 mDmHandler
= nullptr;
620 hr
= mDmUpdateManager
->Update(nullptr);
621 if (!SUCCEEDED(hr
)) {
622 NS_WARNING("mDmUpdateManager->Update failed");
623 mDmManager
= nullptr;
624 mDmUpdateManager
= nullptr;
625 mDmViewport
= nullptr;
626 mDmHandler
= nullptr;
631 void DirectManipulationOwner::ResizeViewport(
632 const LayoutDeviceIntRect
& aBounds
) {
634 mDmHandler
->mBounds
= aBounds
;
638 RECT rect
= {0, 0, aBounds
.Width(), aBounds
.Height()};
639 HRESULT hr
= mDmViewport
->SetViewportRect(&rect
);
640 if (!SUCCEEDED(hr
)) {
641 NS_WARNING("SetViewportRect failed");
646 void DirectManipulationOwner::Destroy() {
648 mDmHandler
->mWindow
= nullptr;
649 mDmHandler
->mOwner
= nullptr;
650 if (mDmHandler
->mObserver
) {
651 gfxWindowsPlatform::GetPlatform()
652 ->GetGlobalVsyncDispatcher()
653 ->RemoveMainThreadObserver(mDmHandler
->mObserver
);
654 mDmHandler
->mObserver
->ClearOwner();
655 mDmHandler
->mObserver
= nullptr;
661 hr
= mDmViewport
->Stop();
662 if (!SUCCEEDED(hr
)) {
663 NS_WARNING("mDmViewport->Stop() failed");
666 hr
= mDmViewport
->Disable();
667 if (!SUCCEEDED(hr
)) {
668 NS_WARNING("mDmViewport->Disable() failed");
671 hr
= mDmViewport
->RemoveEventHandler(mDmViewportHandlerCookie
);
672 if (!SUCCEEDED(hr
)) {
673 NS_WARNING("mDmViewport->RemoveEventHandler() failed");
676 hr
= mDmViewport
->Abandon();
677 if (!SUCCEEDED(hr
)) {
678 NS_WARNING("mDmViewport->Abandon() failed");
683 HWND wnd
= static_cast<HWND
>(mWindow
->GetNativeData(NS_NATIVE_WINDOW
));
686 hr
= mDmManager
->Deactivate(wnd
);
687 if (!SUCCEEDED(hr
)) {
688 NS_WARNING("mDmManager->Deactivate() failed");
693 mDmHandler
= nullptr;
694 mDmViewport
= nullptr;
695 mDmUpdateManager
= nullptr;
696 mDmManager
= nullptr;
700 void DirectManipulationOwner::SetContact(UINT aContactId
) {
702 mDmViewport
->SetContact(aContactId
);
706 /*static */ void DirectManipulationOwner::SynthesizeNativeTouchpadPan(
707 nsWindow
* aWindow
, nsIWidget::TouchpadGesturePhase aEventPhase
,
708 LayoutDeviceIntPoint aPoint
, double aDeltaX
, double aDeltaY
,
709 int32_t aModifierFlags
) {
710 DManipEventHandler::SynthesizeNativeTouchpadPan(
711 aWindow
, aEventPhase
, aPoint
, aDeltaX
, aDeltaY
, aModifierFlags
);
714 /*static */ void DManipEventHandler::SynthesizeNativeTouchpadPan(
715 nsWindow
* aWindow
, nsIWidget::TouchpadGesturePhase aEventPhase
,
716 LayoutDeviceIntPoint aPoint
, double aDeltaX
, double aDeltaY
,
717 int32_t aModifierFlags
) {
718 ScreenPoint position
= {(float)aPoint
.x
, (float)aPoint
.y
};
719 Phase phase
= Phase::eStart
;
720 if (aEventPhase
== nsIWidget::PHASE_UPDATE
) {
721 phase
= Phase::eMiddle
;
724 if (aEventPhase
== nsIWidget::PHASE_END
) {
727 SendPanCommon(aWindow
, phase
, position
, aDeltaX
, aDeltaY
, aModifierFlags
,
728 /* aIsInertia = */ false);
731 } // namespace widget
732 } // namespace mozilla