widget/windows/WinMouseScrollHandler.h

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/widget/windows/WinMouseScrollHandler.h	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,518 @@
     1.4 +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
     1.5 +/* vim: set ts=2 et sw=2 tw=80: */
     1.6 +/* This Source Code Form is subject to the terms of the Mozilla Public
     1.7 + * License, v. 2.0. If a copy of the MPL was not distributed with this file,
     1.8 + * You can obtain one at http://mozilla.org/MPL/2.0/. */
     1.9 +
    1.10 +#ifndef mozilla_widget_WinMouseScrollHandler_h__
    1.11 +#define mozilla_widget_WinMouseScrollHandler_h__
    1.12 +
    1.13 +#include "nscore.h"
    1.14 +#include "nsDebug.h"
    1.15 +#include "mozilla/Assertions.h"
    1.16 +#include "mozilla/EventForwards.h"
    1.17 +#include "mozilla/TimeStamp.h"
    1.18 +#include <windows.h>
    1.19 +
    1.20 +class nsWindowBase;
    1.21 +struct nsIntPoint;
    1.22 +
    1.23 +namespace mozilla {
    1.24 +namespace widget {
    1.25 +
    1.26 +class ModifierKeyState;
    1.27 +
    1.28 +struct MSGResult;
    1.29 +
    1.30 +class MouseScrollHandler {
    1.31 +public:
    1.32 +  static MouseScrollHandler* GetInstance();
    1.33 +
    1.34 +  static void Initialize();
    1.35 +  static void Shutdown();
    1.36 +
    1.37 +  static bool NeedsMessage(UINT aMsg);
    1.38 +  static bool ProcessMessage(nsWindowBase* aWidget,
    1.39 +                             UINT msg,
    1.40 +                             WPARAM wParam,
    1.41 +                             LPARAM lParam,
    1.42 +                             MSGResult& aResult);
    1.43 +
    1.44 +  /**
    1.45 +   * See nsIWidget::SynthesizeNativeMouseScrollEvent() for the detail about
    1.46 +   * this method.
    1.47 +   */
    1.48 +  static nsresult SynthesizeNativeMouseScrollEvent(nsWindowBase* aWidget,
    1.49 +                                                   const nsIntPoint& aPoint,
    1.50 +                                                   uint32_t aNativeMessage,
    1.51 +                                                   int32_t aDelta,
    1.52 +                                                   uint32_t aModifierFlags,
    1.53 +                                                   uint32_t aAdditionalFlags);
    1.54 +
    1.55 +  /**
    1.56 +   * IsWaitingInternalMessage() returns true if MouseScrollHandler posted
    1.57 +   * an internal message for a native mouse wheel message and has not
    1.58 +   * received it. Otherwise, false.
    1.59 +   */
    1.60 +  static bool IsWaitingInternalMessage()
    1.61 +  {
    1.62 +    return sInstance && sInstance->mIsWaitingInternalMessage;
    1.63 +  }
    1.64 +
    1.65 +private:
    1.66 +  MouseScrollHandler();
    1.67 +  ~MouseScrollHandler();
    1.68 +
    1.69 +  bool mIsWaitingInternalMessage;
    1.70 +
    1.71 +  static MouseScrollHandler* sInstance;
    1.72 +
    1.73 +  /**
    1.74 +   * DispatchEvent() dispatches aEvent on aWidget.
    1.75 +   *
    1.76 +   * @return TRUE if the event was consumed.  Otherwise, FALSE.
    1.77 +   */
    1.78 +  static bool DispatchEvent(nsWindowBase* aWidget, WidgetGUIEvent& aEvent);
    1.79 +
    1.80 +  /**
    1.81 +   * InitEvent() initializes the aEvent.  If aPoint is null, the result of
    1.82 +   * GetCurrentMessagePos() will be used.
    1.83 +   */
    1.84 +  static void InitEvent(nsWindowBase* aWidget,
    1.85 +                        WidgetGUIEvent& aEvent,
    1.86 +                        nsIntPoint* aPoint = nullptr);
    1.87 +
    1.88 +  /**
    1.89 +   * GetModifierKeyState() returns current modifier key state.
    1.90 +   * Note that some devices need some hack for the modifier key state.
    1.91 +   * This method does it automatically.
    1.92 +   *
    1.93 +   * @param aMessage    Handling message.
    1.94 +   */
    1.95 +  static ModifierKeyState GetModifierKeyState(UINT aMessage);
    1.96 +
    1.97 +  /**
    1.98 +   * MozGetMessagePos() returns the mouse cursor position when GetMessage()
    1.99 +   * was called last time.  However, if we're sending a native message,
   1.100 +   * this returns the specified cursor position by
   1.101 +   * SynthesizeNativeMouseScrollEvent().
   1.102 +   */
   1.103 +  static POINTS GetCurrentMessagePos();
   1.104 +
   1.105 +  /**
   1.106 +   * ProcessNativeMouseWheelMessage() processes WM_MOUSEWHEEL and
   1.107 +   * WM_MOUSEHWHEEL.  Additionally, processes WM_VSCROLL and WM_HSCROLL if they
   1.108 +   * should be processed as mouse wheel message.
   1.109 +   * This method posts MOZ_WM_MOUSEVWHEEL, MOZ_WM_MOUSEHWHEEL,
   1.110 +   * MOZ_WM_VSCROLL or MOZ_WM_HSCROLL if we need to dispatch mouse scroll
   1.111 +   * events.  That avoids deadlock with plugin process.
   1.112 +   *
   1.113 +   * @param aWidget     A window which receives the message.
   1.114 +   * @param aMessage    WM_MOUSEWHEEL, WM_MOUSEHWHEEL, WM_VSCROLL or
   1.115 +   *                    WM_HSCROLL.
   1.116 +   * @param aWParam     The wParam value of the message.
   1.117 +   * @param aLParam     The lParam value of the message.
   1.118 +   */
   1.119 +  void ProcessNativeMouseWheelMessage(nsWindowBase* aWidget,
   1.120 +                                      UINT aMessage,
   1.121 +                                      WPARAM aWParam,
   1.122 +                                      LPARAM aLParam);
   1.123 +
   1.124 +  /**
   1.125 +   * ProcessNativeScrollMessage() processes WM_VSCROLL and WM_HSCROLL.
   1.126 +   * This method just call ProcessMouseWheelMessage() if the message should be
   1.127 +   * processed as mouse wheel message.  Otherwise, dispatches a content
   1.128 +   * command event.
   1.129 +   *
   1.130 +   * @param aWidget     A window which receives the message.
   1.131 +   * @param aMessage    WM_VSCROLL or WM_HSCROLL.
   1.132 +   * @param aWParam     The wParam value of the message.
   1.133 +   * @param aLParam     The lParam value of the message.
   1.134 +   * @return            TRUE if the message is processed.  Otherwise, FALSE.
   1.135 +   */
   1.136 +  bool ProcessNativeScrollMessage(nsWindowBase* aWidget,
   1.137 +                                  UINT aMessage,
   1.138 +                                  WPARAM aWParam,
   1.139 +                                  LPARAM aLParam);
   1.140 +
   1.141 +  /**
   1.142 +   * HandleMouseWheelMessage() processes MOZ_WM_MOUSEVWHEEL and
   1.143 +   * MOZ_WM_MOUSEHWHEEL which are posted when one of our windows received
   1.144 +   * WM_MOUSEWHEEL or WM_MOUSEHWHEEL for avoiding deadlock with OOPP.
   1.145 +   *
   1.146 +   * @param aWidget     A window which receives the wheel message.
   1.147 +   * @param aMessage    MOZ_WM_MOUSEWHEEL or MOZ_WM_MOUSEHWHEEL.
   1.148 +   * @param aWParam     The wParam value of the original message.
   1.149 +   * @param aLParam     The lParam value of the original message.
   1.150 +   */
   1.151 +  void HandleMouseWheelMessage(nsWindowBase* aWidget,
   1.152 +                               UINT aMessage,
   1.153 +                               WPARAM aWParam,
   1.154 +                               LPARAM aLParam);
   1.155 +
   1.156 +  /**
   1.157 +   * HandleScrollMessageAsMouseWheelMessage() processes the MOZ_WM_VSCROLL and
   1.158 +   * MOZ_WM_HSCROLL which are posted when one of mouse windows received
   1.159 +   * WM_VSCROLL or WM_HSCROLL and user wants them to emulate mouse wheel
   1.160 +   * message's behavior.
   1.161 +   *
   1.162 +   * @param aWidget     A window which receives the scroll message.
   1.163 +   * @param aMessage    MOZ_WM_VSCROLL or MOZ_WM_HSCROLL.
   1.164 +   * @param aWParam     The wParam value of the original message.
   1.165 +   * @param aLParam     The lParam value of the original message.
   1.166 +   */
   1.167 +  void HandleScrollMessageAsMouseWheelMessage(nsWindowBase* aWidget,
   1.168 +                                              UINT aMessage,
   1.169 +                                              WPARAM aWParam,
   1.170 +                                              LPARAM aLParam);
   1.171 +
   1.172 +  /**
   1.173 +   * ComputeMessagePos() computes the cursor position when the message was
   1.174 +   * added to the queue.
   1.175 +   *
   1.176 +   * @param aMessage    Handling message.
   1.177 +   * @param aWParam     Handling message's wParam.
   1.178 +   * @param aLParam     Handling message's lParam.
   1.179 +   * @return            Mouse cursor position when the message is added to
   1.180 +   *                    the queue or current cursor position if the result of
   1.181 +   *                    ::GetMessagePos() is broken.
   1.182 +   */
   1.183 +  POINT ComputeMessagePos(UINT aMessage,
   1.184 +                          WPARAM aWParam,
   1.185 +                          LPARAM aLParam);
   1.186 +
   1.187 +  class EventInfo {
   1.188 +  public:
   1.189 +    /**
   1.190 +     * @param aWidget   An nsWindow which is handling the event.
   1.191 +     * @param aMessage  Must be WM_MOUSEWHEEL or WM_MOUSEHWHEEL.
   1.192 +     */
   1.193 +    EventInfo(nsWindowBase* aWidget, UINT aMessage, WPARAM aWParam, LPARAM aLParam);
   1.194 +
   1.195 +    bool CanDispatchWheelEvent() const;
   1.196 +
   1.197 +    int32_t GetNativeDelta() const { return mDelta; }
   1.198 +    HWND GetWindowHandle() const { return mWnd; }
   1.199 +    const TimeStamp& GetTimeStamp() const { return mTimeStamp; }
   1.200 +    bool IsVertical() const { return mIsVertical; }
   1.201 +    bool IsPositive() const { return (mDelta > 0); }
   1.202 +    bool IsPage() const { return mIsPage; }
   1.203 +
   1.204 +    /**
   1.205 +     * @return          Number of lines or pages scrolled per WHEEL_DELTA.
   1.206 +     */
   1.207 +    int32_t GetScrollAmount() const;
   1.208 +
   1.209 +  protected:
   1.210 +    EventInfo() :
   1.211 +      mIsVertical(false), mIsPage(false), mDelta(0), mWnd(nullptr)
   1.212 +    {
   1.213 +    }
   1.214 +
   1.215 +    // TRUE if event is for vertical scroll.  Otherwise, FALSE.
   1.216 +    bool mIsVertical;
   1.217 +    // TRUE if event scrolls per page, otherwise, FALSE.
   1.218 +    bool mIsPage;
   1.219 +    // The native delta value.
   1.220 +    int32_t mDelta;
   1.221 +    // The window handle which is handling the event.
   1.222 +    HWND mWnd;
   1.223 +    // Timestamp of the event.
   1.224 +    TimeStamp mTimeStamp;
   1.225 +  };
   1.226 +
   1.227 +  class LastEventInfo : public EventInfo {
   1.228 +  public:
   1.229 +    LastEventInfo() :
   1.230 +      EventInfo(), mAccumulatedDelta(0)
   1.231 +    {
   1.232 +    }
   1.233 +
   1.234 +    /**
   1.235 +     * CanContinueTransaction() checks whether the new event can continue the
   1.236 +     * last transaction or not.  Note that if there is no transaction, this
   1.237 +     * returns true.
   1.238 +     */
   1.239 +    bool CanContinueTransaction(const EventInfo& aNewEvent);
   1.240 +
   1.241 +    /**
   1.242 +     * ResetTransaction() resets the transaction, i.e., the instance forgets
   1.243 +     * the last event information.
   1.244 +     */
   1.245 +    void ResetTransaction();
   1.246 +
   1.247 +    /**
   1.248 +     * RecordEvent() saves the information of new event.
   1.249 +     */
   1.250 +    void RecordEvent(const EventInfo& aEvent);
   1.251 +
   1.252 +    /**
   1.253 +     * InitWheelEvent() initializes NS_WHEEL_WHEEL event and
   1.254 +     * recomputes the remaning detla for the event.
   1.255 +     * This must be called only once during handling a message and after
   1.256 +     * RecordEvent() is called.
   1.257 +     *
   1.258 +     * @param aWidget           A window which will dispatch the event.
   1.259 +     * @param aWheelEvent       An NS_WHEEL_WHEEL event, this will be
   1.260 +     *                          initialized.
   1.261 +     * @param aModKeyState      Current modifier key state.
   1.262 +     * @return                  TRUE if the event is ready to dispatch.
   1.263 +     *                          Otherwise, FALSE.
   1.264 +     */
   1.265 +    bool InitWheelEvent(nsWindowBase* aWidget,
   1.266 +                        WidgetWheelEvent& aWheelEvent,
   1.267 +                        const ModifierKeyState& aModKeyState);
   1.268 +
   1.269 +  private:
   1.270 +    static int32_t RoundDelta(double aDelta);
   1.271 +
   1.272 +    int32_t mAccumulatedDelta;
   1.273 +  };
   1.274 +
   1.275 +  LastEventInfo mLastEventInfo;
   1.276 +
   1.277 +  class SystemSettings {
   1.278 +  public:
   1.279 +    SystemSettings() : mInitialized(false) {}
   1.280 +
   1.281 +    void Init();
   1.282 +    void MarkDirty();
   1.283 +    void NotifyUserPrefsMayOverrideSystemSettings();
   1.284 +
   1.285 +    int32_t GetScrollAmount(bool aForVertical) const
   1.286 +    {
   1.287 +      MOZ_ASSERT(mInitialized, "SystemSettings must be initialized");
   1.288 +      return aForVertical ? mScrollLines : mScrollChars;
   1.289 +    }
   1.290 +
   1.291 +    bool IsPageScroll(bool aForVertical) const
   1.292 +    {
   1.293 +      MOZ_ASSERT(mInitialized, "SystemSettings must be initialized");
   1.294 +      return aForVertical ? (mScrollLines == WHEEL_PAGESCROLL) :
   1.295 +                            (mScrollChars == WHEEL_PAGESCROLL);
   1.296 +    }
   1.297 +
   1.298 +  private:
   1.299 +    bool mInitialized;
   1.300 +    int32_t mScrollLines;
   1.301 +    int32_t mScrollChars;
   1.302 +  };
   1.303 +
   1.304 +  SystemSettings mSystemSettings;
   1.305 +
   1.306 +  class UserPrefs {
   1.307 +  public:
   1.308 +    UserPrefs();
   1.309 +    ~UserPrefs();
   1.310 +
   1.311 +    void MarkDirty();
   1.312 +
   1.313 +    bool IsScrollMessageHandledAsWheelMessage()
   1.314 +    {
   1.315 +      Init();
   1.316 +      return mScrollMessageHandledAsWheelMessage;
   1.317 +    }
   1.318 +
   1.319 +    int32_t GetOverriddenVerticalScrollAmout()
   1.320 +    {
   1.321 +      Init();
   1.322 +      return mOverriddenVerticalScrollAmount;
   1.323 +    }
   1.324 +
   1.325 +    int32_t GetOverriddenHorizontalScrollAmout()
   1.326 +    {
   1.327 +      Init();
   1.328 +      return mOverriddenHorizontalScrollAmount;
   1.329 +    }
   1.330 +
   1.331 +    int32_t GetMouseScrollTransactionTimeout()
   1.332 +    {
   1.333 +      Init();
   1.334 +      return mMouseScrollTransactionTimeout;
   1.335 +    }
   1.336 +
   1.337 +  private:
   1.338 +    void Init();
   1.339 +
   1.340 +    static void OnChange(const char* aPrefName, void* aClosure)
   1.341 +    {
   1.342 +      static_cast<UserPrefs*>(aClosure)->MarkDirty();
   1.343 +    }
   1.344 +
   1.345 +    bool mInitialized;
   1.346 +    bool mScrollMessageHandledAsWheelMessage;
   1.347 +    int32_t mOverriddenVerticalScrollAmount;
   1.348 +    int32_t mOverriddenHorizontalScrollAmount;
   1.349 +    int32_t mMouseScrollTransactionTimeout;
   1.350 +  };
   1.351 +
   1.352 +  UserPrefs mUserPrefs;
   1.353 +
   1.354 +  class SynthesizingEvent {
   1.355 +  public:
   1.356 +    SynthesizingEvent() :
   1.357 +      mWnd(nullptr), mMessage(0), mWParam(0), mLParam(0),
   1.358 +      mStatus(NOT_SYNTHESIZING)
   1.359 +    {
   1.360 +    }
   1.361 +
   1.362 +    ~SynthesizingEvent() {}
   1.363 +
   1.364 +    static bool IsSynthesizing();
   1.365 +
   1.366 +    nsresult Synthesize(const POINTS& aCursorPoint, HWND aWnd,
   1.367 +                        UINT aMessage, WPARAM aWParam, LPARAM aLParam,
   1.368 +                        const BYTE (&aKeyStates)[256]);
   1.369 +
   1.370 +    void NativeMessageReceived(nsWindowBase* aWidget, UINT aMessage,
   1.371 +                               WPARAM aWParam, LPARAM aLParam);
   1.372 +
   1.373 +    void NotifyNativeMessageHandlingFinished();
   1.374 +    void NotifyInternalMessageHandlingFinished();
   1.375 +
   1.376 +    const POINTS& GetCursorPoint() const { return mCursorPoint; }
   1.377 +
   1.378 +  private:
   1.379 +    POINTS mCursorPoint;
   1.380 +    HWND mWnd;
   1.381 +    UINT mMessage;
   1.382 +    WPARAM mWParam;
   1.383 +    LPARAM mLParam;
   1.384 +    BYTE mKeyState[256];
   1.385 +    BYTE mOriginalKeyState[256];
   1.386 +
   1.387 +    enum Status {
   1.388 +      NOT_SYNTHESIZING,
   1.389 +      SENDING_MESSAGE,
   1.390 +      NATIVE_MESSAGE_RECEIVED,
   1.391 +      INTERNAL_MESSAGE_POSTED,
   1.392 +    };
   1.393 +    Status mStatus;
   1.394 +
   1.395 +#ifdef PR_LOGGING
   1.396 +    const char* GetStatusName()
   1.397 +    {
   1.398 +      switch (mStatus) {
   1.399 +        case NOT_SYNTHESIZING:
   1.400 +          return "NOT_SYNTHESIZING";
   1.401 +        case SENDING_MESSAGE:
   1.402 +          return "SENDING_MESSAGE";
   1.403 +        case NATIVE_MESSAGE_RECEIVED:
   1.404 +          return "NATIVE_MESSAGE_RECEIVED";
   1.405 +        case INTERNAL_MESSAGE_POSTED:
   1.406 +          return "INTERNAL_MESSAGE_POSTED";
   1.407 +        default:
   1.408 +          return "Unknown";
   1.409 +      }
   1.410 +    }
   1.411 +#endif
   1.412 +
   1.413 +    void Finish();
   1.414 +  }; // SynthesizingEvent
   1.415 +
   1.416 +  SynthesizingEvent* mSynthesizingEvent;
   1.417 +
   1.418 +public:
   1.419 +
   1.420 +  class Device {
   1.421 +  public:
   1.422 +    class Elantech {
   1.423 +    public:
   1.424 +      /**
   1.425 +       * GetDriverMajorVersion() returns the installed driver's major version.
   1.426 +       * If Elantech's driver was installed, returns 0.
   1.427 +       */
   1.428 +      static int32_t GetDriverMajorVersion();
   1.429 +
   1.430 +      /**
   1.431 +       * IsHelperWindow() checks whether aWnd is a helper window of Elantech's
   1.432 +       * touchpad.  Returns TRUE if so.  Otherwise, FALSE.
   1.433 +       */
   1.434 +      static bool IsHelperWindow(HWND aWnd);
   1.435 +
   1.436 +      /**
   1.437 +       * Key message handler for Elantech's hack.  Returns TRUE if the message
   1.438 +       * is consumed by this handler.  Otherwise, FALSE.
   1.439 +       */
   1.440 +      static bool HandleKeyMessage(nsWindowBase* aWidget,
   1.441 +                                   UINT aMsg,
   1.442 +                                   WPARAM aWParam);
   1.443 +
   1.444 +      static void UpdateZoomUntil();
   1.445 +      static bool IsZooming();
   1.446 +
   1.447 +      static void Init();
   1.448 +
   1.449 +      static bool IsPinchHackNeeded() { return sUsePinchHack; }
   1.450 +
   1.451 +
   1.452 +    private:
   1.453 +      // Whether to enable the Elantech swipe gesture hack.
   1.454 +      static bool sUseSwipeHack;
   1.455 +      // Whether to enable the Elantech pinch-to-zoom gesture hack.
   1.456 +      static bool sUsePinchHack;
   1.457 +      static DWORD sZoomUntil;
   1.458 +    }; // class Elantech
   1.459 +
   1.460 +    class TrackPoint {
   1.461 +    public:
   1.462 +      /**
   1.463 +       * IsDriverInstalled() returns TRUE if TrackPoint's driver is installed.
   1.464 +       * Otherwise, returns FALSE.
   1.465 +       */
   1.466 +      static bool IsDriverInstalled();
   1.467 +    }; // class TrackPoint
   1.468 +
   1.469 +    class UltraNav {
   1.470 +    public:
   1.471 +      /**
   1.472 +       * IsObsoleteDriverInstalled() checks whether obsoleted UltraNav
   1.473 +       * is installed on the environment.
   1.474 +       * Returns TRUE if it was installed.  Otherwise, FALSE.
   1.475 +       */
   1.476 +      static bool IsObsoleteDriverInstalled();
   1.477 +    }; // class UltraNav
   1.478 +
   1.479 +    class SetPoint {
   1.480 +    public:
   1.481 +      /**
   1.482 +       * SetPoint, Logitech's mouse driver, may report wrong cursor position
   1.483 +       * for WM_MOUSEHWHEEL message.  See comment in the implementation for
   1.484 +       * the detail.
   1.485 +       */
   1.486 +      static bool IsGetMessagePosResponseValid(UINT aMessage,
   1.487 +                                               WPARAM aWParam,
   1.488 +                                               LPARAM aLParam);
   1.489 +    private:
   1.490 +      static bool sMightBeUsing;
   1.491 +    };
   1.492 +
   1.493 +    static void Init();
   1.494 +
   1.495 +    static bool IsFakeScrollableWindowNeeded()
   1.496 +    {
   1.497 +      return sFakeScrollableWindowNeeded;
   1.498 +    }
   1.499 +
   1.500 +  private:
   1.501 +    /**
   1.502 +     * Gets the bool value of aPrefName used to enable or disable an input
   1.503 +     * workaround (like the Trackpoint hack).  The pref can take values 0 (for
   1.504 +     * disabled), 1 (for enabled) or -1 (to automatically detect whether to
   1.505 +     * enable the workaround).
   1.506 +     *
   1.507 +     * @param aPrefName The name of the pref.
   1.508 +     * @param aValueIfAutomatic Whether the given input workaround should be
   1.509 +     *                          enabled by default.
   1.510 +     */
   1.511 +    static bool GetWorkaroundPref(const char* aPrefName,
   1.512 +                                  bool aValueIfAutomatic);
   1.513 +
   1.514 +    static bool sFakeScrollableWindowNeeded;
   1.515 +  }; // class Device
   1.516 +};
   1.517 +
   1.518 +} // namespace widget
   1.519 +} // namespace mozilla
   1.520 +
   1.521 +#endif // mozilla_widget_WinMouseScrollHandler_h__

mercurial