mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-23 12:51:06 +00:00
61f5a287e8
Differential Revision: https://phabricator.services.mozilla.com/D200462
931 lines
31 KiB
C++
931 lines
31 KiB
C++
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
|
|
#include "InputData.h"
|
|
|
|
#include "mozilla/dom/MouseEventBinding.h"
|
|
#include "mozilla/dom/Touch.h"
|
|
#include "mozilla/dom/WheelEventBinding.h"
|
|
#include "mozilla/TextEvents.h"
|
|
#include "nsContentUtils.h"
|
|
#include "nsDebug.h"
|
|
#include "nsThreadUtils.h"
|
|
#include "mozilla/MouseEvents.h"
|
|
#include "mozilla/TouchEvents.h"
|
|
#include "mozilla/SwipeTracker.h"
|
|
#include "UnitTransforms.h"
|
|
|
|
namespace mozilla {
|
|
|
|
using namespace dom;
|
|
|
|
InputData::~InputData() = default;
|
|
|
|
InputData::InputData(InputType aInputType)
|
|
: mInputType(aInputType),
|
|
mFocusSequenceNumber(0),
|
|
mLayersId{0},
|
|
modifiers(0) {}
|
|
|
|
InputData::InputData(InputType aInputType, TimeStamp aTimeStamp,
|
|
Modifiers aModifiers)
|
|
: mInputType(aInputType),
|
|
mTimeStamp(aTimeStamp),
|
|
mFocusSequenceNumber(0),
|
|
mLayersId{0},
|
|
modifiers(aModifiers) {}
|
|
|
|
SingleTouchData::SingleTouchData(int32_t aIdentifier,
|
|
ScreenIntPoint aScreenPoint,
|
|
ScreenSize aRadius, float aRotationAngle,
|
|
float aForce)
|
|
: mIdentifier(aIdentifier),
|
|
mScreenPoint(aScreenPoint),
|
|
mRadius(aRadius),
|
|
mRotationAngle(aRotationAngle),
|
|
mForce(aForce) {}
|
|
|
|
SingleTouchData::SingleTouchData(int32_t aIdentifier,
|
|
ParentLayerPoint aLocalScreenPoint,
|
|
ScreenSize aRadius, float aRotationAngle,
|
|
float aForce)
|
|
: mIdentifier(aIdentifier),
|
|
mLocalScreenPoint(aLocalScreenPoint),
|
|
mRadius(aRadius),
|
|
mRotationAngle(aRotationAngle),
|
|
mForce(aForce) {}
|
|
|
|
SingleTouchData::SingleTouchData()
|
|
: mIdentifier(0), mRotationAngle(0.0), mForce(0.0) {}
|
|
|
|
already_AddRefed<Touch> SingleTouchData::ToNewDOMTouch() const {
|
|
MOZ_ASSERT(NS_IsMainThread(),
|
|
"Can only create dom::Touch instances on main thread");
|
|
RefPtr<Touch> touch =
|
|
new Touch(mIdentifier,
|
|
LayoutDeviceIntPoint::Truncate(mScreenPoint.x, mScreenPoint.y),
|
|
LayoutDeviceIntPoint::Truncate(mRadius.width, mRadius.height),
|
|
mRotationAngle, mForce);
|
|
touch->tiltX = mTiltX;
|
|
touch->tiltY = mTiltY;
|
|
touch->twist = mTwist;
|
|
return touch.forget();
|
|
}
|
|
|
|
MultiTouchInput::MultiTouchInput(MultiTouchType aType, uint32_t aTime,
|
|
TimeStamp aTimeStamp, Modifiers aModifiers)
|
|
: InputData(MULTITOUCH_INPUT, aTimeStamp, aModifiers),
|
|
mType(aType),
|
|
mHandledByAPZ(false) {}
|
|
|
|
MultiTouchInput::MultiTouchInput()
|
|
: InputData(MULTITOUCH_INPUT),
|
|
mType(MULTITOUCH_START),
|
|
mHandledByAPZ(false) {}
|
|
|
|
MultiTouchInput::MultiTouchInput(const WidgetTouchEvent& aTouchEvent)
|
|
: InputData(MULTITOUCH_INPUT, aTouchEvent.mTimeStamp,
|
|
aTouchEvent.mModifiers),
|
|
mHandledByAPZ(aTouchEvent.mFlags.mHandledByAPZ),
|
|
mButton(aTouchEvent.mButton),
|
|
mButtons(aTouchEvent.mButtons) {
|
|
MOZ_ASSERT(NS_IsMainThread(),
|
|
"Can only copy from WidgetTouchEvent on main thread");
|
|
|
|
switch (aTouchEvent.mMessage) {
|
|
case eTouchStart:
|
|
mType = MULTITOUCH_START;
|
|
break;
|
|
case eTouchMove:
|
|
mType = MULTITOUCH_MOVE;
|
|
break;
|
|
case eTouchEnd:
|
|
mType = MULTITOUCH_END;
|
|
break;
|
|
case eTouchCancel:
|
|
mType = MULTITOUCH_CANCEL;
|
|
break;
|
|
default:
|
|
MOZ_ASSERT_UNREACHABLE("Did not assign a type to a MultiTouchInput");
|
|
break;
|
|
}
|
|
|
|
mScreenOffset = ViewAs<ExternalPixel>(
|
|
aTouchEvent.mWidget->WidgetToScreenOffset(),
|
|
PixelCastJustification::LayoutDeviceIsScreenForUntransformedEvent);
|
|
|
|
for (size_t i = 0; i < aTouchEvent.mTouches.Length(); i++) {
|
|
const Touch* domTouch = aTouchEvent.mTouches[i];
|
|
|
|
// Extract data from weird interfaces.
|
|
int32_t identifier = domTouch->Identifier();
|
|
int32_t radiusX = domTouch->RadiusX(CallerType::System);
|
|
int32_t radiusY = domTouch->RadiusY(CallerType::System);
|
|
float rotationAngle = domTouch->RotationAngle(CallerType::System);
|
|
float force = domTouch->Force(CallerType::System);
|
|
|
|
SingleTouchData data(
|
|
identifier,
|
|
ViewAs<ScreenPixel>(
|
|
domTouch->mRefPoint,
|
|
PixelCastJustification::LayoutDeviceIsScreenForUntransformedEvent),
|
|
ScreenSize((float)radiusX, (float)radiusY), rotationAngle, force);
|
|
|
|
mTouches.AppendElement(data);
|
|
}
|
|
}
|
|
|
|
void MultiTouchInput::Translate(const ScreenPoint& aTranslation) {
|
|
ScreenIntPoint translation = RoundedToInt(aTranslation);
|
|
|
|
for (auto& touchData : mTouches) {
|
|
for (auto& historicalData : touchData.mHistoricalData) {
|
|
historicalData.mScreenPoint.MoveBy(translation.x, translation.y);
|
|
}
|
|
touchData.mScreenPoint.MoveBy(translation.x, translation.y);
|
|
}
|
|
}
|
|
|
|
WidgetTouchEvent MultiTouchInput::ToWidgetEvent(nsIWidget* aWidget,
|
|
uint16_t aInputSource) const {
|
|
MOZ_ASSERT(NS_IsMainThread(),
|
|
"Can only convert To WidgetTouchEvent on main thread");
|
|
MOZ_ASSERT(aInputSource ==
|
|
mozilla::dom::MouseEvent_Binding::MOZ_SOURCE_TOUCH ||
|
|
aInputSource == mozilla::dom::MouseEvent_Binding::MOZ_SOURCE_PEN);
|
|
|
|
EventMessage touchEventMessage = eVoidEvent;
|
|
switch (mType) {
|
|
case MULTITOUCH_START:
|
|
touchEventMessage = eTouchStart;
|
|
break;
|
|
case MULTITOUCH_MOVE:
|
|
touchEventMessage = eTouchMove;
|
|
break;
|
|
case MULTITOUCH_END:
|
|
touchEventMessage = eTouchEnd;
|
|
break;
|
|
case MULTITOUCH_CANCEL:
|
|
touchEventMessage = eTouchCancel;
|
|
break;
|
|
default:
|
|
MOZ_ASSERT_UNREACHABLE(
|
|
"Did not assign a type to WidgetTouchEvent in MultiTouchInput");
|
|
break;
|
|
}
|
|
|
|
WidgetTouchEvent event(true, touchEventMessage, aWidget);
|
|
if (touchEventMessage == eVoidEvent) {
|
|
return event;
|
|
}
|
|
|
|
event.mModifiers = this->modifiers;
|
|
event.mTimeStamp = this->mTimeStamp;
|
|
event.mFlags.mHandledByAPZ = mHandledByAPZ;
|
|
event.mFocusSequenceNumber = mFocusSequenceNumber;
|
|
event.mLayersId = mLayersId;
|
|
event.mInputSource = aInputSource;
|
|
event.mButton = mButton;
|
|
event.mButtons = mButtons;
|
|
|
|
for (size_t i = 0; i < mTouches.Length(); i++) {
|
|
*event.mTouches.AppendElement() = mTouches[i].ToNewDOMTouch();
|
|
}
|
|
|
|
return event;
|
|
}
|
|
|
|
int32_t MultiTouchInput::IndexOfTouch(int32_t aTouchIdentifier) {
|
|
for (size_t i = 0; i < mTouches.Length(); i++) {
|
|
if (mTouches[i].mIdentifier == aTouchIdentifier) {
|
|
return (int32_t)i;
|
|
}
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
bool MultiTouchInput::TransformToLocal(
|
|
const ScreenToParentLayerMatrix4x4& aTransform) {
|
|
for (auto& touchData : mTouches) {
|
|
for (auto& historicalData : touchData.mHistoricalData) {
|
|
Maybe<ParentLayerIntPoint> historicalPoint =
|
|
UntransformBy(aTransform, historicalData.mScreenPoint);
|
|
if (!historicalPoint) {
|
|
return false;
|
|
}
|
|
historicalData.mLocalScreenPoint = *historicalPoint;
|
|
}
|
|
Maybe<ParentLayerIntPoint> point =
|
|
UntransformBy(aTransform, touchData.mScreenPoint);
|
|
if (!point) {
|
|
return false;
|
|
}
|
|
touchData.mLocalScreenPoint = *point;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
MouseInput::MouseInput()
|
|
: InputData(MOUSE_INPUT),
|
|
mType(MOUSE_NONE),
|
|
mButtonType(NONE),
|
|
mInputSource(0),
|
|
mButtons(0),
|
|
mHandledByAPZ(false),
|
|
mPreventClickEvent(false) {}
|
|
|
|
MouseInput::MouseInput(MouseType aType, ButtonType aButtonType,
|
|
uint16_t aInputSource, int16_t aButtons,
|
|
const ScreenPoint& aPoint, TimeStamp aTimeStamp,
|
|
Modifiers aModifiers)
|
|
: InputData(MOUSE_INPUT, aTimeStamp, aModifiers),
|
|
mType(aType),
|
|
mButtonType(aButtonType),
|
|
mInputSource(aInputSource),
|
|
mButtons(aButtons),
|
|
mOrigin(aPoint),
|
|
mHandledByAPZ(false),
|
|
mPreventClickEvent(false) {}
|
|
|
|
MouseInput::MouseInput(const WidgetMouseEventBase& aMouseEvent)
|
|
: InputData(MOUSE_INPUT, aMouseEvent.mTimeStamp, aMouseEvent.mModifiers),
|
|
mType(MOUSE_NONE),
|
|
mButtonType(NONE),
|
|
mInputSource(aMouseEvent.mInputSource),
|
|
mButtons(aMouseEvent.mButtons),
|
|
mHandledByAPZ(aMouseEvent.mFlags.mHandledByAPZ),
|
|
mPreventClickEvent(aMouseEvent.mClass == eMouseEventClass &&
|
|
aMouseEvent.AsMouseEvent()->mClickEventPrevented) {
|
|
MOZ_ASSERT(NS_IsMainThread(),
|
|
"Can only copy from WidgetTouchEvent on main thread");
|
|
|
|
mButtonType = NONE;
|
|
|
|
switch (aMouseEvent.mButton) {
|
|
case MouseButton::ePrimary:
|
|
mButtonType = MouseInput::PRIMARY_BUTTON;
|
|
break;
|
|
case MouseButton::eMiddle:
|
|
mButtonType = MouseInput::MIDDLE_BUTTON;
|
|
break;
|
|
case MouseButton::eSecondary:
|
|
mButtonType = MouseInput::SECONDARY_BUTTON;
|
|
break;
|
|
}
|
|
|
|
switch (aMouseEvent.mMessage) {
|
|
case eMouseMove:
|
|
mType = MOUSE_MOVE;
|
|
break;
|
|
case eMouseUp:
|
|
mType = MOUSE_UP;
|
|
break;
|
|
case eMouseDown:
|
|
mType = MOUSE_DOWN;
|
|
break;
|
|
case eDragStart:
|
|
mType = MOUSE_DRAG_START;
|
|
break;
|
|
case eDragEnd:
|
|
mType = MOUSE_DRAG_END;
|
|
break;
|
|
case eMouseEnterIntoWidget:
|
|
mType = MOUSE_WIDGET_ENTER;
|
|
break;
|
|
case eMouseExitFromWidget:
|
|
mType = MOUSE_WIDGET_EXIT;
|
|
break;
|
|
case eMouseExploreByTouch:
|
|
mType = MOUSE_EXPLORE_BY_TOUCH;
|
|
break;
|
|
case eMouseHitTest:
|
|
mType = MOUSE_HITTEST;
|
|
break;
|
|
case eContextMenu:
|
|
mType = MOUSE_CONTEXTMENU;
|
|
break;
|
|
default:
|
|
MOZ_ASSERT_UNREACHABLE("Mouse event type not supported");
|
|
break;
|
|
}
|
|
|
|
mOrigin = ScreenPoint(ViewAs<ScreenPixel>(
|
|
aMouseEvent.mRefPoint,
|
|
PixelCastJustification::LayoutDeviceIsScreenForUntransformedEvent));
|
|
}
|
|
|
|
bool MouseInput::IsLeftButton() const { return mButtonType == PRIMARY_BUTTON; }
|
|
|
|
bool MouseInput::TransformToLocal(
|
|
const ScreenToParentLayerMatrix4x4& aTransform) {
|
|
Maybe<ParentLayerPoint> point = UntransformBy(aTransform, mOrigin);
|
|
if (!point) {
|
|
return false;
|
|
}
|
|
mLocalOrigin = *point;
|
|
|
|
return true;
|
|
}
|
|
|
|
WidgetMouseEvent MouseInput::ToWidgetEvent(nsIWidget* aWidget) const {
|
|
MOZ_ASSERT(NS_IsMainThread(),
|
|
"Can only convert To WidgetTouchEvent on main thread");
|
|
|
|
EventMessage msg = eVoidEvent;
|
|
uint32_t clickCount = 0;
|
|
Maybe<WidgetMouseEvent::ExitFrom> exitFrom;
|
|
switch (mType) {
|
|
case MOUSE_MOVE:
|
|
msg = eMouseMove;
|
|
break;
|
|
case MOUSE_UP:
|
|
msg = eMouseUp;
|
|
clickCount = 1;
|
|
break;
|
|
case MOUSE_DOWN:
|
|
msg = eMouseDown;
|
|
clickCount = 1;
|
|
break;
|
|
case MOUSE_DRAG_START:
|
|
msg = eDragStart;
|
|
break;
|
|
case MOUSE_DRAG_END:
|
|
msg = eDragEnd;
|
|
break;
|
|
case MOUSE_WIDGET_ENTER:
|
|
msg = eMouseEnterIntoWidget;
|
|
break;
|
|
case MOUSE_WIDGET_EXIT:
|
|
msg = eMouseExitFromWidget;
|
|
exitFrom = Some(WidgetMouseEvent::ePlatformChild);
|
|
break;
|
|
case MOUSE_EXPLORE_BY_TOUCH:
|
|
msg = eMouseExploreByTouch;
|
|
break;
|
|
case MOUSE_HITTEST:
|
|
msg = eMouseHitTest;
|
|
break;
|
|
case MOUSE_CONTEXTMENU:
|
|
msg = eContextMenu;
|
|
break;
|
|
default:
|
|
MOZ_ASSERT_UNREACHABLE(
|
|
"Did not assign a type to WidgetMouseEvent in MouseInput");
|
|
break;
|
|
}
|
|
|
|
WidgetMouseEvent event(true, msg, aWidget, WidgetMouseEvent::eReal,
|
|
WidgetMouseEvent::eNormal);
|
|
|
|
if (msg == eVoidEvent) {
|
|
return event;
|
|
}
|
|
|
|
switch (mButtonType) {
|
|
case MouseInput::PRIMARY_BUTTON:
|
|
event.mButton = MouseButton::ePrimary;
|
|
break;
|
|
case MouseInput::MIDDLE_BUTTON:
|
|
event.mButton = MouseButton::eMiddle;
|
|
break;
|
|
case MouseInput::SECONDARY_BUTTON:
|
|
event.mButton = MouseButton::eSecondary;
|
|
break;
|
|
case MouseInput::NONE:
|
|
default:
|
|
break;
|
|
}
|
|
|
|
event.mButtons = mButtons;
|
|
event.mModifiers = modifiers;
|
|
event.mTimeStamp = mTimeStamp;
|
|
event.mLayersId = mLayersId;
|
|
event.mFlags.mHandledByAPZ = mHandledByAPZ;
|
|
event.mRefPoint = RoundedToInt(ViewAs<LayoutDevicePixel>(
|
|
mOrigin,
|
|
PixelCastJustification::LayoutDeviceIsScreenForUntransformedEvent));
|
|
event.mClickCount = clickCount;
|
|
event.mInputSource = mInputSource;
|
|
event.mFocusSequenceNumber = mFocusSequenceNumber;
|
|
event.mExitFrom = exitFrom;
|
|
event.mClickEventPrevented = mPreventClickEvent;
|
|
|
|
return event;
|
|
}
|
|
|
|
PanGestureInput::PanGestureInput()
|
|
: InputData(PANGESTURE_INPUT),
|
|
mType(PANGESTURE_MAYSTART),
|
|
mLineOrPageDeltaX(0),
|
|
mLineOrPageDeltaY(0),
|
|
mUserDeltaMultiplierX(1.0),
|
|
mUserDeltaMultiplierY(1.0),
|
|
mHandledByAPZ(false),
|
|
mOverscrollBehaviorAllowsSwipe(false),
|
|
mSimulateMomentum(false),
|
|
mIsNoLineOrPageDelta(true),
|
|
mMayTriggerSwipe(false) {}
|
|
|
|
PanGestureInput::PanGestureInput(PanGestureType aType, TimeStamp aTimeStamp,
|
|
const ScreenPoint& aPanStartPoint,
|
|
const ScreenPoint& aPanDisplacement,
|
|
Modifiers aModifiers)
|
|
: InputData(PANGESTURE_INPUT, aTimeStamp, aModifiers),
|
|
mType(aType),
|
|
mPanStartPoint(aPanStartPoint),
|
|
mPanDisplacement(aPanDisplacement),
|
|
mLineOrPageDeltaX(0),
|
|
mLineOrPageDeltaY(0),
|
|
mUserDeltaMultiplierX(1.0),
|
|
mUserDeltaMultiplierY(1.0),
|
|
mHandledByAPZ(false),
|
|
mOverscrollBehaviorAllowsSwipe(false),
|
|
mSimulateMomentum(false),
|
|
mIsNoLineOrPageDelta(true) {
|
|
mMayTriggerSwipe = SwipeTracker::CanTriggerSwipe(*this);
|
|
}
|
|
|
|
PanGestureInput::PanGestureInput(PanGestureType aType, TimeStamp aTimeStamp,
|
|
const ScreenPoint& aPanStartPoint,
|
|
const ScreenPoint& aPanDisplacement,
|
|
Modifiers aModifiers,
|
|
IsEligibleForSwipe aIsEligibleForSwipe)
|
|
: PanGestureInput(aType, aTimeStamp, aPanStartPoint, aPanDisplacement,
|
|
aModifiers) {
|
|
mMayTriggerSwipe &= bool(aIsEligibleForSwipe);
|
|
}
|
|
|
|
void PanGestureInput::SetLineOrPageDeltas(int32_t aLineOrPageDeltaX,
|
|
int32_t aLineOrPageDeltaY) {
|
|
mLineOrPageDeltaX = aLineOrPageDeltaX;
|
|
mLineOrPageDeltaY = aLineOrPageDeltaY;
|
|
mIsNoLineOrPageDelta = false;
|
|
}
|
|
|
|
bool PanGestureInput::IsMomentum() const {
|
|
switch (mType) {
|
|
case PanGestureInput::PANGESTURE_MOMENTUMSTART:
|
|
case PanGestureInput::PANGESTURE_MOMENTUMPAN:
|
|
case PanGestureInput::PANGESTURE_MOMENTUMEND:
|
|
return true;
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
|
|
WidgetWheelEvent PanGestureInput::ToWidgetEvent(nsIWidget* aWidget) const {
|
|
WidgetWheelEvent wheelEvent(true, eWheel, aWidget);
|
|
wheelEvent.mModifiers = this->modifiers;
|
|
wheelEvent.mTimeStamp = mTimeStamp;
|
|
wheelEvent.mLayersId = mLayersId;
|
|
wheelEvent.mRefPoint = RoundedToInt(ViewAs<LayoutDevicePixel>(
|
|
mPanStartPoint,
|
|
PixelCastJustification::LayoutDeviceIsScreenForUntransformedEvent));
|
|
wheelEvent.mButtons = 0;
|
|
wheelEvent.mMayHaveMomentum = true; // pan inputs may have momentum
|
|
wheelEvent.mIsMomentum = IsMomentum();
|
|
wheelEvent.mLineOrPageDeltaX = mLineOrPageDeltaX;
|
|
wheelEvent.mLineOrPageDeltaY = mLineOrPageDeltaY;
|
|
wheelEvent.mDeltaX = mPanDisplacement.x;
|
|
wheelEvent.mDeltaY = mPanDisplacement.y;
|
|
wheelEvent.mFlags.mHandledByAPZ = mHandledByAPZ;
|
|
wheelEvent.mFocusSequenceNumber = mFocusSequenceNumber;
|
|
wheelEvent.mIsNoLineOrPageDelta = mIsNoLineOrPageDelta;
|
|
if (mDeltaType == PanGestureInput::PANDELTA_PAGE) {
|
|
// widget/gtk is currently the only consumer that uses delta type
|
|
// PANDELTA_PAGE
|
|
// Emulate legacy widget/gtk behavior
|
|
wheelEvent.mDeltaMode = WheelEvent_Binding::DOM_DELTA_LINE;
|
|
wheelEvent.mScrollType = WidgetWheelEvent::SCROLL_ASYNCHRONOUSLY;
|
|
wheelEvent.mDeltaX *= 3;
|
|
wheelEvent.mDeltaY *= 3;
|
|
} else {
|
|
wheelEvent.mDeltaMode = WheelEvent_Binding::DOM_DELTA_PIXEL;
|
|
}
|
|
return wheelEvent;
|
|
}
|
|
|
|
bool PanGestureInput::TransformToLocal(
|
|
const ScreenToParentLayerMatrix4x4& aTransform) {
|
|
Maybe<ParentLayerPoint> panStartPoint =
|
|
UntransformBy(aTransform, mPanStartPoint);
|
|
if (!panStartPoint) {
|
|
return false;
|
|
}
|
|
mLocalPanStartPoint = *panStartPoint;
|
|
|
|
if (mDeltaType == PanGestureInput::PANDELTA_PAGE) {
|
|
// Skip transforming the pan displacement because we want
|
|
// raw page proportion counts.
|
|
mLocalPanDisplacement = ViewAs<ParentLayerPixel>(
|
|
mPanDisplacement, PixelCastJustification::DeltaIsPageProportion);
|
|
return true;
|
|
}
|
|
|
|
Maybe<ParentLayerPoint> panDisplacement =
|
|
UntransformVector(aTransform, mPanDisplacement, mPanStartPoint);
|
|
if (!panDisplacement) {
|
|
return false;
|
|
}
|
|
mLocalPanDisplacement = *panDisplacement;
|
|
return true;
|
|
}
|
|
|
|
ScreenPoint PanGestureInput::UserMultipliedPanDisplacement() const {
|
|
return ScreenPoint(mPanDisplacement.x * mUserDeltaMultiplierX,
|
|
mPanDisplacement.y * mUserDeltaMultiplierY);
|
|
}
|
|
|
|
ParentLayerPoint PanGestureInput::UserMultipliedLocalPanDisplacement() const {
|
|
return ParentLayerPoint(mLocalPanDisplacement.x * mUserDeltaMultiplierX,
|
|
mLocalPanDisplacement.y * mUserDeltaMultiplierY);
|
|
}
|
|
|
|
static int32_t TakeLargestInt(gfx::Coord* aCoord) {
|
|
int32_t result(aCoord->value); // truncate towards zero
|
|
aCoord->value -= result;
|
|
return result;
|
|
}
|
|
|
|
/* static */ gfx::IntPoint PanGestureInput::GetIntegerDeltaForEvent(
|
|
bool aIsStart, float x, float y) {
|
|
static gfx::Point sAccumulator(0.0f, 0.0f);
|
|
if (aIsStart) {
|
|
sAccumulator = gfx::Point(0.0f, 0.0f);
|
|
}
|
|
sAccumulator.x += x;
|
|
sAccumulator.y += y;
|
|
return gfx::IntPoint(TakeLargestInt(&sAccumulator.x),
|
|
TakeLargestInt(&sAccumulator.y));
|
|
}
|
|
|
|
PinchGestureInput::PinchGestureInput()
|
|
: InputData(PINCHGESTURE_INPUT),
|
|
mType(PINCHGESTURE_START),
|
|
mSource(UNKNOWN),
|
|
mHandledByAPZ(false) {}
|
|
|
|
PinchGestureInput::PinchGestureInput(
|
|
PinchGestureType aType, PinchGestureSource aSource, TimeStamp aTimeStamp,
|
|
const ExternalPoint& aScreenOffset, const ScreenPoint& aFocusPoint,
|
|
ScreenCoord aCurrentSpan, ScreenCoord aPreviousSpan, Modifiers aModifiers)
|
|
: InputData(PINCHGESTURE_INPUT, aTimeStamp, aModifiers),
|
|
mType(aType),
|
|
mSource(aSource),
|
|
mFocusPoint(aFocusPoint),
|
|
mScreenOffset(aScreenOffset),
|
|
mCurrentSpan(aCurrentSpan),
|
|
mPreviousSpan(aPreviousSpan),
|
|
mLineOrPageDeltaY(0),
|
|
mHandledByAPZ(false) {}
|
|
|
|
bool PinchGestureInput::TransformToLocal(
|
|
const ScreenToParentLayerMatrix4x4& aTransform) {
|
|
Maybe<ParentLayerPoint> point = UntransformBy(aTransform, mFocusPoint);
|
|
if (!point) {
|
|
return false;
|
|
}
|
|
mLocalFocusPoint = *point;
|
|
return true;
|
|
}
|
|
|
|
WidgetWheelEvent PinchGestureInput::ToWidgetEvent(nsIWidget* aWidget) const {
|
|
WidgetWheelEvent wheelEvent(true, eWheel, aWidget);
|
|
wheelEvent.mModifiers = this->modifiers | MODIFIER_CONTROL;
|
|
wheelEvent.mTimeStamp = mTimeStamp;
|
|
wheelEvent.mLayersId = mLayersId;
|
|
wheelEvent.mRefPoint = RoundedToInt(ViewAs<LayoutDevicePixel>(
|
|
mFocusPoint,
|
|
PixelCastJustification::LayoutDeviceIsScreenForUntransformedEvent));
|
|
wheelEvent.mButtons = 0;
|
|
wheelEvent.mFlags.mHandledByAPZ = mHandledByAPZ;
|
|
wheelEvent.mDeltaMode = WheelEvent_Binding::DOM_DELTA_PIXEL;
|
|
|
|
wheelEvent.mDeltaY = ComputeDeltaY(aWidget);
|
|
|
|
wheelEvent.mLineOrPageDeltaY = mLineOrPageDeltaY;
|
|
|
|
MOZ_ASSERT(mType == PINCHGESTURE_END || wheelEvent.mDeltaY != 0.0);
|
|
|
|
return wheelEvent;
|
|
}
|
|
|
|
double PinchGestureInput::ComputeDeltaY(nsIWidget* aWidget) const {
|
|
#if defined(XP_DARWIN)
|
|
// This converts the pinch gesture value to a fake wheel event that has the
|
|
// control key pressed so that pages can implement custom pinch gesture
|
|
// handling. It may seem strange that this doesn't use a wheel event with
|
|
// the deltaZ property set, but this matches Chrome's behavior as described
|
|
// at https://code.google.com/p/chromium/issues/detail?id=289887
|
|
//
|
|
// The intent of the formula below is to produce numbers similar to Chrome's
|
|
// implementation of this feature. Chrome implements deltaY using the formula
|
|
// "-100 * log(1 + [event magnification])" which is unfortunately incorrect.
|
|
// All deltas for a single pinch gesture should sum to 0 if the start and end
|
|
// of a pinch gesture end up in the same place. This doesn't happen in Chrome
|
|
// because they followed Apple's misleading documentation, which implies that
|
|
// "1 + [event magnification]" is the scale factor. The scale factor is
|
|
// instead "pow(ratio, [event magnification])" so "[event magnification]" is
|
|
// already in log space.
|
|
//
|
|
// The multiplication by the backing scale factor below counteracts the
|
|
// division by the backing scale factor in WheelEvent.
|
|
|
|
// We want to set deltaY to |-100.0 * M * GetDefaultScaleInternal()| where M
|
|
// is [event magnification] but [event magnification] is only available in the
|
|
// macOS widget code so we have to reverse engineer from mCurrentSpan and
|
|
// mPreviousSpan (which are derived from [event magnification]) to get it.
|
|
// Specifically, we know |mCurrentSpan == 100.0| and |mPreviousSpan == 100.0 *
|
|
// (1.0 - M)|. We can calculate deltaY by solving the mPreviousSpan equation
|
|
// for M in terms of mPreviousSpan and plugging that into to the formula for
|
|
// deltaY.
|
|
return (mPreviousSpan - 100.0) *
|
|
(aWidget ? aWidget->GetDefaultScaleInternal() : 1.f);
|
|
#else
|
|
// This calculation is based on what the Windows and Linux widget code does.
|
|
// Specifically, it creates a PinchGestureInput with |mCurrentSpan == 100.0 *
|
|
// currentScale| and |mPreviousSpan == 100.0 * lastScale| where currentScale
|
|
// is the scale from the current OS event and lastScale is the scale when the
|
|
// previous OS event happened. On macOS [event magnification] is a relative
|
|
// change in scale factor, ie if the scale factor changed from 1 to 1.1 it
|
|
// will be 0.1, similarly if it changed from 1 to 0.9 it will be -0.1. To
|
|
// calculate the relative scale change on Windows we would calculate |M =
|
|
// currentScale - lastScale = (mCurrentSpan-mPreviousSpan)/100| and use the
|
|
// same formula as the macOS code
|
|
// (|-100.0 * M * GetDefaultScaleInternal()|).
|
|
|
|
return (mPreviousSpan - mCurrentSpan) *
|
|
(aWidget ? aWidget->GetDefaultScaleInternal() : 1.f);
|
|
#endif
|
|
}
|
|
|
|
bool PinchGestureInput::SetLineOrPageDeltaY(nsIWidget* aWidget) {
|
|
double deltaY = ComputeDeltaY(aWidget);
|
|
if (deltaY == 0 && mType != PINCHGESTURE_END) {
|
|
return false;
|
|
}
|
|
gfx::IntPoint lineOrPageDelta = PinchGestureInput::GetIntegerDeltaForEvent(
|
|
(mType == PINCHGESTURE_START), 0, deltaY);
|
|
mLineOrPageDeltaY = lineOrPageDelta.y;
|
|
if (mLineOrPageDeltaY == 0) {
|
|
// For PINCHGESTURE_SCALE events, don't dispatch them. Note that the delta
|
|
// isn't lost; it remains in the accumulator in GetIntegerDeltaForEvent().
|
|
if (mType == PINCHGESTURE_SCALE) {
|
|
return false;
|
|
}
|
|
// On Windows, drop PINCHGESTURE_START as well (the Windows widget code will
|
|
// defer the START event until we accumulate enough delta).
|
|
// The Linux widget code doesn't support this, so instead set the event's
|
|
// mLineOrPageDeltaY to the smallest nonzero amount in the relevant
|
|
// direction.
|
|
if (mType == PINCHGESTURE_START) {
|
|
#ifdef XP_WIN
|
|
return false;
|
|
#else
|
|
mLineOrPageDeltaY = (deltaY >= 0) ? 1 : -1;
|
|
#endif
|
|
}
|
|
// For PINCHGESTURE_END events, not dispatching a DOMMouseScroll for them is
|
|
// fine.
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/* static */ gfx::IntPoint PinchGestureInput::GetIntegerDeltaForEvent(
|
|
bool aIsStart, float x, float y) {
|
|
static gfx::Point sAccumulator(0.0f, 0.0f);
|
|
if (aIsStart) {
|
|
sAccumulator = gfx::Point(0.0f, 0.0f);
|
|
}
|
|
sAccumulator.x += x;
|
|
sAccumulator.y += y;
|
|
return gfx::IntPoint(TakeLargestInt(&sAccumulator.x),
|
|
TakeLargestInt(&sAccumulator.y));
|
|
}
|
|
|
|
TapGestureInput::TapGestureInput()
|
|
: InputData(TAPGESTURE_INPUT), mType(TAPGESTURE_LONG) {}
|
|
|
|
TapGestureInput::TapGestureInput(TapGestureType aType, TimeStamp aTimeStamp,
|
|
const ScreenIntPoint& aPoint,
|
|
Modifiers aModifiers)
|
|
: InputData(TAPGESTURE_INPUT, aTimeStamp, aModifiers),
|
|
mType(aType),
|
|
mPoint(aPoint) {}
|
|
|
|
TapGestureInput::TapGestureInput(TapGestureType aType, TimeStamp aTimeStamp,
|
|
const ParentLayerPoint& aLocalPoint,
|
|
Modifiers aModifiers)
|
|
: InputData(TAPGESTURE_INPUT, aTimeStamp, aModifiers),
|
|
mType(aType),
|
|
mLocalPoint(aLocalPoint) {}
|
|
|
|
bool TapGestureInput::TransformToLocal(
|
|
const ScreenToParentLayerMatrix4x4& aTransform) {
|
|
Maybe<ParentLayerIntPoint> point = UntransformBy(aTransform, mPoint);
|
|
if (!point) {
|
|
return false;
|
|
}
|
|
mLocalPoint = *point;
|
|
return true;
|
|
}
|
|
|
|
WidgetSimpleGestureEvent TapGestureInput::ToWidgetEvent(
|
|
nsIWidget* aWidget) const {
|
|
WidgetSimpleGestureEvent event(true, eTapGesture, aWidget);
|
|
|
|
event.mTimeStamp = mTimeStamp;
|
|
event.mLayersId = mLayersId;
|
|
event.mRefPoint = ViewAs<LayoutDevicePixel>(
|
|
mPoint,
|
|
PixelCastJustification::LayoutDeviceIsScreenForUntransformedEvent);
|
|
event.mButtons = 0;
|
|
event.mClickCount = 1;
|
|
event.mModifiers = modifiers;
|
|
|
|
return event;
|
|
}
|
|
|
|
ScrollWheelInput::ScrollWheelInput()
|
|
: InputData(SCROLLWHEEL_INPUT),
|
|
mDeltaType(SCROLLDELTA_LINE),
|
|
mScrollMode(SCROLLMODE_INSTANT),
|
|
mHandledByAPZ(false),
|
|
mDeltaX(0.0),
|
|
mDeltaY(0.0),
|
|
mLineOrPageDeltaX(0),
|
|
mLineOrPageDeltaY(0),
|
|
mScrollSeriesNumber(0),
|
|
mUserDeltaMultiplierX(1.0),
|
|
mUserDeltaMultiplierY(1.0),
|
|
mMayHaveMomentum(false),
|
|
mIsMomentum(false),
|
|
mAPZAction(APZWheelAction::Scroll) {}
|
|
|
|
ScrollWheelInput::ScrollWheelInput(
|
|
TimeStamp aTimeStamp, Modifiers aModifiers, ScrollMode aScrollMode,
|
|
ScrollDeltaType aDeltaType, const ScreenPoint& aOrigin, double aDeltaX,
|
|
double aDeltaY, bool aAllowToOverrideSystemScrollSpeed,
|
|
WheelDeltaAdjustmentStrategy aWheelDeltaAdjustmentStrategy)
|
|
: InputData(SCROLLWHEEL_INPUT, aTimeStamp, aModifiers),
|
|
mDeltaType(aDeltaType),
|
|
mScrollMode(aScrollMode),
|
|
mOrigin(aOrigin),
|
|
mHandledByAPZ(false),
|
|
mDeltaX(aDeltaX),
|
|
mDeltaY(aDeltaY),
|
|
mLineOrPageDeltaX(0),
|
|
mLineOrPageDeltaY(0),
|
|
mScrollSeriesNumber(0),
|
|
mUserDeltaMultiplierX(1.0),
|
|
mUserDeltaMultiplierY(1.0),
|
|
mMayHaveMomentum(false),
|
|
mIsMomentum(false),
|
|
mAllowToOverrideSystemScrollSpeed(aAllowToOverrideSystemScrollSpeed),
|
|
mWheelDeltaAdjustmentStrategy(aWheelDeltaAdjustmentStrategy),
|
|
mAPZAction(APZWheelAction::Scroll) {}
|
|
|
|
ScrollWheelInput::ScrollWheelInput(const WidgetWheelEvent& aWheelEvent)
|
|
: InputData(SCROLLWHEEL_INPUT, aWheelEvent.mTimeStamp,
|
|
aWheelEvent.mModifiers),
|
|
mDeltaType(DeltaTypeForDeltaMode(aWheelEvent.mDeltaMode)),
|
|
mScrollMode(SCROLLMODE_INSTANT),
|
|
mHandledByAPZ(aWheelEvent.mFlags.mHandledByAPZ),
|
|
mDeltaX(aWheelEvent.mDeltaX),
|
|
mDeltaY(aWheelEvent.mDeltaY),
|
|
mWheelTicksX(aWheelEvent.mWheelTicksX),
|
|
mWheelTicksY(aWheelEvent.mWheelTicksX),
|
|
mLineOrPageDeltaX(aWheelEvent.mLineOrPageDeltaX),
|
|
mLineOrPageDeltaY(aWheelEvent.mLineOrPageDeltaY),
|
|
mScrollSeriesNumber(0),
|
|
mUserDeltaMultiplierX(1.0),
|
|
mUserDeltaMultiplierY(1.0),
|
|
mMayHaveMomentum(aWheelEvent.mMayHaveMomentum),
|
|
mIsMomentum(aWheelEvent.mIsMomentum),
|
|
mAllowToOverrideSystemScrollSpeed(
|
|
aWheelEvent.mAllowToOverrideSystemScrollSpeed),
|
|
mWheelDeltaAdjustmentStrategy(WheelDeltaAdjustmentStrategy::eNone),
|
|
mAPZAction(APZWheelAction::Scroll) {
|
|
mOrigin = ScreenPoint(ViewAs<ScreenPixel>(
|
|
aWheelEvent.mRefPoint,
|
|
PixelCastJustification::LayoutDeviceIsScreenForUntransformedEvent));
|
|
}
|
|
|
|
ScrollWheelInput::ScrollDeltaType ScrollWheelInput::DeltaTypeForDeltaMode(
|
|
uint32_t aDeltaMode) {
|
|
switch (aDeltaMode) {
|
|
case WheelEvent_Binding::DOM_DELTA_LINE:
|
|
return SCROLLDELTA_LINE;
|
|
case WheelEvent_Binding::DOM_DELTA_PAGE:
|
|
return SCROLLDELTA_PAGE;
|
|
case WheelEvent_Binding::DOM_DELTA_PIXEL:
|
|
return SCROLLDELTA_PIXEL;
|
|
default:
|
|
MOZ_CRASH();
|
|
}
|
|
return SCROLLDELTA_LINE;
|
|
}
|
|
|
|
uint32_t ScrollWheelInput::DeltaModeForDeltaType(ScrollDeltaType aDeltaType) {
|
|
switch (aDeltaType) {
|
|
case ScrollWheelInput::SCROLLDELTA_LINE:
|
|
return WheelEvent_Binding::DOM_DELTA_LINE;
|
|
case ScrollWheelInput::SCROLLDELTA_PAGE:
|
|
return WheelEvent_Binding::DOM_DELTA_PAGE;
|
|
case ScrollWheelInput::SCROLLDELTA_PIXEL:
|
|
default:
|
|
return WheelEvent_Binding::DOM_DELTA_PIXEL;
|
|
}
|
|
}
|
|
|
|
ScrollUnit ScrollWheelInput::ScrollUnitForDeltaType(
|
|
ScrollDeltaType aDeltaType) {
|
|
switch (aDeltaType) {
|
|
case SCROLLDELTA_LINE:
|
|
return ScrollUnit::LINES;
|
|
case SCROLLDELTA_PAGE:
|
|
return ScrollUnit::PAGES;
|
|
case SCROLLDELTA_PIXEL:
|
|
return ScrollUnit::DEVICE_PIXELS;
|
|
default:
|
|
MOZ_CRASH();
|
|
}
|
|
return ScrollUnit::LINES;
|
|
}
|
|
|
|
WidgetWheelEvent ScrollWheelInput::ToWidgetEvent(nsIWidget* aWidget) const {
|
|
WidgetWheelEvent wheelEvent(true, eWheel, aWidget);
|
|
wheelEvent.mModifiers = this->modifiers;
|
|
wheelEvent.mTimeStamp = mTimeStamp;
|
|
wheelEvent.mLayersId = mLayersId;
|
|
wheelEvent.mRefPoint = RoundedToInt(ViewAs<LayoutDevicePixel>(
|
|
mOrigin,
|
|
PixelCastJustification::LayoutDeviceIsScreenForUntransformedEvent));
|
|
wheelEvent.mButtons = 0;
|
|
wheelEvent.mDeltaMode = DeltaModeForDeltaType(mDeltaType);
|
|
wheelEvent.mMayHaveMomentum = mMayHaveMomentum;
|
|
wheelEvent.mIsMomentum = mIsMomentum;
|
|
wheelEvent.mDeltaX = mDeltaX;
|
|
wheelEvent.mDeltaY = mDeltaY;
|
|
wheelEvent.mWheelTicksX = mWheelTicksX;
|
|
wheelEvent.mWheelTicksY = mWheelTicksY;
|
|
wheelEvent.mLineOrPageDeltaX = mLineOrPageDeltaX;
|
|
wheelEvent.mLineOrPageDeltaY = mLineOrPageDeltaY;
|
|
wheelEvent.mAllowToOverrideSystemScrollSpeed =
|
|
mAllowToOverrideSystemScrollSpeed;
|
|
wheelEvent.mFlags.mHandledByAPZ = mHandledByAPZ;
|
|
wheelEvent.mFocusSequenceNumber = mFocusSequenceNumber;
|
|
return wheelEvent;
|
|
}
|
|
|
|
bool ScrollWheelInput::TransformToLocal(
|
|
const ScreenToParentLayerMatrix4x4& aTransform) {
|
|
Maybe<ParentLayerPoint> point = UntransformBy(aTransform, mOrigin);
|
|
if (!point) {
|
|
return false;
|
|
}
|
|
mLocalOrigin = *point;
|
|
return true;
|
|
}
|
|
|
|
bool ScrollWheelInput::IsCustomizedByUserPrefs() const {
|
|
return mUserDeltaMultiplierX != 1.0 || mUserDeltaMultiplierY != 1.0;
|
|
}
|
|
|
|
KeyboardInput::KeyboardInput(const WidgetKeyboardEvent& aEvent)
|
|
: InputData(KEYBOARD_INPUT, aEvent.mTimeStamp, aEvent.mModifiers),
|
|
mKeyCode(aEvent.mKeyCode),
|
|
mCharCode(aEvent.mCharCode),
|
|
mHandledByAPZ(false) {
|
|
switch (aEvent.mMessage) {
|
|
case eKeyPress: {
|
|
mType = KeyboardInput::KEY_PRESS;
|
|
break;
|
|
}
|
|
case eKeyUp: {
|
|
mType = KeyboardInput::KEY_UP;
|
|
break;
|
|
}
|
|
case eKeyDown: {
|
|
mType = KeyboardInput::KEY_DOWN;
|
|
break;
|
|
}
|
|
default:
|
|
mType = KeyboardInput::KEY_OTHER;
|
|
break;
|
|
}
|
|
|
|
aEvent.GetShortcutKeyCandidates(mShortcutCandidates);
|
|
}
|
|
|
|
KeyboardInput::KeyboardInput()
|
|
: InputData(KEYBOARD_INPUT),
|
|
mType(KEY_DOWN),
|
|
mKeyCode(0),
|
|
mCharCode(0),
|
|
mHandledByAPZ(false) {}
|
|
|
|
} // namespace mozilla
|