|
1 /* -*- Mode: C++; tab-width: 4; 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 #include "nsWindowBase.h" |
|
7 |
|
8 #include "mozilla/MiscEvents.h" |
|
9 #include "nsGkAtoms.h" |
|
10 #include "WinUtils.h" |
|
11 #include "npapi.h" |
|
12 |
|
13 using namespace mozilla; |
|
14 using namespace mozilla::widget; |
|
15 |
|
16 static const wchar_t kUser32LibName[] = L"user32.dll"; |
|
17 bool nsWindowBase::sTouchInjectInitialized = false; |
|
18 InjectTouchInputPtr nsWindowBase::sInjectTouchFuncPtr; |
|
19 |
|
20 bool |
|
21 nsWindowBase::DispatchPluginEvent(const MSG& aMsg) |
|
22 { |
|
23 if (!PluginHasFocus()) { |
|
24 return false; |
|
25 } |
|
26 WidgetPluginEvent pluginEvent(true, NS_PLUGIN_INPUT_EVENT, this); |
|
27 nsIntPoint point(0, 0); |
|
28 InitEvent(pluginEvent, &point); |
|
29 NPEvent npEvent; |
|
30 npEvent.event = aMsg.message; |
|
31 npEvent.wParam = aMsg.wParam; |
|
32 npEvent.lParam = aMsg.lParam; |
|
33 pluginEvent.pluginEvent = &npEvent; |
|
34 pluginEvent.retargetToFocusedDocument = true; |
|
35 return DispatchWindowEvent(&pluginEvent); |
|
36 } |
|
37 |
|
38 // static |
|
39 bool |
|
40 nsWindowBase::InitTouchInjection() |
|
41 { |
|
42 if (!sTouchInjectInitialized) { |
|
43 // Initialize touch injection on the first call |
|
44 HMODULE hMod = LoadLibraryW(kUser32LibName); |
|
45 if (!hMod) { |
|
46 return false; |
|
47 } |
|
48 |
|
49 InitializeTouchInjectionPtr func = |
|
50 (InitializeTouchInjectionPtr)GetProcAddress(hMod, "InitializeTouchInjection"); |
|
51 if (!func) { |
|
52 WinUtils::Log("InitializeTouchInjection not available."); |
|
53 return false; |
|
54 } |
|
55 |
|
56 if (!func(TOUCH_INJECT_MAX_POINTS, TOUCH_FEEDBACK_DEFAULT)) { |
|
57 WinUtils::Log("InitializeTouchInjection failure. GetLastError=%d", GetLastError()); |
|
58 return false; |
|
59 } |
|
60 |
|
61 sInjectTouchFuncPtr = |
|
62 (InjectTouchInputPtr)GetProcAddress(hMod, "InjectTouchInput"); |
|
63 if (!sInjectTouchFuncPtr) { |
|
64 WinUtils::Log("InjectTouchInput not available."); |
|
65 return false; |
|
66 } |
|
67 sTouchInjectInitialized = true; |
|
68 } |
|
69 return true; |
|
70 } |
|
71 |
|
72 bool |
|
73 nsWindowBase::InjectTouchPoint(uint32_t aId, nsIntPoint& aPointerScreenPoint, |
|
74 POINTER_FLAGS aFlags, uint32_t aPressure, |
|
75 uint32_t aOrientation) |
|
76 { |
|
77 if (aId > TOUCH_INJECT_MAX_POINTS) { |
|
78 WinUtils::Log("Pointer ID exceeds maximum. See TOUCH_INJECT_MAX_POINTS."); |
|
79 return false; |
|
80 } |
|
81 |
|
82 POINTER_TOUCH_INFO info; |
|
83 memset(&info, 0, sizeof(POINTER_TOUCH_INFO)); |
|
84 |
|
85 info.touchFlags = TOUCH_FLAG_NONE; |
|
86 info.touchMask = TOUCH_MASK_CONTACTAREA|TOUCH_MASK_ORIENTATION|TOUCH_MASK_PRESSURE; |
|
87 info.pressure = aPressure; |
|
88 info.orientation = aOrientation; |
|
89 |
|
90 info.pointerInfo.pointerFlags = aFlags; |
|
91 info.pointerInfo.pointerType = PT_TOUCH; |
|
92 info.pointerInfo.pointerId = aId; |
|
93 info.pointerInfo.ptPixelLocation.x = WinUtils::LogToPhys(aPointerScreenPoint.x); |
|
94 info.pointerInfo.ptPixelLocation.y = WinUtils::LogToPhys(aPointerScreenPoint.y); |
|
95 |
|
96 info.rcContact.top = info.pointerInfo.ptPixelLocation.y - 2; |
|
97 info.rcContact.bottom = info.pointerInfo.ptPixelLocation.y + 2; |
|
98 info.rcContact.left = info.pointerInfo.ptPixelLocation.x - 2; |
|
99 info.rcContact.right = info.pointerInfo.ptPixelLocation.x + 2; |
|
100 |
|
101 if (!sInjectTouchFuncPtr(1, &info)) { |
|
102 WinUtils::Log("InjectTouchInput failure. GetLastError=%d", GetLastError()); |
|
103 return false; |
|
104 } |
|
105 return true; |
|
106 } |
|
107 |
|
108 nsresult |
|
109 nsWindowBase::SynthesizeNativeTouchPoint(uint32_t aPointerId, |
|
110 nsIWidget::TouchPointerState aPointerState, |
|
111 nsIntPoint aPointerScreenPoint, |
|
112 double aPointerPressure, |
|
113 uint32_t aPointerOrientation) |
|
114 { |
|
115 if (!InitTouchInjection()) { |
|
116 return NS_ERROR_NOT_IMPLEMENTED; |
|
117 } |
|
118 |
|
119 bool hover = aPointerState & TOUCH_HOVER; |
|
120 bool contact = aPointerState & TOUCH_CONTACT; |
|
121 bool remove = aPointerState & TOUCH_REMOVE; |
|
122 bool cancel = aPointerState & TOUCH_CANCEL; |
|
123 |
|
124 // win api expects a value from 0 to 1024. aPointerPressure is a value |
|
125 // from 0.0 to 1.0. |
|
126 uint32_t pressure = (uint32_t)ceil(aPointerPressure * 1024); |
|
127 |
|
128 // If we already know about this pointer id get it's record |
|
129 PointerInfo* info = mActivePointers.Get(aPointerId); |
|
130 |
|
131 // We know about this pointer, send an update |
|
132 if (info) { |
|
133 POINTER_FLAGS flags = POINTER_FLAG_UPDATE; |
|
134 if (hover) { |
|
135 flags |= POINTER_FLAG_INRANGE; |
|
136 } else if (contact) { |
|
137 flags |= POINTER_FLAG_INCONTACT|POINTER_FLAG_INRANGE; |
|
138 } else if (remove) { |
|
139 flags = POINTER_FLAG_UP; |
|
140 // Remove the pointer from our tracking list. This is nsAutPtr wrapped, |
|
141 // so shouldn't leak. |
|
142 mActivePointers.Remove(aPointerId); |
|
143 } |
|
144 |
|
145 if (cancel) { |
|
146 flags |= POINTER_FLAG_CANCELED; |
|
147 } |
|
148 |
|
149 return !InjectTouchPoint(aPointerId, aPointerScreenPoint, flags, |
|
150 pressure, aPointerOrientation) ? |
|
151 NS_ERROR_UNEXPECTED : NS_OK; |
|
152 } |
|
153 |
|
154 // Missing init state, error out |
|
155 if (remove || cancel) { |
|
156 return NS_ERROR_INVALID_ARG; |
|
157 } |
|
158 |
|
159 // Create a new pointer |
|
160 info = new PointerInfo(aPointerId, aPointerScreenPoint); |
|
161 |
|
162 POINTER_FLAGS flags = POINTER_FLAG_INRANGE; |
|
163 if (contact) { |
|
164 flags |= POINTER_FLAG_INCONTACT|POINTER_FLAG_DOWN; |
|
165 } |
|
166 |
|
167 mActivePointers.Put(aPointerId, info); |
|
168 return !InjectTouchPoint(aPointerId, aPointerScreenPoint, flags, |
|
169 pressure, aPointerOrientation) ? |
|
170 NS_ERROR_UNEXPECTED : NS_OK; |
|
171 } |
|
172 |
|
173 // static |
|
174 PLDHashOperator |
|
175 nsWindowBase::CancelTouchPoints(const unsigned int& aPointerId, nsAutoPtr<PointerInfo>& aInfo, void* aUserArg) |
|
176 { |
|
177 nsWindowBase* self = static_cast<nsWindowBase*>(aUserArg); |
|
178 self->InjectTouchPoint(aInfo.get()->mPointerId, aInfo.get()->mPosition, POINTER_FLAG_CANCELED); |
|
179 return (PLDHashOperator)(PL_DHASH_NEXT|PL_DHASH_REMOVE); |
|
180 } |
|
181 |
|
182 nsresult |
|
183 nsWindowBase::ClearNativeTouchSequence() |
|
184 { |
|
185 if (!sTouchInjectInitialized) { |
|
186 return NS_OK; |
|
187 } |
|
188 |
|
189 // cancel all input points |
|
190 mActivePointers.Enumerate(CancelTouchPoints, (void*)this); |
|
191 |
|
192 nsBaseWidget::ClearNativeTouchSequence(); |
|
193 |
|
194 return NS_OK; |
|
195 } |
|
196 |
|
197 bool |
|
198 nsWindowBase::DispatchCommandEvent(uint32_t aEventCommand) |
|
199 { |
|
200 nsCOMPtr<nsIAtom> command; |
|
201 switch (aEventCommand) { |
|
202 case APPCOMMAND_BROWSER_BACKWARD: |
|
203 command = nsGkAtoms::Back; |
|
204 break; |
|
205 case APPCOMMAND_BROWSER_FORWARD: |
|
206 command = nsGkAtoms::Forward; |
|
207 break; |
|
208 case APPCOMMAND_BROWSER_REFRESH: |
|
209 command = nsGkAtoms::Reload; |
|
210 break; |
|
211 case APPCOMMAND_BROWSER_STOP: |
|
212 command = nsGkAtoms::Stop; |
|
213 break; |
|
214 case APPCOMMAND_BROWSER_SEARCH: |
|
215 command = nsGkAtoms::Search; |
|
216 break; |
|
217 case APPCOMMAND_BROWSER_FAVORITES: |
|
218 command = nsGkAtoms::Bookmarks; |
|
219 break; |
|
220 case APPCOMMAND_BROWSER_HOME: |
|
221 command = nsGkAtoms::Home; |
|
222 break; |
|
223 case APPCOMMAND_CLOSE: |
|
224 command = nsGkAtoms::Close; |
|
225 break; |
|
226 case APPCOMMAND_FIND: |
|
227 command = nsGkAtoms::Find; |
|
228 break; |
|
229 case APPCOMMAND_HELP: |
|
230 command = nsGkAtoms::Help; |
|
231 break; |
|
232 case APPCOMMAND_NEW: |
|
233 command = nsGkAtoms::New; |
|
234 break; |
|
235 case APPCOMMAND_OPEN: |
|
236 command = nsGkAtoms::Open; |
|
237 break; |
|
238 case APPCOMMAND_PRINT: |
|
239 command = nsGkAtoms::Print; |
|
240 break; |
|
241 case APPCOMMAND_SAVE: |
|
242 command = nsGkAtoms::Save; |
|
243 break; |
|
244 case APPCOMMAND_FORWARD_MAIL: |
|
245 command = nsGkAtoms::ForwardMail; |
|
246 break; |
|
247 case APPCOMMAND_REPLY_TO_MAIL: |
|
248 command = nsGkAtoms::ReplyToMail; |
|
249 break; |
|
250 case APPCOMMAND_SEND_MAIL: |
|
251 command = nsGkAtoms::SendMail; |
|
252 break; |
|
253 default: |
|
254 return false; |
|
255 } |
|
256 WidgetCommandEvent event(true, nsGkAtoms::onAppCommand, command, this); |
|
257 |
|
258 InitEvent(event); |
|
259 return DispatchWindowEvent(&event); |
|
260 } |
|
261 |
|
262 bool |
|
263 nsWindowBase::HandleAppCommandMsg(WPARAM aWParam, |
|
264 LPARAM aLParam, |
|
265 LRESULT *aRetValue) |
|
266 { |
|
267 uint32_t appCommand = GET_APPCOMMAND_LPARAM(aLParam); |
|
268 uint32_t contentCommandMessage = NS_EVENT_NULL; |
|
269 // XXX After we implement KeyboardEvent.key, we should dispatch the |
|
270 // key event if (GET_DEVICE_LPARAM(lParam) == FAPPCOMMAND_KEY) is. |
|
271 switch (appCommand) |
|
272 { |
|
273 case APPCOMMAND_BROWSER_BACKWARD: |
|
274 case APPCOMMAND_BROWSER_FORWARD: |
|
275 case APPCOMMAND_BROWSER_REFRESH: |
|
276 case APPCOMMAND_BROWSER_STOP: |
|
277 case APPCOMMAND_BROWSER_SEARCH: |
|
278 case APPCOMMAND_BROWSER_FAVORITES: |
|
279 case APPCOMMAND_BROWSER_HOME: |
|
280 case APPCOMMAND_CLOSE: |
|
281 case APPCOMMAND_FIND: |
|
282 case APPCOMMAND_HELP: |
|
283 case APPCOMMAND_NEW: |
|
284 case APPCOMMAND_OPEN: |
|
285 case APPCOMMAND_PRINT: |
|
286 case APPCOMMAND_SAVE: |
|
287 case APPCOMMAND_FORWARD_MAIL: |
|
288 case APPCOMMAND_REPLY_TO_MAIL: |
|
289 case APPCOMMAND_SEND_MAIL: |
|
290 // We shouldn't consume the message always because if we don't handle |
|
291 // the message, the sender (typically, utility of keyboard or mouse) |
|
292 // may send other key messages which indicate well known shortcut key. |
|
293 if (DispatchCommandEvent(appCommand)) { |
|
294 // tell the driver that we handled the event |
|
295 *aRetValue = 1; |
|
296 return true; |
|
297 } |
|
298 break; |
|
299 |
|
300 // Use content command for following commands: |
|
301 case APPCOMMAND_COPY: |
|
302 contentCommandMessage = NS_CONTENT_COMMAND_COPY; |
|
303 break; |
|
304 case APPCOMMAND_CUT: |
|
305 contentCommandMessage = NS_CONTENT_COMMAND_CUT; |
|
306 break; |
|
307 case APPCOMMAND_PASTE: |
|
308 contentCommandMessage = NS_CONTENT_COMMAND_PASTE; |
|
309 break; |
|
310 case APPCOMMAND_REDO: |
|
311 contentCommandMessage = NS_CONTENT_COMMAND_REDO; |
|
312 break; |
|
313 case APPCOMMAND_UNDO: |
|
314 contentCommandMessage = NS_CONTENT_COMMAND_UNDO; |
|
315 break; |
|
316 } |
|
317 |
|
318 if (contentCommandMessage) { |
|
319 WidgetContentCommandEvent contentCommand(true, contentCommandMessage, |
|
320 this); |
|
321 DispatchWindowEvent(&contentCommand); |
|
322 // tell the driver that we handled the event |
|
323 *aRetValue = 1; |
|
324 return true; |
|
325 } |
|
326 |
|
327 // default = false - tell the driver that the event was not handled |
|
328 return false; |
|
329 } |