/* -*- 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/. */ #ifndef InputData_h__ #define InputData_h__ #include "nsDebug.h" #include "nsIScrollableFrame.h" #include "nsPoint.h" #include "nsTArray.h" #include "Units.h" #include "mozilla/DefineEnum.h" #include "mozilla/EventForwards.h" #include "mozilla/TimeStamp.h" #include "mozilla/WheelHandlingHelper.h" // for WheelDeltaAdjustmentStrategy #include "mozilla/gfx/MatrixFwd.h" #include "mozilla/layers/APZUtils.h" #include "mozilla/layers/KeyboardScrollAction.h" template struct already_AddRefed; class nsIWidget; namespace mozilla { namespace layers { class APZInputBridgeChild; class PAPZInputBridgeParent; } // namespace layers namespace dom { class Touch; } // namespace dom // clang-format off MOZ_DEFINE_ENUM( InputType, ( MULTITOUCH_INPUT, MOUSE_INPUT, PANGESTURE_INPUT, PINCHGESTURE_INPUT, TAPGESTURE_INPUT, SCROLLWHEEL_INPUT, KEYBOARD_INPUT )); // clang-format on class MultiTouchInput; class MouseInput; class PanGestureInput; class PinchGestureInput; class TapGestureInput; class ScrollWheelInput; class KeyboardInput; // This looks unnecessary now, but as we add more and more classes that derive // from InputType (eventually probably almost as many as *Events.h has), it // will be more and more clear what's going on with a macro that shortens the // definition of the RTTI functions. #define INPUTDATA_AS_CHILD_TYPE(type, enumID) \ const type& As##type() const { \ MOZ_ASSERT(mInputType == enumID, "Invalid cast of InputData."); \ return (const type&)*this; \ } \ type& As##type() { \ MOZ_ASSERT(mInputType == enumID, "Invalid cast of InputData."); \ return (type&)*this; \ } /** Base input data class. Should never be instantiated. */ class InputData { public: // Warning, this class is serialized and sent over IPC. Any change to its // fields must be reflected in its ParamTraits<>, in nsGUIEventIPC.h InputType mInputType; // Time in milliseconds that this data is relevant to. This only really // matters when this data is used as an event. We use uint32_t instead of // TimeStamp because it is easier to convert from WidgetInputEvent. The time // is platform-specific but it in the case of B2G and Fennec it is since // startup. uint32_t mTime; // Set in parallel to mTime until we determine it is safe to drop // platform-specific event times (see bug 77992). TimeStamp mTimeStamp; // The sequence number of the last potentially focus changing event handled // by APZ. This is used to track when that event has been processed by // content, and focus can be reconfirmed for async keyboard scrolling. uint64_t mFocusSequenceNumber; Modifiers modifiers; INPUTDATA_AS_CHILD_TYPE(MultiTouchInput, MULTITOUCH_INPUT) INPUTDATA_AS_CHILD_TYPE(MouseInput, MOUSE_INPUT) INPUTDATA_AS_CHILD_TYPE(PanGestureInput, PANGESTURE_INPUT) INPUTDATA_AS_CHILD_TYPE(PinchGestureInput, PINCHGESTURE_INPUT) INPUTDATA_AS_CHILD_TYPE(TapGestureInput, TAPGESTURE_INPUT) INPUTDATA_AS_CHILD_TYPE(ScrollWheelInput, SCROLLWHEEL_INPUT) INPUTDATA_AS_CHILD_TYPE(KeyboardInput, KEYBOARD_INPUT) virtual ~InputData(); explicit InputData(InputType aInputType); protected: InputData(InputType aInputType, uint32_t aTime, TimeStamp aTimeStamp, Modifiers aModifiers); }; /** * Data container for a single touch input. Similar to dom::Touch, but used in * off-main-thread situations. This is more for just storing touch data, whereas * dom::Touch is more useful for dispatching through the DOM (which can only * happen on the main thread). dom::Touch also bears the problem of storing * pointers to nsIWidget instances which can only be used on the main thread, * so if instead we used dom::Touch and ever set these pointers * off-main-thread, Bad Things Can Happen(tm). * * Note that this doesn't inherit from InputData because this itself is not an * event. It is only a container/struct that should have any number of instances * within a MultiTouchInput. * * fixme/bug 775746: Make dom::Touch inherit from this class. */ class SingleTouchData { public: // Construct a SingleTouchData from a Screen point. // mLocalScreenPoint remains (0,0) unless it's set later. SingleTouchData(int32_t aIdentifier, ScreenIntPoint aScreenPoint, ScreenSize aRadius, float aRotationAngle, float aForce); // Construct a SingleTouchData from a ParentLayer point. // mScreenPoint remains (0,0) unless it's set later. // Note: if APZ starts using the radius for anything, we should add a local // version of that too, and have this constructor take it as a // ParentLayerSize. SingleTouchData(int32_t aIdentifier, ParentLayerPoint aLocalScreenPoint, ScreenSize aRadius, float aRotationAngle, float aForce); SingleTouchData(); already_AddRefed ToNewDOMTouch() const; // Warning, this class is serialized and sent over IPC. Any change to its // fields must be reflected in its ParamTraits<>, in nsGUIEventIPC.h // A unique number assigned to each SingleTouchData within a MultiTouchInput // so that they can be easily distinguished when handling a touch // start/move/end. int32_t mIdentifier; // Point on the screen that the touch hit, in device pixels. They are // coordinates on the screen. ScreenIntPoint mScreenPoint; // |mScreenPoint| transformed to the local coordinates of the APZC targeted // by the hit. This is set and used by APZ. ParentLayerPoint mLocalScreenPoint; // Radius that the touch covers, i.e. if you're using your thumb it will // probably be larger than using your pinky, even with the same force. // Radius can be different along x and y. For example, if you press down with // your entire finger vertically, the y radius will be much larger than the x // radius. ScreenSize mRadius; float mRotationAngle; // How hard the screen is being pressed. float mForce; }; /** * Similar to WidgetTouchEvent, but for use off-main-thread. Also only stores a * screen touch point instead of the many different coordinate spaces * WidgetTouchEvent stores its touch point in. This includes a way to initialize * itself from a WidgetTouchEvent by copying all relevant data over. Note that * this copying from WidgetTouchEvent functionality can only be used on the main * thread. * * Stores an array of SingleTouchData. */ class MultiTouchInput : public InputData { public: // clang-format off MOZ_DEFINE_ENUM_AT_CLASS_SCOPE( MultiTouchType, ( MULTITOUCH_START, MULTITOUCH_MOVE, MULTITOUCH_END, MULTITOUCH_CANCEL )); // clang-format on MultiTouchInput(MultiTouchType aType, uint32_t aTime, TimeStamp aTimeStamp, Modifiers aModifiers); MultiTouchInput(); MultiTouchInput(const MultiTouchInput& aOther); explicit MultiTouchInput(const WidgetTouchEvent& aTouchEvent); void Translate(const ScreenPoint& aTranslation); WidgetTouchEvent ToWidgetTouchEvent(nsIWidget* aWidget) const; WidgetMouseEvent ToWidgetMouseEvent(nsIWidget* aWidget) const; // Return the index into mTouches of the SingleTouchData with the given // identifier, or -1 if there is no such SingleTouchData. int32_t IndexOfTouch(int32_t aTouchIdentifier); bool TransformToLocal(const ScreenToParentLayerMatrix4x4& aTransform); // Warning, this class is serialized and sent over IPC. Any change to its // fields must be reflected in its ParamTraits<>, in nsGUIEventIPC.h MultiTouchType mType; nsTArray mTouches; // The screen offset of the root widget. This can be changing along with // the touch interaction, so we sstore it in the event. ExternalPoint mScreenOffset; bool mHandledByAPZ; }; class MouseInput : public InputData { protected: friend mozilla::layers::APZInputBridgeChild; friend mozilla::layers::PAPZInputBridgeParent; MouseInput(); public: // clang-format off MOZ_DEFINE_ENUM_AT_CLASS_SCOPE( MouseType, ( MOUSE_NONE, MOUSE_MOVE, MOUSE_DOWN, MOUSE_UP, MOUSE_DRAG_START, MOUSE_DRAG_END, MOUSE_WIDGET_ENTER, MOUSE_WIDGET_EXIT, MOUSE_HITTEST )); MOZ_DEFINE_ENUM_AT_CLASS_SCOPE( ButtonType, ( LEFT_BUTTON, MIDDLE_BUTTON, RIGHT_BUTTON, NONE )); // clang-format on MouseInput(MouseType aType, ButtonType aButtonType, uint16_t aInputSource, int16_t aButtons, const ScreenPoint& aPoint, uint32_t aTime, TimeStamp aTimeStamp, Modifiers aModifiers); explicit MouseInput(const WidgetMouseEventBase& aMouseEvent); bool IsLeftButton() const; bool TransformToLocal(const ScreenToParentLayerMatrix4x4& aTransform); WidgetMouseEvent ToWidgetMouseEvent(nsIWidget* aWidget) const; // Warning, this class is serialized and sent over IPC. Any change to its // fields must be reflected in its ParamTraits<>, in nsGUIEventIPC.h MouseType mType; ButtonType mButtonType; uint16_t mInputSource; int16_t mButtons; ScreenPoint mOrigin; ParentLayerPoint mLocalOrigin; bool mHandledByAPZ; }; /** * Encapsulation class for pan events, can be used off-main-thread. * These events are currently only used for scrolling on desktop. */ class PanGestureInput : public InputData { protected: friend mozilla::layers::APZInputBridgeChild; friend mozilla::layers::PAPZInputBridgeParent; PanGestureInput(); public: // clang-format off MOZ_DEFINE_ENUM_AT_CLASS_SCOPE( PanGestureType, ( // MayStart: Dispatched before any actual panning has occurred but when a // pan gesture is probably about to start, for example when the user // starts touching the touchpad. Should interrupt any ongoing APZ // animation and can be used to trigger scrollability indicators (e.g. // flashing overlay scrollbars). PANGESTURE_MAYSTART, // Cancelled: Dispatched after MayStart when no pan gesture is going to // happen after all, for example when the user lifts their fingers from a // touchpad without having done any scrolling. PANGESTURE_CANCELLED, // Start: A pan gesture is starting. // For devices that do not support the MayStart event type, this event can // be used to interrupt ongoing APZ animations. PANGESTURE_START, // Pan: The actual pan motion by mPanDisplacement. PANGESTURE_PAN, // End: The pan gesture has ended, for example because the user has lifted // their fingers from a touchpad after scrolling. // Any potential momentum events fire after this event. PANGESTURE_END, // The following momentum event types are used in order to control the pan // momentum animation. Using these instead of our own animation ensures // that the animation curve is OS native and that the animation stops // reliably if it is cancelled by the user. // MomentumStart: Dispatched between the End event of the actual // user-controlled pan, and the first MomentumPan event of the momentum // animation. PANGESTURE_MOMENTUMSTART, // MomentumPan: The actual momentum motion by mPanDisplacement. PANGESTURE_MOMENTUMPAN, // MomentumEnd: The momentum animation has ended, for example because the // momentum velocity has gone below the stopping threshold, or because the // user has stopped the animation by putting their fingers on a touchpad. PANGESTURE_MOMENTUMEND )); // clang-format on PanGestureInput(PanGestureType aType, uint32_t aTime, TimeStamp aTimeStamp, const ScreenPoint& aPanStartPoint, const ScreenPoint& aPanDisplacement, Modifiers aModifiers); bool IsMomentum() const; WidgetWheelEvent ToWidgetWheelEvent(nsIWidget* aWidget) const; bool TransformToLocal(const ScreenToParentLayerMatrix4x4& aTransform); ScreenPoint UserMultipliedPanDisplacement() const; ParentLayerPoint UserMultipliedLocalPanDisplacement() const; // Warning, this class is serialized and sent over IPC. Any change to its // fields must be reflected in its ParamTraits<>, in nsGUIEventIPC.h PanGestureType mType; ScreenPoint mPanStartPoint; // The delta. This can be non-zero on any type of event. ScreenPoint mPanDisplacement; // Versions of |mPanStartPoint| and |mPanDisplacement| in the local // coordinates of the APZC receiving the pan. These are set and used by APZ. ParentLayerPoint mLocalPanStartPoint; ParentLayerPoint mLocalPanDisplacement; // See lineOrPageDeltaX/Y on WidgetWheelEvent. int32_t mLineOrPageDeltaX; int32_t mLineOrPageDeltaY; // User-set delta multipliers. double mUserDeltaMultiplierX; double mUserDeltaMultiplierY; bool mHandledByAPZ; // true if this is a PANGESTURE_END event that will be followed by a // PANGESTURE_MOMENTUMSTART event. bool mFollowedByMomentum; // If this is true, and this event started a new input block that couldn't // find a scrollable target which is scrollable in the horizontal component // of the scroll start direction, then this input block needs to be put on // hold until a content response has arrived, even if the block has a // confirmed target. // This is used by events that can result in a swipe instead of a scroll. bool mRequiresContentResponseIfCannotScrollHorizontallyInStartDirection; // This is used by APZ to communicate to the macOS widget code whether // the overscroll-behavior of the scroll frame handling this swipe allows // non-local overscroll behaviors in the horizontal direction (such as // swipe navigation). bool mOverscrollBehaviorAllowsSwipe; // XXX: If adding any more bools, switch to using bitfields instead. }; /** * Encapsulation class for pinch events. In general, these will be generated by * a gesture listener by looking at SingleTouchData/MultiTouchInput instances * and determining whether or not the user was trying to do a gesture. */ class PinchGestureInput : public InputData { protected: friend mozilla::layers::APZInputBridgeChild; friend mozilla::layers::PAPZInputBridgeParent; PinchGestureInput(); public: // clang-format off MOZ_DEFINE_ENUM_AT_CLASS_SCOPE( PinchGestureType, ( PINCHGESTURE_START, PINCHGESTURE_SCALE, PINCHGESTURE_END )); // clang-format on // Construct a pinch gesture from a Screen point. PinchGestureInput(PinchGestureType aType, uint32_t aTime, TimeStamp aTimeStamp, const ExternalPoint& aScreenOffset, const ScreenPoint& aFocusPoint, ScreenCoord aCurrentSpan, ScreenCoord aPreviousSpan, Modifiers aModifiers); bool TransformToLocal(const ScreenToParentLayerMatrix4x4& aTransform); // Warning, this class is serialized and sent over IPC. Any change to its // fields must be reflected in its ParamTraits<>, in nsGUIEventIPC.h PinchGestureType mType; // Center point of the pinch gesture. That is, if there are two fingers on the // screen, it is their midpoint. In the case of more than two fingers, the // point is implementation-specific, but can for example be the midpoint // between the very first and very last touch. This is in device pixels and // are the coordinates on the screen of this midpoint. // For PINCHGESTURE_END events, this instead will hold the coordinates of // the remaining finger, if there is one. If there isn't one then it will // store |BothFingersLifted()|. ScreenPoint mFocusPoint; // The screen offset of the root widget. This can be changing along with // the touch interaction, so we sstore it in the event. ExternalPoint mScreenOffset; // |mFocusPoint| transformed to the local coordinates of the APZC targeted // by the hit. This is set and used by APZ. ParentLayerPoint mLocalFocusPoint; // The distance between the touches responsible for the pinch gesture. ScreenCoord mCurrentSpan; // The previous |mCurrentSpan| in the PinchGestureInput preceding this one. // This is only really relevant during a PINCHGESTURE_SCALE because when it is // of this type then there must have been a history of spans. ScreenCoord mPreviousSpan; // A special value for mFocusPoint used in PINCHGESTURE_END events to // indicate that both fingers have been lifted. If only one finger has // been lifted, the coordinates of the remaining finger are expected to // be stored in mFocusPoint. // For pinch events that were not triggered by touch gestures, the // value of mFocusPoint in a PINCHGESTURE_END event is always expected // to be this value. // For convenience, we allow retrieving this value in any coordinate system. // Since it's a special value, no conversion is needed. template static gfx::PointTyped BothFingersLifted() { return gfx::PointTyped{-1, -1}; } }; /** * Encapsulation class for tap events. In general, these will be generated by * a gesture listener by looking at SingleTouchData/MultiTouchInput instances * and determining whether or not the user was trying to do a gesture. */ class TapGestureInput : public InputData { protected: friend mozilla::layers::APZInputBridgeChild; friend mozilla::layers::PAPZInputBridgeParent; TapGestureInput(); public: // clang-format off MOZ_DEFINE_ENUM_AT_CLASS_SCOPE( TapGestureType, ( TAPGESTURE_LONG, TAPGESTURE_LONG_UP, TAPGESTURE_UP, TAPGESTURE_CONFIRMED, TAPGESTURE_DOUBLE, TAPGESTURE_SECOND, // See GeckoContentController::TapType::eSecondTap TAPGESTURE_CANCEL )); // clang-format on // Construct a tap gesture from a Screen point. // mLocalPoint remains (0,0) unless it's set later. TapGestureInput(TapGestureType aType, uint32_t aTime, TimeStamp aTimeStamp, const ScreenIntPoint& aPoint, Modifiers aModifiers); // Construct a tap gesture from a ParentLayer point. // mPoint remains (0,0) unless it's set later. TapGestureInput(TapGestureType aType, uint32_t aTime, TimeStamp aTimeStamp, const ParentLayerPoint& aLocalPoint, Modifiers aModifiers); bool TransformToLocal(const ScreenToParentLayerMatrix4x4& aTransform); // Warning, this class is serialized and sent over IPC. Any change to its // fields must be reflected in its ParamTraits<>, in nsGUIEventIPC.h TapGestureType mType; // The location of the tap in screen pixels. ScreenIntPoint mPoint; // The location of the tap in the local coordinates of the APZC receiving it. // This is set and used by APZ. ParentLayerPoint mLocalPoint; }; // Encapsulation class for scroll-wheel events. These are generated by mice // with physical scroll wheels, and on Windows by most touchpads when using // scroll gestures. class ScrollWheelInput : public InputData { protected: friend mozilla::layers::APZInputBridgeChild; friend mozilla::layers::PAPZInputBridgeParent; typedef mozilla::layers::APZWheelAction APZWheelAction; ScrollWheelInput(); public: // clang-format off MOZ_DEFINE_ENUM_AT_CLASS_SCOPE( ScrollDeltaType, ( // There are three kinds of scroll delta modes in Gecko: "page", "line" // and "pixel". SCROLLDELTA_LINE, SCROLLDELTA_PAGE, SCROLLDELTA_PIXEL )); MOZ_DEFINE_ENUM_AT_CLASS_SCOPE( ScrollMode, ( SCROLLMODE_INSTANT, SCROLLMODE_SMOOTH ) ); // clang-format on ScrollWheelInput(uint32_t aTime, TimeStamp aTimeStamp, Modifiers aModifiers, ScrollMode aScrollMode, ScrollDeltaType aDeltaType, const ScreenPoint& aOrigin, double aDeltaX, double aDeltaY, bool aAllowToOverrideSystemScrollSpeed, WheelDeltaAdjustmentStrategy aWheelDeltaAdjustmentStrategy); explicit ScrollWheelInput(const WidgetWheelEvent& aEvent); static ScrollDeltaType DeltaTypeForDeltaMode(uint32_t aDeltaMode); static uint32_t DeltaModeForDeltaType(ScrollDeltaType aDeltaType); static nsIScrollableFrame::ScrollUnit ScrollUnitForDeltaType( ScrollDeltaType aDeltaType); WidgetWheelEvent ToWidgetWheelEvent(nsIWidget* aWidget) const; bool TransformToLocal(const ScreenToParentLayerMatrix4x4& aTransform); bool IsCustomizedByUserPrefs() const; // The following two functions are for auto-dir scrolling. For detailed // information on auto-dir, @see mozilla::WheelDeltaAdjustmentStrategy bool IsAutoDir() const { switch (mWheelDeltaAdjustmentStrategy) { case WheelDeltaAdjustmentStrategy::eAutoDir: case WheelDeltaAdjustmentStrategy::eAutoDirWithRootHonour: return true; default: // Prevent compilation errors generated by -Werror=switch break; } return false; } // Indicates which element this scroll honours if it's an auto-dir scroll. // If true, honour the root element; otherwise, honour the currently scrolling // target. // Note that if IsAutoDir() returns false, then this function also returns // false, but false in this case is meaningless as IsAutoDir() indicates it's // not an auto-dir scroll. // For detailed information on auto-dir, // @see mozilla::WheelDeltaAdjustmentStrategy bool HonoursRoot() const { return WheelDeltaAdjustmentStrategy::eAutoDirWithRootHonour == mWheelDeltaAdjustmentStrategy; } // Warning, this class is serialized and sent over IPC. Any change to its // fields must be reflected in its ParamTraits<>, in nsGUIEventIPC.h ScrollDeltaType mDeltaType; ScrollMode mScrollMode; ScreenPoint mOrigin; bool mHandledByAPZ; // Deltas are in units corresponding to the delta type. For line deltas, they // are the number of line units to scroll. The number of device pixels for a // horizontal and vertical line unit are in FrameMetrics::mLineScrollAmount. // For pixel deltas, these values are in ScreenCoords. // // The horizontal (X) delta is > 0 for scrolling right and < 0 for scrolling // left. The vertical (Y) delta is < 0 for scrolling up and > 0 for // scrolling down. double mDeltaX; double mDeltaY; // The location of the scroll in local coordinates. This is set and used by // APZ. ParentLayerPoint mLocalOrigin; // See lineOrPageDeltaX/Y on WidgetWheelEvent. int32_t mLineOrPageDeltaX; int32_t mLineOrPageDeltaY; // Indicates the order in which this event was added to a transaction. The // first event is 1; if not a member of a transaction, this is 0. uint32_t mScrollSeriesNumber; // User-set delta multipliers. double mUserDeltaMultiplierX; double mUserDeltaMultiplierY; bool mMayHaveMomentum; bool mIsMomentum; bool mAllowToOverrideSystemScrollSpeed; // Sometimes a wheel event input's wheel delta should be adjusted. This member // specifies how to adjust the wheel delta. WheelDeltaAdjustmentStrategy mWheelDeltaAdjustmentStrategy; APZWheelAction mAPZAction; }; class KeyboardInput : public InputData { public: typedef mozilla::layers::KeyboardScrollAction KeyboardScrollAction; // Note that if you change the first member in this enum(I.e. KEY_DOWN) to one // other member, don't forget to update the minimum value in // ContiguousEnumSerializer for KeyboardEventType in widget/nsGUIEventIPC // accordingly. enum KeyboardEventType { KEY_DOWN, KEY_PRESS, KEY_UP, // Any other key event such as eKeyDownOnPlugin KEY_OTHER, // Used as an upper bound for ContiguousEnumSerializer KEY_SENTINEL, }; explicit KeyboardInput(const WidgetKeyboardEvent& aEvent); // Warning, this class is serialized and sent over IPC. Any change to its // fields must be reflected in its ParamTraits<>, in nsGUIEventIPC.h KeyboardEventType mType; uint32_t mKeyCode; uint32_t mCharCode; nsTArray mShortcutCandidates; bool mHandledByAPZ; // The scroll action to perform on a layer for this keyboard input. This is // only used in APZ and is NOT serialized over IPC. KeyboardScrollAction mAction; protected: friend mozilla::layers::APZInputBridgeChild; friend mozilla::layers::PAPZInputBridgeParent; KeyboardInput(); }; } // namespace mozilla #endif // InputData_h__