1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* vim: set ts=2 et sw=2 tw=80: */ 3 /* This Source Code Form is subject to the terms of the Mozilla Public 4 * License, v. 2.0. If a copy of the MPL was not distributed with this file, 5 * You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 7 #ifndef mozilla_widget_WinMouseScrollHandler_h__ 8 #define mozilla_widget_WinMouseScrollHandler_h__ 9 10 #include "nscore.h" 11 #include "nsDebug.h" 12 #include "mozilla/Assertions.h" 13 #include "mozilla/EventForwards.h" 14 #include "mozilla/TimeStamp.h" 15 #include "Units.h" 16 #include <windows.h> 17 #include "nsPoint.h" 18 19 class nsWindowBase; 20 21 namespace mozilla { 22 namespace widget { 23 24 class ModifierKeyState; 25 26 struct MSGResult; 27 28 class MouseScrollHandler { 29 public: 30 static MouseScrollHandler* GetInstance(); 31 32 static void Initialize(); 33 static void Shutdown(); 34 35 static bool NeedsMessage(UINT aMsg); 36 static bool ProcessMessage(nsWindowBase* aWidget, UINT msg, WPARAM wParam, 37 LPARAM lParam, MSGResult& aResult); 38 39 /** 40 * See nsIWidget::SynthesizeNativeMouseScrollEvent() for the detail about 41 * this method. 42 */ 43 static nsresult SynthesizeNativeMouseScrollEvent( 44 nsWindowBase* aWidget, const LayoutDeviceIntPoint& aPoint, 45 uint32_t aNativeMessage, int32_t aDelta, uint32_t aModifierFlags, 46 uint32_t aAdditionalFlags); 47 48 /** 49 * IsWaitingInternalMessage() returns true if MouseScrollHandler posted 50 * an internal message for a native mouse wheel message and has not 51 * received it. Otherwise, false. 52 */ IsWaitingInternalMessage()53 static bool IsWaitingInternalMessage() { 54 return sInstance && sInstance->mIsWaitingInternalMessage; 55 } 56 57 private: 58 MouseScrollHandler(); 59 ~MouseScrollHandler(); 60 61 bool mIsWaitingInternalMessage; 62 63 static void MaybeLogKeyState(); 64 65 static MouseScrollHandler* sInstance; 66 67 /** 68 * InitEvent() initializes the aEvent. If aPoint is null, the result of 69 * GetCurrentMessagePos() will be used. 70 */ 71 static void InitEvent(nsWindowBase* aWidget, WidgetGUIEvent& aEvent, 72 LayoutDeviceIntPoint* aPoint = nullptr); 73 74 /** 75 * GetModifierKeyState() returns current modifier key state. 76 * Note that some devices need some hack for the modifier key state. 77 * This method does it automatically. 78 * 79 * @param aMessage Handling message. 80 */ 81 static ModifierKeyState GetModifierKeyState(UINT aMessage); 82 83 /** 84 * MozGetMessagePos() returns the mouse cursor position when GetMessage() 85 * was called last time. However, if we're sending a native message, 86 * this returns the specified cursor position by 87 * SynthesizeNativeMouseScrollEvent(). 88 */ 89 static POINTS GetCurrentMessagePos(); 90 91 /** 92 * ProcessNativeMouseWheelMessage() processes WM_MOUSEWHEEL and 93 * WM_MOUSEHWHEEL. Additionally, processes WM_VSCROLL and WM_HSCROLL if they 94 * should be processed as mouse wheel message. 95 * This method posts MOZ_WM_MOUSEVWHEEL, MOZ_WM_MOUSEHWHEEL, 96 * MOZ_WM_VSCROLL or MOZ_WM_HSCROLL if we need to dispatch mouse scroll 97 * events. That avoids deadlock with plugin process. 98 * 99 * @param aWidget A window which receives the message. 100 * @param aMessage WM_MOUSEWHEEL, WM_MOUSEHWHEEL, WM_VSCROLL or 101 * WM_HSCROLL. 102 * @param aWParam The wParam value of the message. 103 * @param aLParam The lParam value of the message. 104 */ 105 void ProcessNativeMouseWheelMessage(nsWindowBase* aWidget, UINT aMessage, 106 WPARAM aWParam, LPARAM aLParam); 107 108 /** 109 * ProcessNativeScrollMessage() processes WM_VSCROLL and WM_HSCROLL. 110 * This method just call ProcessMouseWheelMessage() if the message should be 111 * processed as mouse wheel message. Otherwise, dispatches a content 112 * command event. 113 * 114 * @param aWidget A window which receives the message. 115 * @param aMessage WM_VSCROLL or WM_HSCROLL. 116 * @param aWParam The wParam value of the message. 117 * @param aLParam The lParam value of the message. 118 * @return TRUE if the message is processed. Otherwise, FALSE. 119 */ 120 bool ProcessNativeScrollMessage(nsWindowBase* aWidget, UINT aMessage, 121 WPARAM aWParam, LPARAM aLParam); 122 123 /** 124 * HandleMouseWheelMessage() processes MOZ_WM_MOUSEVWHEEL and 125 * MOZ_WM_MOUSEHWHEEL which are posted when one of our windows received 126 * WM_MOUSEWHEEL or WM_MOUSEHWHEEL for avoiding deadlock with OOPP. 127 * 128 * @param aWidget A window which receives the wheel message. 129 * @param aMessage MOZ_WM_MOUSEWHEEL or MOZ_WM_MOUSEHWHEEL. 130 * @param aWParam The wParam value of the original message. 131 * @param aLParam The lParam value of the original message. 132 */ 133 void HandleMouseWheelMessage(nsWindowBase* aWidget, UINT aMessage, 134 WPARAM aWParam, LPARAM aLParam); 135 136 /** 137 * HandleScrollMessageAsMouseWheelMessage() processes the MOZ_WM_VSCROLL and 138 * MOZ_WM_HSCROLL which are posted when one of mouse windows received 139 * WM_VSCROLL or WM_HSCROLL and user wants them to emulate mouse wheel 140 * message's behavior. 141 * 142 * @param aWidget A window which receives the scroll message. 143 * @param aMessage MOZ_WM_VSCROLL or MOZ_WM_HSCROLL. 144 * @param aWParam The wParam value of the original message. 145 * @param aLParam The lParam value of the original message. 146 */ 147 void HandleScrollMessageAsMouseWheelMessage(nsWindowBase* aWidget, 148 UINT aMessage, WPARAM aWParam, 149 LPARAM aLParam); 150 151 /** 152 * ComputeMessagePos() computes the cursor position when the message was 153 * added to the queue. 154 * 155 * @param aMessage Handling message. 156 * @param aWParam Handling message's wParam. 157 * @param aLParam Handling message's lParam. 158 * @return Mouse cursor position when the message is added to 159 * the queue or current cursor position if the result of 160 * ::GetMessagePos() is broken. 161 */ 162 POINT ComputeMessagePos(UINT aMessage, WPARAM aWParam, LPARAM aLParam); 163 164 class EventInfo { 165 public: 166 /** 167 * @param aWidget An nsWindow which is handling the event. 168 * @param aMessage Must be WM_MOUSEWHEEL or WM_MOUSEHWHEEL. 169 */ 170 EventInfo(nsWindowBase* aWidget, UINT aMessage, WPARAM aWParam, 171 LPARAM aLParam); 172 173 bool CanDispatchWheelEvent() const; 174 GetNativeDelta()175 int32_t GetNativeDelta() const { return mDelta; } GetWindowHandle()176 HWND GetWindowHandle() const { return mWnd; } GetTimeStamp()177 const TimeStamp& GetTimeStamp() const { return mTimeStamp; } IsVertical()178 bool IsVertical() const { return mIsVertical; } IsPositive()179 bool IsPositive() const { return (mDelta > 0); } IsPage()180 bool IsPage() const { return mIsPage; } 181 182 /** 183 * @return Number of lines or pages scrolled per WHEEL_DELTA. 184 */ 185 int32_t GetScrollAmount() const; 186 187 protected: EventInfo()188 EventInfo() 189 : mIsVertical(false), mIsPage(false), mDelta(0), mWnd(nullptr) {} 190 191 // TRUE if event is for vertical scroll. Otherwise, FALSE. 192 bool mIsVertical; 193 // TRUE if event scrolls per page, otherwise, FALSE. 194 bool mIsPage; 195 // The native delta value. 196 int32_t mDelta; 197 // The window handle which is handling the event. 198 HWND mWnd; 199 // Timestamp of the event. 200 TimeStamp mTimeStamp; 201 }; 202 203 class LastEventInfo : public EventInfo { 204 public: LastEventInfo()205 LastEventInfo() : EventInfo(), mAccumulatedDelta(0) {} 206 207 /** 208 * CanContinueTransaction() checks whether the new event can continue the 209 * last transaction or not. Note that if there is no transaction, this 210 * returns true. 211 */ 212 bool CanContinueTransaction(const EventInfo& aNewEvent); 213 214 /** 215 * ResetTransaction() resets the transaction, i.e., the instance forgets 216 * the last event information. 217 */ 218 void ResetTransaction(); 219 220 /** 221 * RecordEvent() saves the information of new event. 222 */ 223 void RecordEvent(const EventInfo& aEvent); 224 225 /** 226 * InitWheelEvent() initializes NS_WHEEL_WHEEL event and 227 * recomputes the remaning detla for the event. 228 * This must be called only once during handling a message and after 229 * RecordEvent() is called. 230 * 231 * @param aWidget A window which will dispatch the event. 232 * @param aWheelEvent An NS_WHEEL_WHEEL event, this will be 233 * initialized. 234 * @param aModKeyState Current modifier key state. 235 * @return TRUE if the event is ready to dispatch. 236 * Otherwise, FALSE. 237 */ 238 bool InitWheelEvent(nsWindowBase* aWidget, WidgetWheelEvent& aWheelEvent, 239 const ModifierKeyState& aModKeyState); 240 241 private: 242 static int32_t RoundDelta(double aDelta); 243 244 int32_t mAccumulatedDelta; 245 }; 246 247 LastEventInfo mLastEventInfo; 248 249 class SystemSettings { 250 public: SystemSettings()251 SystemSettings() : mInitialized(false) {} 252 253 void Init(); 254 void MarkDirty(); 255 void NotifyUserPrefsMayOverrideSystemSettings(); 256 257 // On some environments, SystemParametersInfo() may be hooked by touchpad 258 // utility or something. In such case, when user changes active pointing 259 // device to another one, the result of SystemParametersInfo() may be 260 // changed without WM_SETTINGCHANGE message. For avoiding this trouble, 261 // we need to modify cache of system settings at every wheel message 262 // handling if we meet known device whose utility may hook the API. 263 void TrustedScrollSettingsDriver(); 264 265 // Returns true if the system scroll may be overridden for faster scroll. 266 // Otherwise, false. For example, if the user maybe uses an expensive 267 // mouse which supports acceleration of scroll speed, faster scroll makes 268 // the user inconvenient. 269 bool IsOverridingSystemScrollSpeedAllowed(); 270 GetScrollAmount(bool aForVertical)271 int32_t GetScrollAmount(bool aForVertical) const { 272 MOZ_ASSERT(mInitialized, "SystemSettings must be initialized"); 273 return aForVertical ? mScrollLines : mScrollChars; 274 } 275 IsPageScroll(bool aForVertical)276 bool IsPageScroll(bool aForVertical) const { 277 MOZ_ASSERT(mInitialized, "SystemSettings must be initialized"); 278 return aForVertical ? (uint32_t(mScrollLines) == WHEEL_PAGESCROLL) 279 : (uint32_t(mScrollChars) == WHEEL_PAGESCROLL); 280 } 281 282 // The default vertical and horizontal scrolling speed is 3, this is defined 283 // on the document of SystemParametersInfo in MSDN. DefaultScrollLines()284 static int32_t DefaultScrollLines() { return 3; } DefaultScrollChars()285 static int32_t DefaultScrollChars() { return 3; } 286 287 private: 288 bool mInitialized; 289 // The result of SystemParametersInfo() may not be reliable since it may 290 // be hooked. So, if the values are initialized with prefs, we can trust 291 // the value. Following mIsReliableScroll* are set true when mScroll* are 292 // initialized with prefs. 293 bool mIsReliableScrollLines; 294 bool mIsReliableScrollChars; 295 296 int32_t mScrollLines; 297 int32_t mScrollChars; 298 299 // Returns true if cached value is changed. 300 bool InitScrollLines(); 301 bool InitScrollChars(); 302 303 void RefreshCache(); 304 }; 305 306 SystemSettings mSystemSettings; 307 308 class UserPrefs { 309 public: 310 UserPrefs(); 311 ~UserPrefs(); 312 313 void MarkDirty(); 314 IsScrollMessageHandledAsWheelMessage()315 bool IsScrollMessageHandledAsWheelMessage() { 316 Init(); 317 return mScrollMessageHandledAsWheelMessage; 318 } 319 IsSystemSettingCacheEnabled()320 bool IsSystemSettingCacheEnabled() { 321 Init(); 322 return mEnableSystemSettingCache; 323 } 324 IsSystemSettingCacheForciblyEnabled()325 bool IsSystemSettingCacheForciblyEnabled() { 326 Init(); 327 return mForceEnableSystemSettingCache; 328 } 329 ShouldEmulateToMakeWindowUnderCursorForeground()330 bool ShouldEmulateToMakeWindowUnderCursorForeground() { 331 Init(); 332 return mEmulateToMakeWindowUnderCursorForeground; 333 } 334 GetOverriddenVerticalScrollAmout()335 int32_t GetOverriddenVerticalScrollAmout() { 336 Init(); 337 return mOverriddenVerticalScrollAmount; 338 } 339 GetOverriddenHorizontalScrollAmout()340 int32_t GetOverriddenHorizontalScrollAmout() { 341 Init(); 342 return mOverriddenHorizontalScrollAmount; 343 } 344 GetMouseScrollTransactionTimeout()345 int32_t GetMouseScrollTransactionTimeout() { 346 Init(); 347 return mMouseScrollTransactionTimeout; 348 } 349 350 private: 351 void Init(); 352 OnChange(const char * aPrefName,void * aClosure)353 static void OnChange(const char* aPrefName, void* aClosure) { 354 static_cast<UserPrefs*>(aClosure)->MarkDirty(); 355 } 356 357 bool mInitialized; 358 bool mScrollMessageHandledAsWheelMessage; 359 bool mEnableSystemSettingCache; 360 bool mForceEnableSystemSettingCache; 361 bool mEmulateToMakeWindowUnderCursorForeground; 362 int32_t mOverriddenVerticalScrollAmount; 363 int32_t mOverriddenHorizontalScrollAmount; 364 int32_t mMouseScrollTransactionTimeout; 365 }; 366 367 UserPrefs mUserPrefs; 368 369 class SynthesizingEvent { 370 public: SynthesizingEvent()371 SynthesizingEvent() 372 : mWnd(nullptr), 373 mMessage(0), 374 mWParam(0), 375 mLParam(0), 376 mStatus(NOT_SYNTHESIZING) {} 377 ~SynthesizingEvent()378 ~SynthesizingEvent() {} 379 380 static bool IsSynthesizing(); 381 382 nsresult Synthesize(const POINTS& aCursorPoint, HWND aWnd, UINT aMessage, 383 WPARAM aWParam, LPARAM aLParam, 384 const BYTE (&aKeyStates)[256]); 385 386 void NativeMessageReceived(nsWindowBase* aWidget, UINT aMessage, 387 WPARAM aWParam, LPARAM aLParam); 388 389 void NotifyNativeMessageHandlingFinished(); 390 void NotifyInternalMessageHandlingFinished(); 391 GetCursorPoint()392 const POINTS& GetCursorPoint() const { return mCursorPoint; } 393 394 private: 395 POINTS mCursorPoint; 396 HWND mWnd; 397 UINT mMessage; 398 WPARAM mWParam; 399 LPARAM mLParam; 400 BYTE mKeyState[256]; 401 BYTE mOriginalKeyState[256]; 402 403 enum Status { 404 NOT_SYNTHESIZING, 405 SENDING_MESSAGE, 406 NATIVE_MESSAGE_RECEIVED, 407 INTERNAL_MESSAGE_POSTED, 408 }; 409 Status mStatus; 410 GetStatusName()411 const char* GetStatusName() { 412 switch (mStatus) { 413 case NOT_SYNTHESIZING: 414 return "NOT_SYNTHESIZING"; 415 case SENDING_MESSAGE: 416 return "SENDING_MESSAGE"; 417 case NATIVE_MESSAGE_RECEIVED: 418 return "NATIVE_MESSAGE_RECEIVED"; 419 case INTERNAL_MESSAGE_POSTED: 420 return "INTERNAL_MESSAGE_POSTED"; 421 default: 422 return "Unknown"; 423 } 424 } 425 426 void Finish(); 427 }; // SynthesizingEvent 428 429 SynthesizingEvent* mSynthesizingEvent; 430 431 public: 432 class Device { 433 public: 434 // SynTP is a touchpad driver of Synaptics. 435 class SynTP { 436 public: IsDriverInstalled()437 static bool IsDriverInstalled() { return sMajorVersion != 0; } 438 /** 439 * GetDriverMajorVersion() returns the installed driver's major version. 440 * If SynTP driver isn't installed, this returns 0. 441 */ GetDriverMajorVersion()442 static int32_t GetDriverMajorVersion() { return sMajorVersion; } 443 /** 444 * GetDriverMinorVersion() returns the installed driver's minor version. 445 * If SynTP driver isn't installed, this returns -1. 446 */ GetDriverMinorVersion()447 static int32_t GetDriverMinorVersion() { return sMinorVersion; } 448 449 static void Init(); 450 451 private: 452 static bool sInitialized; 453 static int32_t sMajorVersion; 454 static int32_t sMinorVersion; 455 }; 456 457 class Elantech { 458 public: 459 /** 460 * GetDriverMajorVersion() returns the installed driver's major version. 461 * If Elantech's driver was installed, returns 0. 462 */ 463 static int32_t GetDriverMajorVersion(); 464 465 /** 466 * IsHelperWindow() checks whether aWnd is a helper window of Elantech's 467 * touchpad. Returns TRUE if so. Otherwise, FALSE. 468 */ 469 static bool IsHelperWindow(HWND aWnd); 470 471 /** 472 * Key message handler for Elantech's hack. Returns TRUE if the message 473 * is consumed by this handler. Otherwise, FALSE. 474 */ 475 static bool HandleKeyMessage(nsWindowBase* aWidget, UINT aMsg, 476 WPARAM aWParam, LPARAM aLParam); 477 478 static void UpdateZoomUntil(); 479 static bool IsZooming(); 480 481 static void Init(); 482 IsPinchHackNeeded()483 static bool IsPinchHackNeeded() { return sUsePinchHack; } 484 485 private: 486 // Whether to enable the Elantech swipe gesture hack. 487 static bool sUseSwipeHack; 488 // Whether to enable the Elantech pinch-to-zoom gesture hack. 489 static bool sUsePinchHack; 490 static DWORD sZoomUntil; 491 }; // class Elantech 492 493 // Apoint is a touchpad driver of Alps. 494 class Apoint { 495 public: IsDriverInstalled()496 static bool IsDriverInstalled() { return sMajorVersion != 0; } 497 /** 498 * GetDriverMajorVersion() returns the installed driver's major version. 499 * If Apoint driver isn't installed, this returns 0. 500 */ GetDriverMajorVersion()501 static int32_t GetDriverMajorVersion() { return sMajorVersion; } 502 /** 503 * GetDriverMinorVersion() returns the installed driver's minor version. 504 * If Apoint driver isn't installed, this returns -1. 505 */ GetDriverMinorVersion()506 static int32_t GetDriverMinorVersion() { return sMinorVersion; } 507 508 static void Init(); 509 510 private: 511 static bool sInitialized; 512 static int32_t sMajorVersion; 513 static int32_t sMinorVersion; 514 }; 515 516 class TrackPoint { 517 public: 518 /** 519 * IsDriverInstalled() returns TRUE if TrackPoint's driver is installed. 520 * Otherwise, returns FALSE. 521 */ 522 static bool IsDriverInstalled(); 523 }; // class TrackPoint 524 525 class UltraNav { 526 public: 527 /** 528 * IsObsoleteDriverInstalled() checks whether obsoleted UltraNav 529 * is installed on the environment. 530 * Returns TRUE if it was installed. Otherwise, FALSE. 531 */ 532 static bool IsObsoleteDriverInstalled(); 533 }; // class UltraNav 534 535 class SetPoint { 536 public: 537 /** 538 * SetPoint, Logitech's mouse driver, may report wrong cursor position 539 * for WM_MOUSEHWHEEL message. See comment in the implementation for 540 * the detail. 541 */ 542 static bool IsGetMessagePosResponseValid(UINT aMessage, WPARAM aWParam, 543 LPARAM aLParam); 544 545 private: 546 static bool sMightBeUsing; 547 }; 548 549 static void Init(); 550 IsFakeScrollableWindowNeeded()551 static bool IsFakeScrollableWindowNeeded() { 552 return sFakeScrollableWindowNeeded; 553 } 554 555 private: 556 /** 557 * Gets the bool value of aPrefName used to enable or disable an input 558 * workaround (like the Trackpoint hack). The pref can take values 0 (for 559 * disabled), 1 (for enabled) or -1 (to automatically detect whether to 560 * enable the workaround). 561 * 562 * @param aPrefName The name of the pref. 563 * @param aValueIfAutomatic Whether the given input workaround should be 564 * enabled by default. 565 */ 566 static bool GetWorkaroundPref(const char* aPrefName, 567 bool aValueIfAutomatic); 568 569 static bool sFakeScrollableWindowNeeded; 570 }; // class Device 571 }; 572 573 } // namespace widget 574 } // namespace mozilla 575 576 #endif // mozilla_widget_WinMouseScrollHandler_h__ 577