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