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__