1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3  * License, v. 2.0. If a copy of the MPL was not distributed with this
4  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5 
6 /*
7  * WinPointerEvents - Helper functions to retrieve PointerEvent's attributes
8  */
9 
10 #include "nscore.h"
11 #include "WinPointerEvents.h"
12 #include "mozilla/MouseEvents.h"
13 #include "mozilla/WindowsVersion.h"
14 
15 using namespace mozilla;
16 using namespace mozilla::widget;
17 
18 const wchar_t WinPointerEvents::kPointerLibraryName[] = L"user32.dll";
19 HMODULE WinPointerEvents::sLibraryHandle = nullptr;
20 WinPointerEvents::GetPointerTypePtr WinPointerEvents::getPointerType = nullptr;
21 WinPointerEvents::GetPointerInfoPtr WinPointerEvents::getPointerInfo = nullptr;
22 WinPointerEvents::GetPointerPenInfoPtr WinPointerEvents::getPointerPenInfo =
23     nullptr;
24 bool WinPointerEvents::sPointerEventEnabled = true;
25 bool WinPointerEvents::sFirePointerEventsByWinPointerMessages = false;
26 
WinPointerEvents()27 WinPointerEvents::WinPointerEvents() {
28   InitLibrary();
29   static bool addedPointerEventEnabled = false;
30   if (!addedPointerEventEnabled) {
31     Preferences::AddBoolVarCache(&sPointerEventEnabled,
32                                  "dom.w3c_pointer_events.enabled", true);
33     Preferences::AddBoolVarCache(
34         &sFirePointerEventsByWinPointerMessages,
35         "dom.w3c_pointer_events.dispatch_by_pointer_messages", false);
36     addedPointerEventEnabled = true;
37   }
38 }
39 
40 /* Load and shutdown */
InitLibrary()41 void WinPointerEvents::InitLibrary() {
42   MOZ_ASSERT(XRE_IsParentProcess());
43   if (!IsWin8OrLater()) {
44     // Only Win8 or later supports WM_POINTER*
45     return;
46   }
47   if (getPointerType) {
48     // Return if we already initialized the PointerEvent related interfaces
49     return;
50   }
51   sLibraryHandle = ::LoadLibraryW(kPointerLibraryName);
52   MOZ_ASSERT(sLibraryHandle, "cannot load pointer library");
53   if (sLibraryHandle) {
54     getPointerType =
55         (GetPointerTypePtr)GetProcAddress(sLibraryHandle, "GetPointerType");
56     getPointerInfo =
57         (GetPointerInfoPtr)GetProcAddress(sLibraryHandle, "GetPointerInfo");
58     getPointerPenInfo = (GetPointerPenInfoPtr)GetProcAddress(
59         sLibraryHandle, "GetPointerPenInfo");
60   }
61 
62   if (!getPointerType || !getPointerInfo || !getPointerPenInfo) {
63     MOZ_ASSERT(false, "get PointerEvent interfaces failed");
64     getPointerType = nullptr;
65     getPointerInfo = nullptr;
66     getPointerPenInfo = nullptr;
67     return;
68   }
69 }
70 
ShouldHandleWinPointerMessages(UINT aMsg,WPARAM aWParam)71 bool WinPointerEvents::ShouldHandleWinPointerMessages(UINT aMsg,
72                                                       WPARAM aWParam) {
73   MOZ_ASSERT(aMsg == WM_POINTERDOWN || aMsg == WM_POINTERUP ||
74              aMsg == WM_POINTERUPDATE || aMsg == WM_POINTERLEAVE);
75   if (!sLibraryHandle || !sPointerEventEnabled) {
76     return false;
77   }
78 
79   // We only handle WM_POINTER* when the input source is pen. This is because
80   // we need some information (e.g. tiltX, tiltY) which can't be retrieved by
81   // WM_*BUTTONDOWN.
82   uint32_t pointerId = GetPointerId(aWParam);
83   POINTER_INPUT_TYPE pointerType = PT_POINTER;
84   if (!GetPointerType(pointerId, &pointerType)) {
85     MOZ_ASSERT(false, "cannot find PointerType");
86     return false;
87   }
88   return (pointerType == PT_PEN);
89 }
90 
GetPointerType(uint32_t aPointerId,POINTER_INPUT_TYPE * aPointerType)91 bool WinPointerEvents::GetPointerType(uint32_t aPointerId,
92                                       POINTER_INPUT_TYPE *aPointerType) {
93   if (!getPointerType) {
94     return false;
95   }
96   return getPointerType(aPointerId, aPointerType);
97 }
98 
99 POINTER_INPUT_TYPE
GetPointerType(uint32_t aPointerId)100 WinPointerEvents::GetPointerType(uint32_t aPointerId) {
101   POINTER_INPUT_TYPE pointerType = PT_POINTER;
102   Unused << GetPointerType(aPointerId, &pointerType);
103   return pointerType;
104 }
105 
GetPointerInfo(uint32_t aPointerId,POINTER_INFO * aPointerInfo)106 bool WinPointerEvents::GetPointerInfo(uint32_t aPointerId,
107                                       POINTER_INFO *aPointerInfo) {
108   if (!getPointerInfo) {
109     return false;
110   }
111   return getPointerInfo(aPointerId, aPointerInfo);
112 }
113 
GetPointerPenInfo(uint32_t aPointerId,POINTER_PEN_INFO * aPenInfo)114 bool WinPointerEvents::GetPointerPenInfo(uint32_t aPointerId,
115                                          POINTER_PEN_INFO *aPenInfo) {
116   if (!getPointerPenInfo) {
117     return false;
118   }
119   return getPointerPenInfo(aPointerId, aPenInfo);
120 }
121 
ShouldEnableInkCollector()122 bool WinPointerEvents::ShouldEnableInkCollector() {
123   // We need InkCollector on Win7. For Win8 or later, we handle WM_POINTER* for
124   // pen.
125   return !IsWin8OrLater();
126 }
127 
ShouldRollupOnPointerEvent(UINT aMsg,WPARAM aWParam)128 bool WinPointerEvents::ShouldRollupOnPointerEvent(UINT aMsg, WPARAM aWParam) {
129   MOZ_ASSERT(aMsg == WM_POINTERDOWN);
130   // Only roll up popups when we handling WM_POINTER* to fire Gecko
131   // WidgetMouseEvent and suppress Windows WM_*BUTTONDOWN.
132   return ShouldHandleWinPointerMessages(aMsg, aWParam) &&
133          ShouldFirePointerEventByWinPointerMessages();
134 }
135 
ShouldFirePointerEventByWinPointerMessages()136 bool WinPointerEvents::ShouldFirePointerEventByWinPointerMessages() {
137   MOZ_ASSERT(sLibraryHandle && sPointerEventEnabled);
138   return sFirePointerEventsByWinPointerMessages;
139 }
140 
GetCachedPointerInfo(UINT aMsg,WPARAM aWParam)141 WinPointerInfo *WinPointerEvents::GetCachedPointerInfo(UINT aMsg,
142                                                        WPARAM aWParam) {
143   if (!sLibraryHandle || !sPointerEventEnabled ||
144       MOUSE_INPUT_SOURCE() != nsIDOMMouseEvent::MOZ_SOURCE_PEN ||
145       ShouldFirePointerEventByWinPointerMessages()) {
146     return nullptr;
147   }
148   switch (aMsg) {
149     case WM_LBUTTONDOWN:
150     case WM_MBUTTONDOWN:
151     case WM_RBUTTONDOWN:
152       return &mPenPointerDownInfo;
153     case WM_LBUTTONUP:
154     case WM_MBUTTONUP:
155     case WM_RBUTTONUP:
156       return &mPenPointerDownInfo;
157     case WM_MOUSEMOVE:
158       return &mPenPointerUpdateInfo;
159     default:
160       MOZ_ASSERT(false);
161   }
162   return nullptr;
163 }
164 
ConvertAndCachePointerInfo(UINT aMsg,WPARAM aWParam)165 void WinPointerEvents::ConvertAndCachePointerInfo(UINT aMsg, WPARAM aWParam) {
166   MOZ_ASSERT(!sFirePointerEventsByWinPointerMessages);
167   // Windows doesn't support chorded buttons for pen, so we can simply keep the
168   // latest information from pen generated pointer messages and use them when
169   // handling mouse messages. Used different pointer info for pointerdown,
170   // pointerupdate, and pointerup because Windows doesn't always interleave
171   // pointer messages and mouse messages.
172   switch (aMsg) {
173     case WM_POINTERDOWN:
174       ConvertAndCachePointerInfo(aWParam, &mPenPointerDownInfo);
175       break;
176     case WM_POINTERUP:
177       ConvertAndCachePointerInfo(aWParam, &mPenPointerUpInfo);
178       break;
179     case WM_POINTERUPDATE:
180       ConvertAndCachePointerInfo(aWParam, &mPenPointerUpdateInfo);
181       break;
182     default:
183       break;
184   }
185 }
186 
ConvertAndCachePointerInfo(WPARAM aWParam,WinPointerInfo * aInfo)187 void WinPointerEvents::ConvertAndCachePointerInfo(WPARAM aWParam,
188                                                   WinPointerInfo *aInfo) {
189   MOZ_ASSERT(!sFirePointerEventsByWinPointerMessages);
190   aInfo->pointerId = GetPointerId(aWParam);
191   MOZ_ASSERT(GetPointerType(aInfo->pointerId) == PT_PEN);
192   POINTER_PEN_INFO penInfo;
193   GetPointerPenInfo(aInfo->pointerId, &penInfo);
194   aInfo->tiltX = penInfo.tiltX;
195   aInfo->tiltY = penInfo.tiltY;
196   // Windows defines the pen pressure is normalized to a range between 0 and
197   // 1024. Convert it to float.
198   aInfo->mPressure = penInfo.pressure ? (float)penInfo.pressure / 1024 : 0;
199 }
200