summaryrefslogtreecommitdiffstats
path: root/widget/windows/DirectManipulationOwner.cpp
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--widget/windows/DirectManipulationOwner.cpp732
1 files changed, 732 insertions, 0 deletions
diff --git a/widget/windows/DirectManipulationOwner.cpp b/widget/windows/DirectManipulationOwner.cpp
new file mode 100644
index 0000000000..3bda193d3a
--- /dev/null
+++ b/widget/windows/DirectManipulationOwner.cpp
@@ -0,0 +1,732 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "DirectManipulationOwner.h"
+#include "nsWindow.h"
+#include "WinModifierKeyState.h"
+#include "InputData.h"
+#include "mozilla/StaticPrefs_apz.h"
+#include "mozilla/SwipeTracker.h"
+#include "mozilla/TimeStamp.h"
+#include "mozilla/VsyncDispatcher.h"
+
+// Direct Manipulation is only defined for Win8 and newer.
+#if defined(_WIN32_WINNT)
+# undef _WIN32_WINNT
+# define _WIN32_WINNT _WIN32_WINNT_WIN8
+#endif // defined(_WIN32_WINNT)
+#if defined(NTDDI_VERSION)
+# undef NTDDI_VERSION
+# define NTDDI_VERSION NTDDI_WIN8
+#endif // defined(NTDDI_VERSION)
+
+#include "directmanipulation.h"
+
+namespace mozilla {
+namespace widget {
+
+class DManipEventHandler : public IDirectManipulationViewportEventHandler,
+ public IDirectManipulationInteractionEventHandler {
+ public:
+ typedef mozilla::LayoutDeviceIntRect LayoutDeviceIntRect;
+
+ friend class DirectManipulationOwner;
+
+ NS_INLINE_DECL_THREADSAFE_REFCOUNTING(DManipEventHandler)
+
+ STDMETHODIMP QueryInterface(REFIID, void**) override;
+
+ friend class DirectManipulationOwner;
+
+ explicit DManipEventHandler(nsWindow* aWindow,
+ DirectManipulationOwner* aOwner,
+ const LayoutDeviceIntRect& aBounds);
+
+ 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;
+
+ HRESULT STDMETHODCALLTYPE
+ OnInteraction(IDirectManipulationViewport2* viewport,
+ DIRECTMANIPULATION_INTERACTION_TYPE interaction) override;
+
+ void Update();
+
+ class VObserver final : public mozilla::VsyncObserver {
+ NS_INLINE_DECL_THREADSAFE_REFCOUNTING(DManipEventHandler::VObserver,
+ override)
+
+ public:
+ void NotifyVsync(const mozilla::VsyncEvent& aVsync) override {
+ if (mOwner) {
+ mOwner->Update();
+ }
+ }
+ explicit VObserver(DManipEventHandler* aOwner) : mOwner(aOwner) {}
+
+ void ClearOwner() { mOwner = nullptr; }
+
+ private:
+ virtual ~VObserver() {}
+ DManipEventHandler* mOwner;
+ };
+
+ enum class State { eNone, ePanning, eInertia, ePinching };
+ void TransitionToState(State aNewState);
+
+ enum class Phase { eStart, eMiddle, eEnd };
+ // Return value indicates if we sent an event or not and hence if we should
+ // update mLastScale. (We only want to send pinch events if the computed
+ // deltaY for the corresponding WidgetWheelEvent would be non-zero.)
+ bool SendPinch(Phase aPhase, float aScale);
+ void SendPan(Phase aPhase, float x, float y, bool aIsInertia);
+ static void SendPanCommon(nsWindow* aWindow, Phase aPhase,
+ ScreenPoint aPosition, double aDeltaX,
+ double aDeltaY, Modifiers aMods, bool aIsInertia);
+
+ static void SynthesizeNativeTouchpadPan(
+ nsWindow* aWindow, nsIWidget::TouchpadGesturePhase aEventPhase,
+ LayoutDeviceIntPoint aPoint, double aDeltaX, double aDeltaY,
+ int32_t aModifierFlags);
+
+ private:
+ virtual ~DManipEventHandler() = default;
+
+ nsWindow* mWindow;
+ DirectManipulationOwner* mOwner;
+ RefPtr<VObserver> mObserver;
+ float mLastScale;
+ float mLastXOffset;
+ float mLastYOffset;
+ LayoutDeviceIntRect mBounds;
+ bool mShouldSendPanStart;
+ bool mShouldSendPinchStart;
+ State mState = State::eNone;
+};
+
+DManipEventHandler::DManipEventHandler(nsWindow* aWindow,
+ DirectManipulationOwner* aOwner,
+ const LayoutDeviceIntRect& aBounds)
+ : mWindow(aWindow),
+ mOwner(aOwner),
+ mLastScale(1.f),
+ mLastXOffset(0.f),
+ mLastYOffset(0.f),
+ mBounds(aBounds),
+ mShouldSendPanStart(false),
+ mShouldSendPinchStart(false) {}
+
+STDMETHODIMP
+DManipEventHandler::QueryInterface(REFIID iid, void** ppv) {
+ const IID IID_IDirectManipulationViewportEventHandler =
+ __uuidof(IDirectManipulationViewportEventHandler);
+ const IID IID_IDirectManipulationInteractionEventHandler =
+ __uuidof(IDirectManipulationInteractionEventHandler);
+
+ if ((IID_IUnknown == iid) ||
+ (IID_IDirectManipulationViewportEventHandler == iid)) {
+ *ppv = static_cast<IDirectManipulationViewportEventHandler*>(this);
+ AddRef();
+ return S_OK;
+ }
+ if (IID_IDirectManipulationInteractionEventHandler == iid) {
+ *ppv = static_cast<IDirectManipulationInteractionEventHandler*>(this);
+ AddRef();
+ return S_OK;
+ }
+
+ return E_NOINTERFACE;
+}
+
+HRESULT
+DManipEventHandler::OnViewportStatusChanged(
+ IDirectManipulationViewport* viewport, DIRECTMANIPULATION_STATUS current,
+ DIRECTMANIPULATION_STATUS previous) {
+ if (current == previous) {
+ return S_OK;
+ }
+
+ if (current == DIRECTMANIPULATION_INERTIA) {
+ if (previous != DIRECTMANIPULATION_RUNNING || mState != State::ePanning) {
+ // xxx transition to none?
+ return S_OK;
+ }
+
+ TransitionToState(State::eInertia);
+ }
+
+ if (current == DIRECTMANIPULATION_RUNNING) {
+ // INERTIA -> RUNNING, should start a new sequence.
+ if (previous == DIRECTMANIPULATION_INERTIA) {
+ TransitionToState(State::eNone);
+ }
+ }
+
+ if (current != DIRECTMANIPULATION_ENABLED &&
+ current != DIRECTMANIPULATION_READY) {
+ return S_OK;
+ }
+
+ // A session has ended, reset the transform.
+ if (mLastScale != 1.f || mLastXOffset != 0.f || mLastYOffset != 0.f) {
+ HRESULT hr =
+ viewport->ZoomToRect(0, 0, mBounds.width, mBounds.height, false);
+ if (!SUCCEEDED(hr)) {
+ NS_WARNING("ZoomToRect failed");
+ }
+ }
+ mLastScale = 1.f;
+ mLastXOffset = 0.f;
+ mLastYOffset = 0.f;
+
+ TransitionToState(State::eNone);
+
+ return S_OK;
+}
+
+HRESULT
+DManipEventHandler::OnViewportUpdated(IDirectManipulationViewport* viewport) {
+ return S_OK;
+}
+
+void DManipEventHandler::TransitionToState(State aNewState) {
+ if (mState == aNewState) {
+ return;
+ }
+
+ State prevState = mState;
+ mState = aNewState;
+
+ // End the previous sequence.
+ switch (prevState) {
+ case State::ePanning: {
+ // ePanning -> *: PanEnd
+ SendPan(Phase::eEnd, 0.f, 0.f, false);
+ break;
+ }
+ case State::eInertia: {
+ // eInertia -> *: MomentumEnd
+ SendPan(Phase::eEnd, 0.f, 0.f, true);
+ break;
+ }
+ case State::ePinching: {
+ MOZ_ASSERT(aNewState == State::eNone);
+ // ePinching -> eNone: PinchEnd. ePinching should only transition to
+ // eNone.
+ // Only send a pinch end if we sent a pinch start.
+ if (!mShouldSendPinchStart) {
+ SendPinch(Phase::eEnd, 0.f);
+ }
+ mShouldSendPinchStart = false;
+ break;
+ }
+ case State::eNone: {
+ // eNone -> *: no cleanup is needed.
+ break;
+ }
+ default:
+ MOZ_ASSERT(false);
+ }
+
+ // Start the new sequence.
+ switch (aNewState) {
+ case State::ePanning: {
+ // eInertia, eNone -> ePanning: PanStart.
+ // We're being called from OnContentUpdated, it has the coords we need to
+ // pass to SendPan(Phase::eStart), so set mShouldSendPanStart and when we
+ // return OnContentUpdated will check it and call SendPan(Phase::eStart).
+ mShouldSendPanStart = true;
+ break;
+ }
+ case State::eInertia: {
+ // Only ePanning can transition to eInertia.
+ MOZ_ASSERT(prevState == State::ePanning);
+ SendPan(Phase::eStart, 0.f, 0.f, true);
+ break;
+ }
+ case State::ePinching: {
+ // * -> ePinching: PinchStart.
+ // Pinch gesture may begin with some scroll events.
+ // We're being called from OnContentUpdated, it has the scale we need to
+ // pass to SendPinch(Phase::eStart), so set mShouldSendPinchStart and when
+ // we return OnContentUpdated will check it and call
+ // SendPinch(Phase::eStart).
+ mShouldSendPinchStart = true;
+ break;
+ }
+ case State::eNone: {
+ // * -> eNone: only cleanup is needed.
+ break;
+ }
+ default:
+ MOZ_ASSERT(false);
+ }
+}
+
+HRESULT
+DManipEventHandler::OnContentUpdated(IDirectManipulationViewport* viewport,
+ IDirectManipulationContent* content) {
+ float transform[6];
+ HRESULT hr = content->GetContentTransform(transform, ARRAYSIZE(transform));
+ if (!SUCCEEDED(hr)) {
+ NS_WARNING("GetContentTransform failed");
+ return S_OK;
+ }
+
+ float scale = transform[0];
+ float xoffset = transform[4];
+ float yoffset = transform[5];
+
+ // Not different from last time.
+ if (FuzzyEqualsMultiplicative(scale, mLastScale) && xoffset == mLastXOffset &&
+ yoffset == mLastYOffset) {
+ return S_OK;
+ }
+
+ // Consider this is a Scroll when scale factor equals 1.0.
+ if (FuzzyEqualsMultiplicative(scale, 1.f)) {
+ if (mState == State::eNone) {
+ TransitionToState(State::ePanning);
+ }
+ } else {
+ // Pinch gesture may begin with some scroll events.
+ TransitionToState(State::ePinching);
+ }
+
+ if (mState == State::ePanning || mState == State::eInertia) {
+ // Accumulate the offset (by not updating mLastX/YOffset) until we have at
+ // least one pixel.
+ float dx = std::abs(mLastXOffset - xoffset);
+ float dy = std::abs(mLastYOffset - yoffset);
+ if (dx < 1.f && dy < 1.f) {
+ return S_OK;
+ }
+ }
+
+ bool updateLastScale = true;
+ if (mState == State::ePanning) {
+ if (mShouldSendPanStart) {
+ SendPan(Phase::eStart, mLastXOffset - xoffset, mLastYOffset - yoffset,
+ false);
+ mShouldSendPanStart = false;
+ } else {
+ SendPan(Phase::eMiddle, mLastXOffset - xoffset, mLastYOffset - yoffset,
+ false);
+ }
+ } else if (mState == State::eInertia) {
+ SendPan(Phase::eMiddle, mLastXOffset - xoffset, mLastYOffset - yoffset,
+ true);
+ } else if (mState == State::ePinching) {
+ if (mShouldSendPinchStart) {
+ updateLastScale = SendPinch(Phase::eStart, scale);
+ // Only clear mShouldSendPinchStart if we actually sent the event
+ // (updateLastScale tells us if we sent an event).
+ if (updateLastScale) {
+ mShouldSendPinchStart = false;
+ }
+ } else {
+ updateLastScale = SendPinch(Phase::eMiddle, scale);
+ }
+ }
+
+ if (updateLastScale) {
+ mLastScale = scale;
+ }
+ mLastXOffset = xoffset;
+ mLastYOffset = yoffset;
+
+ return S_OK;
+}
+
+HRESULT
+DManipEventHandler::OnInteraction(
+ IDirectManipulationViewport2* viewport,
+ DIRECTMANIPULATION_INTERACTION_TYPE interaction) {
+ if (interaction == DIRECTMANIPULATION_INTERACTION_BEGIN) {
+ if (!mObserver) {
+ mObserver = new VObserver(this);
+ }
+
+ gfxWindowsPlatform::GetPlatform()
+ ->GetGlobalVsyncDispatcher()
+ ->AddMainThreadObserver(mObserver);
+ }
+
+ if (mObserver && interaction == DIRECTMANIPULATION_INTERACTION_END) {
+ gfxWindowsPlatform::GetPlatform()
+ ->GetGlobalVsyncDispatcher()
+ ->RemoveMainThreadObserver(mObserver);
+ }
+
+ return S_OK;
+}
+
+void DManipEventHandler::Update() {
+ if (mOwner) {
+ mOwner->Update();
+ }
+}
+
+void DirectManipulationOwner::Update() {
+ if (mDmUpdateManager) {
+ mDmUpdateManager->Update(nullptr);
+ }
+}
+
+DirectManipulationOwner::DirectManipulationOwner(nsWindow* aWindow)
+ : mWindow(aWindow) {}
+
+DirectManipulationOwner::~DirectManipulationOwner() { Destroy(); }
+
+bool DManipEventHandler::SendPinch(Phase aPhase, float aScale) {
+ if (!mWindow) {
+ return false;
+ }
+
+ if (aScale == mLastScale && aPhase != Phase::eEnd) {
+ return false;
+ }
+
+ PinchGestureInput::PinchGestureType pinchGestureType =
+ PinchGestureInput::PINCHGESTURE_SCALE;
+ switch (aPhase) {
+ case Phase::eStart:
+ pinchGestureType = PinchGestureInput::PINCHGESTURE_START;
+ break;
+ case Phase::eMiddle:
+ pinchGestureType = PinchGestureInput::PINCHGESTURE_SCALE;
+ break;
+ case Phase::eEnd:
+ pinchGestureType = PinchGestureInput::PINCHGESTURE_END;
+ break;
+ default:
+ MOZ_ASSERT_UNREACHABLE("handle all enum values");
+ }
+
+ TimeStamp eventTimeStamp = TimeStamp::Now();
+
+ ModifierKeyState modifierKeyState;
+ Modifiers mods = modifierKeyState.GetModifiers();
+
+ ExternalPoint screenOffset = ViewAs<ExternalPixel>(
+ mWindow->WidgetToScreenOffset(),
+ PixelCastJustification::LayoutDeviceIsScreenForUntransformedEvent);
+
+ POINT cursor_pos;
+ ::GetCursorPos(&cursor_pos);
+ HWND wnd = static_cast<HWND>(mWindow->GetNativeData(NS_NATIVE_WINDOW));
+ ::ScreenToClient(wnd, &cursor_pos);
+ ScreenPoint position = {(float)cursor_pos.x, (float)cursor_pos.y};
+
+ PinchGestureInput event{pinchGestureType,
+ PinchGestureInput::TRACKPAD,
+ eventTimeStamp,
+ screenOffset,
+ position,
+ 100.0 * ((aPhase == Phase::eEnd) ? 1.f : aScale),
+ 100.0 * ((aPhase == Phase::eEnd) ? 1.f : mLastScale),
+ mods};
+
+ if (!event.SetLineOrPageDeltaY(mWindow)) {
+ return false;
+ }
+
+ mWindow->SendAnAPZEvent(event);
+
+ return true;
+}
+
+void DManipEventHandler::SendPan(Phase aPhase, float x, float y,
+ bool aIsInertia) {
+ if (!mWindow) {
+ return;
+ }
+
+ ModifierKeyState modifierKeyState;
+ Modifiers mods = modifierKeyState.GetModifiers();
+
+ POINT cursor_pos;
+ ::GetCursorPos(&cursor_pos);
+ HWND wnd = static_cast<HWND>(mWindow->GetNativeData(NS_NATIVE_WINDOW));
+ ::ScreenToClient(wnd, &cursor_pos);
+ ScreenPoint position = {(float)cursor_pos.x, (float)cursor_pos.y};
+
+ SendPanCommon(mWindow, aPhase, position, x, y, mods, aIsInertia);
+}
+
+/* static */
+void DManipEventHandler::SendPanCommon(nsWindow* aWindow, Phase aPhase,
+ ScreenPoint aPosition, double aDeltaX,
+ double aDeltaY, Modifiers aMods,
+ bool aIsInertia) {
+ if (!aWindow) {
+ return;
+ }
+
+ PanGestureInput::PanGestureType panGestureType =
+ PanGestureInput::PANGESTURE_PAN;
+ if (aIsInertia) {
+ switch (aPhase) {
+ case Phase::eStart:
+ panGestureType = PanGestureInput::PANGESTURE_MOMENTUMSTART;
+ break;
+ case Phase::eMiddle:
+ panGestureType = PanGestureInput::PANGESTURE_MOMENTUMPAN;
+ break;
+ case Phase::eEnd:
+ panGestureType = PanGestureInput::PANGESTURE_MOMENTUMEND;
+ break;
+ default:
+ MOZ_ASSERT_UNREACHABLE("handle all enum values");
+ }
+ } else {
+ switch (aPhase) {
+ case Phase::eStart:
+ panGestureType = PanGestureInput::PANGESTURE_START;
+ break;
+ case Phase::eMiddle:
+ panGestureType = PanGestureInput::PANGESTURE_PAN;
+ break;
+ case Phase::eEnd:
+ panGestureType = PanGestureInput::PANGESTURE_END;
+ break;
+ default:
+ MOZ_ASSERT_UNREACHABLE("handle all enum values");
+ }
+ }
+
+ TimeStamp eventTimeStamp = TimeStamp::Now();
+
+ PanGestureInput event{panGestureType, eventTimeStamp, aPosition,
+ ScreenPoint(aDeltaX, aDeltaY), aMods};
+
+ aWindow->SendAnAPZEvent(event);
+}
+
+void DirectManipulationOwner::Init(const LayoutDeviceIntRect& aBounds) {
+ HRESULT hr = CoCreateInstance(
+ CLSID_DirectManipulationManager, nullptr, CLSCTX_INPROC_SERVER,
+ IID_IDirectManipulationManager, getter_AddRefs(mDmManager));
+ if (!SUCCEEDED(hr)) {
+ NS_WARNING("CoCreateInstance(CLSID_DirectManipulationManager failed");
+ mDmManager = nullptr;
+ return;
+ }
+
+ hr = mDmManager->GetUpdateManager(IID_IDirectManipulationUpdateManager,
+ getter_AddRefs(mDmUpdateManager));
+ if (!SUCCEEDED(hr)) {
+ NS_WARNING("GetUpdateManager failed");
+ mDmManager = nullptr;
+ mDmUpdateManager = nullptr;
+ return;
+ }
+
+ HWND wnd = static_cast<HWND>(mWindow->GetNativeData(NS_NATIVE_WINDOW));
+
+ hr = mDmManager->CreateViewport(nullptr, wnd, IID_IDirectManipulationViewport,
+ getter_AddRefs(mDmViewport));
+ if (!SUCCEEDED(hr)) {
+ NS_WARNING("CreateViewport failed");
+ mDmManager = nullptr;
+ mDmUpdateManager = nullptr;
+ mDmViewport = nullptr;
+ return;
+ }
+
+ DIRECTMANIPULATION_CONFIGURATION configuration =
+ DIRECTMANIPULATION_CONFIGURATION_INTERACTION |
+ DIRECTMANIPULATION_CONFIGURATION_TRANSLATION_X |
+ DIRECTMANIPULATION_CONFIGURATION_TRANSLATION_Y |
+ DIRECTMANIPULATION_CONFIGURATION_TRANSLATION_INERTIA |
+ DIRECTMANIPULATION_CONFIGURATION_RAILS_X |
+ DIRECTMANIPULATION_CONFIGURATION_RAILS_Y;
+ if (StaticPrefs::apz_allow_zooming()) {
+ configuration |= DIRECTMANIPULATION_CONFIGURATION_SCALING;
+ }
+
+ hr = mDmViewport->ActivateConfiguration(configuration);
+ if (!SUCCEEDED(hr)) {
+ NS_WARNING("ActivateConfiguration failed");
+ mDmManager = nullptr;
+ mDmUpdateManager = nullptr;
+ mDmViewport = nullptr;
+ return;
+ }
+
+ hr = mDmViewport->SetViewportOptions(
+ DIRECTMANIPULATION_VIEWPORT_OPTIONS_MANUALUPDATE);
+ if (!SUCCEEDED(hr)) {
+ NS_WARNING("SetViewportOptions failed");
+ mDmManager = nullptr;
+ mDmUpdateManager = nullptr;
+ mDmViewport = nullptr;
+ return;
+ }
+
+ mDmHandler = new DManipEventHandler(mWindow, this, aBounds);
+
+ hr = mDmViewport->AddEventHandler(wnd, mDmHandler.get(),
+ &mDmViewportHandlerCookie);
+ if (!SUCCEEDED(hr)) {
+ NS_WARNING("AddEventHandler failed");
+ mDmManager = nullptr;
+ mDmUpdateManager = nullptr;
+ mDmViewport = nullptr;
+ mDmHandler = nullptr;
+ return;
+ }
+
+ RECT rect = {0, 0, aBounds.Width(), aBounds.Height()};
+ hr = mDmViewport->SetViewportRect(&rect);
+ if (!SUCCEEDED(hr)) {
+ NS_WARNING("SetViewportRect failed");
+ mDmManager = nullptr;
+ mDmUpdateManager = nullptr;
+ mDmViewport = nullptr;
+ mDmHandler = nullptr;
+ return;
+ }
+
+ hr = mDmManager->Activate(wnd);
+ if (!SUCCEEDED(hr)) {
+ NS_WARNING("manager Activate failed");
+ mDmManager = nullptr;
+ mDmUpdateManager = nullptr;
+ mDmViewport = nullptr;
+ mDmHandler = nullptr;
+ return;
+ }
+
+ hr = mDmViewport->Enable();
+ if (!SUCCEEDED(hr)) {
+ NS_WARNING("mDmViewport->Enable failed");
+ mDmManager = nullptr;
+ mDmUpdateManager = nullptr;
+ mDmViewport = nullptr;
+ mDmHandler = nullptr;
+ return;
+ }
+
+ hr = mDmUpdateManager->Update(nullptr);
+ if (!SUCCEEDED(hr)) {
+ NS_WARNING("mDmUpdateManager->Update failed");
+ mDmManager = nullptr;
+ mDmUpdateManager = nullptr;
+ mDmViewport = nullptr;
+ mDmHandler = nullptr;
+ return;
+ }
+}
+
+void DirectManipulationOwner::ResizeViewport(
+ const LayoutDeviceIntRect& aBounds) {
+ if (mDmHandler) {
+ mDmHandler->mBounds = aBounds;
+ }
+
+ if (mDmViewport) {
+ RECT rect = {0, 0, aBounds.Width(), aBounds.Height()};
+ HRESULT hr = mDmViewport->SetViewportRect(&rect);
+ if (!SUCCEEDED(hr)) {
+ NS_WARNING("SetViewportRect failed");
+ }
+ }
+}
+
+void DirectManipulationOwner::Destroy() {
+ if (mDmHandler) {
+ mDmHandler->mWindow = nullptr;
+ mDmHandler->mOwner = nullptr;
+ if (mDmHandler->mObserver) {
+ gfxWindowsPlatform::GetPlatform()
+ ->GetGlobalVsyncDispatcher()
+ ->RemoveMainThreadObserver(mDmHandler->mObserver);
+ mDmHandler->mObserver->ClearOwner();
+ mDmHandler->mObserver = nullptr;
+ }
+ }
+
+ HRESULT hr;
+ if (mDmViewport) {
+ hr = mDmViewport->Stop();
+ if (!SUCCEEDED(hr)) {
+ NS_WARNING("mDmViewport->Stop() failed");
+ }
+
+ hr = mDmViewport->Disable();
+ if (!SUCCEEDED(hr)) {
+ NS_WARNING("mDmViewport->Disable() failed");
+ }
+
+ hr = mDmViewport->RemoveEventHandler(mDmViewportHandlerCookie);
+ if (!SUCCEEDED(hr)) {
+ NS_WARNING("mDmViewport->RemoveEventHandler() failed");
+ }
+
+ hr = mDmViewport->Abandon();
+ if (!SUCCEEDED(hr)) {
+ NS_WARNING("mDmViewport->Abandon() failed");
+ }
+ }
+
+ if (mWindow) {
+ HWND wnd = static_cast<HWND>(mWindow->GetNativeData(NS_NATIVE_WINDOW));
+
+ if (mDmManager) {
+ hr = mDmManager->Deactivate(wnd);
+ if (!SUCCEEDED(hr)) {
+ NS_WARNING("mDmManager->Deactivate() failed");
+ }
+ }
+ }
+
+ mDmHandler = nullptr;
+ mDmViewport = nullptr;
+ mDmUpdateManager = nullptr;
+ mDmManager = nullptr;
+ mWindow = nullptr;
+}
+
+void DirectManipulationOwner::SetContact(UINT aContactId) {
+ if (mDmViewport) {
+ mDmViewport->SetContact(aContactId);
+ }
+}
+
+/*static */ void DirectManipulationOwner::SynthesizeNativeTouchpadPan(
+ nsWindow* aWindow, nsIWidget::TouchpadGesturePhase aEventPhase,
+ LayoutDeviceIntPoint aPoint, double aDeltaX, double aDeltaY,
+ int32_t aModifierFlags) {
+ DManipEventHandler::SynthesizeNativeTouchpadPan(
+ aWindow, aEventPhase, aPoint, aDeltaX, aDeltaY, aModifierFlags);
+}
+
+/*static */ void DManipEventHandler::SynthesizeNativeTouchpadPan(
+ nsWindow* aWindow, nsIWidget::TouchpadGesturePhase aEventPhase,
+ LayoutDeviceIntPoint aPoint, double aDeltaX, double aDeltaY,
+ int32_t aModifierFlags) {
+ ScreenPoint position = {(float)aPoint.x, (float)aPoint.y};
+ Phase phase = Phase::eStart;
+ if (aEventPhase == nsIWidget::PHASE_UPDATE) {
+ phase = Phase::eMiddle;
+ }
+
+ if (aEventPhase == nsIWidget::PHASE_END) {
+ phase = Phase::eEnd;
+ }
+ SendPanCommon(aWindow, phase, position, aDeltaX, aDeltaY, aModifierFlags,
+ /* aIsInertia = */ false);
+}
+
+} // namespace widget
+} // namespace mozilla