OLD | NEW |
| (Empty) |
1 /* | |
2 * Copyright (C) 2006-2009 Google Inc. All rights reserved. | |
3 * | |
4 * Redistribution and use in source and binary forms, with or without | |
5 * modification, are permitted provided that the following conditions are | |
6 * met: | |
7 * | |
8 * * Redistributions of source code must retain the above copyright | |
9 * notice, this list of conditions and the following disclaimer. | |
10 * * Redistributions in binary form must reproduce the above | |
11 * copyright notice, this list of conditions and the following disclaimer | |
12 * in the documentation and/or other materials provided with the | |
13 * distribution. | |
14 * * Neither the name of Google Inc. nor the names of its | |
15 * contributors may be used to endorse or promote products derived from | |
16 * this software without specific prior written permission. | |
17 * | |
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | |
19 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | |
20 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | |
21 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | |
22 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | |
23 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | |
24 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | |
25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | |
26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |
27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | |
28 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
29 */ | |
30 | |
31 #include "config.h" | |
32 #include "WebInputEventFactory.h" | |
33 | |
34 #include "WebInputEvent.h" | |
35 | |
36 #include "wtf/Assertions.h" | |
37 | |
38 namespace WebKit { | |
39 | |
40 static const unsigned long defaultScrollLinesPerWheelDelta = 3; | |
41 static const unsigned long defaultScrollCharsPerWheelDelta = 1; | |
42 | |
43 // WebKeyboardEvent ----------------------------------------------------------- | |
44 | |
45 static bool isKeyDown(WPARAM wparam) | |
46 { | |
47 return GetKeyState(wparam) & 0x8000; | |
48 } | |
49 | |
50 static int getLocationModifier(WPARAM wparam, LPARAM lparam) | |
51 { | |
52 int modifier = 0; | |
53 switch (wparam) { | |
54 case VK_RETURN: | |
55 if ((lparam >> 16) & KF_EXTENDED) | |
56 modifier = WebInputEvent::IsKeyPad; | |
57 break; | |
58 case VK_INSERT: | |
59 case VK_DELETE: | |
60 case VK_HOME: | |
61 case VK_END: | |
62 case VK_PRIOR: | |
63 case VK_NEXT: | |
64 case VK_UP: | |
65 case VK_DOWN: | |
66 case VK_LEFT: | |
67 case VK_RIGHT: | |
68 if (!((lparam >> 16) & KF_EXTENDED)) | |
69 modifier = WebInputEvent::IsKeyPad; | |
70 break; | |
71 case VK_NUMPAD0: | |
72 case VK_NUMPAD1: | |
73 case VK_NUMPAD2: | |
74 case VK_NUMPAD3: | |
75 case VK_NUMPAD4: | |
76 case VK_NUMPAD5: | |
77 case VK_NUMPAD6: | |
78 case VK_NUMPAD7: | |
79 case VK_NUMPAD8: | |
80 case VK_NUMPAD9: | |
81 case VK_DIVIDE: | |
82 case VK_MULTIPLY: | |
83 case VK_SUBTRACT: | |
84 case VK_ADD: | |
85 case VK_DECIMAL: | |
86 case VK_CLEAR: | |
87 modifier = WebInputEvent::IsKeyPad; | |
88 break; | |
89 case VK_SHIFT: | |
90 if (isKeyDown(VK_LSHIFT)) | |
91 modifier = WebInputEvent::IsLeft; | |
92 else if (isKeyDown(VK_RSHIFT)) | |
93 modifier = WebInputEvent::IsRight; | |
94 break; | |
95 case VK_CONTROL: | |
96 if (isKeyDown(VK_LCONTROL)) | |
97 modifier = WebInputEvent::IsLeft; | |
98 else if (isKeyDown(VK_RCONTROL)) | |
99 modifier = WebInputEvent::IsRight; | |
100 break; | |
101 case VK_MENU: | |
102 if (isKeyDown(VK_LMENU)) | |
103 modifier = WebInputEvent::IsLeft; | |
104 else if (isKeyDown(VK_RMENU)) | |
105 modifier = WebInputEvent::IsRight; | |
106 break; | |
107 case VK_LWIN: | |
108 modifier = WebInputEvent::IsLeft; | |
109 break; | |
110 case VK_RWIN: | |
111 modifier = WebInputEvent::IsRight; | |
112 break; | |
113 } | |
114 | |
115 ASSERT(!modifier | |
116 || modifier == WebInputEvent::IsKeyPad | |
117 || modifier == WebInputEvent::IsLeft | |
118 || modifier == WebInputEvent::IsRight); | |
119 return modifier; | |
120 } | |
121 | |
122 // Loads the state for toggle keys into the event. | |
123 static void SetToggleKeyState(WebInputEvent* event) | |
124 { | |
125 // Low bit set from GetKeyState indicates "toggled". | |
126 if (::GetKeyState(VK_NUMLOCK) & 1) | |
127 event->modifiers |= WebInputEvent::NumLockOn; | |
128 if (::GetKeyState(VK_CAPITAL) & 1) | |
129 event->modifiers |= WebInputEvent::CapsLockOn; | |
130 } | |
131 | |
132 WebKeyboardEvent WebInputEventFactory::keyboardEvent(HWND hwnd, UINT message, | |
133 WPARAM wparam, LPARAM lpara
m) | |
134 { | |
135 WebKeyboardEvent result; | |
136 | |
137 // TODO(pkasting): http://b/1117926 Are we guaranteed that the message that | |
138 // GetMessageTime() refers to is the same one that we're passed in? Perhaps | |
139 // one of the construction parameters should be the time passed by the | |
140 // caller, who would know for sure. | |
141 result.timeStampSeconds = GetMessageTime() / 1000.0; | |
142 | |
143 result.windowsKeyCode = static_cast<int>(wparam); | |
144 // Record the scan code (along with other context bits) for this key event. | |
145 result.nativeKeyCode = static_cast<int>(lparam); | |
146 | |
147 switch (message) { | |
148 case WM_SYSKEYDOWN: | |
149 result.isSystemKey = true; | |
150 case WM_KEYDOWN: | |
151 result.type = WebInputEvent::RawKeyDown; | |
152 break; | |
153 case WM_SYSKEYUP: | |
154 result.isSystemKey = true; | |
155 case WM_KEYUP: | |
156 result.type = WebInputEvent::KeyUp; | |
157 break; | |
158 case WM_IME_CHAR: | |
159 result.type = WebInputEvent::Char; | |
160 break; | |
161 case WM_SYSCHAR: | |
162 result.isSystemKey = true; | |
163 result.type = WebInputEvent::Char; | |
164 case WM_CHAR: | |
165 result.type = WebInputEvent::Char; | |
166 break; | |
167 default: | |
168 ASSERT_NOT_REACHED(); | |
169 } | |
170 | |
171 if (result.type == WebInputEvent::Char || result.type == WebInputEvent::RawK
eyDown) { | |
172 result.text[0] = result.windowsKeyCode; | |
173 result.unmodifiedText[0] = result.windowsKeyCode; | |
174 } | |
175 if (result.type != WebInputEvent::Char) | |
176 result.setKeyIdentifierFromWindowsKeyCode(); | |
177 | |
178 if (GetKeyState(VK_SHIFT) & 0x8000) | |
179 result.modifiers |= WebInputEvent::ShiftKey; | |
180 if (GetKeyState(VK_CONTROL) & 0x8000) | |
181 result.modifiers |= WebInputEvent::ControlKey; | |
182 if (GetKeyState(VK_MENU) & 0x8000) | |
183 result.modifiers |= WebInputEvent::AltKey; | |
184 // NOTE: There doesn't seem to be a way to query the mouse button state in | |
185 // this case. | |
186 | |
187 if (LOWORD(lparam) > 1) | |
188 result.modifiers |= WebInputEvent::IsAutoRepeat; | |
189 | |
190 result.modifiers |= getLocationModifier(wparam, lparam); | |
191 | |
192 SetToggleKeyState(&result); | |
193 return result; | |
194 } | |
195 | |
196 // WebMouseEvent -------------------------------------------------------------- | |
197 | |
198 static int gLastClickCount; | |
199 static double gLastClickTime; | |
200 | |
201 static LPARAM GetRelativeCursorPos(HWND hwnd) | |
202 { | |
203 POINT pos = {-1, -1}; | |
204 GetCursorPos(&pos); | |
205 ScreenToClient(hwnd, &pos); | |
206 return MAKELPARAM(pos.x, pos.y); | |
207 } | |
208 | |
209 void WebInputEventFactory::resetLastClickState() | |
210 { | |
211 gLastClickTime = gLastClickCount = 0; | |
212 } | |
213 | |
214 WebMouseEvent WebInputEventFactory::mouseEvent(HWND hwnd, UINT message, | |
215 WPARAM wparam, LPARAM lparam) | |
216 { | |
217 WebMouseEvent result; //(WebInputEvent::Uninitialized()); | |
218 | |
219 switch (message) { | |
220 case WM_MOUSEMOVE: | |
221 result.type = WebInputEvent::MouseMove; | |
222 if (wparam & MK_LBUTTON) | |
223 result.button = WebMouseEvent::ButtonLeft; | |
224 else if (wparam & MK_MBUTTON) | |
225 result.button = WebMouseEvent::ButtonMiddle; | |
226 else if (wparam & MK_RBUTTON) | |
227 result.button = WebMouseEvent::ButtonRight; | |
228 else | |
229 result.button = WebMouseEvent::ButtonNone; | |
230 break; | |
231 case WM_MOUSELEAVE: | |
232 result.type = WebInputEvent::MouseLeave; | |
233 result.button = WebMouseEvent::ButtonNone; | |
234 // set the current mouse position (relative to the client area of the | |
235 // current window) since none is specified for this event | |
236 lparam = GetRelativeCursorPos(hwnd); | |
237 break; | |
238 case WM_LBUTTONDOWN: | |
239 case WM_LBUTTONDBLCLK: | |
240 result.type = WebInputEvent::MouseDown; | |
241 result.button = WebMouseEvent::ButtonLeft; | |
242 break; | |
243 case WM_MBUTTONDOWN: | |
244 case WM_MBUTTONDBLCLK: | |
245 result.type = WebInputEvent::MouseDown; | |
246 result.button = WebMouseEvent::ButtonMiddle; | |
247 break; | |
248 case WM_RBUTTONDOWN: | |
249 case WM_RBUTTONDBLCLK: | |
250 result.type = WebInputEvent::MouseDown; | |
251 result.button = WebMouseEvent::ButtonRight; | |
252 break; | |
253 case WM_LBUTTONUP: | |
254 result.type = WebInputEvent::MouseUp; | |
255 result.button = WebMouseEvent::ButtonLeft; | |
256 break; | |
257 case WM_MBUTTONUP: | |
258 result.type = WebInputEvent::MouseUp; | |
259 result.button = WebMouseEvent::ButtonMiddle; | |
260 break; | |
261 case WM_RBUTTONUP: | |
262 result.type = WebInputEvent::MouseUp; | |
263 result.button = WebMouseEvent::ButtonRight; | |
264 break; | |
265 default: | |
266 ASSERT_NOT_REACHED(); | |
267 } | |
268 | |
269 // TODO(pkasting): http://b/1117926 Are we guaranteed that the message that | |
270 // GetMessageTime() refers to is the same one that we're passed in? Perhaps | |
271 // one of the construction parameters should be the time passed by the | |
272 // caller, who would know for sure. | |
273 result.timeStampSeconds = GetMessageTime() / 1000.0; | |
274 | |
275 // set position fields: | |
276 | |
277 result.x = static_cast<short>(LOWORD(lparam)); | |
278 result.y = static_cast<short>(HIWORD(lparam)); | |
279 result.windowX = result.x; | |
280 result.windowY = result.y; | |
281 | |
282 POINT globalPoint = { result.x, result.y }; | |
283 ClientToScreen(hwnd, &globalPoint); | |
284 | |
285 result.globalX = globalPoint.x; | |
286 result.globalY = globalPoint.y; | |
287 | |
288 // calculate number of clicks: | |
289 | |
290 // This differs slightly from the WebKit code in WebKit/win/WebView.cpp | |
291 // where their original code looks buggy. | |
292 static int lastClickPositionX; | |
293 static int lastClickPositionY; | |
294 static WebMouseEvent::Button lastClickButton = WebMouseEvent::ButtonLeft; | |
295 | |
296 double currentTime = result.timeStampSeconds; | |
297 bool cancelPreviousClick = | |
298 (abs(lastClickPositionX - result.x) > (GetSystemMetrics(SM_CXDOUBLECLK)
/ 2)) | |
299 || (abs(lastClickPositionY - result.y) > (GetSystemMetrics(SM_CYDOUBLECL
K) / 2)) | |
300 || ((currentTime - gLastClickTime) * 1000.0 > GetDoubleClickTime()); | |
301 | |
302 if (result.type == WebInputEvent::MouseDown) { | |
303 if (!cancelPreviousClick && (result.button == lastClickButton)) | |
304 ++gLastClickCount; | |
305 else { | |
306 gLastClickCount = 1; | |
307 lastClickPositionX = result.x; | |
308 lastClickPositionY = result.y; | |
309 } | |
310 gLastClickTime = currentTime; | |
311 lastClickButton = result.button; | |
312 } else if (result.type == WebInputEvent::MouseMove | |
313 || result.type == WebInputEvent::MouseLeave) { | |
314 if (cancelPreviousClick) { | |
315 gLastClickCount = 0; | |
316 lastClickPositionX = 0; | |
317 lastClickPositionY = 0; | |
318 gLastClickTime = 0; | |
319 } | |
320 } | |
321 result.clickCount = gLastClickCount; | |
322 | |
323 // set modifiers: | |
324 | |
325 if (wparam & MK_CONTROL) | |
326 result.modifiers |= WebInputEvent::ControlKey; | |
327 if (wparam & MK_SHIFT) | |
328 result.modifiers |= WebInputEvent::ShiftKey; | |
329 if (GetKeyState(VK_MENU) & 0x8000) | |
330 result.modifiers |= WebInputEvent::AltKey; | |
331 if (wparam & MK_LBUTTON) | |
332 result.modifiers |= WebInputEvent::LeftButtonDown; | |
333 if (wparam & MK_MBUTTON) | |
334 result.modifiers |= WebInputEvent::MiddleButtonDown; | |
335 if (wparam & MK_RBUTTON) | |
336 result.modifiers |= WebInputEvent::RightButtonDown; | |
337 | |
338 SetToggleKeyState(&result); | |
339 return result; | |
340 } | |
341 | |
342 // WebMouseWheelEvent --------------------------------------------------------- | |
343 | |
344 WebMouseWheelEvent WebInputEventFactory::mouseWheelEvent(HWND hwnd, UINT message
, | |
345 WPARAM wparam, LPARAM l
param) | |
346 { | |
347 WebMouseWheelEvent result; //(WebInputEvent::Uninitialized()); | |
348 | |
349 result.type = WebInputEvent::MouseWheel; | |
350 | |
351 // TODO(pkasting): http://b/1117926 Are we guaranteed that the message that | |
352 // GetMessageTime() refers to is the same one that we're passed in? Perhaps | |
353 // one of the construction parameters should be the time passed by the | |
354 // caller, who would know for sure. | |
355 result.timeStampSeconds = GetMessageTime() / 1000.0; | |
356 | |
357 result.button = WebMouseEvent::ButtonNone; | |
358 | |
359 // Get key state, coordinates, and wheel delta from event. | |
360 typedef SHORT (WINAPI *GetKeyStateFunction)(int key); | |
361 GetKeyStateFunction getKeyState; | |
362 UINT keyState; | |
363 float wheelDelta; | |
364 bool horizontalScroll = false; | |
365 if ((message == WM_VSCROLL) || (message == WM_HSCROLL)) { | |
366 // Synthesize mousewheel event from a scroll event. This is needed to | |
367 // simulate middle mouse scrolling in some laptops. Use GetAsyncKeyStat
e | |
368 // for key state since we are synthesizing the input event. | |
369 getKeyState = GetAsyncKeyState; | |
370 keyState = 0; | |
371 if (getKeyState(VK_SHIFT)) | |
372 keyState |= MK_SHIFT; | |
373 if (getKeyState(VK_CONTROL)) | |
374 keyState |= MK_CONTROL; | |
375 // NOTE: There doesn't seem to be a way to query the mouse button state | |
376 // in this case. | |
377 | |
378 POINT cursorPosition = {0}; | |
379 GetCursorPos(&cursorPosition); | |
380 result.globalX = cursorPosition.x; | |
381 result.globalY = cursorPosition.y; | |
382 | |
383 switch (LOWORD(wparam)) { | |
384 case SB_LINEUP: // == SB_LINELEFT | |
385 wheelDelta = WHEEL_DELTA; | |
386 break; | |
387 case SB_LINEDOWN: // == SB_LINERIGHT | |
388 wheelDelta = -WHEEL_DELTA; | |
389 break; | |
390 case SB_PAGEUP: | |
391 wheelDelta = 1; | |
392 result.scrollByPage = true; | |
393 break; | |
394 case SB_PAGEDOWN: | |
395 wheelDelta = -1; | |
396 result.scrollByPage = true; | |
397 break; | |
398 default: // We don't supoprt SB_THUMBPOSITION or SB_THUMBTRACK here. | |
399 wheelDelta = 0; | |
400 break; | |
401 } | |
402 | |
403 if (message == WM_HSCROLL) | |
404 horizontalScroll = true; | |
405 } else { | |
406 // Non-synthesized event; we can just read data off the event. | |
407 getKeyState = GetKeyState; | |
408 keyState = GET_KEYSTATE_WPARAM(wparam); | |
409 | |
410 result.globalX = static_cast<short>(LOWORD(lparam)); | |
411 result.globalY = static_cast<short>(HIWORD(lparam)); | |
412 | |
413 wheelDelta = static_cast<float>(GET_WHEEL_DELTA_WPARAM(wparam)); | |
414 if (message == WM_MOUSEHWHEEL) { | |
415 horizontalScroll = true; | |
416 wheelDelta = -wheelDelta; // Windows is <- -/+ ->, WebKit <- +/- ->
. | |
417 } | |
418 } | |
419 if (keyState & MK_SHIFT) | |
420 horizontalScroll = true; | |
421 | |
422 // Set modifiers based on key state. | |
423 if (keyState & MK_SHIFT) | |
424 result.modifiers |= WebInputEvent::ShiftKey; | |
425 if (keyState & MK_CONTROL) | |
426 result.modifiers |= WebInputEvent::ControlKey; | |
427 if (getKeyState(VK_MENU) & 0x8000) | |
428 result.modifiers |= WebInputEvent::AltKey; | |
429 if (keyState & MK_LBUTTON) | |
430 result.modifiers |= WebInputEvent::LeftButtonDown; | |
431 if (keyState & MK_MBUTTON) | |
432 result.modifiers |= WebInputEvent::MiddleButtonDown; | |
433 if (keyState & MK_RBUTTON) | |
434 result.modifiers |= WebInputEvent::RightButtonDown; | |
435 | |
436 SetToggleKeyState(&result); | |
437 | |
438 // Set coordinates by translating event coordinates from screen to client. | |
439 POINT clientPoint = { result.globalX, result.globalY }; | |
440 MapWindowPoints(0, hwnd, &clientPoint, 1); | |
441 result.x = clientPoint.x; | |
442 result.y = clientPoint.y; | |
443 result.windowX = result.x; | |
444 result.windowY = result.y; | |
445 | |
446 // Convert wheel delta amount to a number of pixels to scroll. | |
447 // | |
448 // How many pixels should we scroll per line? Gecko uses the height of the | |
449 // current line, which means scroll distance changes as you go through the | |
450 // page or go to different pages. IE 8 is ~60 px/line, although the value | |
451 // seems to vary slightly by page and zoom level. Also, IE defaults to | |
452 // smooth scrolling while Firefox doesn't, so it can get away with somewhat | |
453 // larger scroll values without feeling as jerky. Here we use 100 px per | |
454 // three lines (the default scroll amount is three lines per wheel tick). | |
455 // Even though we have smooth scrolling, we don't make this as large as IE | |
456 // because subjectively IE feels like it scrolls farther than you want while | |
457 // reading articles. | |
458 static const float scrollbarPixelsPerLine = 100.0f / 3.0f; | |
459 wheelDelta /= WHEEL_DELTA; | |
460 float scrollDelta = wheelDelta; | |
461 if (horizontalScroll) { | |
462 unsigned long scrollChars = defaultScrollCharsPerWheelDelta; | |
463 SystemParametersInfo(SPI_GETWHEELSCROLLCHARS, 0, &scrollChars, 0); | |
464 // TODO(pkasting): Should probably have a different multiplier | |
465 // scrollbarPixelsPerChar here. | |
466 scrollDelta *= static_cast<float>(scrollChars) * scrollbarPixelsPerLine; | |
467 } else { | |
468 unsigned long scrollLines = defaultScrollLinesPerWheelDelta; | |
469 SystemParametersInfo(SPI_GETWHEELSCROLLLINES, 0, &scrollLines, 0); | |
470 if (scrollLines == WHEEL_PAGESCROLL) | |
471 result.scrollByPage = true; | |
472 if (!result.scrollByPage) | |
473 scrollDelta *= static_cast<float>(scrollLines) * scrollbarPixelsPerL
ine; | |
474 } | |
475 | |
476 // Set scroll amount based on above calculations. WebKit expects positive | |
477 // deltaY to mean "scroll up" and positive deltaX to mean "scroll left". | |
478 if (horizontalScroll) { | |
479 result.deltaX = scrollDelta; | |
480 result.wheelTicksX = wheelDelta; | |
481 } else { | |
482 result.deltaY = scrollDelta; | |
483 result.wheelTicksY = wheelDelta; | |
484 } | |
485 | |
486 return result; | |
487 } | |
488 | |
489 } // namespace WebKit | |
OLD | NEW |