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