widget/windows/winrt/MetroInput.cpp

Thu, 15 Jan 2015 15:59:08 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 15 Jan 2015 15:59:08 +0100
branch
TOR_BUG_9701
changeset 10
ac0c01689b40
permissions
-rw-r--r--

Implement a real Private Browsing Mode condition by changing the API/ABI;
This solves Tor bug #9701, complying with disk avoidance documented in
https://www.torproject.org/projects/torbrowser/design/#disk-avoidance.

michael@0 1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
michael@0 2 /* This Source Code Form is subject to the terms of the Mozilla Public
michael@0 3 * License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 5
michael@0 6 // Moz headers (alphabetical)
michael@0 7 #include "MetroInput.h"
michael@0 8 #include "MetroUtils.h" // Logging, POINT_CEIL_*, ActivateGenericInstance, etc
michael@0 9 #include "MetroWidget.h" // MetroInput::mWidget
michael@0 10 #include "mozilla/dom/Touch.h" // Touch
michael@0 11 #include "nsTArray.h" // Touch lists
michael@0 12 #include "nsIDOMSimpleGestureEvent.h" // Constants for gesture events
michael@0 13 #include "InputData.h"
michael@0 14 #include "UIABridgePrivate.h"
michael@0 15 #include "MetroAppShell.h"
michael@0 16 #include "mozilla/EventStateManager.h"
michael@0 17 #include "mozilla/EventStates.h"
michael@0 18 #include "mozilla/MouseEvents.h"
michael@0 19 #include "mozilla/TouchEvents.h"
michael@0 20 #include "mozilla/Preferences.h" // for Preferences
michael@0 21 #include "WinUtils.h"
michael@0 22 #include "nsIPresShell.h"
michael@0 23
michael@0 24 // System headers (alphabetical)
michael@0 25 #include <windows.ui.core.h> // ABI::Window::UI::Core namespace
michael@0 26 #include <windows.ui.input.h> // ABI::Window::UI::Input namespace
michael@0 27
michael@0 28 //#define DEBUG_INPUT
michael@0 29
michael@0 30 // Using declarations
michael@0 31 using namespace ABI::Windows; // UI, System, Foundation namespaces
michael@0 32 using namespace Microsoft; // WRL namespace (ComPtr, possibly others)
michael@0 33 using namespace mozilla;
michael@0 34 using namespace mozilla::widget;
michael@0 35 using namespace mozilla::widget::winrt;
michael@0 36 using namespace mozilla::dom;
michael@0 37
michael@0 38 // File-scoped statics (unnamed namespace)
michael@0 39 namespace {
michael@0 40 // XXX: Set these min values appropriately
michael@0 41 const double SWIPE_MIN_DISTANCE = 5.0;
michael@0 42 const double SWIPE_MIN_VELOCITY = 5.0;
michael@0 43
michael@0 44 // Convenience typedefs for event handler types
michael@0 45 typedef Foundation::__FITypedEventHandler_2_Windows__CUI__CInput__CEdgeGesture_Windows__CUI__CInput__CEdgeGestureEventArgs_t EdgeGestureHandler;
michael@0 46 typedef Foundation::__FITypedEventHandler_2_Windows__CUI__CCore__CCoreDispatcher_Windows__CUI__CCore__CAcceleratorKeyEventArgs_t AcceleratorKeyActivatedHandler;
michael@0 47 typedef Foundation::__FITypedEventHandler_2_Windows__CUI__CCore__CCoreWindow_Windows__CUI__CCore__CPointerEventArgs_t PointerEventHandler;
michael@0 48 typedef Foundation::__FITypedEventHandler_2_Windows__CUI__CInput__CGestureRecognizer_Windows__CUI__CInput__CTappedEventArgs_t TappedEventHandler;
michael@0 49 typedef Foundation::__FITypedEventHandler_2_Windows__CUI__CInput__CGestureRecognizer_Windows__CUI__CInput__CRightTappedEventArgs_t RightTappedEventHandler;
michael@0 50 typedef Foundation::__FITypedEventHandler_2_Windows__CUI__CInput__CGestureRecognizer_Windows__CUI__CInput__CManipulationStartedEventArgs_t ManipulationStartedEventHandler;
michael@0 51 typedef Foundation::__FITypedEventHandler_2_Windows__CUI__CInput__CGestureRecognizer_Windows__CUI__CInput__CManipulationUpdatedEventArgs_t ManipulationUpdatedEventHandler;
michael@0 52 typedef Foundation::__FITypedEventHandler_2_Windows__CUI__CInput__CGestureRecognizer_Windows__CUI__CInput__CManipulationCompletedEventArgs_t ManipulationCompletedEventHandler;
michael@0 53
michael@0 54 // Other convenience typedefs
michael@0 55 typedef ABI::Windows::UI::Core::ICoreAcceleratorKeys ICoreAcceleratorKeys;
michael@0 56
michael@0 57 /**
michael@0 58 * Specifies whether touch-action property is in force.
michael@0 59 */
michael@0 60 static bool gTouchActionPropertyEnabled = false;
michael@0 61
michael@0 62 /**
michael@0 63 * Creates and returns a new {@link Touch} from the given
michael@0 64 * ABI::Windows::UI::Input::IPointerPoint. Note that the caller is
michael@0 65 * responsible for freeing the memory for the Touch returned from
michael@0 66 * this function.
michael@0 67 *
michael@0 68 * @param aPoint the ABI::Windows::UI::Input::IPointerPoint containing the
michael@0 69 * metadata from which to create our new {@link Touch}
michael@0 70 * @return a new {@link Touch} representing the touch point. The caller
michael@0 71 * is responsible for freeing the memory for this touch point.
michael@0 72 */
michael@0 73 Touch*
michael@0 74 CreateDOMTouch(UI::Input::IPointerPoint* aPoint) {
michael@0 75 WRL::ComPtr<UI::Input::IPointerPointProperties> props;
michael@0 76 Foundation::Point position;
michael@0 77 uint32_t pointerId;
michael@0 78 Foundation::Rect contactRect;
michael@0 79 float pressure;
michael@0 80 float tiltX;
michael@0 81 float tiltY;
michael@0 82
michael@0 83 aPoint->get_Properties(props.GetAddressOf());
michael@0 84 aPoint->get_Position(&position);
michael@0 85 aPoint->get_PointerId(&pointerId);
michael@0 86 props->get_ContactRect(&contactRect);
michael@0 87 props->get_Pressure(&pressure);
michael@0 88 props->get_XTilt(&tiltX);
michael@0 89 props->get_YTilt(&tiltY);
michael@0 90
michael@0 91 nsIntPoint touchPoint = MetroUtils::LogToPhys(position);
michael@0 92 nsIntPoint touchRadius;
michael@0 93 touchRadius.x = WinUtils::LogToPhys(contactRect.Width) / 2;
michael@0 94 touchRadius.y = WinUtils::LogToPhys(contactRect.Height) / 2;
michael@0 95 Touch* touch =
michael@0 96 new Touch(pointerId,
michael@0 97 touchPoint,
michael@0 98 // Rotation radius and angle.
michael@0 99 // W3C touch events v1 do not use these.
michael@0 100 // The draft for W3C touch events v2 explains that
michael@0 101 // radius and angle should describe the ellipse that
michael@0 102 // most closely circumscribes the touching area. Since
michael@0 103 // Windows gives us a bounding rectangle rather than an
michael@0 104 // ellipse, we provide the ellipse that is most closely
michael@0 105 // circumscribed by the bounding rectangle that Windows
michael@0 106 // gave us.
michael@0 107 touchRadius,
michael@0 108 0.0f,
michael@0 109 // Pressure
michael@0 110 // W3C touch events v1 do not use this.
michael@0 111 // The current draft for W3C touch events v2 says that
michael@0 112 // this should be a value between 0.0 and 1.0, which is
michael@0 113 // consistent with what Windows provides us here.
michael@0 114 // XXX: Windows defaults to 0.5, but the current W3C
michael@0 115 // draft says that the value should be 0.0 if no value
michael@0 116 // known.
michael@0 117 pressure);
michael@0 118 touch->tiltX = tiltX;
michael@0 119 touch->tiltY = tiltY;
michael@0 120 return touch;
michael@0 121 }
michael@0 122
michael@0 123 /**
michael@0 124 * Test if a touchpoint position has moved. See Touch.Equals for
michael@0 125 * criteria.
michael@0 126 *
michael@0 127 * @param aTouch previous touch point
michael@0 128 * @param aPoint new winrt touch point
michael@0 129 * @return true if the point has moved
michael@0 130 */
michael@0 131 bool
michael@0 132 HasPointMoved(Touch* aTouch, UI::Input::IPointerPoint* aPoint) {
michael@0 133 WRL::ComPtr<UI::Input::IPointerPointProperties> props;
michael@0 134 Foundation::Point position;
michael@0 135 Foundation::Rect contactRect;
michael@0 136 float pressure;
michael@0 137
michael@0 138 aPoint->get_Properties(props.GetAddressOf());
michael@0 139 aPoint->get_Position(&position);
michael@0 140 props->get_ContactRect(&contactRect);
michael@0 141 props->get_Pressure(&pressure);
michael@0 142 nsIntPoint touchPoint = MetroUtils::LogToPhys(position);
michael@0 143 nsIntPoint touchRadius;
michael@0 144 touchRadius.x = WinUtils::LogToPhys(contactRect.Width) / 2;
michael@0 145 touchRadius.y = WinUtils::LogToPhys(contactRect.Height) / 2;
michael@0 146
michael@0 147 // from Touch.Equals
michael@0 148 return touchPoint != aTouch->mRefPoint ||
michael@0 149 pressure != aTouch->Force() ||
michael@0 150 /* mRotationAngle == aTouch->RotationAngle() || */
michael@0 151 touchRadius.x != aTouch->RadiusX() ||
michael@0 152 touchRadius.y != aTouch->RadiusY();
michael@0 153 }
michael@0 154
michael@0 155 /**
michael@0 156 * Converts from the Devices::Input::PointerDeviceType enumeration
michael@0 157 * to a nsIDOMMouseEvent::MOZ_SOURCE_* value.
michael@0 158 *
michael@0 159 * @param aDeviceType the value to convert
michael@0 160 * @param aMozInputSource the converted value
michael@0 161 */
michael@0 162 void
michael@0 163 MozInputSourceFromDeviceType(
michael@0 164 Devices::Input::PointerDeviceType const& aDeviceType,
michael@0 165 unsigned short& aMozInputSource) {
michael@0 166 if (Devices::Input::PointerDeviceType::PointerDeviceType_Mouse
michael@0 167 == aDeviceType) {
michael@0 168 aMozInputSource = nsIDOMMouseEvent::MOZ_SOURCE_MOUSE;
michael@0 169 } else if (Devices::Input::PointerDeviceType::PointerDeviceType_Touch
michael@0 170 == aDeviceType) {
michael@0 171 aMozInputSource = nsIDOMMouseEvent::MOZ_SOURCE_TOUCH;
michael@0 172 } else if (Devices::Input::PointerDeviceType::PointerDeviceType_Pen
michael@0 173 == aDeviceType) {
michael@0 174 aMozInputSource = nsIDOMMouseEvent::MOZ_SOURCE_PEN;
michael@0 175 }
michael@0 176 }
michael@0 177
michael@0 178 int16_t
michael@0 179 ButtonsForPointerPoint(UI::Input::IPointerPoint* aPoint) {
michael@0 180 WRL::ComPtr<UI::Input::IPointerPointProperties> props;
michael@0 181 aPoint->get_Properties(props.GetAddressOf());
michael@0 182
michael@0 183 int16_t buttons = 0;
michael@0 184 boolean buttonPressed;
michael@0 185
michael@0 186 props->get_IsLeftButtonPressed(&buttonPressed);
michael@0 187 if (buttonPressed) {
michael@0 188 buttons |= WidgetMouseEvent::eLeftButtonFlag;
michael@0 189 }
michael@0 190 props->get_IsMiddleButtonPressed(&buttonPressed);
michael@0 191 if (buttonPressed) {
michael@0 192 buttons |= WidgetMouseEvent::eMiddleButtonFlag;
michael@0 193 }
michael@0 194 props->get_IsRightButtonPressed(&buttonPressed);
michael@0 195 if (buttonPressed) {
michael@0 196 buttons |= WidgetMouseEvent::eRightButtonFlag;
michael@0 197 }
michael@0 198 props->get_IsXButton1Pressed(&buttonPressed);
michael@0 199 if (buttonPressed) {
michael@0 200 buttons |= WidgetMouseEvent::e4thButtonFlag;
michael@0 201 }
michael@0 202 props->get_IsXButton2Pressed(&buttonPressed);
michael@0 203 if (buttonPressed) {
michael@0 204 buttons |= WidgetMouseEvent::e5thButtonFlag;
michael@0 205 }
michael@0 206 return buttons;
michael@0 207 }
michael@0 208
michael@0 209 /**
michael@0 210 * This function is for use with mTouches.Enumerate. It will
michael@0 211 * append each element it encounters to the {@link nsTArray}
michael@0 212 * of {@link mozilla::dom::Touch}es passed in through the third (void*)
michael@0 213 * parameter.
michael@0 214 *
michael@0 215 * NOTE: This function will set the `mChanged` member of each
michael@0 216 * element it encounters to `false`, since this function is only
michael@0 217 * used to populate a touchlist that is about to be dispatched
michael@0 218 * in a gecko touch event.
michael@0 219 *
michael@0 220 * @param aKey the key of the current element being enumerated
michael@0 221 * @param aData the value of the current element being enumerated
michael@0 222 * @param aTouchList the {@link nsTArray} to append to
michael@0 223 */
michael@0 224 PLDHashOperator
michael@0 225 AppendToTouchList(const unsigned int& aKey,
michael@0 226 nsRefPtr<Touch>& aData,
michael@0 227 void *aTouchList)
michael@0 228 {
michael@0 229 nsTArray<nsRefPtr<Touch> > *touches =
michael@0 230 static_cast<nsTArray<nsRefPtr<Touch> > *>(aTouchList);
michael@0 231 nsRefPtr<Touch> copy = new Touch(aData->mIdentifier,
michael@0 232 aData->mRefPoint,
michael@0 233 aData->mRadius,
michael@0 234 aData->mRotationAngle,
michael@0 235 aData->mForce);
michael@0 236 copy->tiltX = aData->tiltX;
michael@0 237 copy->tiltY = aData->tiltY;
michael@0 238 touches->AppendElement(copy);
michael@0 239 aData->mChanged = false;
michael@0 240 return PL_DHASH_NEXT;
michael@0 241 }
michael@0 242
michael@0 243 // Helper for making sure event ptrs get freed.
michael@0 244 class AutoDeleteEvent
michael@0 245 {
michael@0 246 public:
michael@0 247 AutoDeleteEvent(WidgetGUIEvent* aPtr) :
michael@0 248 mPtr(aPtr) {}
michael@0 249 ~AutoDeleteEvent() {
michael@0 250 if (mPtr) {
michael@0 251 delete mPtr;
michael@0 252 }
michael@0 253 }
michael@0 254 WidgetGUIEvent* mPtr;
michael@0 255 };
michael@0 256 }
michael@0 257
michael@0 258 namespace mozilla {
michael@0 259 namespace widget {
michael@0 260 namespace winrt {
michael@0 261
michael@0 262 MetroInput::InputPrecisionLevel MetroInput::sCurrentInputLevel =
michael@0 263 MetroInput::InputPrecisionLevel::LEVEL_IMPRECISE;
michael@0 264
michael@0 265 MetroInput::MetroInput(MetroWidget* aWidget,
michael@0 266 UI::Core::ICoreWindow* aWindow)
michael@0 267 : mWidget(aWidget),
michael@0 268 mNonApzTargetForTouch(false),
michael@0 269 mWindow(aWindow)
michael@0 270 {
michael@0 271 LogFunction();
michael@0 272 NS_ASSERTION(aWidget, "Attempted to create MetroInput for null widget!");
michael@0 273 NS_ASSERTION(aWindow, "Attempted to create MetroInput for null window!");
michael@0 274
michael@0 275 Preferences::AddBoolVarCache(&gTouchActionPropertyEnabled, "layout.css.touch_action.enabled", gTouchActionPropertyEnabled);
michael@0 276 mTokenPointerPressed.value = 0;
michael@0 277 mTokenPointerReleased.value = 0;
michael@0 278 mTokenPointerMoved.value = 0;
michael@0 279 mTokenPointerEntered.value = 0;
michael@0 280 mTokenPointerExited.value = 0;
michael@0 281 mTokenEdgeStarted.value = 0;
michael@0 282 mTokenEdgeCanceled.value = 0;
michael@0 283 mTokenEdgeCompleted.value = 0;
michael@0 284 mTokenManipulationCompleted.value = 0;
michael@0 285 mTokenTapped.value = 0;
michael@0 286 mTokenRightTapped.value = 0;
michael@0 287
michael@0 288 // Create our Gesture Recognizer
michael@0 289 ActivateGenericInstance(RuntimeClass_Windows_UI_Input_GestureRecognizer,
michael@0 290 mGestureRecognizer);
michael@0 291 NS_ASSERTION(mGestureRecognizer, "Failed to create GestureRecognizer!");
michael@0 292
michael@0 293 RegisterInputEvents();
michael@0 294 }
michael@0 295
michael@0 296 MetroInput::~MetroInput()
michael@0 297 {
michael@0 298 LogFunction();
michael@0 299 UnregisterInputEvents();
michael@0 300 }
michael@0 301
michael@0 302 /* static */
michael@0 303 bool MetroInput::IsInputModeImprecise()
michael@0 304 {
michael@0 305 return sCurrentInputLevel == LEVEL_IMPRECISE;
michael@0 306 }
michael@0 307
michael@0 308 /**
michael@0 309 * Tracks the current input level (precise/imprecise) and fires an observer
michael@0 310 * when the mode changes.
michael@0 311 */
michael@0 312 void
michael@0 313 MetroInput::UpdateInputLevel(InputPrecisionLevel aInputLevel)
michael@0 314 {
michael@0 315 // ignore mouse input if we have active touch input.
michael@0 316 if (aInputLevel == LEVEL_PRECISE && mTouches.Count() > 0) {
michael@0 317 return;
michael@0 318 }
michael@0 319 if (sCurrentInputLevel != aInputLevel) {
michael@0 320 sCurrentInputLevel = aInputLevel;
michael@0 321 MetroUtils::FireObserver(sCurrentInputLevel == LEVEL_PRECISE ?
michael@0 322 "metro_precise_input" : "metro_imprecise_input");
michael@0 323 }
michael@0 324 }
michael@0 325
michael@0 326 /**
michael@0 327 * Processes an IEdgeGestureEventArgs and returns the input source type
michael@0 328 * for the event. Also updates input level via UpdateInputLevel.
michael@0 329 */
michael@0 330 uint16_t
michael@0 331 MetroInput::ProcessInputTypeForGesture(UI::Input::IEdgeGestureEventArgs* aArgs)
michael@0 332 {
michael@0 333 MOZ_ASSERT(aArgs);
michael@0 334 UI::Input::EdgeGestureKind kind;
michael@0 335 aArgs->get_Kind(&kind);
michael@0 336 switch(kind) {
michael@0 337 case UI::Input::EdgeGestureKind::EdgeGestureKind_Touch:
michael@0 338 UpdateInputLevel(LEVEL_IMPRECISE);
michael@0 339 return nsIDOMMouseEvent::MOZ_SOURCE_TOUCH;
michael@0 340 break;
michael@0 341 case UI::Input::EdgeGestureKind::EdgeGestureKind_Keyboard:
michael@0 342 return nsIDOMMouseEvent::MOZ_SOURCE_KEYBOARD;
michael@0 343 break;
michael@0 344 case UI::Input::EdgeGestureKind::EdgeGestureKind_Mouse:
michael@0 345 UpdateInputLevel(LEVEL_PRECISE);
michael@0 346 return nsIDOMMouseEvent::MOZ_SOURCE_MOUSE;
michael@0 347 break;
michael@0 348 }
michael@0 349 return nsIDOMMouseEvent::MOZ_SOURCE_UNKNOWN;
michael@0 350 }
michael@0 351
michael@0 352 /**
michael@0 353 * When the user swipes her/his finger in from the top of the screen,
michael@0 354 * we receive this event.
michael@0 355 *
michael@0 356 * @param sender the CoreDispatcher that fired this event
michael@0 357 * @param aArgs the event-specific args we use when processing this event
michael@0 358 * @returns S_OK
michael@0 359 */
michael@0 360 HRESULT
michael@0 361 MetroInput::OnEdgeGestureStarted(UI::Input::IEdgeGesture* sender,
michael@0 362 UI::Input::IEdgeGestureEventArgs* aArgs)
michael@0 363 {
michael@0 364 #ifdef DEBUG_INPUT
michael@0 365 LogFunction();
michael@0 366 #endif
michael@0 367 WidgetSimpleGestureEvent geckoEvent(true,
michael@0 368 NS_SIMPLE_GESTURE_EDGE_STARTED,
michael@0 369 mWidget.Get());
michael@0 370 mModifierKeyState.Update();
michael@0 371 mModifierKeyState.InitInputEvent(geckoEvent);
michael@0 372 geckoEvent.time = ::GetMessageTime();
michael@0 373 geckoEvent.inputSource = ProcessInputTypeForGesture(aArgs);
michael@0 374
michael@0 375 // Safe
michael@0 376 DispatchEventIgnoreStatus(&geckoEvent);
michael@0 377 return S_OK;
michael@0 378 }
michael@0 379
michael@0 380 /**
michael@0 381 * This event can be received if the user swipes her/his finger back to
michael@0 382 * the top of the screen, or continues moving her/his finger such that
michael@0 383 * the movement is interpreted as a "grab this window" gesture
michael@0 384 *
michael@0 385 * @param sender the CoreDispatcher that fired this event
michael@0 386 * @param aArgs the event-specific args we use when processing this event
michael@0 387 * @returns S_OK
michael@0 388 */
michael@0 389 HRESULT
michael@0 390 MetroInput::OnEdgeGestureCanceled(UI::Input::IEdgeGesture* sender,
michael@0 391 UI::Input::IEdgeGestureEventArgs* aArgs)
michael@0 392 {
michael@0 393 #ifdef DEBUG_INPUT
michael@0 394 LogFunction();
michael@0 395 #endif
michael@0 396 WidgetSimpleGestureEvent geckoEvent(true,
michael@0 397 NS_SIMPLE_GESTURE_EDGE_CANCELED,
michael@0 398 mWidget.Get());
michael@0 399 mModifierKeyState.Update();
michael@0 400 mModifierKeyState.InitInputEvent(geckoEvent);
michael@0 401 geckoEvent.time = ::GetMessageTime();
michael@0 402 geckoEvent.inputSource = ProcessInputTypeForGesture(aArgs);
michael@0 403
michael@0 404 // Safe
michael@0 405 DispatchEventIgnoreStatus(&geckoEvent);
michael@0 406 return S_OK;
michael@0 407 }
michael@0 408
michael@0 409 /**
michael@0 410 * This event is received if the user presses ctrl+Z or lifts her/his
michael@0 411 * finger after causing an EdgeGestureStarting event to fire.
michael@0 412 *
michael@0 413 * @param sender the CoreDispatcher that fired this event
michael@0 414 * @param aArgs the event-specific args we use when processing this event
michael@0 415 * @returns S_OK
michael@0 416 */
michael@0 417 HRESULT
michael@0 418 MetroInput::OnEdgeGestureCompleted(UI::Input::IEdgeGesture* sender,
michael@0 419 UI::Input::IEdgeGestureEventArgs* aArgs)
michael@0 420 {
michael@0 421 #ifdef DEBUG_INPUT
michael@0 422 LogFunction();
michael@0 423 #endif
michael@0 424 WidgetSimpleGestureEvent geckoEvent(true,
michael@0 425 NS_SIMPLE_GESTURE_EDGE_COMPLETED,
michael@0 426 mWidget.Get());
michael@0 427 mModifierKeyState.Update();
michael@0 428 mModifierKeyState.InitInputEvent(geckoEvent);
michael@0 429 geckoEvent.time = ::GetMessageTime();
michael@0 430 geckoEvent.inputSource = ProcessInputTypeForGesture(aArgs);
michael@0 431
michael@0 432 // Safe
michael@0 433 DispatchEventIgnoreStatus(&geckoEvent);
michael@0 434 return S_OK;
michael@0 435 }
michael@0 436
michael@0 437 /**
michael@0 438 * This helper function is used by our processing of PointerPressed,
michael@0 439 * PointerReleased, and PointerMoved events.
michael@0 440 * It dispatches a gecko event in response to the input received. This
michael@0 441 * function should only be called for non-touch (i.e. pen or mouse) input
michael@0 442 * events.
michael@0 443 *
michael@0 444 * @param aPoint the PointerPoint for the input event
michael@0 445 */
michael@0 446 void
michael@0 447 MetroInput::OnPointerNonTouch(UI::Input::IPointerPoint* aPoint) {
michael@0 448 WRL::ComPtr<UI::Input::IPointerPointProperties> props;
michael@0 449 UI::Input::PointerUpdateKind pointerUpdateKind;
michael@0 450
michael@0 451 aPoint->get_Properties(props.GetAddressOf());
michael@0 452 props->get_PointerUpdateKind(&pointerUpdateKind);
michael@0 453
michael@0 454 uint32_t message = NS_MOUSE_MOVE;
michael@0 455 int16_t button = 0;
michael@0 456
michael@0 457 switch (pointerUpdateKind) {
michael@0 458 case UI::Input::PointerUpdateKind::PointerUpdateKind_LeftButtonPressed:
michael@0 459 button = WidgetMouseEvent::buttonType::eLeftButton;
michael@0 460 message = NS_MOUSE_BUTTON_DOWN;
michael@0 461 break;
michael@0 462 case UI::Input::PointerUpdateKind::PointerUpdateKind_MiddleButtonPressed:
michael@0 463 button = WidgetMouseEvent::buttonType::eMiddleButton;
michael@0 464 message = NS_MOUSE_BUTTON_DOWN;
michael@0 465 break;
michael@0 466 case UI::Input::PointerUpdateKind::PointerUpdateKind_RightButtonPressed:
michael@0 467 button = WidgetMouseEvent::buttonType::eRightButton;
michael@0 468 message = NS_MOUSE_BUTTON_DOWN;
michael@0 469 break;
michael@0 470 case UI::Input::PointerUpdateKind::PointerUpdateKind_LeftButtonReleased:
michael@0 471 button = WidgetMouseEvent::buttonType::eLeftButton;
michael@0 472 message = NS_MOUSE_BUTTON_UP;
michael@0 473 break;
michael@0 474 case UI::Input::PointerUpdateKind::PointerUpdateKind_MiddleButtonReleased:
michael@0 475 button = WidgetMouseEvent::buttonType::eMiddleButton;
michael@0 476 message = NS_MOUSE_BUTTON_UP;
michael@0 477 break;
michael@0 478 case UI::Input::PointerUpdateKind::PointerUpdateKind_RightButtonReleased:
michael@0 479 button = WidgetMouseEvent::buttonType::eRightButton;
michael@0 480 message = NS_MOUSE_BUTTON_UP;
michael@0 481 break;
michael@0 482 }
michael@0 483
michael@0 484 UpdateInputLevel(LEVEL_PRECISE);
michael@0 485
michael@0 486 WidgetMouseEvent* event =
michael@0 487 new WidgetMouseEvent(true, message, mWidget.Get(),
michael@0 488 WidgetMouseEvent::eReal,
michael@0 489 WidgetMouseEvent::eNormal);
michael@0 490 event->button = button;
michael@0 491 aPoint->get_PointerId(&event->pointerId);
michael@0 492 InitGeckoMouseEventFromPointerPoint(event, aPoint);
michael@0 493 DispatchAsyncEventIgnoreStatus(event);
michael@0 494 }
michael@0 495
michael@0 496 void
michael@0 497 MetroInput::InitTouchEventTouchList(WidgetTouchEvent* aEvent)
michael@0 498 {
michael@0 499 MOZ_ASSERT(aEvent);
michael@0 500 mTouches.Enumerate(&AppendToTouchList,
michael@0 501 static_cast<void*>(&aEvent->touches));
michael@0 502 }
michael@0 503
michael@0 504 bool
michael@0 505 MetroInput::ShouldDeliverInputToRecognizer()
michael@0 506 {
michael@0 507 return mRecognizerWantsEvents;
michael@0 508 }
michael@0 509
michael@0 510 void
michael@0 511 MetroInput::GetAllowedTouchBehavior(WidgetTouchEvent* aTransformedEvent, nsTArray<TouchBehaviorFlags>& aOutBehaviors)
michael@0 512 {
michael@0 513 mWidget->ApzcGetAllowedTouchBehavior(aTransformedEvent, aOutBehaviors);
michael@0 514
michael@0 515 for (uint32_t i = 0; i < aOutBehaviors.Length(); i++) {
michael@0 516 if (aOutBehaviors[i] & AllowedTouchBehavior::UNKNOWN) {
michael@0 517 // performing hit testing fallback: asking content to perform hit testing itself
michael@0 518 // (in spite that this operation has high latency).
michael@0 519 aOutBehaviors[i] = mWidget->ContentGetAllowedTouchBehavior(aTransformedEvent->touches[i]->mRefPoint);
michael@0 520 }
michael@0 521 }
michael@0 522 }
michael@0 523
michael@0 524 bool
michael@0 525 MetroInput::IsTouchBehaviorForbidden(const nsTArray<TouchBehaviorFlags>& aTouchBehaviors)
michael@0 526 {
michael@0 527 for (size_t i = 0; i < aTouchBehaviors.Length(); i++) {
michael@0 528 if (aTouchBehaviors[i] == AllowedTouchBehavior::NONE)
michael@0 529 return true;
michael@0 530 }
michael@0 531
michael@0 532 return false;
michael@0 533 }
michael@0 534
michael@0 535 // This event is raised when the user pushes the left mouse button, presses a
michael@0 536 // pen to the surface, or presses a touch screen.
michael@0 537 HRESULT
michael@0 538 MetroInput::OnPointerPressed(UI::Core::ICoreWindow* aSender,
michael@0 539 UI::Core::IPointerEventArgs* aArgs)
michael@0 540 {
michael@0 541 #ifdef DEBUG_INPUT
michael@0 542 LogFunction();
michael@0 543 #endif
michael@0 544
michael@0 545 WRL::ComPtr<UI::Input::IPointerPoint> currentPoint;
michael@0 546 WRL::ComPtr<Devices::Input::IPointerDevice> device;
michael@0 547 Devices::Input::PointerDeviceType deviceType;
michael@0 548
michael@0 549 aArgs->get_CurrentPoint(currentPoint.GetAddressOf());
michael@0 550 currentPoint->get_PointerDevice(device.GetAddressOf());
michael@0 551 device->get_PointerDeviceType(&deviceType);
michael@0 552
michael@0 553 // For mouse and pen input, simply call our helper function
michael@0 554 if (deviceType !=
michael@0 555 Devices::Input::PointerDeviceType::PointerDeviceType_Touch) {
michael@0 556 OnPointerNonTouch(currentPoint.Get());
michael@0 557 mGestureRecognizer->ProcessDownEvent(currentPoint.Get());
michael@0 558 return S_OK;
michael@0 559 }
michael@0 560
michael@0 561 // This is touch input.
michael@0 562 UpdateInputLevel(LEVEL_IMPRECISE);
michael@0 563
michael@0 564 // Create the new touch point and add it to our event.
michael@0 565 uint32_t pointerId;
michael@0 566 currentPoint->get_PointerId(&pointerId);
michael@0 567 nsRefPtr<Touch> touch = CreateDOMTouch(currentPoint.Get());
michael@0 568 touch->mChanged = true;
michael@0 569 mTouches.Put(pointerId, touch);
michael@0 570
michael@0 571 WidgetTouchEvent* touchEvent =
michael@0 572 new WidgetTouchEvent(true, NS_TOUCH_START, mWidget.Get());
michael@0 573
michael@0 574 if (mTouches.Count() == 1) {
michael@0 575 // If this is the first touchstart of a touch session reset some
michael@0 576 // tracking flags.
michael@0 577 mContentConsumingTouch = false;
michael@0 578 mApzConsumingTouch = false;
michael@0 579 mRecognizerWantsEvents = true;
michael@0 580 mCancelable = true;
michael@0 581 mCanceledIds.Clear();
michael@0 582 } else {
michael@0 583 mCancelable = false;
michael@0 584 }
michael@0 585
michael@0 586 InitTouchEventTouchList(touchEvent);
michael@0 587 DispatchAsyncTouchEvent(touchEvent);
michael@0 588
michael@0 589 if (ShouldDeliverInputToRecognizer()) {
michael@0 590 mGestureRecognizer->ProcessDownEvent(currentPoint.Get());
michael@0 591 }
michael@0 592 return S_OK;
michael@0 593 }
michael@0 594
michael@0 595 void
michael@0 596 MetroInput::AddPointerMoveDataToRecognizer(UI::Core::IPointerEventArgs* aArgs)
michael@0 597 {
michael@0 598 if (ShouldDeliverInputToRecognizer()) {
michael@0 599 WRL::ComPtr<Foundation::Collections::IVector<UI::Input::PointerPoint*>>
michael@0 600 pointerPoints;
michael@0 601 aArgs->GetIntermediatePoints(pointerPoints.GetAddressOf());
michael@0 602 mGestureRecognizer->ProcessMoveEvents(pointerPoints.Get());
michael@0 603 }
michael@0 604 }
michael@0 605
michael@0 606 // This event is raised when the user moves the mouse, moves a pen that is
michael@0 607 // in contact with the surface, or moves a finger that is in contact with
michael@0 608 // a touch screen.
michael@0 609 HRESULT
michael@0 610 MetroInput::OnPointerMoved(UI::Core::ICoreWindow* aSender,
michael@0 611 UI::Core::IPointerEventArgs* aArgs)
michael@0 612 {
michael@0 613 #ifdef DEBUG_INPUT
michael@0 614 LogFunction();
michael@0 615 #endif
michael@0 616
michael@0 617 WRL::ComPtr<UI::Input::IPointerPoint> currentPoint;
michael@0 618 WRL::ComPtr<Devices::Input::IPointerDevice> device;
michael@0 619 Devices::Input::PointerDeviceType deviceType;
michael@0 620
michael@0 621 aArgs->get_CurrentPoint(currentPoint.GetAddressOf());
michael@0 622 currentPoint->get_PointerDevice(device.GetAddressOf());
michael@0 623 device->get_PointerDeviceType(&deviceType);
michael@0 624
michael@0 625 // For mouse and pen input, simply call our helper function
michael@0 626 if (deviceType !=
michael@0 627 Devices::Input::PointerDeviceType::PointerDeviceType_Touch) {
michael@0 628 OnPointerNonTouch(currentPoint.Get());
michael@0 629 AddPointerMoveDataToRecognizer(aArgs);
michael@0 630 return S_OK;
michael@0 631 }
michael@0 632
michael@0 633 // This is touch input.
michael@0 634 UpdateInputLevel(LEVEL_IMPRECISE);
michael@0 635
michael@0 636 // Get the touch associated with this touch point.
michael@0 637 uint32_t pointerId;
michael@0 638 currentPoint->get_PointerId(&pointerId);
michael@0 639 nsRefPtr<Touch> touch = mTouches.Get(pointerId);
michael@0 640
michael@0 641 // Some old drivers cause us to receive a PointerMoved event for a touchId
michael@0 642 // after we've already received a PointerReleased event for that touchId.
michael@0 643 // To work around those busted drivers, we simply ignore TouchMoved events
michael@0 644 // for touchIds that we are not currently tracking. See bug 819223.
michael@0 645 if (!touch) {
michael@0 646 return S_OK;
michael@0 647 }
michael@0 648
michael@0 649 AddPointerMoveDataToRecognizer(aArgs);
michael@0 650
michael@0 651 // If the point hasn't moved, filter it out per the spec. Pres shell does
michael@0 652 // this as well, but we need to know when our first touchmove is going to
michael@0 653 // get delivered so we can check the result.
michael@0 654 if (!HasPointMoved(touch, currentPoint.Get())) {
michael@0 655 return S_OK;
michael@0 656 }
michael@0 657
michael@0 658 touch = CreateDOMTouch(currentPoint.Get());
michael@0 659 touch->mChanged = true;
michael@0 660 // replacing old touch point in mTouches map
michael@0 661 mTouches.Put(pointerId, touch);
michael@0 662
michael@0 663 WidgetTouchEvent* touchEvent =
michael@0 664 new WidgetTouchEvent(true, NS_TOUCH_MOVE, mWidget.Get());
michael@0 665 InitTouchEventTouchList(touchEvent);
michael@0 666 DispatchAsyncTouchEvent(touchEvent);
michael@0 667
michael@0 668 return S_OK;
michael@0 669 }
michael@0 670
michael@0 671 // This event is raised when the user lifts the left mouse button, lifts a
michael@0 672 // pen from the surface, or lifts her/his finger from a touch screen.
michael@0 673 HRESULT
michael@0 674 MetroInput::OnPointerReleased(UI::Core::ICoreWindow* aSender,
michael@0 675 UI::Core::IPointerEventArgs* aArgs)
michael@0 676 {
michael@0 677 #ifdef DEBUG_INPUT
michael@0 678 LogFunction();
michael@0 679 #endif
michael@0 680
michael@0 681 WRL::ComPtr<UI::Input::IPointerPoint> currentPoint;
michael@0 682 WRL::ComPtr<Devices::Input::IPointerDevice> device;
michael@0 683 Devices::Input::PointerDeviceType deviceType;
michael@0 684
michael@0 685 aArgs->get_CurrentPoint(currentPoint.GetAddressOf());
michael@0 686 currentPoint->get_PointerDevice(device.GetAddressOf());
michael@0 687 device->get_PointerDeviceType(&deviceType);
michael@0 688
michael@0 689 // For mouse and pen input, simply call our helper function
michael@0 690 if (deviceType !=
michael@0 691 Devices::Input::PointerDeviceType::PointerDeviceType_Touch) {
michael@0 692 OnPointerNonTouch(currentPoint.Get());
michael@0 693 mGestureRecognizer->ProcessUpEvent(currentPoint.Get());
michael@0 694 return S_OK;
michael@0 695 }
michael@0 696
michael@0 697 // This is touch input.
michael@0 698 UpdateInputLevel(LEVEL_IMPRECISE);
michael@0 699
michael@0 700 // Get the touch associated with this touch point.
michael@0 701 uint32_t pointerId;
michael@0 702 currentPoint->get_PointerId(&pointerId);
michael@0 703 nsRefPtr<Touch> touch = mTouches.Get(pointerId);
michael@0 704
michael@0 705 // Purge any pending moves for this pointer
michael@0 706 if (touch->mChanged) {
michael@0 707 WidgetTouchEvent* touchEvent =
michael@0 708 new WidgetTouchEvent(true, NS_TOUCH_MOVE, mWidget.Get());
michael@0 709 InitTouchEventTouchList(touchEvent);
michael@0 710 DispatchAsyncTouchEvent(touchEvent);
michael@0 711 }
michael@0 712
michael@0 713 // Remove this touch point from our map. Eventually all touch points are
michael@0 714 // removed for this session since we receive released events for every
michael@0 715 // point.
michael@0 716 mTouches.Remove(pointerId);
michael@0 717
michael@0 718 // touchend events only have a single touch; the touch that has been removed
michael@0 719 WidgetTouchEvent* touchEvent =
michael@0 720 new WidgetTouchEvent(true, NS_TOUCH_END, mWidget.Get());
michael@0 721 touchEvent->touches.AppendElement(CreateDOMTouch(currentPoint.Get()));
michael@0 722 DispatchAsyncTouchEvent(touchEvent);
michael@0 723
michael@0 724 if (ShouldDeliverInputToRecognizer()) {
michael@0 725 mGestureRecognizer->ProcessUpEvent(currentPoint.Get());
michael@0 726 }
michael@0 727
michael@0 728 return S_OK;
michael@0 729 }
michael@0 730
michael@0 731 // Tests for chrome vs. content target so we know whether input coordinates need
michael@0 732 // to be transformed through the apz. Eventually this hit testing should move
michael@0 733 // into the apz (bug 918288).
michael@0 734 bool
michael@0 735 MetroInput::HitTestChrome(const LayoutDeviceIntPoint& pt)
michael@0 736 {
michael@0 737 // Confirm this event targets content. We pick this up in browser's input.js.
michael@0 738 WidgetMouseEvent hittest(true, NS_MOUSE_MOZHITTEST, mWidget.Get(),
michael@0 739 WidgetMouseEvent::eReal, WidgetMouseEvent::eNormal);
michael@0 740 hittest.refPoint = pt;
michael@0 741 nsEventStatus status;
michael@0 742 mWidget->DispatchEvent(&hittest, status);
michael@0 743 return (status == nsEventStatus_eConsumeNoDefault);
michael@0 744 }
michael@0 745
michael@0 746 /**
michael@0 747 * Returns true if the position is in chrome, false otherwise.
michael@0 748 */
michael@0 749 bool
michael@0 750 MetroInput::TransformRefPoint(const Foundation::Point& aPosition, LayoutDeviceIntPoint& aRefPointOut)
michael@0 751 {
michael@0 752 // If this event is destined for content we need to transform our ref point through
michael@0 753 // the apz so that zoom can be accounted for.
michael@0 754 aRefPointOut = LayoutDeviceIntPoint::FromUntyped(MetroUtils::LogToPhys(aPosition));
michael@0 755 ScreenIntPoint spt;
michael@0 756 spt.x = aRefPointOut.x;
michael@0 757 spt.y = aRefPointOut.y;
michael@0 758 // This is currently a general contained rect hit test, it may produce a false positive for
michael@0 759 // overlay chrome elements.
michael@0 760 bool apzIntersect = mWidget->ApzHitTest(spt);
michael@0 761 if (!apzIntersect) {
michael@0 762 return true;
michael@0 763 }
michael@0 764 if (HitTestChrome(aRefPointOut)) {
michael@0 765 return true;
michael@0 766 }
michael@0 767 mWidget->ApzTransformGeckoCoordinate(spt, &aRefPointOut);
michael@0 768 return false;
michael@0 769 }
michael@0 770
michael@0 771 void
michael@0 772 MetroInput::TransformTouchEvent(WidgetTouchEvent* aEvent)
michael@0 773 {
michael@0 774 nsTArray< nsRefPtr<dom::Touch> >& touches = aEvent->touches;
michael@0 775 for (uint32_t i = 0; i < touches.Length(); ++i) {
michael@0 776 dom::Touch* touch = touches[i];
michael@0 777 if (touch) {
michael@0 778 LayoutDeviceIntPoint lpt;
michael@0 779 ScreenIntPoint spt;
michael@0 780 spt.x = touch->mRefPoint.x;
michael@0 781 spt.y = touch->mRefPoint.y;
michael@0 782 mWidget->ApzTransformGeckoCoordinate(spt, &lpt);
michael@0 783 touch->mRefPoint.x = lpt.x;
michael@0 784 touch->mRefPoint.y = lpt.y;
michael@0 785 }
michael@0 786 }
michael@0 787 }
michael@0 788
michael@0 789 void
michael@0 790 MetroInput::InitGeckoMouseEventFromPointerPoint(
michael@0 791 WidgetMouseEvent* aEvent,
michael@0 792 UI::Input::IPointerPoint* aPointerPoint)
michael@0 793 {
michael@0 794 NS_ASSERTION(aPointerPoint, "InitGeckoMouseEventFromPointerPoint "
michael@0 795 "called with null PointerPoint!");
michael@0 796
michael@0 797 WRL::ComPtr<UI::Input::IPointerPointProperties> props;
michael@0 798 WRL::ComPtr<Devices::Input::IPointerDevice> device;
michael@0 799 Devices::Input::PointerDeviceType deviceType;
michael@0 800 Foundation::Point position;
michael@0 801 uint64_t timestamp;
michael@0 802 float pressure;
michael@0 803 boolean canBeDoubleTap;
michael@0 804 float tiltX;
michael@0 805 float tiltY;
michael@0 806
michael@0 807 aPointerPoint->get_Position(&position);
michael@0 808 aPointerPoint->get_Timestamp(&timestamp);
michael@0 809 aPointerPoint->get_PointerDevice(device.GetAddressOf());
michael@0 810 device->get_PointerDeviceType(&deviceType);
michael@0 811 aPointerPoint->get_Properties(props.GetAddressOf());
michael@0 812 aPointerPoint->get_PointerId(&aEvent->pointerId);
michael@0 813 props->get_Pressure(&pressure);
michael@0 814 props->get_XTilt(&tiltX);
michael@0 815 props->get_YTilt(&tiltY);
michael@0 816
michael@0 817 mGestureRecognizer->CanBeDoubleTap(aPointerPoint, &canBeDoubleTap);
michael@0 818
michael@0 819 TransformRefPoint(position, aEvent->refPoint);
michael@0 820
michael@0 821 if (!canBeDoubleTap) {
michael@0 822 aEvent->clickCount = 1;
michael@0 823 } else {
michael@0 824 aEvent->clickCount = 2;
michael@0 825 }
michael@0 826 aEvent->pressure = pressure;
michael@0 827 aEvent->tiltX = tiltX;
michael@0 828 aEvent->tiltY = tiltY;
michael@0 829 aEvent->buttons = ButtonsForPointerPoint(aPointerPoint);
michael@0 830
michael@0 831 MozInputSourceFromDeviceType(deviceType, aEvent->inputSource);
michael@0 832 }
michael@0 833
michael@0 834 // This event is raised when a precise pointer moves into the bounding box of
michael@0 835 // our window. For touch input, this will be raised before the PointerPressed
michael@0 836 // event.
michael@0 837 HRESULT
michael@0 838 MetroInput::OnPointerEntered(UI::Core::ICoreWindow* aSender,
michael@0 839 UI::Core::IPointerEventArgs* aArgs)
michael@0 840 {
michael@0 841 #ifdef DEBUG_INPUT
michael@0 842 LogFunction();
michael@0 843 #endif
michael@0 844
michael@0 845 WRL::ComPtr<UI::Input::IPointerPoint> currentPoint;
michael@0 846 WRL::ComPtr<Devices::Input::IPointerDevice> device;
michael@0 847 Devices::Input::PointerDeviceType deviceType;
michael@0 848
michael@0 849 aArgs->get_CurrentPoint(currentPoint.GetAddressOf());
michael@0 850 currentPoint->get_PointerDevice(device.GetAddressOf());
michael@0 851 device->get_PointerDeviceType(&deviceType);
michael@0 852
michael@0 853 // We only dispatch mouseenter and mouseexit events for mouse and pen input.
michael@0 854 if (deviceType !=
michael@0 855 Devices::Input::PointerDeviceType::PointerDeviceType_Touch) {
michael@0 856 WidgetMouseEvent* event =
michael@0 857 new WidgetMouseEvent(true, NS_MOUSE_ENTER, mWidget.Get(),
michael@0 858 WidgetMouseEvent::eReal, WidgetMouseEvent::eNormal);
michael@0 859 UpdateInputLevel(LEVEL_PRECISE);
michael@0 860 InitGeckoMouseEventFromPointerPoint(event, currentPoint.Get());
michael@0 861 DispatchAsyncEventIgnoreStatus(event);
michael@0 862 return S_OK;
michael@0 863 }
michael@0 864 UpdateInputLevel(LEVEL_IMPRECISE);
michael@0 865 return S_OK;
michael@0 866 }
michael@0 867
michael@0 868 // This event is raised when a precise pointer leaves the bounding box of
michael@0 869 // our window. For touch input, this will be raised before the
michael@0 870 // PointerReleased event.
michael@0 871 HRESULT
michael@0 872 MetroInput::OnPointerExited(UI::Core::ICoreWindow* aSender,
michael@0 873 UI::Core::IPointerEventArgs* aArgs)
michael@0 874 {
michael@0 875 #ifdef DEBUG_INPUT
michael@0 876 LogFunction();
michael@0 877 #endif
michael@0 878
michael@0 879 WRL::ComPtr<UI::Input::IPointerPoint> currentPoint;
michael@0 880 WRL::ComPtr<Devices::Input::IPointerDevice> device;
michael@0 881 Devices::Input::PointerDeviceType deviceType;
michael@0 882
michael@0 883 aArgs->get_CurrentPoint(currentPoint.GetAddressOf());
michael@0 884 currentPoint->get_PointerDevice(device.GetAddressOf());
michael@0 885 device->get_PointerDeviceType(&deviceType);
michael@0 886
michael@0 887 // We only dispatch mouseenter and mouseexit events for mouse and pen input.
michael@0 888 if (deviceType !=
michael@0 889 Devices::Input::PointerDeviceType::PointerDeviceType_Touch) {
michael@0 890 WidgetMouseEvent* event =
michael@0 891 new WidgetMouseEvent(true, NS_MOUSE_EXIT, mWidget.Get(),
michael@0 892 WidgetMouseEvent::eReal, WidgetMouseEvent::eNormal);
michael@0 893 UpdateInputLevel(LEVEL_PRECISE);
michael@0 894 InitGeckoMouseEventFromPointerPoint(event, currentPoint.Get());
michael@0 895 DispatchAsyncEventIgnoreStatus(event);
michael@0 896 return S_OK;
michael@0 897 }
michael@0 898 UpdateInputLevel(LEVEL_IMPRECISE);
michael@0 899 return S_OK;
michael@0 900 }
michael@0 901
michael@0 902 // Gecko expects a "finished" event to be sent that has the cumulative
michael@0 903 // changes since the gesture began. The idea is that consumers could hook
michael@0 904 // only this last event and still effectively support magnification and
michael@0 905 // rotation. We accomplish sending this "finished" event by calling our
michael@0 906 // helper function with a cumulative "delta" value.
michael@0 907 //
michael@0 908 // After sending the "finished" event, this function detects and sends
michael@0 909 // swipe gestures.
michael@0 910 HRESULT
michael@0 911 MetroInput::OnManipulationCompleted(
michael@0 912 UI::Input::IGestureRecognizer* aSender,
michael@0 913 UI::Input::IManipulationCompletedEventArgs* aArgs)
michael@0 914 {
michael@0 915 #ifdef DEBUG_INPUT
michael@0 916 LogFunction();
michael@0 917 #endif
michael@0 918
michael@0 919 Devices::Input::PointerDeviceType deviceType;
michael@0 920 aArgs->get_PointerDeviceType(&deviceType);
michael@0 921 if (deviceType ==
michael@0 922 Devices::Input::PointerDeviceType::PointerDeviceType_Mouse) {
michael@0 923 return S_OK;
michael@0 924 }
michael@0 925
michael@0 926 UI::Input::ManipulationDelta delta;
michael@0 927 Foundation::Point position;
michael@0 928
michael@0 929 aArgs->get_Position(&position);
michael@0 930 aArgs->get_Cumulative(&delta);
michael@0 931
michael@0 932 // We check that the distance the user's finger traveled and the
michael@0 933 // velocity with which it traveled exceed our thresholds for
michael@0 934 // classifying the movement as a swipe.
michael@0 935 UI::Input::ManipulationVelocities velocities;
michael@0 936 aArgs->get_Velocities(&velocities);
michael@0 937
michael@0 938 bool isHorizontalSwipe =
michael@0 939 abs(velocities.Linear.X) >= SWIPE_MIN_VELOCITY
michael@0 940 && abs(delta.Translation.X) >= SWIPE_MIN_DISTANCE;
michael@0 941 bool isVerticalSwipe =
michael@0 942 abs(velocities.Linear.Y) >= SWIPE_MIN_VELOCITY
michael@0 943 && abs(delta.Translation.Y) >= SWIPE_MIN_DISTANCE;
michael@0 944
michael@0 945 // If our thresholds were exceeded for both a vertical and a horizontal
michael@0 946 // swipe, it means the user is flinging her/his finger around and we
michael@0 947 // should just ignore the input.
michael@0 948 if (isHorizontalSwipe && isVerticalSwipe) {
michael@0 949 return S_OK;
michael@0 950 }
michael@0 951
michael@0 952 if (isHorizontalSwipe) {
michael@0 953 WidgetSimpleGestureEvent* swipeEvent =
michael@0 954 new WidgetSimpleGestureEvent(true, NS_SIMPLE_GESTURE_SWIPE,
michael@0 955 mWidget.Get());
michael@0 956 swipeEvent->direction = delta.Translation.X > 0
michael@0 957 ? nsIDOMSimpleGestureEvent::DIRECTION_RIGHT
michael@0 958 : nsIDOMSimpleGestureEvent::DIRECTION_LEFT;
michael@0 959 swipeEvent->delta = delta.Translation.X;
michael@0 960 swipeEvent->inputSource = nsIDOMMouseEvent::MOZ_SOURCE_TOUCH;
michael@0 961 swipeEvent->refPoint = LayoutDeviceIntPoint::FromUntyped(MetroUtils::LogToPhys(position));
michael@0 962 DispatchAsyncEventIgnoreStatus(swipeEvent);
michael@0 963 }
michael@0 964
michael@0 965 if (isVerticalSwipe) {
michael@0 966 WidgetSimpleGestureEvent* swipeEvent =
michael@0 967 new WidgetSimpleGestureEvent(true, NS_SIMPLE_GESTURE_SWIPE,
michael@0 968 mWidget.Get());
michael@0 969 swipeEvent->direction = delta.Translation.Y > 0
michael@0 970 ? nsIDOMSimpleGestureEvent::DIRECTION_DOWN
michael@0 971 : nsIDOMSimpleGestureEvent::DIRECTION_UP;
michael@0 972 swipeEvent->delta = delta.Translation.Y;
michael@0 973 swipeEvent->inputSource = nsIDOMMouseEvent::MOZ_SOURCE_TOUCH;
michael@0 974 swipeEvent->refPoint = LayoutDeviceIntPoint::FromUntyped(MetroUtils::LogToPhys(position));
michael@0 975 DispatchAsyncEventIgnoreStatus(swipeEvent);
michael@0 976 }
michael@0 977
michael@0 978 return S_OK;
michael@0 979 }
michael@0 980
michael@0 981 // This event is raised when a sequence of pointer events has been
michael@0 982 // interpreted by the GestureRecognizer as a tap (this could be a mouse
michael@0 983 // click, a pen tap, or a tap on a touch surface).
michael@0 984 HRESULT
michael@0 985 MetroInput::OnTapped(UI::Input::IGestureRecognizer* aSender,
michael@0 986 UI::Input::ITappedEventArgs* aArgs)
michael@0 987 {
michael@0 988 #ifdef DEBUG_INPUT
michael@0 989 LogFunction();
michael@0 990 #endif
michael@0 991
michael@0 992 Devices::Input::PointerDeviceType deviceType;
michael@0 993 aArgs->get_PointerDeviceType(&deviceType);
michael@0 994
michael@0 995 unsigned int tapCount;
michael@0 996 aArgs->get_TapCount(&tapCount);
michael@0 997
michael@0 998 // For mouse and pen input, we send mousedown/mouseup/mousemove
michael@0 999 // events as soon as we detect the input event. For touch input, a set of
michael@0 1000 // mousedown/mouseup events will be sent only once a tap has been detected.
michael@0 1001 if (deviceType != Devices::Input::PointerDeviceType::PointerDeviceType_Touch) {
michael@0 1002 return S_OK;
michael@0 1003 }
michael@0 1004
michael@0 1005 Foundation::Point position;
michael@0 1006 aArgs->get_Position(&position);
michael@0 1007 HandleTap(position, tapCount);
michael@0 1008 return S_OK;
michael@0 1009 }
michael@0 1010
michael@0 1011 // This event is raised when a sequence of pointer events has been
michael@0 1012 // interpreted by the GestureRecognizer as a right tap.
michael@0 1013 // This could be a mouse right-click, a right-click on a pen, or
michael@0 1014 // a tap-and-hold on a touch surface.
michael@0 1015 HRESULT
michael@0 1016 MetroInput::OnRightTapped(UI::Input::IGestureRecognizer* aSender,
michael@0 1017 UI::Input::IRightTappedEventArgs* aArgs)
michael@0 1018 {
michael@0 1019 #ifdef DEBUG_INPUT
michael@0 1020 LogFunction();
michael@0 1021 #endif
michael@0 1022
michael@0 1023 Devices::Input::PointerDeviceType deviceType;
michael@0 1024 aArgs->get_PointerDeviceType(&deviceType);
michael@0 1025
michael@0 1026 Foundation::Point position;
michael@0 1027 aArgs->get_Position(&position);
michael@0 1028 HandleLongTap(position);
michael@0 1029
michael@0 1030 return S_OK;
michael@0 1031 }
michael@0 1032
michael@0 1033 void
michael@0 1034 MetroInput::HandleTap(const Foundation::Point& aPoint, unsigned int aTapCount)
michael@0 1035 {
michael@0 1036 #ifdef DEBUG_INPUT
michael@0 1037 LogFunction();
michael@0 1038 #endif
michael@0 1039
michael@0 1040 LayoutDeviceIntPoint refPoint;
michael@0 1041 TransformRefPoint(aPoint, refPoint);
michael@0 1042
michael@0 1043 WidgetMouseEvent* mouseEvent =
michael@0 1044 new WidgetMouseEvent(true, NS_MOUSE_MOVE, mWidget.Get(),
michael@0 1045 WidgetMouseEvent::eReal, WidgetMouseEvent::eNormal);
michael@0 1046 mouseEvent->refPoint = refPoint;
michael@0 1047 mouseEvent->clickCount = aTapCount;
michael@0 1048 mouseEvent->inputSource = nsIDOMMouseEvent::MOZ_SOURCE_TOUCH;
michael@0 1049 DispatchAsyncEventIgnoreStatus(mouseEvent);
michael@0 1050
michael@0 1051 mouseEvent =
michael@0 1052 new WidgetMouseEvent(true, NS_MOUSE_BUTTON_DOWN, mWidget.Get(),
michael@0 1053 WidgetMouseEvent::eReal, WidgetMouseEvent::eNormal);
michael@0 1054 mouseEvent->refPoint = refPoint;
michael@0 1055 mouseEvent->clickCount = aTapCount;
michael@0 1056 mouseEvent->inputSource = nsIDOMMouseEvent::MOZ_SOURCE_TOUCH;
michael@0 1057 mouseEvent->button = WidgetMouseEvent::buttonType::eLeftButton;
michael@0 1058 DispatchAsyncEventIgnoreStatus(mouseEvent);
michael@0 1059
michael@0 1060 mouseEvent =
michael@0 1061 new WidgetMouseEvent(true, NS_MOUSE_BUTTON_UP, mWidget.Get(),
michael@0 1062 WidgetMouseEvent::eReal, WidgetMouseEvent::eNormal);
michael@0 1063 mouseEvent->refPoint = refPoint;
michael@0 1064 mouseEvent->clickCount = aTapCount;
michael@0 1065 mouseEvent->inputSource = nsIDOMMouseEvent::MOZ_SOURCE_TOUCH;
michael@0 1066 mouseEvent->button = WidgetMouseEvent::buttonType::eLeftButton;
michael@0 1067 DispatchAsyncEventIgnoreStatus(mouseEvent);
michael@0 1068
michael@0 1069 // Make sure all gecko events are dispatched and the dom is up to date
michael@0 1070 // so that when ui automation comes in looking for focus info it gets
michael@0 1071 // the right information.
michael@0 1072 MetroAppShell::MarkEventQueueForPurge();
michael@0 1073 }
michael@0 1074
michael@0 1075 void
michael@0 1076 MetroInput::HandleLongTap(const Foundation::Point& aPoint)
michael@0 1077 {
michael@0 1078 #ifdef DEBUG_INPUT
michael@0 1079 LogFunction();
michael@0 1080 #endif
michael@0 1081 LayoutDeviceIntPoint refPoint;
michael@0 1082 TransformRefPoint(aPoint, refPoint);
michael@0 1083
michael@0 1084 WidgetMouseEvent* contextEvent =
michael@0 1085 new WidgetMouseEvent(true, NS_CONTEXTMENU, mWidget.Get(),
michael@0 1086 WidgetMouseEvent::eReal, WidgetMouseEvent::eNormal);
michael@0 1087 contextEvent->refPoint = refPoint;
michael@0 1088 contextEvent->inputSource = nsIDOMMouseEvent::MOZ_SOURCE_TOUCH;
michael@0 1089 DispatchAsyncEventIgnoreStatus(contextEvent);
michael@0 1090 }
michael@0 1091
michael@0 1092 /**
michael@0 1093 * Implementation Details
michael@0 1094 */
michael@0 1095 nsEventStatus MetroInput::sThrowawayStatus;
michael@0 1096
michael@0 1097 void
michael@0 1098 MetroInput::DispatchAsyncEventIgnoreStatus(WidgetInputEvent* aEvent)
michael@0 1099 {
michael@0 1100 aEvent->time = ::GetMessageTime();
michael@0 1101 mModifierKeyState.Update();
michael@0 1102 mModifierKeyState.InitInputEvent(*aEvent);
michael@0 1103 mInputEventQueue.Push(aEvent);
michael@0 1104 nsCOMPtr<nsIRunnable> runnable =
michael@0 1105 NS_NewRunnableMethod(this, &MetroInput::DeliverNextQueuedEventIgnoreStatus);
michael@0 1106 NS_DispatchToCurrentThread(runnable);
michael@0 1107 }
michael@0 1108
michael@0 1109 void
michael@0 1110 MetroInput::DeliverNextQueuedEventIgnoreStatus()
michael@0 1111 {
michael@0 1112 nsAutoPtr<WidgetGUIEvent> event =
michael@0 1113 static_cast<WidgetGUIEvent*>(mInputEventQueue.PopFront());
michael@0 1114 MOZ_ASSERT(event.get());
michael@0 1115 DispatchEventIgnoreStatus(event.get());
michael@0 1116
michael@0 1117 // Let app shell know we've delivered that last input we wanted purged
michael@0 1118 // via a call to MarkEventQueueForPurge().
michael@0 1119 if (event->message == NS_MOUSE_BUTTON_UP) {
michael@0 1120 MetroAppShell::InputEventsDispatched();
michael@0 1121 }
michael@0 1122
michael@0 1123 // Clear :hover/:active states for mouse events generated by HandleTap
michael@0 1124 WidgetMouseEvent* mouseEvent = event.get()->AsMouseEvent();
michael@0 1125 if (!mouseEvent) {
michael@0 1126 return;
michael@0 1127 }
michael@0 1128 if (mouseEvent->message != NS_MOUSE_BUTTON_UP ||
michael@0 1129 mouseEvent->inputSource != nsIDOMMouseEvent::MOZ_SOURCE_TOUCH) {
michael@0 1130 return;
michael@0 1131 }
michael@0 1132 nsCOMPtr<nsIPresShell> presShell = mWidget->GetPresShell();
michael@0 1133 if (presShell) {
michael@0 1134 EventStateManager* esm = presShell->GetPresContext()->EventStateManager();
michael@0 1135 if (esm) {
michael@0 1136 esm->SetContentState(nullptr, NS_EVENT_STATE_HOVER);
michael@0 1137 }
michael@0 1138 }
michael@0 1139 }
michael@0 1140
michael@0 1141 void
michael@0 1142 MetroInput::DispatchAsyncTouchEvent(WidgetTouchEvent* aEvent)
michael@0 1143 {
michael@0 1144 aEvent->time = ::GetMessageTime();
michael@0 1145 mModifierKeyState.Update();
michael@0 1146 mModifierKeyState.InitInputEvent(*aEvent);
michael@0 1147 mInputEventQueue.Push(aEvent);
michael@0 1148 nsCOMPtr<nsIRunnable> runnable =
michael@0 1149 NS_NewRunnableMethod(this, &MetroInput::DeliverNextQueuedTouchEvent);
michael@0 1150 NS_DispatchToCurrentThread(runnable);
michael@0 1151 }
michael@0 1152
michael@0 1153 static void DumpTouchIds(const char* aTarget, WidgetTouchEvent* aEvent)
michael@0 1154 {
michael@0 1155 // comment out for touch moves
michael@0 1156 if (aEvent->message == NS_TOUCH_MOVE) {
michael@0 1157 return;
michael@0 1158 }
michael@0 1159 switch(aEvent->message) {
michael@0 1160 case NS_TOUCH_START:
michael@0 1161 WinUtils::Log("DumpTouchIds: NS_TOUCH_START block");
michael@0 1162 break;
michael@0 1163 case NS_TOUCH_MOVE:
michael@0 1164 WinUtils::Log("DumpTouchIds: NS_TOUCH_MOVE block");
michael@0 1165 break;
michael@0 1166 case NS_TOUCH_END:
michael@0 1167 WinUtils::Log("DumpTouchIds: NS_TOUCH_END block");
michael@0 1168 break;
michael@0 1169 case NS_TOUCH_CANCEL:
michael@0 1170 WinUtils::Log("DumpTouchIds: NS_TOUCH_CANCEL block");
michael@0 1171 break;
michael@0 1172 }
michael@0 1173 nsTArray< nsRefPtr<dom::Touch> >& touches = aEvent->touches;
michael@0 1174 for (uint32_t i = 0; i < touches.Length(); ++i) {
michael@0 1175 dom::Touch* touch = touches[i];
michael@0 1176 if (!touch) {
michael@0 1177 continue;
michael@0 1178 }
michael@0 1179 int32_t id = touch->Identifier();
michael@0 1180 WinUtils::Log(" id=%d target=%s", id, aTarget);
michael@0 1181 }
michael@0 1182 }
michael@0 1183
michael@0 1184 static void DumpTouchBehavior(nsTArray<uint32_t>& aBehavior)
michael@0 1185 {
michael@0 1186 WinUtils::Log("DumpTouchBehavior: Touch behavior flags set for current touch session:");
michael@0 1187 for (uint32_t i = 0; i < aBehavior.Length(); i++) {
michael@0 1188 if (mozilla::layers::AllowedTouchBehavior::VERTICAL_PAN & aBehavior[i]) {
michael@0 1189 WinUtils::Log("VERTICAL_PAN");
michael@0 1190 }
michael@0 1191
michael@0 1192 if (mozilla::layers::AllowedTouchBehavior::HORIZONTAL_PAN & aBehavior[i]) {
michael@0 1193 WinUtils::Log("HORIZONTAL_PAN");
michael@0 1194 }
michael@0 1195
michael@0 1196 if (mozilla::layers::AllowedTouchBehavior::UNKNOWN & aBehavior[i]) {
michael@0 1197 WinUtils::Log("UNKNOWN");
michael@0 1198 }
michael@0 1199
michael@0 1200 if ((mozilla::layers::AllowedTouchBehavior::NONE & aBehavior[i]) == 0) {
michael@0 1201 WinUtils::Log("NONE");
michael@0 1202 }
michael@0 1203 }
michael@0 1204 }
michael@0 1205
michael@0 1206 /*
michael@0 1207 * nsPreShell's processing of WidgetTouchEvent events:
michael@0 1208 *
michael@0 1209 * NS_TOUCH_START:
michael@0 1210 * Interprets a single touch point as the first touch point of a block and will reset its
michael@0 1211 * queue when it receives this. For multiple touch points it sets all points in its queue
michael@0 1212 * and marks new points as changed.
michael@0 1213 * NS_TOUCH_MOVE:
michael@0 1214 * Uses the equality tests in dom::Touch to test if a touch point has changed (moved).
michael@0 1215 * If a point has moved, keeps this touch point in the event, otherwise it removes
michael@0 1216 * the touch point. Note if no points have changed, it exits without sending a dom event.
michael@0 1217 * NS_TOUCH_CANCEL/NS_TOUCH_END
michael@0 1218 * Assumes any point in touchEvent->touches has been removed or canceled.
michael@0 1219 */
michael@0 1220
michael@0 1221 //#define DUMP_TOUCH_IDS(aTarget, aEvent) DumpTouchIds(aTarget, aEvent)
michael@0 1222 #define DUMP_TOUCH_IDS(...)
michael@0 1223
michael@0 1224 //#define DUMP_ALLOWED_TOUCH_BEHAVIOR(aBehavior) DumpTouchBehavior(aBehavior)
michael@0 1225 #define DUMP_ALLOWED_TOUCH_BEHAVIOR(...)
michael@0 1226
michael@0 1227 void
michael@0 1228 MetroInput::HandleFirstTouchStartEvent(WidgetTouchEvent* aEvent)
michael@0 1229 {
michael@0 1230 nsEventStatus contentStatus = nsEventStatus_eIgnore;
michael@0 1231
michael@0 1232 WidgetTouchEvent transformedEvent(*aEvent);
michael@0 1233 DUMP_TOUCH_IDS("APZC(1)", aEvent);
michael@0 1234 mWidget->ApzReceiveInputEvent(&transformedEvent, &mTargetAPZCGuid);
michael@0 1235
michael@0 1236 if (gTouchActionPropertyEnabled) {
michael@0 1237 nsTArray<TouchBehaviorFlags> touchBehaviors;
michael@0 1238 // Retrieving touch behaviors from apzctm and from the content (if needed)
michael@0 1239 // then setting it back to the apzc. The apzc we retrieved touch behaviors
michael@0 1240 // from and we're setting to may changes if there are multiple touches (in that
michael@0 1241 // case apzctm needs to take common ancestor of them).
michael@0 1242 GetAllowedTouchBehavior(&transformedEvent, touchBehaviors);
michael@0 1243 // Setting the touch behaviors to the apzc that will be responsible
michael@0 1244 // for interpreting it. It may be not the same apzc we retrieved touch
michael@0 1245 // action values from. E.g. for zooming we're taking parent apzc of a few ones
michael@0 1246 // that were touched but touch behaviors would be taken from childs.
michael@0 1247 DUMP_ALLOWED_TOUCH_BEHAVIOR(touchBehaviors);
michael@0 1248 mWidget->ApzcSetAllowedTouchBehavior(mTargetAPZCGuid, touchBehaviors);
michael@0 1249 if (IsTouchBehaviorForbidden(touchBehaviors)) {
michael@0 1250 mContentConsumingTouch = true;
michael@0 1251 }
michael@0 1252 }
michael@0 1253
michael@0 1254 DUMP_TOUCH_IDS("DOM(2)", aEvent);
michael@0 1255 mWidget->DispatchEvent(&transformedEvent, contentStatus);
michael@0 1256 if (nsEventStatus_eConsumeNoDefault == contentStatus) {
michael@0 1257 mContentConsumingTouch = true;
michael@0 1258 }
michael@0 1259
michael@0 1260 if (mContentConsumingTouch) {
michael@0 1261 mCancelable = false;
michael@0 1262 mWidget->ApzContentConsumingTouch(mTargetAPZCGuid);
michael@0 1263 DispatchTouchCancel(aEvent);
michael@0 1264 }
michael@0 1265
michael@0 1266 // Disable gesture based events (taps, swipes, rotation) if
michael@0 1267 // preventDefault is called on touchstart.
michael@0 1268 mRecognizerWantsEvents = !(nsEventStatus_eConsumeNoDefault == contentStatus);
michael@0 1269
michael@0 1270 // If content is consuming touch don't generate any gesture based
michael@0 1271 // input - clear the recognizer state without sending any events.
michael@0 1272 if (!ShouldDeliverInputToRecognizer()) {
michael@0 1273 mGestureRecognizer->CompleteGesture();
michael@0 1274 }
michael@0 1275 }
michael@0 1276
michael@0 1277 void
michael@0 1278 MetroInput::HandleFirstTouchMoveEvent(WidgetTouchEvent* aEvent)
michael@0 1279 {
michael@0 1280 mCancelable = false;
michael@0 1281
michael@0 1282 nsEventStatus contentStatus = nsEventStatus_eIgnore;
michael@0 1283 nsEventStatus apzcStatus = nsEventStatus_eIgnore;
michael@0 1284
michael@0 1285 WidgetTouchEvent transformedEvent(*aEvent);
michael@0 1286 DUMP_TOUCH_IDS("APZC(2)", aEvent);
michael@0 1287 apzcStatus = mWidget->ApzReceiveInputEvent(&transformedEvent, &mTargetAPZCGuid);
michael@0 1288
michael@0 1289 // We need to dispatch here only touch event, not pointer one.
michael@0 1290 // That's because according to the spec pointer events doesn't imply pointermove event
michael@0 1291 // between pointerdown and pointercancel (If default touch behavior is triggered).
michael@0 1292 // But at the same time we need to dispatch at least touchmove event to let content to
michael@0 1293 // consume it (or not consume).
michael@0 1294 // TODO: determine how to dispatch only one kind of events: currently there are two options:
michael@0 1295 // 1) Create two separate instances of the WidgetTouchEvent and WidgetPointerEvent and
michael@0 1296 // dispatch them separately.
michael@0 1297 // 2) Add a boolean flag to the WidgetTouchEvent that states whether this event should produce
michael@0 1298 // both touch and pointer event or only touch one.
michael@0 1299 // Anyway it's worth to add this stuff only after patches from bug 822898 (Pointer events) are
michael@0 1300 // fully commited.
michael@0 1301 DUMP_TOUCH_IDS("DOM(3)", aEvent);
michael@0 1302 mWidget->DispatchEvent(&transformedEvent, contentStatus);
michael@0 1303
michael@0 1304 // Checking content result first since content can override apzc wish and disallow apzc touch
michael@0 1305 // behavior (via preventDefault).
michael@0 1306 if (nsEventStatus_eConsumeNoDefault == contentStatus) {
michael@0 1307 // Touchmove handler consumed touch.
michael@0 1308 mContentConsumingTouch = true;
michael@0 1309 } else if (nsEventStatus_eConsumeNoDefault == apzcStatus) {
michael@0 1310 // Apzc triggered default behavior.
michael@0 1311 mApzConsumingTouch = true;
michael@0 1312 }
michael@0 1313
michael@0 1314 // Let the apz know if content wants to consume touch events, or cancel
michael@0 1315 // the touch block for content.
michael@0 1316 if (mContentConsumingTouch) {
michael@0 1317 mWidget->ApzContentConsumingTouch(mTargetAPZCGuid);
michael@0 1318 DispatchTouchCancel(aEvent);
michael@0 1319 } else {
michael@0 1320 mWidget->ApzContentIgnoringTouch(mTargetAPZCGuid);
michael@0 1321 }
michael@0 1322
michael@0 1323 if (mApzConsumingTouch) {
michael@0 1324 // Dispatching cancel to the content.
michael@0 1325 DispatchTouchCancel(&transformedEvent);
michael@0 1326 }
michael@0 1327 }
michael@0 1328
michael@0 1329 void
michael@0 1330 MetroInput::DeliverNextQueuedTouchEvent()
michael@0 1331 {
michael@0 1332 /*
michael@0 1333 * We go through states here and make different decisions in each:
michael@0 1334 *
michael@0 1335 * 1) Hit test for apz on first touchstart
michael@0 1336 * If non-apzc content/chrome is the target simplify event delivery from
michael@0 1337 * that point on by directing all input to chrome, bypassing the apz.
michael@0 1338 * 2) Process first touchstart and touchmove events
michael@0 1339 * If touch behavior value associated with the TouchStart's touches doesn't
michael@0 1340 * allow zooming or panning we explicitly set mContentConsumingTouch to true.
michael@0 1341 * Otherwise check the result and set mContentConsumingTouch appropriately.
michael@0 1342 * Deliver touch events to the apz (ignoring return result) and to content.
michael@0 1343 * 3) If mContentConsumingTouch is true: deliver touch to content after
michael@0 1344 * transforming through the apz. Also let the apz know content is
michael@0 1345 * consuming touch and deliver cancel event to apz.
michael@0 1346 * 4) If mContentConsumingTouch is false: check the result from the apz and
michael@0 1347 * set mApzConsumingTouch appropriately.
michael@0 1348 * 5) If mApzConsumingTouch is true: send a touchcancel to content
michael@0 1349 * and deliver all events to the apz. If the apz is doing something with
michael@0 1350 * the events we can save ourselves the overhead of delivering dom events.
michael@0 1351 *
michael@0 1352 * Notes:
michael@0 1353 * - never rely on the contents of mTouches here, since this is a delayed
michael@0 1354 * callback. mTouches will likely have been modified.
michael@0 1355 */
michael@0 1356 nsEventStatus status = nsEventStatus_eIgnore;
michael@0 1357
michael@0 1358 WidgetTouchEvent* event =
michael@0 1359 static_cast<WidgetTouchEvent*>(mInputEventQueue.PopFront());
michael@0 1360 MOZ_ASSERT(event);
michael@0 1361
michael@0 1362 AutoDeleteEvent wrap(event);
michael@0 1363
michael@0 1364 // Test for non-apz vs. apz target. To do this we only use the first touch
michael@0 1365 // point since that will be the input batch target. Cache this for touch events
michael@0 1366 // since HitTestChrome has to send a dom event.
michael@0 1367 if (mCancelable && event->message == NS_TOUCH_START) {
michael@0 1368 nsRefPtr<Touch> touch = event->touches[0];
michael@0 1369 LayoutDeviceIntPoint pt = LayoutDeviceIntPoint::FromUntyped(touch->mRefPoint);
michael@0 1370 // This is currently a general contained rect hit test, it may produce a false
michael@0 1371 // positive for overlay chrome elements. Also, some content pages won't support
michael@0 1372 // apzc, so this may be false for content as well.
michael@0 1373 bool apzIntersect = mWidget->ApzHitTest(mozilla::ScreenIntPoint(pt.x, pt.y));
michael@0 1374 mNonApzTargetForTouch = (!apzIntersect || HitTestChrome(pt));
michael@0 1375 }
michael@0 1376
michael@0 1377 // If this event is destined for dom, deliver it directly there bypassing
michael@0 1378 // the apz.
michael@0 1379 if (mNonApzTargetForTouch) {
michael@0 1380 DUMP_TOUCH_IDS("DOM(1)", event);
michael@0 1381 mWidget->DispatchEvent(event, status);
michael@0 1382 if (mCancelable) {
michael@0 1383 // Disable gesture based events (taps, swipes, rotation) if
michael@0 1384 // preventDefault is called on touchstart.
michael@0 1385 if (nsEventStatus_eConsumeNoDefault == status) {
michael@0 1386 mRecognizerWantsEvents = false;
michael@0 1387 mGestureRecognizer->CompleteGesture();
michael@0 1388 }
michael@0 1389 if (event->message == NS_TOUCH_MOVE) {
michael@0 1390 mCancelable = false;
michael@0 1391 }
michael@0 1392 }
michael@0 1393 return;
michael@0 1394 }
michael@0 1395
michael@0 1396 if (mCancelable && event->message == NS_TOUCH_START) {
michael@0 1397 HandleFirstTouchStartEvent(event);
michael@0 1398 return;
michael@0 1399 } else if (mCancelable && event->message == NS_TOUCH_MOVE) {
michael@0 1400 HandleFirstTouchMoveEvent(event);
michael@0 1401 return;
michael@0 1402 }
michael@0 1403 // Let TouchEnd events go through even if mCancelable is true since we
michael@0 1404 // don't need to check whether it is prevented by content or consumed
michael@0 1405 // by apzc.
michael@0 1406
michael@0 1407 // If content is consuming touch, we may need to transform event coords
michael@0 1408 // through the apzc before sending to the dom. Otherwise send the event
michael@0 1409 // to apzc.
michael@0 1410 if (mContentConsumingTouch) {
michael@0 1411 // Only translate if we're dealing with web content that's transformed
michael@0 1412 // by the apzc.
michael@0 1413 TransformTouchEvent(event);
michael@0 1414 DUMP_TOUCH_IDS("DOM(4)", event);
michael@0 1415 mWidget->DispatchEvent(event, status);
michael@0 1416 return;
michael@0 1417 }
michael@0 1418
michael@0 1419 DUMP_TOUCH_IDS("APZC(3)", event);
michael@0 1420 status = mWidget->ApzReceiveInputEvent(event, nullptr);
michael@0 1421
michael@0 1422 // If we're getting a new touch (touch start) after some touch start/move
michael@0 1423 // events we need to reset touch behavior for touches.
michael@0 1424 if (gTouchActionPropertyEnabled && event->message == NS_TOUCH_START) {
michael@0 1425 nsTArray<TouchBehaviorFlags> touchBehaviors;
michael@0 1426 GetAllowedTouchBehavior(event, touchBehaviors);
michael@0 1427 DUMP_ALLOWED_TOUCH_BEHAVIOR(touchBehaviors);
michael@0 1428 mWidget->ApzcSetAllowedTouchBehavior(mTargetAPZCGuid, touchBehaviors);
michael@0 1429 }
michael@0 1430
michael@0 1431 // Send the event to content unless APZC is consuming it.
michael@0 1432 if (!mApzConsumingTouch) {
michael@0 1433 if (status == nsEventStatus_eConsumeNoDefault) {
michael@0 1434 mApzConsumingTouch = true;
michael@0 1435 DispatchTouchCancel(event);
michael@0 1436 return;
michael@0 1437 }
michael@0 1438 TransformTouchEvent(event);
michael@0 1439 DUMP_TOUCH_IDS("DOM(5)", event);
michael@0 1440 mWidget->DispatchEvent(event, status);
michael@0 1441 }
michael@0 1442 }
michael@0 1443
michael@0 1444 void
michael@0 1445 MetroInput::DispatchTouchCancel(WidgetTouchEvent* aEvent)
michael@0 1446 {
michael@0 1447 MOZ_ASSERT(aEvent);
michael@0 1448 // Send a touchcancel for each pointer id we have a corresponding start
michael@0 1449 // for. Note we can't rely on mTouches here since touchends remove points
michael@0 1450 // from it.
michael@0 1451 WidgetTouchEvent touchEvent(true, NS_TOUCH_CANCEL, mWidget.Get());
michael@0 1452 nsTArray< nsRefPtr<dom::Touch> >& touches = aEvent->touches;
michael@0 1453 for (uint32_t i = 0; i < touches.Length(); ++i) {
michael@0 1454 dom::Touch* touch = touches[i];
michael@0 1455 if (!touch) {
michael@0 1456 continue;
michael@0 1457 }
michael@0 1458 int32_t id = touch->Identifier();
michael@0 1459 if (mCanceledIds.Contains(id)) {
michael@0 1460 continue;
michael@0 1461 }
michael@0 1462 mCanceledIds.AppendElement(id);
michael@0 1463 touchEvent.touches.AppendElement(touch);
michael@0 1464 }
michael@0 1465 if (!touchEvent.touches.Length()) {
michael@0 1466 return;
michael@0 1467 }
michael@0 1468 if (mContentConsumingTouch) {
michael@0 1469 DUMP_TOUCH_IDS("APZC(4)", &touchEvent);
michael@0 1470 mWidget->ApzReceiveInputEvent(&touchEvent, nullptr);
michael@0 1471 } else {
michael@0 1472 DUMP_TOUCH_IDS("DOM(6)", &touchEvent);
michael@0 1473 mWidget->DispatchEvent(&touchEvent, sThrowawayStatus);
michael@0 1474 }
michael@0 1475 }
michael@0 1476
michael@0 1477 void
michael@0 1478 MetroInput::DispatchEventIgnoreStatus(WidgetGUIEvent *aEvent)
michael@0 1479 {
michael@0 1480 mWidget->DispatchEvent(aEvent, sThrowawayStatus);
michael@0 1481 }
michael@0 1482
michael@0 1483 void
michael@0 1484 MetroInput::UnregisterInputEvents() {
michael@0 1485 // Unregister ourselves for the edge swipe event
michael@0 1486 WRL::ComPtr<UI::Input::IEdgeGestureStatics> edgeStatics;
michael@0 1487 if (SUCCEEDED(Foundation::GetActivationFactory(
michael@0 1488 WRL::Wrappers::HStringReference(
michael@0 1489 RuntimeClass_Windows_UI_Input_EdgeGesture).Get(),
michael@0 1490 edgeStatics.GetAddressOf()))) {
michael@0 1491 WRL::ComPtr<UI::Input::IEdgeGesture> edge;
michael@0 1492 if (SUCCEEDED(edgeStatics->GetForCurrentView(edge.GetAddressOf()))) {
michael@0 1493 edge->remove_Starting(mTokenEdgeStarted);
michael@0 1494 edge->remove_Canceled(mTokenEdgeCanceled);
michael@0 1495 edge->remove_Completed(mTokenEdgeCompleted);
michael@0 1496 }
michael@0 1497 }
michael@0 1498 // Unregister ourselves from the window events. This is extremely important;
michael@0 1499 // once this object is destroyed we don't want Windows to try to send events
michael@0 1500 // to it.
michael@0 1501 mWindow->remove_PointerPressed(mTokenPointerPressed);
michael@0 1502 mWindow->remove_PointerReleased(mTokenPointerReleased);
michael@0 1503 mWindow->remove_PointerMoved(mTokenPointerMoved);
michael@0 1504 mWindow->remove_PointerEntered(mTokenPointerEntered);
michael@0 1505 mWindow->remove_PointerExited(mTokenPointerExited);
michael@0 1506
michael@0 1507 // Unregistering from the gesture recognizer events probably isn't as
michael@0 1508 // necessary since we're about to destroy the gesture recognizer, but
michael@0 1509 // it can't hurt.
michael@0 1510 mGestureRecognizer->remove_ManipulationCompleted(
michael@0 1511 mTokenManipulationCompleted);
michael@0 1512 mGestureRecognizer->remove_Tapped(mTokenTapped);
michael@0 1513 mGestureRecognizer->remove_RightTapped(mTokenRightTapped);
michael@0 1514 }
michael@0 1515
michael@0 1516 void
michael@0 1517 MetroInput::RegisterInputEvents()
michael@0 1518 {
michael@0 1519 NS_ASSERTION(mWindow, "Must have a window to register for input events!");
michael@0 1520 NS_ASSERTION(mGestureRecognizer,
michael@0 1521 "Must have a GestureRecognizer for input events!");
michael@0 1522 // Register for edge swipe
michael@0 1523 WRL::ComPtr<UI::Input::IEdgeGestureStatics> edgeStatics;
michael@0 1524 Foundation::GetActivationFactory(
michael@0 1525 WRL::Wrappers::HStringReference(
michael@0 1526 RuntimeClass_Windows_UI_Input_EdgeGesture)
michael@0 1527 .Get(),
michael@0 1528 edgeStatics.GetAddressOf());
michael@0 1529 WRL::ComPtr<UI::Input::IEdgeGesture> edge;
michael@0 1530 edgeStatics->GetForCurrentView(edge.GetAddressOf());
michael@0 1531
michael@0 1532 edge->add_Starting(
michael@0 1533 WRL::Callback<EdgeGestureHandler>(
michael@0 1534 this,
michael@0 1535 &MetroInput::OnEdgeGestureStarted).Get(),
michael@0 1536 &mTokenEdgeStarted);
michael@0 1537
michael@0 1538 edge->add_Canceled(
michael@0 1539 WRL::Callback<EdgeGestureHandler>(
michael@0 1540 this,
michael@0 1541 &MetroInput::OnEdgeGestureCanceled).Get(),
michael@0 1542 &mTokenEdgeCanceled);
michael@0 1543
michael@0 1544 edge->add_Completed(
michael@0 1545 WRL::Callback<EdgeGestureHandler>(
michael@0 1546 this,
michael@0 1547 &MetroInput::OnEdgeGestureCompleted).Get(),
michael@0 1548 &mTokenEdgeCompleted);
michael@0 1549
michael@0 1550 // Set up our Gesture Recognizer to raise events for the gestures we
michael@0 1551 // care about
michael@0 1552 mGestureRecognizer->put_GestureSettings(
michael@0 1553 UI::Input::GestureSettings::GestureSettings_Tap
michael@0 1554 | UI::Input::GestureSettings::GestureSettings_DoubleTap
michael@0 1555 | UI::Input::GestureSettings::GestureSettings_RightTap
michael@0 1556 | UI::Input::GestureSettings::GestureSettings_Hold
michael@0 1557 | UI::Input::GestureSettings::GestureSettings_ManipulationTranslateX
michael@0 1558 | UI::Input::GestureSettings::GestureSettings_ManipulationTranslateY);
michael@0 1559
michael@0 1560 // Register for the pointer events on our Window
michael@0 1561 mWindow->add_PointerPressed(
michael@0 1562 WRL::Callback<PointerEventHandler>(
michael@0 1563 this,
michael@0 1564 &MetroInput::OnPointerPressed).Get(),
michael@0 1565 &mTokenPointerPressed);
michael@0 1566
michael@0 1567 mWindow->add_PointerReleased(
michael@0 1568 WRL::Callback<PointerEventHandler>(
michael@0 1569 this,
michael@0 1570 &MetroInput::OnPointerReleased).Get(),
michael@0 1571 &mTokenPointerReleased);
michael@0 1572
michael@0 1573 mWindow->add_PointerMoved(
michael@0 1574 WRL::Callback<PointerEventHandler>(
michael@0 1575 this,
michael@0 1576 &MetroInput::OnPointerMoved).Get(),
michael@0 1577 &mTokenPointerMoved);
michael@0 1578
michael@0 1579 mWindow->add_PointerEntered(
michael@0 1580 WRL::Callback<PointerEventHandler>(
michael@0 1581 this,
michael@0 1582 &MetroInput::OnPointerEntered).Get(),
michael@0 1583 &mTokenPointerEntered);
michael@0 1584
michael@0 1585 mWindow->add_PointerExited(
michael@0 1586 WRL::Callback<PointerEventHandler>(
michael@0 1587 this,
michael@0 1588 &MetroInput::OnPointerExited).Get(),
michael@0 1589 &mTokenPointerExited);
michael@0 1590
michael@0 1591 // Register for the events raised by our Gesture Recognizer
michael@0 1592 mGestureRecognizer->add_Tapped(
michael@0 1593 WRL::Callback<TappedEventHandler>(
michael@0 1594 this,
michael@0 1595 &MetroInput::OnTapped).Get(),
michael@0 1596 &mTokenTapped);
michael@0 1597
michael@0 1598 mGestureRecognizer->add_RightTapped(
michael@0 1599 WRL::Callback<RightTappedEventHandler>(
michael@0 1600 this,
michael@0 1601 &MetroInput::OnRightTapped).Get(),
michael@0 1602 &mTokenRightTapped);
michael@0 1603
michael@0 1604 mGestureRecognizer->add_ManipulationCompleted(
michael@0 1605 WRL::Callback<ManipulationCompletedEventHandler>(
michael@0 1606 this,
michael@0 1607 &MetroInput::OnManipulationCompleted).Get(),
michael@0 1608 &mTokenManipulationCompleted);
michael@0 1609 }
michael@0 1610
michael@0 1611 } } }

mercurial