michael@0: /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ michael@0: /* vim: set ts=2 et sw=2 tw=80: */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this file, michael@0: * You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #ifndef mozilla_widget_WinMouseScrollHandler_h__ michael@0: #define mozilla_widget_WinMouseScrollHandler_h__ michael@0: michael@0: #include "nscore.h" michael@0: #include "nsDebug.h" michael@0: #include "mozilla/Assertions.h" michael@0: #include "mozilla/EventForwards.h" michael@0: #include "mozilla/TimeStamp.h" michael@0: #include michael@0: michael@0: class nsWindowBase; michael@0: struct nsIntPoint; michael@0: michael@0: namespace mozilla { michael@0: namespace widget { michael@0: michael@0: class ModifierKeyState; michael@0: michael@0: struct MSGResult; michael@0: michael@0: class MouseScrollHandler { michael@0: public: michael@0: static MouseScrollHandler* GetInstance(); michael@0: michael@0: static void Initialize(); michael@0: static void Shutdown(); michael@0: michael@0: static bool NeedsMessage(UINT aMsg); michael@0: static bool ProcessMessage(nsWindowBase* aWidget, michael@0: UINT msg, michael@0: WPARAM wParam, michael@0: LPARAM lParam, michael@0: MSGResult& aResult); michael@0: michael@0: /** michael@0: * See nsIWidget::SynthesizeNativeMouseScrollEvent() for the detail about michael@0: * this method. michael@0: */ michael@0: static nsresult SynthesizeNativeMouseScrollEvent(nsWindowBase* aWidget, michael@0: const nsIntPoint& aPoint, michael@0: uint32_t aNativeMessage, michael@0: int32_t aDelta, michael@0: uint32_t aModifierFlags, michael@0: uint32_t aAdditionalFlags); michael@0: michael@0: /** michael@0: * IsWaitingInternalMessage() returns true if MouseScrollHandler posted michael@0: * an internal message for a native mouse wheel message and has not michael@0: * received it. Otherwise, false. michael@0: */ michael@0: static bool IsWaitingInternalMessage() michael@0: { michael@0: return sInstance && sInstance->mIsWaitingInternalMessage; michael@0: } michael@0: michael@0: private: michael@0: MouseScrollHandler(); michael@0: ~MouseScrollHandler(); michael@0: michael@0: bool mIsWaitingInternalMessage; michael@0: michael@0: static MouseScrollHandler* sInstance; michael@0: michael@0: /** michael@0: * DispatchEvent() dispatches aEvent on aWidget. michael@0: * michael@0: * @return TRUE if the event was consumed. Otherwise, FALSE. michael@0: */ michael@0: static bool DispatchEvent(nsWindowBase* aWidget, WidgetGUIEvent& aEvent); michael@0: michael@0: /** michael@0: * InitEvent() initializes the aEvent. If aPoint is null, the result of michael@0: * GetCurrentMessagePos() will be used. michael@0: */ michael@0: static void InitEvent(nsWindowBase* aWidget, michael@0: WidgetGUIEvent& aEvent, michael@0: nsIntPoint* aPoint = nullptr); michael@0: michael@0: /** michael@0: * GetModifierKeyState() returns current modifier key state. michael@0: * Note that some devices need some hack for the modifier key state. michael@0: * This method does it automatically. michael@0: * michael@0: * @param aMessage Handling message. michael@0: */ michael@0: static ModifierKeyState GetModifierKeyState(UINT aMessage); michael@0: michael@0: /** michael@0: * MozGetMessagePos() returns the mouse cursor position when GetMessage() michael@0: * was called last time. However, if we're sending a native message, michael@0: * this returns the specified cursor position by michael@0: * SynthesizeNativeMouseScrollEvent(). michael@0: */ michael@0: static POINTS GetCurrentMessagePos(); michael@0: michael@0: /** michael@0: * ProcessNativeMouseWheelMessage() processes WM_MOUSEWHEEL and michael@0: * WM_MOUSEHWHEEL. Additionally, processes WM_VSCROLL and WM_HSCROLL if they michael@0: * should be processed as mouse wheel message. michael@0: * This method posts MOZ_WM_MOUSEVWHEEL, MOZ_WM_MOUSEHWHEEL, michael@0: * MOZ_WM_VSCROLL or MOZ_WM_HSCROLL if we need to dispatch mouse scroll michael@0: * events. That avoids deadlock with plugin process. michael@0: * michael@0: * @param aWidget A window which receives the message. michael@0: * @param aMessage WM_MOUSEWHEEL, WM_MOUSEHWHEEL, WM_VSCROLL or michael@0: * WM_HSCROLL. michael@0: * @param aWParam The wParam value of the message. michael@0: * @param aLParam The lParam value of the message. michael@0: */ michael@0: void ProcessNativeMouseWheelMessage(nsWindowBase* aWidget, michael@0: UINT aMessage, michael@0: WPARAM aWParam, michael@0: LPARAM aLParam); michael@0: michael@0: /** michael@0: * ProcessNativeScrollMessage() processes WM_VSCROLL and WM_HSCROLL. michael@0: * This method just call ProcessMouseWheelMessage() if the message should be michael@0: * processed as mouse wheel message. Otherwise, dispatches a content michael@0: * command event. michael@0: * michael@0: * @param aWidget A window which receives the message. michael@0: * @param aMessage WM_VSCROLL or WM_HSCROLL. michael@0: * @param aWParam The wParam value of the message. michael@0: * @param aLParam The lParam value of the message. michael@0: * @return TRUE if the message is processed. Otherwise, FALSE. michael@0: */ michael@0: bool ProcessNativeScrollMessage(nsWindowBase* aWidget, michael@0: UINT aMessage, michael@0: WPARAM aWParam, michael@0: LPARAM aLParam); michael@0: michael@0: /** michael@0: * HandleMouseWheelMessage() processes MOZ_WM_MOUSEVWHEEL and michael@0: * MOZ_WM_MOUSEHWHEEL which are posted when one of our windows received michael@0: * WM_MOUSEWHEEL or WM_MOUSEHWHEEL for avoiding deadlock with OOPP. michael@0: * michael@0: * @param aWidget A window which receives the wheel message. michael@0: * @param aMessage MOZ_WM_MOUSEWHEEL or MOZ_WM_MOUSEHWHEEL. michael@0: * @param aWParam The wParam value of the original message. michael@0: * @param aLParam The lParam value of the original message. michael@0: */ michael@0: void HandleMouseWheelMessage(nsWindowBase* aWidget, michael@0: UINT aMessage, michael@0: WPARAM aWParam, michael@0: LPARAM aLParam); michael@0: michael@0: /** michael@0: * HandleScrollMessageAsMouseWheelMessage() processes the MOZ_WM_VSCROLL and michael@0: * MOZ_WM_HSCROLL which are posted when one of mouse windows received michael@0: * WM_VSCROLL or WM_HSCROLL and user wants them to emulate mouse wheel michael@0: * message's behavior. michael@0: * michael@0: * @param aWidget A window which receives the scroll message. michael@0: * @param aMessage MOZ_WM_VSCROLL or MOZ_WM_HSCROLL. michael@0: * @param aWParam The wParam value of the original message. michael@0: * @param aLParam The lParam value of the original message. michael@0: */ michael@0: void HandleScrollMessageAsMouseWheelMessage(nsWindowBase* aWidget, michael@0: UINT aMessage, michael@0: WPARAM aWParam, michael@0: LPARAM aLParam); michael@0: michael@0: /** michael@0: * ComputeMessagePos() computes the cursor position when the message was michael@0: * added to the queue. michael@0: * michael@0: * @param aMessage Handling message. michael@0: * @param aWParam Handling message's wParam. michael@0: * @param aLParam Handling message's lParam. michael@0: * @return Mouse cursor position when the message is added to michael@0: * the queue or current cursor position if the result of michael@0: * ::GetMessagePos() is broken. michael@0: */ michael@0: POINT ComputeMessagePos(UINT aMessage, michael@0: WPARAM aWParam, michael@0: LPARAM aLParam); michael@0: michael@0: class EventInfo { michael@0: public: michael@0: /** michael@0: * @param aWidget An nsWindow which is handling the event. michael@0: * @param aMessage Must be WM_MOUSEWHEEL or WM_MOUSEHWHEEL. michael@0: */ michael@0: EventInfo(nsWindowBase* aWidget, UINT aMessage, WPARAM aWParam, LPARAM aLParam); michael@0: michael@0: bool CanDispatchWheelEvent() const; michael@0: michael@0: int32_t GetNativeDelta() const { return mDelta; } michael@0: HWND GetWindowHandle() const { return mWnd; } michael@0: const TimeStamp& GetTimeStamp() const { return mTimeStamp; } michael@0: bool IsVertical() const { return mIsVertical; } michael@0: bool IsPositive() const { return (mDelta > 0); } michael@0: bool IsPage() const { return mIsPage; } michael@0: michael@0: /** michael@0: * @return Number of lines or pages scrolled per WHEEL_DELTA. michael@0: */ michael@0: int32_t GetScrollAmount() const; michael@0: michael@0: protected: michael@0: EventInfo() : michael@0: mIsVertical(false), mIsPage(false), mDelta(0), mWnd(nullptr) michael@0: { michael@0: } michael@0: michael@0: // TRUE if event is for vertical scroll. Otherwise, FALSE. michael@0: bool mIsVertical; michael@0: // TRUE if event scrolls per page, otherwise, FALSE. michael@0: bool mIsPage; michael@0: // The native delta value. michael@0: int32_t mDelta; michael@0: // The window handle which is handling the event. michael@0: HWND mWnd; michael@0: // Timestamp of the event. michael@0: TimeStamp mTimeStamp; michael@0: }; michael@0: michael@0: class LastEventInfo : public EventInfo { michael@0: public: michael@0: LastEventInfo() : michael@0: EventInfo(), mAccumulatedDelta(0) michael@0: { michael@0: } michael@0: michael@0: /** michael@0: * CanContinueTransaction() checks whether the new event can continue the michael@0: * last transaction or not. Note that if there is no transaction, this michael@0: * returns true. michael@0: */ michael@0: bool CanContinueTransaction(const EventInfo& aNewEvent); michael@0: michael@0: /** michael@0: * ResetTransaction() resets the transaction, i.e., the instance forgets michael@0: * the last event information. michael@0: */ michael@0: void ResetTransaction(); michael@0: michael@0: /** michael@0: * RecordEvent() saves the information of new event. michael@0: */ michael@0: void RecordEvent(const EventInfo& aEvent); michael@0: michael@0: /** michael@0: * InitWheelEvent() initializes NS_WHEEL_WHEEL event and michael@0: * recomputes the remaning detla for the event. michael@0: * This must be called only once during handling a message and after michael@0: * RecordEvent() is called. michael@0: * michael@0: * @param aWidget A window which will dispatch the event. michael@0: * @param aWheelEvent An NS_WHEEL_WHEEL event, this will be michael@0: * initialized. michael@0: * @param aModKeyState Current modifier key state. michael@0: * @return TRUE if the event is ready to dispatch. michael@0: * Otherwise, FALSE. michael@0: */ michael@0: bool InitWheelEvent(nsWindowBase* aWidget, michael@0: WidgetWheelEvent& aWheelEvent, michael@0: const ModifierKeyState& aModKeyState); michael@0: michael@0: private: michael@0: static int32_t RoundDelta(double aDelta); michael@0: michael@0: int32_t mAccumulatedDelta; michael@0: }; michael@0: michael@0: LastEventInfo mLastEventInfo; michael@0: michael@0: class SystemSettings { michael@0: public: michael@0: SystemSettings() : mInitialized(false) {} michael@0: michael@0: void Init(); michael@0: void MarkDirty(); michael@0: void NotifyUserPrefsMayOverrideSystemSettings(); michael@0: michael@0: int32_t GetScrollAmount(bool aForVertical) const michael@0: { michael@0: MOZ_ASSERT(mInitialized, "SystemSettings must be initialized"); michael@0: return aForVertical ? mScrollLines : mScrollChars; michael@0: } michael@0: michael@0: bool IsPageScroll(bool aForVertical) const michael@0: { michael@0: MOZ_ASSERT(mInitialized, "SystemSettings must be initialized"); michael@0: return aForVertical ? (mScrollLines == WHEEL_PAGESCROLL) : michael@0: (mScrollChars == WHEEL_PAGESCROLL); michael@0: } michael@0: michael@0: private: michael@0: bool mInitialized; michael@0: int32_t mScrollLines; michael@0: int32_t mScrollChars; michael@0: }; michael@0: michael@0: SystemSettings mSystemSettings; michael@0: michael@0: class UserPrefs { michael@0: public: michael@0: UserPrefs(); michael@0: ~UserPrefs(); michael@0: michael@0: void MarkDirty(); michael@0: michael@0: bool IsScrollMessageHandledAsWheelMessage() michael@0: { michael@0: Init(); michael@0: return mScrollMessageHandledAsWheelMessage; michael@0: } michael@0: michael@0: int32_t GetOverriddenVerticalScrollAmout() michael@0: { michael@0: Init(); michael@0: return mOverriddenVerticalScrollAmount; michael@0: } michael@0: michael@0: int32_t GetOverriddenHorizontalScrollAmout() michael@0: { michael@0: Init(); michael@0: return mOverriddenHorizontalScrollAmount; michael@0: } michael@0: michael@0: int32_t GetMouseScrollTransactionTimeout() michael@0: { michael@0: Init(); michael@0: return mMouseScrollTransactionTimeout; michael@0: } michael@0: michael@0: private: michael@0: void Init(); michael@0: michael@0: static void OnChange(const char* aPrefName, void* aClosure) michael@0: { michael@0: static_cast(aClosure)->MarkDirty(); michael@0: } michael@0: michael@0: bool mInitialized; michael@0: bool mScrollMessageHandledAsWheelMessage; michael@0: int32_t mOverriddenVerticalScrollAmount; michael@0: int32_t mOverriddenHorizontalScrollAmount; michael@0: int32_t mMouseScrollTransactionTimeout; michael@0: }; michael@0: michael@0: UserPrefs mUserPrefs; michael@0: michael@0: class SynthesizingEvent { michael@0: public: michael@0: SynthesizingEvent() : michael@0: mWnd(nullptr), mMessage(0), mWParam(0), mLParam(0), michael@0: mStatus(NOT_SYNTHESIZING) michael@0: { michael@0: } michael@0: michael@0: ~SynthesizingEvent() {} michael@0: michael@0: static bool IsSynthesizing(); michael@0: michael@0: nsresult Synthesize(const POINTS& aCursorPoint, HWND aWnd, michael@0: UINT aMessage, WPARAM aWParam, LPARAM aLParam, michael@0: const BYTE (&aKeyStates)[256]); michael@0: michael@0: void NativeMessageReceived(nsWindowBase* aWidget, UINT aMessage, michael@0: WPARAM aWParam, LPARAM aLParam); michael@0: michael@0: void NotifyNativeMessageHandlingFinished(); michael@0: void NotifyInternalMessageHandlingFinished(); michael@0: michael@0: const POINTS& GetCursorPoint() const { return mCursorPoint; } michael@0: michael@0: private: michael@0: POINTS mCursorPoint; michael@0: HWND mWnd; michael@0: UINT mMessage; michael@0: WPARAM mWParam; michael@0: LPARAM mLParam; michael@0: BYTE mKeyState[256]; michael@0: BYTE mOriginalKeyState[256]; michael@0: michael@0: enum Status { michael@0: NOT_SYNTHESIZING, michael@0: SENDING_MESSAGE, michael@0: NATIVE_MESSAGE_RECEIVED, michael@0: INTERNAL_MESSAGE_POSTED, michael@0: }; michael@0: Status mStatus; michael@0: michael@0: #ifdef PR_LOGGING michael@0: const char* GetStatusName() michael@0: { michael@0: switch (mStatus) { michael@0: case NOT_SYNTHESIZING: michael@0: return "NOT_SYNTHESIZING"; michael@0: case SENDING_MESSAGE: michael@0: return "SENDING_MESSAGE"; michael@0: case NATIVE_MESSAGE_RECEIVED: michael@0: return "NATIVE_MESSAGE_RECEIVED"; michael@0: case INTERNAL_MESSAGE_POSTED: michael@0: return "INTERNAL_MESSAGE_POSTED"; michael@0: default: michael@0: return "Unknown"; michael@0: } michael@0: } michael@0: #endif michael@0: michael@0: void Finish(); michael@0: }; // SynthesizingEvent michael@0: michael@0: SynthesizingEvent* mSynthesizingEvent; michael@0: michael@0: public: michael@0: michael@0: class Device { michael@0: public: michael@0: class Elantech { michael@0: public: michael@0: /** michael@0: * GetDriverMajorVersion() returns the installed driver's major version. michael@0: * If Elantech's driver was installed, returns 0. michael@0: */ michael@0: static int32_t GetDriverMajorVersion(); michael@0: michael@0: /** michael@0: * IsHelperWindow() checks whether aWnd is a helper window of Elantech's michael@0: * touchpad. Returns TRUE if so. Otherwise, FALSE. michael@0: */ michael@0: static bool IsHelperWindow(HWND aWnd); michael@0: michael@0: /** michael@0: * Key message handler for Elantech's hack. Returns TRUE if the message michael@0: * is consumed by this handler. Otherwise, FALSE. michael@0: */ michael@0: static bool HandleKeyMessage(nsWindowBase* aWidget, michael@0: UINT aMsg, michael@0: WPARAM aWParam); michael@0: michael@0: static void UpdateZoomUntil(); michael@0: static bool IsZooming(); michael@0: michael@0: static void Init(); michael@0: michael@0: static bool IsPinchHackNeeded() { return sUsePinchHack; } michael@0: michael@0: michael@0: private: michael@0: // Whether to enable the Elantech swipe gesture hack. michael@0: static bool sUseSwipeHack; michael@0: // Whether to enable the Elantech pinch-to-zoom gesture hack. michael@0: static bool sUsePinchHack; michael@0: static DWORD sZoomUntil; michael@0: }; // class Elantech michael@0: michael@0: class TrackPoint { michael@0: public: michael@0: /** michael@0: * IsDriverInstalled() returns TRUE if TrackPoint's driver is installed. michael@0: * Otherwise, returns FALSE. michael@0: */ michael@0: static bool IsDriverInstalled(); michael@0: }; // class TrackPoint michael@0: michael@0: class UltraNav { michael@0: public: michael@0: /** michael@0: * IsObsoleteDriverInstalled() checks whether obsoleted UltraNav michael@0: * is installed on the environment. michael@0: * Returns TRUE if it was installed. Otherwise, FALSE. michael@0: */ michael@0: static bool IsObsoleteDriverInstalled(); michael@0: }; // class UltraNav michael@0: michael@0: class SetPoint { michael@0: public: michael@0: /** michael@0: * SetPoint, Logitech's mouse driver, may report wrong cursor position michael@0: * for WM_MOUSEHWHEEL message. See comment in the implementation for michael@0: * the detail. michael@0: */ michael@0: static bool IsGetMessagePosResponseValid(UINT aMessage, michael@0: WPARAM aWParam, michael@0: LPARAM aLParam); michael@0: private: michael@0: static bool sMightBeUsing; michael@0: }; michael@0: michael@0: static void Init(); michael@0: michael@0: static bool IsFakeScrollableWindowNeeded() michael@0: { michael@0: return sFakeScrollableWindowNeeded; michael@0: } michael@0: michael@0: private: michael@0: /** michael@0: * Gets the bool value of aPrefName used to enable or disable an input michael@0: * workaround (like the Trackpoint hack). The pref can take values 0 (for michael@0: * disabled), 1 (for enabled) or -1 (to automatically detect whether to michael@0: * enable the workaround). michael@0: * michael@0: * @param aPrefName The name of the pref. michael@0: * @param aValueIfAutomatic Whether the given input workaround should be michael@0: * enabled by default. michael@0: */ michael@0: static bool GetWorkaroundPref(const char* aPrefName, michael@0: bool aValueIfAutomatic); michael@0: michael@0: static bool sFakeScrollableWindowNeeded; michael@0: }; // class Device michael@0: }; michael@0: michael@0: } // namespace widget michael@0: } // namespace mozilla michael@0: michael@0: #endif // mozilla_widget_WinMouseScrollHandler_h__