michael@0: /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: // Moz headers (alphabetical) michael@0: #include "MetroInput.h" michael@0: #include "MetroUtils.h" // Logging, POINT_CEIL_*, ActivateGenericInstance, etc michael@0: #include "MetroWidget.h" // MetroInput::mWidget michael@0: #include "mozilla/dom/Touch.h" // Touch michael@0: #include "nsTArray.h" // Touch lists michael@0: #include "nsIDOMSimpleGestureEvent.h" // Constants for gesture events michael@0: #include "InputData.h" michael@0: #include "UIABridgePrivate.h" michael@0: #include "MetroAppShell.h" michael@0: #include "mozilla/EventStateManager.h" michael@0: #include "mozilla/EventStates.h" michael@0: #include "mozilla/MouseEvents.h" michael@0: #include "mozilla/TouchEvents.h" michael@0: #include "mozilla/Preferences.h" // for Preferences michael@0: #include "WinUtils.h" michael@0: #include "nsIPresShell.h" michael@0: michael@0: // System headers (alphabetical) michael@0: #include // ABI::Window::UI::Core namespace michael@0: #include // ABI::Window::UI::Input namespace michael@0: michael@0: //#define DEBUG_INPUT michael@0: michael@0: // Using declarations michael@0: using namespace ABI::Windows; // UI, System, Foundation namespaces michael@0: using namespace Microsoft; // WRL namespace (ComPtr, possibly others) michael@0: using namespace mozilla; michael@0: using namespace mozilla::widget; michael@0: using namespace mozilla::widget::winrt; michael@0: using namespace mozilla::dom; michael@0: michael@0: // File-scoped statics (unnamed namespace) michael@0: namespace { michael@0: // XXX: Set these min values appropriately michael@0: const double SWIPE_MIN_DISTANCE = 5.0; michael@0: const double SWIPE_MIN_VELOCITY = 5.0; michael@0: michael@0: // Convenience typedefs for event handler types michael@0: typedef Foundation::__FITypedEventHandler_2_Windows__CUI__CInput__CEdgeGesture_Windows__CUI__CInput__CEdgeGestureEventArgs_t EdgeGestureHandler; michael@0: typedef Foundation::__FITypedEventHandler_2_Windows__CUI__CCore__CCoreDispatcher_Windows__CUI__CCore__CAcceleratorKeyEventArgs_t AcceleratorKeyActivatedHandler; michael@0: typedef Foundation::__FITypedEventHandler_2_Windows__CUI__CCore__CCoreWindow_Windows__CUI__CCore__CPointerEventArgs_t PointerEventHandler; michael@0: typedef Foundation::__FITypedEventHandler_2_Windows__CUI__CInput__CGestureRecognizer_Windows__CUI__CInput__CTappedEventArgs_t TappedEventHandler; michael@0: typedef Foundation::__FITypedEventHandler_2_Windows__CUI__CInput__CGestureRecognizer_Windows__CUI__CInput__CRightTappedEventArgs_t RightTappedEventHandler; michael@0: typedef Foundation::__FITypedEventHandler_2_Windows__CUI__CInput__CGestureRecognizer_Windows__CUI__CInput__CManipulationStartedEventArgs_t ManipulationStartedEventHandler; michael@0: typedef Foundation::__FITypedEventHandler_2_Windows__CUI__CInput__CGestureRecognizer_Windows__CUI__CInput__CManipulationUpdatedEventArgs_t ManipulationUpdatedEventHandler; michael@0: typedef Foundation::__FITypedEventHandler_2_Windows__CUI__CInput__CGestureRecognizer_Windows__CUI__CInput__CManipulationCompletedEventArgs_t ManipulationCompletedEventHandler; michael@0: michael@0: // Other convenience typedefs michael@0: typedef ABI::Windows::UI::Core::ICoreAcceleratorKeys ICoreAcceleratorKeys; michael@0: michael@0: /** michael@0: * Specifies whether touch-action property is in force. michael@0: */ michael@0: static bool gTouchActionPropertyEnabled = false; michael@0: michael@0: /** michael@0: * Creates and returns a new {@link Touch} from the given michael@0: * ABI::Windows::UI::Input::IPointerPoint. Note that the caller is michael@0: * responsible for freeing the memory for the Touch returned from michael@0: * this function. michael@0: * michael@0: * @param aPoint the ABI::Windows::UI::Input::IPointerPoint containing the michael@0: * metadata from which to create our new {@link Touch} michael@0: * @return a new {@link Touch} representing the touch point. The caller michael@0: * is responsible for freeing the memory for this touch point. michael@0: */ michael@0: Touch* michael@0: CreateDOMTouch(UI::Input::IPointerPoint* aPoint) { michael@0: WRL::ComPtr props; michael@0: Foundation::Point position; michael@0: uint32_t pointerId; michael@0: Foundation::Rect contactRect; michael@0: float pressure; michael@0: float tiltX; michael@0: float tiltY; michael@0: michael@0: aPoint->get_Properties(props.GetAddressOf()); michael@0: aPoint->get_Position(&position); michael@0: aPoint->get_PointerId(&pointerId); michael@0: props->get_ContactRect(&contactRect); michael@0: props->get_Pressure(&pressure); michael@0: props->get_XTilt(&tiltX); michael@0: props->get_YTilt(&tiltY); michael@0: michael@0: nsIntPoint touchPoint = MetroUtils::LogToPhys(position); michael@0: nsIntPoint touchRadius; michael@0: touchRadius.x = WinUtils::LogToPhys(contactRect.Width) / 2; michael@0: touchRadius.y = WinUtils::LogToPhys(contactRect.Height) / 2; michael@0: Touch* touch = michael@0: new Touch(pointerId, michael@0: touchPoint, michael@0: // Rotation radius and angle. michael@0: // W3C touch events v1 do not use these. michael@0: // The draft for W3C touch events v2 explains that michael@0: // radius and angle should describe the ellipse that michael@0: // most closely circumscribes the touching area. Since michael@0: // Windows gives us a bounding rectangle rather than an michael@0: // ellipse, we provide the ellipse that is most closely michael@0: // circumscribed by the bounding rectangle that Windows michael@0: // gave us. michael@0: touchRadius, michael@0: 0.0f, michael@0: // Pressure michael@0: // W3C touch events v1 do not use this. michael@0: // The current draft for W3C touch events v2 says that michael@0: // this should be a value between 0.0 and 1.0, which is michael@0: // consistent with what Windows provides us here. michael@0: // XXX: Windows defaults to 0.5, but the current W3C michael@0: // draft says that the value should be 0.0 if no value michael@0: // known. michael@0: pressure); michael@0: touch->tiltX = tiltX; michael@0: touch->tiltY = tiltY; michael@0: return touch; michael@0: } michael@0: michael@0: /** michael@0: * Test if a touchpoint position has moved. See Touch.Equals for michael@0: * criteria. michael@0: * michael@0: * @param aTouch previous touch point michael@0: * @param aPoint new winrt touch point michael@0: * @return true if the point has moved michael@0: */ michael@0: bool michael@0: HasPointMoved(Touch* aTouch, UI::Input::IPointerPoint* aPoint) { michael@0: WRL::ComPtr props; michael@0: Foundation::Point position; michael@0: Foundation::Rect contactRect; michael@0: float pressure; michael@0: michael@0: aPoint->get_Properties(props.GetAddressOf()); michael@0: aPoint->get_Position(&position); michael@0: props->get_ContactRect(&contactRect); michael@0: props->get_Pressure(&pressure); michael@0: nsIntPoint touchPoint = MetroUtils::LogToPhys(position); michael@0: nsIntPoint touchRadius; michael@0: touchRadius.x = WinUtils::LogToPhys(contactRect.Width) / 2; michael@0: touchRadius.y = WinUtils::LogToPhys(contactRect.Height) / 2; michael@0: michael@0: // from Touch.Equals michael@0: return touchPoint != aTouch->mRefPoint || michael@0: pressure != aTouch->Force() || michael@0: /* mRotationAngle == aTouch->RotationAngle() || */ michael@0: touchRadius.x != aTouch->RadiusX() || michael@0: touchRadius.y != aTouch->RadiusY(); michael@0: } michael@0: michael@0: /** michael@0: * Converts from the Devices::Input::PointerDeviceType enumeration michael@0: * to a nsIDOMMouseEvent::MOZ_SOURCE_* value. michael@0: * michael@0: * @param aDeviceType the value to convert michael@0: * @param aMozInputSource the converted value michael@0: */ michael@0: void michael@0: MozInputSourceFromDeviceType( michael@0: Devices::Input::PointerDeviceType const& aDeviceType, michael@0: unsigned short& aMozInputSource) { michael@0: if (Devices::Input::PointerDeviceType::PointerDeviceType_Mouse michael@0: == aDeviceType) { michael@0: aMozInputSource = nsIDOMMouseEvent::MOZ_SOURCE_MOUSE; michael@0: } else if (Devices::Input::PointerDeviceType::PointerDeviceType_Touch michael@0: == aDeviceType) { michael@0: aMozInputSource = nsIDOMMouseEvent::MOZ_SOURCE_TOUCH; michael@0: } else if (Devices::Input::PointerDeviceType::PointerDeviceType_Pen michael@0: == aDeviceType) { michael@0: aMozInputSource = nsIDOMMouseEvent::MOZ_SOURCE_PEN; michael@0: } michael@0: } michael@0: michael@0: int16_t michael@0: ButtonsForPointerPoint(UI::Input::IPointerPoint* aPoint) { michael@0: WRL::ComPtr props; michael@0: aPoint->get_Properties(props.GetAddressOf()); michael@0: michael@0: int16_t buttons = 0; michael@0: boolean buttonPressed; michael@0: michael@0: props->get_IsLeftButtonPressed(&buttonPressed); michael@0: if (buttonPressed) { michael@0: buttons |= WidgetMouseEvent::eLeftButtonFlag; michael@0: } michael@0: props->get_IsMiddleButtonPressed(&buttonPressed); michael@0: if (buttonPressed) { michael@0: buttons |= WidgetMouseEvent::eMiddleButtonFlag; michael@0: } michael@0: props->get_IsRightButtonPressed(&buttonPressed); michael@0: if (buttonPressed) { michael@0: buttons |= WidgetMouseEvent::eRightButtonFlag; michael@0: } michael@0: props->get_IsXButton1Pressed(&buttonPressed); michael@0: if (buttonPressed) { michael@0: buttons |= WidgetMouseEvent::e4thButtonFlag; michael@0: } michael@0: props->get_IsXButton2Pressed(&buttonPressed); michael@0: if (buttonPressed) { michael@0: buttons |= WidgetMouseEvent::e5thButtonFlag; michael@0: } michael@0: return buttons; michael@0: } michael@0: michael@0: /** michael@0: * This function is for use with mTouches.Enumerate. It will michael@0: * append each element it encounters to the {@link nsTArray} michael@0: * of {@link mozilla::dom::Touch}es passed in through the third (void*) michael@0: * parameter. michael@0: * michael@0: * NOTE: This function will set the `mChanged` member of each michael@0: * element it encounters to `false`, since this function is only michael@0: * used to populate a touchlist that is about to be dispatched michael@0: * in a gecko touch event. michael@0: * michael@0: * @param aKey the key of the current element being enumerated michael@0: * @param aData the value of the current element being enumerated michael@0: * @param aTouchList the {@link nsTArray} to append to michael@0: */ michael@0: PLDHashOperator michael@0: AppendToTouchList(const unsigned int& aKey, michael@0: nsRefPtr& aData, michael@0: void *aTouchList) michael@0: { michael@0: nsTArray > *touches = michael@0: static_cast > *>(aTouchList); michael@0: nsRefPtr copy = new Touch(aData->mIdentifier, michael@0: aData->mRefPoint, michael@0: aData->mRadius, michael@0: aData->mRotationAngle, michael@0: aData->mForce); michael@0: copy->tiltX = aData->tiltX; michael@0: copy->tiltY = aData->tiltY; michael@0: touches->AppendElement(copy); michael@0: aData->mChanged = false; michael@0: return PL_DHASH_NEXT; michael@0: } michael@0: michael@0: // Helper for making sure event ptrs get freed. michael@0: class AutoDeleteEvent michael@0: { michael@0: public: michael@0: AutoDeleteEvent(WidgetGUIEvent* aPtr) : michael@0: mPtr(aPtr) {} michael@0: ~AutoDeleteEvent() { michael@0: if (mPtr) { michael@0: delete mPtr; michael@0: } michael@0: } michael@0: WidgetGUIEvent* mPtr; michael@0: }; michael@0: } michael@0: michael@0: namespace mozilla { michael@0: namespace widget { michael@0: namespace winrt { michael@0: michael@0: MetroInput::InputPrecisionLevel MetroInput::sCurrentInputLevel = michael@0: MetroInput::InputPrecisionLevel::LEVEL_IMPRECISE; michael@0: michael@0: MetroInput::MetroInput(MetroWidget* aWidget, michael@0: UI::Core::ICoreWindow* aWindow) michael@0: : mWidget(aWidget), michael@0: mNonApzTargetForTouch(false), michael@0: mWindow(aWindow) michael@0: { michael@0: LogFunction(); michael@0: NS_ASSERTION(aWidget, "Attempted to create MetroInput for null widget!"); michael@0: NS_ASSERTION(aWindow, "Attempted to create MetroInput for null window!"); michael@0: michael@0: Preferences::AddBoolVarCache(&gTouchActionPropertyEnabled, "layout.css.touch_action.enabled", gTouchActionPropertyEnabled); michael@0: mTokenPointerPressed.value = 0; michael@0: mTokenPointerReleased.value = 0; michael@0: mTokenPointerMoved.value = 0; michael@0: mTokenPointerEntered.value = 0; michael@0: mTokenPointerExited.value = 0; michael@0: mTokenEdgeStarted.value = 0; michael@0: mTokenEdgeCanceled.value = 0; michael@0: mTokenEdgeCompleted.value = 0; michael@0: mTokenManipulationCompleted.value = 0; michael@0: mTokenTapped.value = 0; michael@0: mTokenRightTapped.value = 0; michael@0: michael@0: // Create our Gesture Recognizer michael@0: ActivateGenericInstance(RuntimeClass_Windows_UI_Input_GestureRecognizer, michael@0: mGestureRecognizer); michael@0: NS_ASSERTION(mGestureRecognizer, "Failed to create GestureRecognizer!"); michael@0: michael@0: RegisterInputEvents(); michael@0: } michael@0: michael@0: MetroInput::~MetroInput() michael@0: { michael@0: LogFunction(); michael@0: UnregisterInputEvents(); michael@0: } michael@0: michael@0: /* static */ michael@0: bool MetroInput::IsInputModeImprecise() michael@0: { michael@0: return sCurrentInputLevel == LEVEL_IMPRECISE; michael@0: } michael@0: michael@0: /** michael@0: * Tracks the current input level (precise/imprecise) and fires an observer michael@0: * when the mode changes. michael@0: */ michael@0: void michael@0: MetroInput::UpdateInputLevel(InputPrecisionLevel aInputLevel) michael@0: { michael@0: // ignore mouse input if we have active touch input. michael@0: if (aInputLevel == LEVEL_PRECISE && mTouches.Count() > 0) { michael@0: return; michael@0: } michael@0: if (sCurrentInputLevel != aInputLevel) { michael@0: sCurrentInputLevel = aInputLevel; michael@0: MetroUtils::FireObserver(sCurrentInputLevel == LEVEL_PRECISE ? michael@0: "metro_precise_input" : "metro_imprecise_input"); michael@0: } michael@0: } michael@0: michael@0: /** michael@0: * Processes an IEdgeGestureEventArgs and returns the input source type michael@0: * for the event. Also updates input level via UpdateInputLevel. michael@0: */ michael@0: uint16_t michael@0: MetroInput::ProcessInputTypeForGesture(UI::Input::IEdgeGestureEventArgs* aArgs) michael@0: { michael@0: MOZ_ASSERT(aArgs); michael@0: UI::Input::EdgeGestureKind kind; michael@0: aArgs->get_Kind(&kind); michael@0: switch(kind) { michael@0: case UI::Input::EdgeGestureKind::EdgeGestureKind_Touch: michael@0: UpdateInputLevel(LEVEL_IMPRECISE); michael@0: return nsIDOMMouseEvent::MOZ_SOURCE_TOUCH; michael@0: break; michael@0: case UI::Input::EdgeGestureKind::EdgeGestureKind_Keyboard: michael@0: return nsIDOMMouseEvent::MOZ_SOURCE_KEYBOARD; michael@0: break; michael@0: case UI::Input::EdgeGestureKind::EdgeGestureKind_Mouse: michael@0: UpdateInputLevel(LEVEL_PRECISE); michael@0: return nsIDOMMouseEvent::MOZ_SOURCE_MOUSE; michael@0: break; michael@0: } michael@0: return nsIDOMMouseEvent::MOZ_SOURCE_UNKNOWN; michael@0: } michael@0: michael@0: /** michael@0: * When the user swipes her/his finger in from the top of the screen, michael@0: * we receive this event. michael@0: * michael@0: * @param sender the CoreDispatcher that fired this event michael@0: * @param aArgs the event-specific args we use when processing this event michael@0: * @returns S_OK michael@0: */ michael@0: HRESULT michael@0: MetroInput::OnEdgeGestureStarted(UI::Input::IEdgeGesture* sender, michael@0: UI::Input::IEdgeGestureEventArgs* aArgs) michael@0: { michael@0: #ifdef DEBUG_INPUT michael@0: LogFunction(); michael@0: #endif michael@0: WidgetSimpleGestureEvent geckoEvent(true, michael@0: NS_SIMPLE_GESTURE_EDGE_STARTED, michael@0: mWidget.Get()); michael@0: mModifierKeyState.Update(); michael@0: mModifierKeyState.InitInputEvent(geckoEvent); michael@0: geckoEvent.time = ::GetMessageTime(); michael@0: geckoEvent.inputSource = ProcessInputTypeForGesture(aArgs); michael@0: michael@0: // Safe michael@0: DispatchEventIgnoreStatus(&geckoEvent); michael@0: return S_OK; michael@0: } michael@0: michael@0: /** michael@0: * This event can be received if the user swipes her/his finger back to michael@0: * the top of the screen, or continues moving her/his finger such that michael@0: * the movement is interpreted as a "grab this window" gesture michael@0: * michael@0: * @param sender the CoreDispatcher that fired this event michael@0: * @param aArgs the event-specific args we use when processing this event michael@0: * @returns S_OK michael@0: */ michael@0: HRESULT michael@0: MetroInput::OnEdgeGestureCanceled(UI::Input::IEdgeGesture* sender, michael@0: UI::Input::IEdgeGestureEventArgs* aArgs) michael@0: { michael@0: #ifdef DEBUG_INPUT michael@0: LogFunction(); michael@0: #endif michael@0: WidgetSimpleGestureEvent geckoEvent(true, michael@0: NS_SIMPLE_GESTURE_EDGE_CANCELED, michael@0: mWidget.Get()); michael@0: mModifierKeyState.Update(); michael@0: mModifierKeyState.InitInputEvent(geckoEvent); michael@0: geckoEvent.time = ::GetMessageTime(); michael@0: geckoEvent.inputSource = ProcessInputTypeForGesture(aArgs); michael@0: michael@0: // Safe michael@0: DispatchEventIgnoreStatus(&geckoEvent); michael@0: return S_OK; michael@0: } michael@0: michael@0: /** michael@0: * This event is received if the user presses ctrl+Z or lifts her/his michael@0: * finger after causing an EdgeGestureStarting event to fire. michael@0: * michael@0: * @param sender the CoreDispatcher that fired this event michael@0: * @param aArgs the event-specific args we use when processing this event michael@0: * @returns S_OK michael@0: */ michael@0: HRESULT michael@0: MetroInput::OnEdgeGestureCompleted(UI::Input::IEdgeGesture* sender, michael@0: UI::Input::IEdgeGestureEventArgs* aArgs) michael@0: { michael@0: #ifdef DEBUG_INPUT michael@0: LogFunction(); michael@0: #endif michael@0: WidgetSimpleGestureEvent geckoEvent(true, michael@0: NS_SIMPLE_GESTURE_EDGE_COMPLETED, michael@0: mWidget.Get()); michael@0: mModifierKeyState.Update(); michael@0: mModifierKeyState.InitInputEvent(geckoEvent); michael@0: geckoEvent.time = ::GetMessageTime(); michael@0: geckoEvent.inputSource = ProcessInputTypeForGesture(aArgs); michael@0: michael@0: // Safe michael@0: DispatchEventIgnoreStatus(&geckoEvent); michael@0: return S_OK; michael@0: } michael@0: michael@0: /** michael@0: * This helper function is used by our processing of PointerPressed, michael@0: * PointerReleased, and PointerMoved events. michael@0: * It dispatches a gecko event in response to the input received. This michael@0: * function should only be called for non-touch (i.e. pen or mouse) input michael@0: * events. michael@0: * michael@0: * @param aPoint the PointerPoint for the input event michael@0: */ michael@0: void michael@0: MetroInput::OnPointerNonTouch(UI::Input::IPointerPoint* aPoint) { michael@0: WRL::ComPtr props; michael@0: UI::Input::PointerUpdateKind pointerUpdateKind; michael@0: michael@0: aPoint->get_Properties(props.GetAddressOf()); michael@0: props->get_PointerUpdateKind(&pointerUpdateKind); michael@0: michael@0: uint32_t message = NS_MOUSE_MOVE; michael@0: int16_t button = 0; michael@0: michael@0: switch (pointerUpdateKind) { michael@0: case UI::Input::PointerUpdateKind::PointerUpdateKind_LeftButtonPressed: michael@0: button = WidgetMouseEvent::buttonType::eLeftButton; michael@0: message = NS_MOUSE_BUTTON_DOWN; michael@0: break; michael@0: case UI::Input::PointerUpdateKind::PointerUpdateKind_MiddleButtonPressed: michael@0: button = WidgetMouseEvent::buttonType::eMiddleButton; michael@0: message = NS_MOUSE_BUTTON_DOWN; michael@0: break; michael@0: case UI::Input::PointerUpdateKind::PointerUpdateKind_RightButtonPressed: michael@0: button = WidgetMouseEvent::buttonType::eRightButton; michael@0: message = NS_MOUSE_BUTTON_DOWN; michael@0: break; michael@0: case UI::Input::PointerUpdateKind::PointerUpdateKind_LeftButtonReleased: michael@0: button = WidgetMouseEvent::buttonType::eLeftButton; michael@0: message = NS_MOUSE_BUTTON_UP; michael@0: break; michael@0: case UI::Input::PointerUpdateKind::PointerUpdateKind_MiddleButtonReleased: michael@0: button = WidgetMouseEvent::buttonType::eMiddleButton; michael@0: message = NS_MOUSE_BUTTON_UP; michael@0: break; michael@0: case UI::Input::PointerUpdateKind::PointerUpdateKind_RightButtonReleased: michael@0: button = WidgetMouseEvent::buttonType::eRightButton; michael@0: message = NS_MOUSE_BUTTON_UP; michael@0: break; michael@0: } michael@0: michael@0: UpdateInputLevel(LEVEL_PRECISE); michael@0: michael@0: WidgetMouseEvent* event = michael@0: new WidgetMouseEvent(true, message, mWidget.Get(), michael@0: WidgetMouseEvent::eReal, michael@0: WidgetMouseEvent::eNormal); michael@0: event->button = button; michael@0: aPoint->get_PointerId(&event->pointerId); michael@0: InitGeckoMouseEventFromPointerPoint(event, aPoint); michael@0: DispatchAsyncEventIgnoreStatus(event); michael@0: } michael@0: michael@0: void michael@0: MetroInput::InitTouchEventTouchList(WidgetTouchEvent* aEvent) michael@0: { michael@0: MOZ_ASSERT(aEvent); michael@0: mTouches.Enumerate(&AppendToTouchList, michael@0: static_cast(&aEvent->touches)); michael@0: } michael@0: michael@0: bool michael@0: MetroInput::ShouldDeliverInputToRecognizer() michael@0: { michael@0: return mRecognizerWantsEvents; michael@0: } michael@0: michael@0: void michael@0: MetroInput::GetAllowedTouchBehavior(WidgetTouchEvent* aTransformedEvent, nsTArray& aOutBehaviors) michael@0: { michael@0: mWidget->ApzcGetAllowedTouchBehavior(aTransformedEvent, aOutBehaviors); michael@0: michael@0: for (uint32_t i = 0; i < aOutBehaviors.Length(); i++) { michael@0: if (aOutBehaviors[i] & AllowedTouchBehavior::UNKNOWN) { michael@0: // performing hit testing fallback: asking content to perform hit testing itself michael@0: // (in spite that this operation has high latency). michael@0: aOutBehaviors[i] = mWidget->ContentGetAllowedTouchBehavior(aTransformedEvent->touches[i]->mRefPoint); michael@0: } michael@0: } michael@0: } michael@0: michael@0: bool michael@0: MetroInput::IsTouchBehaviorForbidden(const nsTArray& aTouchBehaviors) michael@0: { michael@0: for (size_t i = 0; i < aTouchBehaviors.Length(); i++) { michael@0: if (aTouchBehaviors[i] == AllowedTouchBehavior::NONE) michael@0: return true; michael@0: } michael@0: michael@0: return false; michael@0: } michael@0: michael@0: // This event is raised when the user pushes the left mouse button, presses a michael@0: // pen to the surface, or presses a touch screen. michael@0: HRESULT michael@0: MetroInput::OnPointerPressed(UI::Core::ICoreWindow* aSender, michael@0: UI::Core::IPointerEventArgs* aArgs) michael@0: { michael@0: #ifdef DEBUG_INPUT michael@0: LogFunction(); michael@0: #endif michael@0: michael@0: WRL::ComPtr currentPoint; michael@0: WRL::ComPtr device; michael@0: Devices::Input::PointerDeviceType deviceType; michael@0: michael@0: aArgs->get_CurrentPoint(currentPoint.GetAddressOf()); michael@0: currentPoint->get_PointerDevice(device.GetAddressOf()); michael@0: device->get_PointerDeviceType(&deviceType); michael@0: michael@0: // For mouse and pen input, simply call our helper function michael@0: if (deviceType != michael@0: Devices::Input::PointerDeviceType::PointerDeviceType_Touch) { michael@0: OnPointerNonTouch(currentPoint.Get()); michael@0: mGestureRecognizer->ProcessDownEvent(currentPoint.Get()); michael@0: return S_OK; michael@0: } michael@0: michael@0: // This is touch input. michael@0: UpdateInputLevel(LEVEL_IMPRECISE); michael@0: michael@0: // Create the new touch point and add it to our event. michael@0: uint32_t pointerId; michael@0: currentPoint->get_PointerId(&pointerId); michael@0: nsRefPtr touch = CreateDOMTouch(currentPoint.Get()); michael@0: touch->mChanged = true; michael@0: mTouches.Put(pointerId, touch); michael@0: michael@0: WidgetTouchEvent* touchEvent = michael@0: new WidgetTouchEvent(true, NS_TOUCH_START, mWidget.Get()); michael@0: michael@0: if (mTouches.Count() == 1) { michael@0: // If this is the first touchstart of a touch session reset some michael@0: // tracking flags. michael@0: mContentConsumingTouch = false; michael@0: mApzConsumingTouch = false; michael@0: mRecognizerWantsEvents = true; michael@0: mCancelable = true; michael@0: mCanceledIds.Clear(); michael@0: } else { michael@0: mCancelable = false; michael@0: } michael@0: michael@0: InitTouchEventTouchList(touchEvent); michael@0: DispatchAsyncTouchEvent(touchEvent); michael@0: michael@0: if (ShouldDeliverInputToRecognizer()) { michael@0: mGestureRecognizer->ProcessDownEvent(currentPoint.Get()); michael@0: } michael@0: return S_OK; michael@0: } michael@0: michael@0: void michael@0: MetroInput::AddPointerMoveDataToRecognizer(UI::Core::IPointerEventArgs* aArgs) michael@0: { michael@0: if (ShouldDeliverInputToRecognizer()) { michael@0: WRL::ComPtr> michael@0: pointerPoints; michael@0: aArgs->GetIntermediatePoints(pointerPoints.GetAddressOf()); michael@0: mGestureRecognizer->ProcessMoveEvents(pointerPoints.Get()); michael@0: } michael@0: } michael@0: michael@0: // This event is raised when the user moves the mouse, moves a pen that is michael@0: // in contact with the surface, or moves a finger that is in contact with michael@0: // a touch screen. michael@0: HRESULT michael@0: MetroInput::OnPointerMoved(UI::Core::ICoreWindow* aSender, michael@0: UI::Core::IPointerEventArgs* aArgs) michael@0: { michael@0: #ifdef DEBUG_INPUT michael@0: LogFunction(); michael@0: #endif michael@0: michael@0: WRL::ComPtr currentPoint; michael@0: WRL::ComPtr device; michael@0: Devices::Input::PointerDeviceType deviceType; michael@0: michael@0: aArgs->get_CurrentPoint(currentPoint.GetAddressOf()); michael@0: currentPoint->get_PointerDevice(device.GetAddressOf()); michael@0: device->get_PointerDeviceType(&deviceType); michael@0: michael@0: // For mouse and pen input, simply call our helper function michael@0: if (deviceType != michael@0: Devices::Input::PointerDeviceType::PointerDeviceType_Touch) { michael@0: OnPointerNonTouch(currentPoint.Get()); michael@0: AddPointerMoveDataToRecognizer(aArgs); michael@0: return S_OK; michael@0: } michael@0: michael@0: // This is touch input. michael@0: UpdateInputLevel(LEVEL_IMPRECISE); michael@0: michael@0: // Get the touch associated with this touch point. michael@0: uint32_t pointerId; michael@0: currentPoint->get_PointerId(&pointerId); michael@0: nsRefPtr touch = mTouches.Get(pointerId); michael@0: michael@0: // Some old drivers cause us to receive a PointerMoved event for a touchId michael@0: // after we've already received a PointerReleased event for that touchId. michael@0: // To work around those busted drivers, we simply ignore TouchMoved events michael@0: // for touchIds that we are not currently tracking. See bug 819223. michael@0: if (!touch) { michael@0: return S_OK; michael@0: } michael@0: michael@0: AddPointerMoveDataToRecognizer(aArgs); michael@0: michael@0: // If the point hasn't moved, filter it out per the spec. Pres shell does michael@0: // this as well, but we need to know when our first touchmove is going to michael@0: // get delivered so we can check the result. michael@0: if (!HasPointMoved(touch, currentPoint.Get())) { michael@0: return S_OK; michael@0: } michael@0: michael@0: touch = CreateDOMTouch(currentPoint.Get()); michael@0: touch->mChanged = true; michael@0: // replacing old touch point in mTouches map michael@0: mTouches.Put(pointerId, touch); michael@0: michael@0: WidgetTouchEvent* touchEvent = michael@0: new WidgetTouchEvent(true, NS_TOUCH_MOVE, mWidget.Get()); michael@0: InitTouchEventTouchList(touchEvent); michael@0: DispatchAsyncTouchEvent(touchEvent); michael@0: michael@0: return S_OK; michael@0: } michael@0: michael@0: // This event is raised when the user lifts the left mouse button, lifts a michael@0: // pen from the surface, or lifts her/his finger from a touch screen. michael@0: HRESULT michael@0: MetroInput::OnPointerReleased(UI::Core::ICoreWindow* aSender, michael@0: UI::Core::IPointerEventArgs* aArgs) michael@0: { michael@0: #ifdef DEBUG_INPUT michael@0: LogFunction(); michael@0: #endif michael@0: michael@0: WRL::ComPtr currentPoint; michael@0: WRL::ComPtr device; michael@0: Devices::Input::PointerDeviceType deviceType; michael@0: michael@0: aArgs->get_CurrentPoint(currentPoint.GetAddressOf()); michael@0: currentPoint->get_PointerDevice(device.GetAddressOf()); michael@0: device->get_PointerDeviceType(&deviceType); michael@0: michael@0: // For mouse and pen input, simply call our helper function michael@0: if (deviceType != michael@0: Devices::Input::PointerDeviceType::PointerDeviceType_Touch) { michael@0: OnPointerNonTouch(currentPoint.Get()); michael@0: mGestureRecognizer->ProcessUpEvent(currentPoint.Get()); michael@0: return S_OK; michael@0: } michael@0: michael@0: // This is touch input. michael@0: UpdateInputLevel(LEVEL_IMPRECISE); michael@0: michael@0: // Get the touch associated with this touch point. michael@0: uint32_t pointerId; michael@0: currentPoint->get_PointerId(&pointerId); michael@0: nsRefPtr touch = mTouches.Get(pointerId); michael@0: michael@0: // Purge any pending moves for this pointer michael@0: if (touch->mChanged) { michael@0: WidgetTouchEvent* touchEvent = michael@0: new WidgetTouchEvent(true, NS_TOUCH_MOVE, mWidget.Get()); michael@0: InitTouchEventTouchList(touchEvent); michael@0: DispatchAsyncTouchEvent(touchEvent); michael@0: } michael@0: michael@0: // Remove this touch point from our map. Eventually all touch points are michael@0: // removed for this session since we receive released events for every michael@0: // point. michael@0: mTouches.Remove(pointerId); michael@0: michael@0: // touchend events only have a single touch; the touch that has been removed michael@0: WidgetTouchEvent* touchEvent = michael@0: new WidgetTouchEvent(true, NS_TOUCH_END, mWidget.Get()); michael@0: touchEvent->touches.AppendElement(CreateDOMTouch(currentPoint.Get())); michael@0: DispatchAsyncTouchEvent(touchEvent); michael@0: michael@0: if (ShouldDeliverInputToRecognizer()) { michael@0: mGestureRecognizer->ProcessUpEvent(currentPoint.Get()); michael@0: } michael@0: michael@0: return S_OK; michael@0: } michael@0: michael@0: // Tests for chrome vs. content target so we know whether input coordinates need michael@0: // to be transformed through the apz. Eventually this hit testing should move michael@0: // into the apz (bug 918288). michael@0: bool michael@0: MetroInput::HitTestChrome(const LayoutDeviceIntPoint& pt) michael@0: { michael@0: // Confirm this event targets content. We pick this up in browser's input.js. michael@0: WidgetMouseEvent hittest(true, NS_MOUSE_MOZHITTEST, mWidget.Get(), michael@0: WidgetMouseEvent::eReal, WidgetMouseEvent::eNormal); michael@0: hittest.refPoint = pt; michael@0: nsEventStatus status; michael@0: mWidget->DispatchEvent(&hittest, status); michael@0: return (status == nsEventStatus_eConsumeNoDefault); michael@0: } michael@0: michael@0: /** michael@0: * Returns true if the position is in chrome, false otherwise. michael@0: */ michael@0: bool michael@0: MetroInput::TransformRefPoint(const Foundation::Point& aPosition, LayoutDeviceIntPoint& aRefPointOut) michael@0: { michael@0: // If this event is destined for content we need to transform our ref point through michael@0: // the apz so that zoom can be accounted for. michael@0: aRefPointOut = LayoutDeviceIntPoint::FromUntyped(MetroUtils::LogToPhys(aPosition)); michael@0: ScreenIntPoint spt; michael@0: spt.x = aRefPointOut.x; michael@0: spt.y = aRefPointOut.y; michael@0: // This is currently a general contained rect hit test, it may produce a false positive for michael@0: // overlay chrome elements. michael@0: bool apzIntersect = mWidget->ApzHitTest(spt); michael@0: if (!apzIntersect) { michael@0: return true; michael@0: } michael@0: if (HitTestChrome(aRefPointOut)) { michael@0: return true; michael@0: } michael@0: mWidget->ApzTransformGeckoCoordinate(spt, &aRefPointOut); michael@0: return false; michael@0: } michael@0: michael@0: void michael@0: MetroInput::TransformTouchEvent(WidgetTouchEvent* aEvent) michael@0: { michael@0: nsTArray< nsRefPtr >& touches = aEvent->touches; michael@0: for (uint32_t i = 0; i < touches.Length(); ++i) { michael@0: dom::Touch* touch = touches[i]; michael@0: if (touch) { michael@0: LayoutDeviceIntPoint lpt; michael@0: ScreenIntPoint spt; michael@0: spt.x = touch->mRefPoint.x; michael@0: spt.y = touch->mRefPoint.y; michael@0: mWidget->ApzTransformGeckoCoordinate(spt, &lpt); michael@0: touch->mRefPoint.x = lpt.x; michael@0: touch->mRefPoint.y = lpt.y; michael@0: } michael@0: } michael@0: } michael@0: michael@0: void michael@0: MetroInput::InitGeckoMouseEventFromPointerPoint( michael@0: WidgetMouseEvent* aEvent, michael@0: UI::Input::IPointerPoint* aPointerPoint) michael@0: { michael@0: NS_ASSERTION(aPointerPoint, "InitGeckoMouseEventFromPointerPoint " michael@0: "called with null PointerPoint!"); michael@0: michael@0: WRL::ComPtr props; michael@0: WRL::ComPtr device; michael@0: Devices::Input::PointerDeviceType deviceType; michael@0: Foundation::Point position; michael@0: uint64_t timestamp; michael@0: float pressure; michael@0: boolean canBeDoubleTap; michael@0: float tiltX; michael@0: float tiltY; michael@0: michael@0: aPointerPoint->get_Position(&position); michael@0: aPointerPoint->get_Timestamp(×tamp); michael@0: aPointerPoint->get_PointerDevice(device.GetAddressOf()); michael@0: device->get_PointerDeviceType(&deviceType); michael@0: aPointerPoint->get_Properties(props.GetAddressOf()); michael@0: aPointerPoint->get_PointerId(&aEvent->pointerId); michael@0: props->get_Pressure(&pressure); michael@0: props->get_XTilt(&tiltX); michael@0: props->get_YTilt(&tiltY); michael@0: michael@0: mGestureRecognizer->CanBeDoubleTap(aPointerPoint, &canBeDoubleTap); michael@0: michael@0: TransformRefPoint(position, aEvent->refPoint); michael@0: michael@0: if (!canBeDoubleTap) { michael@0: aEvent->clickCount = 1; michael@0: } else { michael@0: aEvent->clickCount = 2; michael@0: } michael@0: aEvent->pressure = pressure; michael@0: aEvent->tiltX = tiltX; michael@0: aEvent->tiltY = tiltY; michael@0: aEvent->buttons = ButtonsForPointerPoint(aPointerPoint); michael@0: michael@0: MozInputSourceFromDeviceType(deviceType, aEvent->inputSource); michael@0: } michael@0: michael@0: // This event is raised when a precise pointer moves into the bounding box of michael@0: // our window. For touch input, this will be raised before the PointerPressed michael@0: // event. michael@0: HRESULT michael@0: MetroInput::OnPointerEntered(UI::Core::ICoreWindow* aSender, michael@0: UI::Core::IPointerEventArgs* aArgs) michael@0: { michael@0: #ifdef DEBUG_INPUT michael@0: LogFunction(); michael@0: #endif michael@0: michael@0: WRL::ComPtr currentPoint; michael@0: WRL::ComPtr device; michael@0: Devices::Input::PointerDeviceType deviceType; michael@0: michael@0: aArgs->get_CurrentPoint(currentPoint.GetAddressOf()); michael@0: currentPoint->get_PointerDevice(device.GetAddressOf()); michael@0: device->get_PointerDeviceType(&deviceType); michael@0: michael@0: // We only dispatch mouseenter and mouseexit events for mouse and pen input. michael@0: if (deviceType != michael@0: Devices::Input::PointerDeviceType::PointerDeviceType_Touch) { michael@0: WidgetMouseEvent* event = michael@0: new WidgetMouseEvent(true, NS_MOUSE_ENTER, mWidget.Get(), michael@0: WidgetMouseEvent::eReal, WidgetMouseEvent::eNormal); michael@0: UpdateInputLevel(LEVEL_PRECISE); michael@0: InitGeckoMouseEventFromPointerPoint(event, currentPoint.Get()); michael@0: DispatchAsyncEventIgnoreStatus(event); michael@0: return S_OK; michael@0: } michael@0: UpdateInputLevel(LEVEL_IMPRECISE); michael@0: return S_OK; michael@0: } michael@0: michael@0: // This event is raised when a precise pointer leaves the bounding box of michael@0: // our window. For touch input, this will be raised before the michael@0: // PointerReleased event. michael@0: HRESULT michael@0: MetroInput::OnPointerExited(UI::Core::ICoreWindow* aSender, michael@0: UI::Core::IPointerEventArgs* aArgs) michael@0: { michael@0: #ifdef DEBUG_INPUT michael@0: LogFunction(); michael@0: #endif michael@0: michael@0: WRL::ComPtr currentPoint; michael@0: WRL::ComPtr device; michael@0: Devices::Input::PointerDeviceType deviceType; michael@0: michael@0: aArgs->get_CurrentPoint(currentPoint.GetAddressOf()); michael@0: currentPoint->get_PointerDevice(device.GetAddressOf()); michael@0: device->get_PointerDeviceType(&deviceType); michael@0: michael@0: // We only dispatch mouseenter and mouseexit events for mouse and pen input. michael@0: if (deviceType != michael@0: Devices::Input::PointerDeviceType::PointerDeviceType_Touch) { michael@0: WidgetMouseEvent* event = michael@0: new WidgetMouseEvent(true, NS_MOUSE_EXIT, mWidget.Get(), michael@0: WidgetMouseEvent::eReal, WidgetMouseEvent::eNormal); michael@0: UpdateInputLevel(LEVEL_PRECISE); michael@0: InitGeckoMouseEventFromPointerPoint(event, currentPoint.Get()); michael@0: DispatchAsyncEventIgnoreStatus(event); michael@0: return S_OK; michael@0: } michael@0: UpdateInputLevel(LEVEL_IMPRECISE); michael@0: return S_OK; michael@0: } michael@0: michael@0: // Gecko expects a "finished" event to be sent that has the cumulative michael@0: // changes since the gesture began. The idea is that consumers could hook michael@0: // only this last event and still effectively support magnification and michael@0: // rotation. We accomplish sending this "finished" event by calling our michael@0: // helper function with a cumulative "delta" value. michael@0: // michael@0: // After sending the "finished" event, this function detects and sends michael@0: // swipe gestures. michael@0: HRESULT michael@0: MetroInput::OnManipulationCompleted( michael@0: UI::Input::IGestureRecognizer* aSender, michael@0: UI::Input::IManipulationCompletedEventArgs* aArgs) michael@0: { michael@0: #ifdef DEBUG_INPUT michael@0: LogFunction(); michael@0: #endif michael@0: michael@0: Devices::Input::PointerDeviceType deviceType; michael@0: aArgs->get_PointerDeviceType(&deviceType); michael@0: if (deviceType == michael@0: Devices::Input::PointerDeviceType::PointerDeviceType_Mouse) { michael@0: return S_OK; michael@0: } michael@0: michael@0: UI::Input::ManipulationDelta delta; michael@0: Foundation::Point position; michael@0: michael@0: aArgs->get_Position(&position); michael@0: aArgs->get_Cumulative(&delta); michael@0: michael@0: // We check that the distance the user's finger traveled and the michael@0: // velocity with which it traveled exceed our thresholds for michael@0: // classifying the movement as a swipe. michael@0: UI::Input::ManipulationVelocities velocities; michael@0: aArgs->get_Velocities(&velocities); michael@0: michael@0: bool isHorizontalSwipe = michael@0: abs(velocities.Linear.X) >= SWIPE_MIN_VELOCITY michael@0: && abs(delta.Translation.X) >= SWIPE_MIN_DISTANCE; michael@0: bool isVerticalSwipe = michael@0: abs(velocities.Linear.Y) >= SWIPE_MIN_VELOCITY michael@0: && abs(delta.Translation.Y) >= SWIPE_MIN_DISTANCE; michael@0: michael@0: // If our thresholds were exceeded for both a vertical and a horizontal michael@0: // swipe, it means the user is flinging her/his finger around and we michael@0: // should just ignore the input. michael@0: if (isHorizontalSwipe && isVerticalSwipe) { michael@0: return S_OK; michael@0: } michael@0: michael@0: if (isHorizontalSwipe) { michael@0: WidgetSimpleGestureEvent* swipeEvent = michael@0: new WidgetSimpleGestureEvent(true, NS_SIMPLE_GESTURE_SWIPE, michael@0: mWidget.Get()); michael@0: swipeEvent->direction = delta.Translation.X > 0 michael@0: ? nsIDOMSimpleGestureEvent::DIRECTION_RIGHT michael@0: : nsIDOMSimpleGestureEvent::DIRECTION_LEFT; michael@0: swipeEvent->delta = delta.Translation.X; michael@0: swipeEvent->inputSource = nsIDOMMouseEvent::MOZ_SOURCE_TOUCH; michael@0: swipeEvent->refPoint = LayoutDeviceIntPoint::FromUntyped(MetroUtils::LogToPhys(position)); michael@0: DispatchAsyncEventIgnoreStatus(swipeEvent); michael@0: } michael@0: michael@0: if (isVerticalSwipe) { michael@0: WidgetSimpleGestureEvent* swipeEvent = michael@0: new WidgetSimpleGestureEvent(true, NS_SIMPLE_GESTURE_SWIPE, michael@0: mWidget.Get()); michael@0: swipeEvent->direction = delta.Translation.Y > 0 michael@0: ? nsIDOMSimpleGestureEvent::DIRECTION_DOWN michael@0: : nsIDOMSimpleGestureEvent::DIRECTION_UP; michael@0: swipeEvent->delta = delta.Translation.Y; michael@0: swipeEvent->inputSource = nsIDOMMouseEvent::MOZ_SOURCE_TOUCH; michael@0: swipeEvent->refPoint = LayoutDeviceIntPoint::FromUntyped(MetroUtils::LogToPhys(position)); michael@0: DispatchAsyncEventIgnoreStatus(swipeEvent); michael@0: } michael@0: michael@0: return S_OK; michael@0: } michael@0: michael@0: // This event is raised when a sequence of pointer events has been michael@0: // interpreted by the GestureRecognizer as a tap (this could be a mouse michael@0: // click, a pen tap, or a tap on a touch surface). michael@0: HRESULT michael@0: MetroInput::OnTapped(UI::Input::IGestureRecognizer* aSender, michael@0: UI::Input::ITappedEventArgs* aArgs) michael@0: { michael@0: #ifdef DEBUG_INPUT michael@0: LogFunction(); michael@0: #endif michael@0: michael@0: Devices::Input::PointerDeviceType deviceType; michael@0: aArgs->get_PointerDeviceType(&deviceType); michael@0: michael@0: unsigned int tapCount; michael@0: aArgs->get_TapCount(&tapCount); michael@0: michael@0: // For mouse and pen input, we send mousedown/mouseup/mousemove michael@0: // events as soon as we detect the input event. For touch input, a set of michael@0: // mousedown/mouseup events will be sent only once a tap has been detected. michael@0: if (deviceType != Devices::Input::PointerDeviceType::PointerDeviceType_Touch) { michael@0: return S_OK; michael@0: } michael@0: michael@0: Foundation::Point position; michael@0: aArgs->get_Position(&position); michael@0: HandleTap(position, tapCount); michael@0: return S_OK; michael@0: } michael@0: michael@0: // This event is raised when a sequence of pointer events has been michael@0: // interpreted by the GestureRecognizer as a right tap. michael@0: // This could be a mouse right-click, a right-click on a pen, or michael@0: // a tap-and-hold on a touch surface. michael@0: HRESULT michael@0: MetroInput::OnRightTapped(UI::Input::IGestureRecognizer* aSender, michael@0: UI::Input::IRightTappedEventArgs* aArgs) michael@0: { michael@0: #ifdef DEBUG_INPUT michael@0: LogFunction(); michael@0: #endif michael@0: michael@0: Devices::Input::PointerDeviceType deviceType; michael@0: aArgs->get_PointerDeviceType(&deviceType); michael@0: michael@0: Foundation::Point position; michael@0: aArgs->get_Position(&position); michael@0: HandleLongTap(position); michael@0: michael@0: return S_OK; michael@0: } michael@0: michael@0: void michael@0: MetroInput::HandleTap(const Foundation::Point& aPoint, unsigned int aTapCount) michael@0: { michael@0: #ifdef DEBUG_INPUT michael@0: LogFunction(); michael@0: #endif michael@0: michael@0: LayoutDeviceIntPoint refPoint; michael@0: TransformRefPoint(aPoint, refPoint); michael@0: michael@0: WidgetMouseEvent* mouseEvent = michael@0: new WidgetMouseEvent(true, NS_MOUSE_MOVE, mWidget.Get(), michael@0: WidgetMouseEvent::eReal, WidgetMouseEvent::eNormal); michael@0: mouseEvent->refPoint = refPoint; michael@0: mouseEvent->clickCount = aTapCount; michael@0: mouseEvent->inputSource = nsIDOMMouseEvent::MOZ_SOURCE_TOUCH; michael@0: DispatchAsyncEventIgnoreStatus(mouseEvent); michael@0: michael@0: mouseEvent = michael@0: new WidgetMouseEvent(true, NS_MOUSE_BUTTON_DOWN, mWidget.Get(), michael@0: WidgetMouseEvent::eReal, WidgetMouseEvent::eNormal); michael@0: mouseEvent->refPoint = refPoint; michael@0: mouseEvent->clickCount = aTapCount; michael@0: mouseEvent->inputSource = nsIDOMMouseEvent::MOZ_SOURCE_TOUCH; michael@0: mouseEvent->button = WidgetMouseEvent::buttonType::eLeftButton; michael@0: DispatchAsyncEventIgnoreStatus(mouseEvent); michael@0: michael@0: mouseEvent = michael@0: new WidgetMouseEvent(true, NS_MOUSE_BUTTON_UP, mWidget.Get(), michael@0: WidgetMouseEvent::eReal, WidgetMouseEvent::eNormal); michael@0: mouseEvent->refPoint = refPoint; michael@0: mouseEvent->clickCount = aTapCount; michael@0: mouseEvent->inputSource = nsIDOMMouseEvent::MOZ_SOURCE_TOUCH; michael@0: mouseEvent->button = WidgetMouseEvent::buttonType::eLeftButton; michael@0: DispatchAsyncEventIgnoreStatus(mouseEvent); michael@0: michael@0: // Make sure all gecko events are dispatched and the dom is up to date michael@0: // so that when ui automation comes in looking for focus info it gets michael@0: // the right information. michael@0: MetroAppShell::MarkEventQueueForPurge(); michael@0: } michael@0: michael@0: void michael@0: MetroInput::HandleLongTap(const Foundation::Point& aPoint) michael@0: { michael@0: #ifdef DEBUG_INPUT michael@0: LogFunction(); michael@0: #endif michael@0: LayoutDeviceIntPoint refPoint; michael@0: TransformRefPoint(aPoint, refPoint); michael@0: michael@0: WidgetMouseEvent* contextEvent = michael@0: new WidgetMouseEvent(true, NS_CONTEXTMENU, mWidget.Get(), michael@0: WidgetMouseEvent::eReal, WidgetMouseEvent::eNormal); michael@0: contextEvent->refPoint = refPoint; michael@0: contextEvent->inputSource = nsIDOMMouseEvent::MOZ_SOURCE_TOUCH; michael@0: DispatchAsyncEventIgnoreStatus(contextEvent); michael@0: } michael@0: michael@0: /** michael@0: * Implementation Details michael@0: */ michael@0: nsEventStatus MetroInput::sThrowawayStatus; michael@0: michael@0: void michael@0: MetroInput::DispatchAsyncEventIgnoreStatus(WidgetInputEvent* aEvent) michael@0: { michael@0: aEvent->time = ::GetMessageTime(); michael@0: mModifierKeyState.Update(); michael@0: mModifierKeyState.InitInputEvent(*aEvent); michael@0: mInputEventQueue.Push(aEvent); michael@0: nsCOMPtr runnable = michael@0: NS_NewRunnableMethod(this, &MetroInput::DeliverNextQueuedEventIgnoreStatus); michael@0: NS_DispatchToCurrentThread(runnable); michael@0: } michael@0: michael@0: void michael@0: MetroInput::DeliverNextQueuedEventIgnoreStatus() michael@0: { michael@0: nsAutoPtr event = michael@0: static_cast(mInputEventQueue.PopFront()); michael@0: MOZ_ASSERT(event.get()); michael@0: DispatchEventIgnoreStatus(event.get()); michael@0: michael@0: // Let app shell know we've delivered that last input we wanted purged michael@0: // via a call to MarkEventQueueForPurge(). michael@0: if (event->message == NS_MOUSE_BUTTON_UP) { michael@0: MetroAppShell::InputEventsDispatched(); michael@0: } michael@0: michael@0: // Clear :hover/:active states for mouse events generated by HandleTap michael@0: WidgetMouseEvent* mouseEvent = event.get()->AsMouseEvent(); michael@0: if (!mouseEvent) { michael@0: return; michael@0: } michael@0: if (mouseEvent->message != NS_MOUSE_BUTTON_UP || michael@0: mouseEvent->inputSource != nsIDOMMouseEvent::MOZ_SOURCE_TOUCH) { michael@0: return; michael@0: } michael@0: nsCOMPtr presShell = mWidget->GetPresShell(); michael@0: if (presShell) { michael@0: EventStateManager* esm = presShell->GetPresContext()->EventStateManager(); michael@0: if (esm) { michael@0: esm->SetContentState(nullptr, NS_EVENT_STATE_HOVER); michael@0: } michael@0: } michael@0: } michael@0: michael@0: void michael@0: MetroInput::DispatchAsyncTouchEvent(WidgetTouchEvent* aEvent) michael@0: { michael@0: aEvent->time = ::GetMessageTime(); michael@0: mModifierKeyState.Update(); michael@0: mModifierKeyState.InitInputEvent(*aEvent); michael@0: mInputEventQueue.Push(aEvent); michael@0: nsCOMPtr runnable = michael@0: NS_NewRunnableMethod(this, &MetroInput::DeliverNextQueuedTouchEvent); michael@0: NS_DispatchToCurrentThread(runnable); michael@0: } michael@0: michael@0: static void DumpTouchIds(const char* aTarget, WidgetTouchEvent* aEvent) michael@0: { michael@0: // comment out for touch moves michael@0: if (aEvent->message == NS_TOUCH_MOVE) { michael@0: return; michael@0: } michael@0: switch(aEvent->message) { michael@0: case NS_TOUCH_START: michael@0: WinUtils::Log("DumpTouchIds: NS_TOUCH_START block"); michael@0: break; michael@0: case NS_TOUCH_MOVE: michael@0: WinUtils::Log("DumpTouchIds: NS_TOUCH_MOVE block"); michael@0: break; michael@0: case NS_TOUCH_END: michael@0: WinUtils::Log("DumpTouchIds: NS_TOUCH_END block"); michael@0: break; michael@0: case NS_TOUCH_CANCEL: michael@0: WinUtils::Log("DumpTouchIds: NS_TOUCH_CANCEL block"); michael@0: break; michael@0: } michael@0: nsTArray< nsRefPtr >& touches = aEvent->touches; michael@0: for (uint32_t i = 0; i < touches.Length(); ++i) { michael@0: dom::Touch* touch = touches[i]; michael@0: if (!touch) { michael@0: continue; michael@0: } michael@0: int32_t id = touch->Identifier(); michael@0: WinUtils::Log(" id=%d target=%s", id, aTarget); michael@0: } michael@0: } michael@0: michael@0: static void DumpTouchBehavior(nsTArray& aBehavior) michael@0: { michael@0: WinUtils::Log("DumpTouchBehavior: Touch behavior flags set for current touch session:"); michael@0: for (uint32_t i = 0; i < aBehavior.Length(); i++) { michael@0: if (mozilla::layers::AllowedTouchBehavior::VERTICAL_PAN & aBehavior[i]) { michael@0: WinUtils::Log("VERTICAL_PAN"); michael@0: } michael@0: michael@0: if (mozilla::layers::AllowedTouchBehavior::HORIZONTAL_PAN & aBehavior[i]) { michael@0: WinUtils::Log("HORIZONTAL_PAN"); michael@0: } michael@0: michael@0: if (mozilla::layers::AllowedTouchBehavior::UNKNOWN & aBehavior[i]) { michael@0: WinUtils::Log("UNKNOWN"); michael@0: } michael@0: michael@0: if ((mozilla::layers::AllowedTouchBehavior::NONE & aBehavior[i]) == 0) { michael@0: WinUtils::Log("NONE"); michael@0: } michael@0: } michael@0: } michael@0: michael@0: /* michael@0: * nsPreShell's processing of WidgetTouchEvent events: michael@0: * michael@0: * NS_TOUCH_START: michael@0: * Interprets a single touch point as the first touch point of a block and will reset its michael@0: * queue when it receives this. For multiple touch points it sets all points in its queue michael@0: * and marks new points as changed. michael@0: * NS_TOUCH_MOVE: michael@0: * Uses the equality tests in dom::Touch to test if a touch point has changed (moved). michael@0: * If a point has moved, keeps this touch point in the event, otherwise it removes michael@0: * the touch point. Note if no points have changed, it exits without sending a dom event. michael@0: * NS_TOUCH_CANCEL/NS_TOUCH_END michael@0: * Assumes any point in touchEvent->touches has been removed or canceled. michael@0: */ michael@0: michael@0: //#define DUMP_TOUCH_IDS(aTarget, aEvent) DumpTouchIds(aTarget, aEvent) michael@0: #define DUMP_TOUCH_IDS(...) michael@0: michael@0: //#define DUMP_ALLOWED_TOUCH_BEHAVIOR(aBehavior) DumpTouchBehavior(aBehavior) michael@0: #define DUMP_ALLOWED_TOUCH_BEHAVIOR(...) michael@0: michael@0: void michael@0: MetroInput::HandleFirstTouchStartEvent(WidgetTouchEvent* aEvent) michael@0: { michael@0: nsEventStatus contentStatus = nsEventStatus_eIgnore; michael@0: michael@0: WidgetTouchEvent transformedEvent(*aEvent); michael@0: DUMP_TOUCH_IDS("APZC(1)", aEvent); michael@0: mWidget->ApzReceiveInputEvent(&transformedEvent, &mTargetAPZCGuid); michael@0: michael@0: if (gTouchActionPropertyEnabled) { michael@0: nsTArray touchBehaviors; michael@0: // Retrieving touch behaviors from apzctm and from the content (if needed) michael@0: // then setting it back to the apzc. The apzc we retrieved touch behaviors michael@0: // from and we're setting to may changes if there are multiple touches (in that michael@0: // case apzctm needs to take common ancestor of them). michael@0: GetAllowedTouchBehavior(&transformedEvent, touchBehaviors); michael@0: // Setting the touch behaviors to the apzc that will be responsible michael@0: // for interpreting it. It may be not the same apzc we retrieved touch michael@0: // action values from. E.g. for zooming we're taking parent apzc of a few ones michael@0: // that were touched but touch behaviors would be taken from childs. michael@0: DUMP_ALLOWED_TOUCH_BEHAVIOR(touchBehaviors); michael@0: mWidget->ApzcSetAllowedTouchBehavior(mTargetAPZCGuid, touchBehaviors); michael@0: if (IsTouchBehaviorForbidden(touchBehaviors)) { michael@0: mContentConsumingTouch = true; michael@0: } michael@0: } michael@0: michael@0: DUMP_TOUCH_IDS("DOM(2)", aEvent); michael@0: mWidget->DispatchEvent(&transformedEvent, contentStatus); michael@0: if (nsEventStatus_eConsumeNoDefault == contentStatus) { michael@0: mContentConsumingTouch = true; michael@0: } michael@0: michael@0: if (mContentConsumingTouch) { michael@0: mCancelable = false; michael@0: mWidget->ApzContentConsumingTouch(mTargetAPZCGuid); michael@0: DispatchTouchCancel(aEvent); michael@0: } michael@0: michael@0: // Disable gesture based events (taps, swipes, rotation) if michael@0: // preventDefault is called on touchstart. michael@0: mRecognizerWantsEvents = !(nsEventStatus_eConsumeNoDefault == contentStatus); michael@0: michael@0: // If content is consuming touch don't generate any gesture based michael@0: // input - clear the recognizer state without sending any events. michael@0: if (!ShouldDeliverInputToRecognizer()) { michael@0: mGestureRecognizer->CompleteGesture(); michael@0: } michael@0: } michael@0: michael@0: void michael@0: MetroInput::HandleFirstTouchMoveEvent(WidgetTouchEvent* aEvent) michael@0: { michael@0: mCancelable = false; michael@0: michael@0: nsEventStatus contentStatus = nsEventStatus_eIgnore; michael@0: nsEventStatus apzcStatus = nsEventStatus_eIgnore; michael@0: michael@0: WidgetTouchEvent transformedEvent(*aEvent); michael@0: DUMP_TOUCH_IDS("APZC(2)", aEvent); michael@0: apzcStatus = mWidget->ApzReceiveInputEvent(&transformedEvent, &mTargetAPZCGuid); michael@0: michael@0: // We need to dispatch here only touch event, not pointer one. michael@0: // That's because according to the spec pointer events doesn't imply pointermove event michael@0: // between pointerdown and pointercancel (If default touch behavior is triggered). michael@0: // But at the same time we need to dispatch at least touchmove event to let content to michael@0: // consume it (or not consume). michael@0: // TODO: determine how to dispatch only one kind of events: currently there are two options: michael@0: // 1) Create two separate instances of the WidgetTouchEvent and WidgetPointerEvent and michael@0: // dispatch them separately. michael@0: // 2) Add a boolean flag to the WidgetTouchEvent that states whether this event should produce michael@0: // both touch and pointer event or only touch one. michael@0: // Anyway it's worth to add this stuff only after patches from bug 822898 (Pointer events) are michael@0: // fully commited. michael@0: DUMP_TOUCH_IDS("DOM(3)", aEvent); michael@0: mWidget->DispatchEvent(&transformedEvent, contentStatus); michael@0: michael@0: // Checking content result first since content can override apzc wish and disallow apzc touch michael@0: // behavior (via preventDefault). michael@0: if (nsEventStatus_eConsumeNoDefault == contentStatus) { michael@0: // Touchmove handler consumed touch. michael@0: mContentConsumingTouch = true; michael@0: } else if (nsEventStatus_eConsumeNoDefault == apzcStatus) { michael@0: // Apzc triggered default behavior. michael@0: mApzConsumingTouch = true; michael@0: } michael@0: michael@0: // Let the apz know if content wants to consume touch events, or cancel michael@0: // the touch block for content. michael@0: if (mContentConsumingTouch) { michael@0: mWidget->ApzContentConsumingTouch(mTargetAPZCGuid); michael@0: DispatchTouchCancel(aEvent); michael@0: } else { michael@0: mWidget->ApzContentIgnoringTouch(mTargetAPZCGuid); michael@0: } michael@0: michael@0: if (mApzConsumingTouch) { michael@0: // Dispatching cancel to the content. michael@0: DispatchTouchCancel(&transformedEvent); michael@0: } michael@0: } michael@0: michael@0: void michael@0: MetroInput::DeliverNextQueuedTouchEvent() michael@0: { michael@0: /* michael@0: * We go through states here and make different decisions in each: michael@0: * michael@0: * 1) Hit test for apz on first touchstart michael@0: * If non-apzc content/chrome is the target simplify event delivery from michael@0: * that point on by directing all input to chrome, bypassing the apz. michael@0: * 2) Process first touchstart and touchmove events michael@0: * If touch behavior value associated with the TouchStart's touches doesn't michael@0: * allow zooming or panning we explicitly set mContentConsumingTouch to true. michael@0: * Otherwise check the result and set mContentConsumingTouch appropriately. michael@0: * Deliver touch events to the apz (ignoring return result) and to content. michael@0: * 3) If mContentConsumingTouch is true: deliver touch to content after michael@0: * transforming through the apz. Also let the apz know content is michael@0: * consuming touch and deliver cancel event to apz. michael@0: * 4) If mContentConsumingTouch is false: check the result from the apz and michael@0: * set mApzConsumingTouch appropriately. michael@0: * 5) If mApzConsumingTouch is true: send a touchcancel to content michael@0: * and deliver all events to the apz. If the apz is doing something with michael@0: * the events we can save ourselves the overhead of delivering dom events. michael@0: * michael@0: * Notes: michael@0: * - never rely on the contents of mTouches here, since this is a delayed michael@0: * callback. mTouches will likely have been modified. michael@0: */ michael@0: nsEventStatus status = nsEventStatus_eIgnore; michael@0: michael@0: WidgetTouchEvent* event = michael@0: static_cast(mInputEventQueue.PopFront()); michael@0: MOZ_ASSERT(event); michael@0: michael@0: AutoDeleteEvent wrap(event); michael@0: michael@0: // Test for non-apz vs. apz target. To do this we only use the first touch michael@0: // point since that will be the input batch target. Cache this for touch events michael@0: // since HitTestChrome has to send a dom event. michael@0: if (mCancelable && event->message == NS_TOUCH_START) { michael@0: nsRefPtr touch = event->touches[0]; michael@0: LayoutDeviceIntPoint pt = LayoutDeviceIntPoint::FromUntyped(touch->mRefPoint); michael@0: // This is currently a general contained rect hit test, it may produce a false michael@0: // positive for overlay chrome elements. Also, some content pages won't support michael@0: // apzc, so this may be false for content as well. michael@0: bool apzIntersect = mWidget->ApzHitTest(mozilla::ScreenIntPoint(pt.x, pt.y)); michael@0: mNonApzTargetForTouch = (!apzIntersect || HitTestChrome(pt)); michael@0: } michael@0: michael@0: // If this event is destined for dom, deliver it directly there bypassing michael@0: // the apz. michael@0: if (mNonApzTargetForTouch) { michael@0: DUMP_TOUCH_IDS("DOM(1)", event); michael@0: mWidget->DispatchEvent(event, status); michael@0: if (mCancelable) { michael@0: // Disable gesture based events (taps, swipes, rotation) if michael@0: // preventDefault is called on touchstart. michael@0: if (nsEventStatus_eConsumeNoDefault == status) { michael@0: mRecognizerWantsEvents = false; michael@0: mGestureRecognizer->CompleteGesture(); michael@0: } michael@0: if (event->message == NS_TOUCH_MOVE) { michael@0: mCancelable = false; michael@0: } michael@0: } michael@0: return; michael@0: } michael@0: michael@0: if (mCancelable && event->message == NS_TOUCH_START) { michael@0: HandleFirstTouchStartEvent(event); michael@0: return; michael@0: } else if (mCancelable && event->message == NS_TOUCH_MOVE) { michael@0: HandleFirstTouchMoveEvent(event); michael@0: return; michael@0: } michael@0: // Let TouchEnd events go through even if mCancelable is true since we michael@0: // don't need to check whether it is prevented by content or consumed michael@0: // by apzc. michael@0: michael@0: // If content is consuming touch, we may need to transform event coords michael@0: // through the apzc before sending to the dom. Otherwise send the event michael@0: // to apzc. michael@0: if (mContentConsumingTouch) { michael@0: // Only translate if we're dealing with web content that's transformed michael@0: // by the apzc. michael@0: TransformTouchEvent(event); michael@0: DUMP_TOUCH_IDS("DOM(4)", event); michael@0: mWidget->DispatchEvent(event, status); michael@0: return; michael@0: } michael@0: michael@0: DUMP_TOUCH_IDS("APZC(3)", event); michael@0: status = mWidget->ApzReceiveInputEvent(event, nullptr); michael@0: michael@0: // If we're getting a new touch (touch start) after some touch start/move michael@0: // events we need to reset touch behavior for touches. michael@0: if (gTouchActionPropertyEnabled && event->message == NS_TOUCH_START) { michael@0: nsTArray touchBehaviors; michael@0: GetAllowedTouchBehavior(event, touchBehaviors); michael@0: DUMP_ALLOWED_TOUCH_BEHAVIOR(touchBehaviors); michael@0: mWidget->ApzcSetAllowedTouchBehavior(mTargetAPZCGuid, touchBehaviors); michael@0: } michael@0: michael@0: // Send the event to content unless APZC is consuming it. michael@0: if (!mApzConsumingTouch) { michael@0: if (status == nsEventStatus_eConsumeNoDefault) { michael@0: mApzConsumingTouch = true; michael@0: DispatchTouchCancel(event); michael@0: return; michael@0: } michael@0: TransformTouchEvent(event); michael@0: DUMP_TOUCH_IDS("DOM(5)", event); michael@0: mWidget->DispatchEvent(event, status); michael@0: } michael@0: } michael@0: michael@0: void michael@0: MetroInput::DispatchTouchCancel(WidgetTouchEvent* aEvent) michael@0: { michael@0: MOZ_ASSERT(aEvent); michael@0: // Send a touchcancel for each pointer id we have a corresponding start michael@0: // for. Note we can't rely on mTouches here since touchends remove points michael@0: // from it. michael@0: WidgetTouchEvent touchEvent(true, NS_TOUCH_CANCEL, mWidget.Get()); michael@0: nsTArray< nsRefPtr >& touches = aEvent->touches; michael@0: for (uint32_t i = 0; i < touches.Length(); ++i) { michael@0: dom::Touch* touch = touches[i]; michael@0: if (!touch) { michael@0: continue; michael@0: } michael@0: int32_t id = touch->Identifier(); michael@0: if (mCanceledIds.Contains(id)) { michael@0: continue; michael@0: } michael@0: mCanceledIds.AppendElement(id); michael@0: touchEvent.touches.AppendElement(touch); michael@0: } michael@0: if (!touchEvent.touches.Length()) { michael@0: return; michael@0: } michael@0: if (mContentConsumingTouch) { michael@0: DUMP_TOUCH_IDS("APZC(4)", &touchEvent); michael@0: mWidget->ApzReceiveInputEvent(&touchEvent, nullptr); michael@0: } else { michael@0: DUMP_TOUCH_IDS("DOM(6)", &touchEvent); michael@0: mWidget->DispatchEvent(&touchEvent, sThrowawayStatus); michael@0: } michael@0: } michael@0: michael@0: void michael@0: MetroInput::DispatchEventIgnoreStatus(WidgetGUIEvent *aEvent) michael@0: { michael@0: mWidget->DispatchEvent(aEvent, sThrowawayStatus); michael@0: } michael@0: michael@0: void michael@0: MetroInput::UnregisterInputEvents() { michael@0: // Unregister ourselves for the edge swipe event michael@0: WRL::ComPtr edgeStatics; michael@0: if (SUCCEEDED(Foundation::GetActivationFactory( michael@0: WRL::Wrappers::HStringReference( michael@0: RuntimeClass_Windows_UI_Input_EdgeGesture).Get(), michael@0: edgeStatics.GetAddressOf()))) { michael@0: WRL::ComPtr edge; michael@0: if (SUCCEEDED(edgeStatics->GetForCurrentView(edge.GetAddressOf()))) { michael@0: edge->remove_Starting(mTokenEdgeStarted); michael@0: edge->remove_Canceled(mTokenEdgeCanceled); michael@0: edge->remove_Completed(mTokenEdgeCompleted); michael@0: } michael@0: } michael@0: // Unregister ourselves from the window events. This is extremely important; michael@0: // once this object is destroyed we don't want Windows to try to send events michael@0: // to it. michael@0: mWindow->remove_PointerPressed(mTokenPointerPressed); michael@0: mWindow->remove_PointerReleased(mTokenPointerReleased); michael@0: mWindow->remove_PointerMoved(mTokenPointerMoved); michael@0: mWindow->remove_PointerEntered(mTokenPointerEntered); michael@0: mWindow->remove_PointerExited(mTokenPointerExited); michael@0: michael@0: // Unregistering from the gesture recognizer events probably isn't as michael@0: // necessary since we're about to destroy the gesture recognizer, but michael@0: // it can't hurt. michael@0: mGestureRecognizer->remove_ManipulationCompleted( michael@0: mTokenManipulationCompleted); michael@0: mGestureRecognizer->remove_Tapped(mTokenTapped); michael@0: mGestureRecognizer->remove_RightTapped(mTokenRightTapped); michael@0: } michael@0: michael@0: void michael@0: MetroInput::RegisterInputEvents() michael@0: { michael@0: NS_ASSERTION(mWindow, "Must have a window to register for input events!"); michael@0: NS_ASSERTION(mGestureRecognizer, michael@0: "Must have a GestureRecognizer for input events!"); michael@0: // Register for edge swipe michael@0: WRL::ComPtr edgeStatics; michael@0: Foundation::GetActivationFactory( michael@0: WRL::Wrappers::HStringReference( michael@0: RuntimeClass_Windows_UI_Input_EdgeGesture) michael@0: .Get(), michael@0: edgeStatics.GetAddressOf()); michael@0: WRL::ComPtr edge; michael@0: edgeStatics->GetForCurrentView(edge.GetAddressOf()); michael@0: michael@0: edge->add_Starting( michael@0: WRL::Callback( michael@0: this, michael@0: &MetroInput::OnEdgeGestureStarted).Get(), michael@0: &mTokenEdgeStarted); michael@0: michael@0: edge->add_Canceled( michael@0: WRL::Callback( michael@0: this, michael@0: &MetroInput::OnEdgeGestureCanceled).Get(), michael@0: &mTokenEdgeCanceled); michael@0: michael@0: edge->add_Completed( michael@0: WRL::Callback( michael@0: this, michael@0: &MetroInput::OnEdgeGestureCompleted).Get(), michael@0: &mTokenEdgeCompleted); michael@0: michael@0: // Set up our Gesture Recognizer to raise events for the gestures we michael@0: // care about michael@0: mGestureRecognizer->put_GestureSettings( michael@0: UI::Input::GestureSettings::GestureSettings_Tap michael@0: | UI::Input::GestureSettings::GestureSettings_DoubleTap michael@0: | UI::Input::GestureSettings::GestureSettings_RightTap michael@0: | UI::Input::GestureSettings::GestureSettings_Hold michael@0: | UI::Input::GestureSettings::GestureSettings_ManipulationTranslateX michael@0: | UI::Input::GestureSettings::GestureSettings_ManipulationTranslateY); michael@0: michael@0: // Register for the pointer events on our Window michael@0: mWindow->add_PointerPressed( michael@0: WRL::Callback( michael@0: this, michael@0: &MetroInput::OnPointerPressed).Get(), michael@0: &mTokenPointerPressed); michael@0: michael@0: mWindow->add_PointerReleased( michael@0: WRL::Callback( michael@0: this, michael@0: &MetroInput::OnPointerReleased).Get(), michael@0: &mTokenPointerReleased); michael@0: michael@0: mWindow->add_PointerMoved( michael@0: WRL::Callback( michael@0: this, michael@0: &MetroInput::OnPointerMoved).Get(), michael@0: &mTokenPointerMoved); michael@0: michael@0: mWindow->add_PointerEntered( michael@0: WRL::Callback( michael@0: this, michael@0: &MetroInput::OnPointerEntered).Get(), michael@0: &mTokenPointerEntered); michael@0: michael@0: mWindow->add_PointerExited( michael@0: WRL::Callback( michael@0: this, michael@0: &MetroInput::OnPointerExited).Get(), michael@0: &mTokenPointerExited); michael@0: michael@0: // Register for the events raised by our Gesture Recognizer michael@0: mGestureRecognizer->add_Tapped( michael@0: WRL::Callback( michael@0: this, michael@0: &MetroInput::OnTapped).Get(), michael@0: &mTokenTapped); michael@0: michael@0: mGestureRecognizer->add_RightTapped( michael@0: WRL::Callback( michael@0: this, michael@0: &MetroInput::OnRightTapped).Get(), michael@0: &mTokenRightTapped); michael@0: michael@0: mGestureRecognizer->add_ManipulationCompleted( michael@0: WRL::Callback( michael@0: this, michael@0: &MetroInput::OnManipulationCompleted).Get(), michael@0: &mTokenManipulationCompleted); michael@0: } michael@0: michael@0: } } }