|
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 isKeyPad(WPARAM wparam, LPARAM lparam) |
|
46 { |
|
47 bool keypad = false; |
|
48 switch (wparam) { |
|
49 case VK_RETURN: |
|
50 keypad = (lparam >> 16) & KF_EXTENDED; |
|
51 break; |
|
52 case VK_INSERT: |
|
53 case VK_DELETE: |
|
54 case VK_HOME: |
|
55 case VK_END: |
|
56 case VK_PRIOR: |
|
57 case VK_NEXT: |
|
58 case VK_UP: |
|
59 case VK_DOWN: |
|
60 case VK_LEFT: |
|
61 case VK_RIGHT: |
|
62 keypad = !((lparam >> 16) & KF_EXTENDED); |
|
63 break; |
|
64 case VK_NUMLOCK: |
|
65 case VK_NUMPAD0: |
|
66 case VK_NUMPAD1: |
|
67 case VK_NUMPAD2: |
|
68 case VK_NUMPAD3: |
|
69 case VK_NUMPAD4: |
|
70 case VK_NUMPAD5: |
|
71 case VK_NUMPAD6: |
|
72 case VK_NUMPAD7: |
|
73 case VK_NUMPAD8: |
|
74 case VK_NUMPAD9: |
|
75 case VK_DIVIDE: |
|
76 case VK_MULTIPLY: |
|
77 case VK_SUBTRACT: |
|
78 case VK_ADD: |
|
79 case VK_DECIMAL: |
|
80 case VK_CLEAR: |
|
81 keypad = true; |
|
82 break; |
|
83 default: |
|
84 keypad = false; |
|
85 } |
|
86 return keypad; |
|
87 } |
|
88 |
|
89 WebKeyboardEvent WebInputEventFactory::keyboardEvent(HWND hwnd, UINT message, |
|
90 WPARAM wparam, LPARAM lparam) |
|
91 { |
|
92 WebKeyboardEvent result; |
|
93 |
|
94 // TODO(pkasting): http://b/1117926 Are we guaranteed that the message that |
|
95 // GetMessageTime() refers to is the same one that we're passed in? Perhaps |
|
96 // one of the construction parameters should be the time passed by the |
|
97 // caller, who would know for sure. |
|
98 result.timeStampSeconds = GetMessageTime() / 1000.0; |
|
99 |
|
100 result.windowsKeyCode = result.nativeKeyCode = static_cast<int>(wparam); |
|
101 |
|
102 switch (message) { |
|
103 case WM_SYSKEYDOWN: |
|
104 result.isSystemKey = true; |
|
105 case WM_KEYDOWN: |
|
106 result.type = WebInputEvent::RawKeyDown; |
|
107 break; |
|
108 case WM_SYSKEYUP: |
|
109 result.isSystemKey = true; |
|
110 case WM_KEYUP: |
|
111 result.type = WebInputEvent::KeyUp; |
|
112 break; |
|
113 case WM_IME_CHAR: |
|
114 result.type = WebInputEvent::Char; |
|
115 break; |
|
116 case WM_SYSCHAR: |
|
117 result.isSystemKey = true; |
|
118 result.type = WebInputEvent::Char; |
|
119 case WM_CHAR: |
|
120 result.type = WebInputEvent::Char; |
|
121 break; |
|
122 default: |
|
123 ASSERT_NOT_REACHED(); |
|
124 } |
|
125 |
|
126 if (result.type == WebInputEvent::Char || result.type == WebInputEvent::RawKeyDown) { |
|
127 result.text[0] = result.windowsKeyCode; |
|
128 result.unmodifiedText[0] = result.windowsKeyCode; |
|
129 } |
|
130 if (result.type != WebInputEvent::Char) |
|
131 result.setKeyIdentifierFromWindowsKeyCode(); |
|
132 |
|
133 if (GetKeyState(VK_SHIFT) & 0x8000) |
|
134 result.modifiers |= WebInputEvent::ShiftKey; |
|
135 if (GetKeyState(VK_CONTROL) & 0x8000) |
|
136 result.modifiers |= WebInputEvent::ControlKey; |
|
137 if (GetKeyState(VK_MENU) & 0x8000) |
|
138 result.modifiers |= WebInputEvent::AltKey; |
|
139 // NOTE: There doesn't seem to be a way to query the mouse button state in |
|
140 // this case. |
|
141 |
|
142 if (LOWORD(lparam) > 1) |
|
143 result.modifiers |= WebInputEvent::IsAutoRepeat; |
|
144 if (isKeyPad(wparam, lparam)) |
|
145 result.modifiers |= WebInputEvent::IsKeyPad; |
|
146 |
|
147 return result; |
|
148 } |
|
149 |
|
150 // WebMouseEvent -------------------------------------------------------------- |
|
151 |
|
152 static int gLastClickCount; |
|
153 static double gLastClickTime; |
|
154 |
|
155 static LPARAM GetRelativeCursorPos(HWND hwnd) |
|
156 { |
|
157 POINT pos = {-1, -1}; |
|
158 GetCursorPos(&pos); |
|
159 ScreenToClient(hwnd, &pos); |
|
160 return MAKELPARAM(pos.x, pos.y); |
|
161 } |
|
162 |
|
163 void WebInputEventFactory::resetLastClickState() |
|
164 { |
|
165 gLastClickTime = gLastClickCount = 0; |
|
166 } |
|
167 |
|
168 WebMouseEvent WebInputEventFactory::mouseEvent(HWND hwnd, UINT message, |
|
169 WPARAM wparam, LPARAM lparam) |
|
170 { |
|
171 WebMouseEvent result; //(WebInputEvent::Uninitialized()); |
|
172 |
|
173 switch (message) { |
|
174 case WM_MOUSEMOVE: |
|
175 result.type = WebInputEvent::MouseMove; |
|
176 if (wparam & MK_LBUTTON) |
|
177 result.button = WebMouseEvent::ButtonLeft; |
|
178 else if (wparam & MK_MBUTTON) |
|
179 result.button = WebMouseEvent::ButtonMiddle; |
|
180 else if (wparam & MK_RBUTTON) |
|
181 result.button = WebMouseEvent::ButtonRight; |
|
182 else |
|
183 result.button = WebMouseEvent::ButtonNone; |
|
184 break; |
|
185 case WM_MOUSELEAVE: |
|
186 result.type = WebInputEvent::MouseLeave; |
|
187 result.button = WebMouseEvent::ButtonNone; |
|
188 // set the current mouse position (relative to the client area of the |
|
189 // current window) since none is specified for this event |
|
190 lparam = GetRelativeCursorPos(hwnd); |
|
191 break; |
|
192 case WM_LBUTTONDOWN: |
|
193 case WM_LBUTTONDBLCLK: |
|
194 result.type = WebInputEvent::MouseDown; |
|
195 result.button = WebMouseEvent::ButtonLeft; |
|
196 break; |
|
197 case WM_MBUTTONDOWN: |
|
198 case WM_MBUTTONDBLCLK: |
|
199 result.type = WebInputEvent::MouseDown; |
|
200 result.button = WebMouseEvent::ButtonMiddle; |
|
201 break; |
|
202 case WM_RBUTTONDOWN: |
|
203 case WM_RBUTTONDBLCLK: |
|
204 result.type = WebInputEvent::MouseDown; |
|
205 result.button = WebMouseEvent::ButtonRight; |
|
206 break; |
|
207 case WM_LBUTTONUP: |
|
208 result.type = WebInputEvent::MouseUp; |
|
209 result.button = WebMouseEvent::ButtonLeft; |
|
210 break; |
|
211 case WM_MBUTTONUP: |
|
212 result.type = WebInputEvent::MouseUp; |
|
213 result.button = WebMouseEvent::ButtonMiddle; |
|
214 break; |
|
215 case WM_RBUTTONUP: |
|
216 result.type = WebInputEvent::MouseUp; |
|
217 result.button = WebMouseEvent::ButtonRight; |
|
218 break; |
|
219 default: |
|
220 ASSERT_NOT_REACHED(); |
|
221 } |
|
222 |
|
223 // TODO(pkasting): http://b/1117926 Are we guaranteed that the message that |
|
224 // GetMessageTime() refers to is the same one that we're passed in? Perhaps |
|
225 // one of the construction parameters should be the time passed by the |
|
226 // caller, who would know for sure. |
|
227 result.timeStampSeconds = GetMessageTime() / 1000.0; |
|
228 |
|
229 // set position fields: |
|
230 |
|
231 result.x = static_cast<short>(LOWORD(lparam)); |
|
232 result.y = static_cast<short>(HIWORD(lparam)); |
|
233 result.windowX = result.x; |
|
234 result.windowY = result.y; |
|
235 |
|
236 POINT globalPoint = { result.x, result.y }; |
|
237 ClientToScreen(hwnd, &globalPoint); |
|
238 |
|
239 result.globalX = globalPoint.x; |
|
240 result.globalY = globalPoint.y; |
|
241 |
|
242 // calculate number of clicks: |
|
243 |
|
244 // This differs slightly from the WebKit code in WebKit/win/WebView.cpp |
|
245 // where their original code looks buggy. |
|
246 static int lastClickPositionX; |
|
247 static int lastClickPositionY; |
|
248 static WebMouseEvent::Button lastClickButton = WebMouseEvent::ButtonLeft; |
|
249 |
|
250 double currentTime = result.timeStampSeconds; |
|
251 bool cancelPreviousClick = |
|
252 (abs(lastClickPositionX - result.x) > (GetSystemMetrics(SM_CXDOUBLECLK) / 2)) |
|
253 || (abs(lastClickPositionY - result.y) > (GetSystemMetrics(SM_CYDOUBLECLK) / 2)) |
|
254 || ((currentTime - gLastClickTime) * 1000.0 > GetDoubleClickTime()); |
|
255 |
|
256 if (result.type == WebInputEvent::MouseDown) { |
|
257 if (!cancelPreviousClick && (result.button == lastClickButton)) |
|
258 ++gLastClickCount; |
|
259 else { |
|
260 gLastClickCount = 1; |
|
261 lastClickPositionX = result.x; |
|
262 lastClickPositionY = result.y; |
|
263 } |
|
264 gLastClickTime = currentTime; |
|
265 lastClickButton = result.button; |
|
266 } else if (result.type == WebInputEvent::MouseMove |
|
267 || result.type == WebInputEvent::MouseLeave) { |
|
268 if (cancelPreviousClick) { |
|
269 gLastClickCount = 0; |
|
270 lastClickPositionX = 0; |
|
271 lastClickPositionY = 0; |
|
272 gLastClickTime = 0; |
|
273 } |
|
274 } |
|
275 result.clickCount = gLastClickCount; |
|
276 |
|
277 // set modifiers: |
|
278 |
|
279 if (wparam & MK_CONTROL) |
|
280 result.modifiers |= WebInputEvent::ControlKey; |
|
281 if (wparam & MK_SHIFT) |
|
282 result.modifiers |= WebInputEvent::ShiftKey; |
|
283 if (GetKeyState(VK_MENU) & 0x8000) |
|
284 result.modifiers |= WebInputEvent::AltKey; |
|
285 if (wparam & MK_LBUTTON) |
|
286 result.modifiers |= WebInputEvent::LeftButtonDown; |
|
287 if (wparam & MK_MBUTTON) |
|
288 result.modifiers |= WebInputEvent::MiddleButtonDown; |
|
289 if (wparam & MK_RBUTTON) |
|
290 result.modifiers |= WebInputEvent::RightButtonDown; |
|
291 |
|
292 return result; |
|
293 } |
|
294 |
|
295 // WebMouseWheelEvent --------------------------------------------------------- |
|
296 |
|
297 WebMouseWheelEvent WebInputEventFactory::mouseWheelEvent(HWND hwnd, UINT message, |
|
298 WPARAM wparam, LPARAM lparam) |
|
299 { |
|
300 WebMouseWheelEvent result; //(WebInputEvent::Uninitialized()); |
|
301 |
|
302 result.type = WebInputEvent::MouseWheel; |
|
303 |
|
304 // TODO(pkasting): http://b/1117926 Are we guaranteed that the message that |
|
305 // GetMessageTime() refers to is the same one that we're passed in? Perhaps |
|
306 // one of the construction parameters should be the time passed by the |
|
307 // caller, who would know for sure. |
|
308 result.timeStampSeconds = GetMessageTime() / 1000.0; |
|
309 |
|
310 result.button = WebMouseEvent::ButtonNone; |
|
311 |
|
312 // Get key state, coordinates, and wheel delta from event. |
|
313 typedef SHORT (WINAPI *GetKeyStateFunction)(int key); |
|
314 GetKeyStateFunction getKeyState; |
|
315 UINT keyState; |
|
316 float wheelDelta; |
|
317 bool horizontalScroll = false; |
|
318 if ((message == WM_VSCROLL) || (message == WM_HSCROLL)) { |
|
319 // Synthesize mousewheel event from a scroll event. This is needed to |
|
320 // simulate middle mouse scrolling in some laptops. Use GetAsyncKeyState |
|
321 // for key state since we are synthesizing the input event. |
|
322 getKeyState = GetAsyncKeyState; |
|
323 keyState = 0; |
|
324 if (getKeyState(VK_SHIFT)) |
|
325 keyState |= MK_SHIFT; |
|
326 if (getKeyState(VK_CONTROL)) |
|
327 keyState |= MK_CONTROL; |
|
328 // NOTE: There doesn't seem to be a way to query the mouse button state |
|
329 // in this case. |
|
330 |
|
331 POINT cursorPosition = {0}; |
|
332 GetCursorPos(&cursorPosition); |
|
333 result.globalX = cursorPosition.x; |
|
334 result.globalY = cursorPosition.y; |
|
335 |
|
336 switch (LOWORD(wparam)) { |
|
337 case SB_LINEUP: // == SB_LINELEFT |
|
338 wheelDelta = WHEEL_DELTA; |
|
339 break; |
|
340 case SB_LINEDOWN: // == SB_LINERIGHT |
|
341 wheelDelta = -WHEEL_DELTA; |
|
342 break; |
|
343 case SB_PAGEUP: |
|
344 wheelDelta = 1; |
|
345 result.scrollByPage = true; |
|
346 break; |
|
347 case SB_PAGEDOWN: |
|
348 wheelDelta = -1; |
|
349 result.scrollByPage = true; |
|
350 break; |
|
351 default: // We don't supoprt SB_THUMBPOSITION or SB_THUMBTRACK here. |
|
352 wheelDelta = 0; |
|
353 break; |
|
354 } |
|
355 |
|
356 if (message == WM_HSCROLL) |
|
357 horizontalScroll = true; |
|
358 } else { |
|
359 // Non-synthesized event; we can just read data off the event. |
|
360 getKeyState = GetKeyState; |
|
361 keyState = GET_KEYSTATE_WPARAM(wparam); |
|
362 |
|
363 result.globalX = static_cast<short>(LOWORD(lparam)); |
|
364 result.globalY = static_cast<short>(HIWORD(lparam)); |
|
365 |
|
366 wheelDelta = static_cast<float>(GET_WHEEL_DELTA_WPARAM(wparam)); |
|
367 if (message == WM_MOUSEHWHEEL) { |
|
368 horizontalScroll = true; |
|
369 wheelDelta = -wheelDelta; // Windows is <- -/+ ->, WebKit <- +/- ->. |
|
370 } |
|
371 } |
|
372 if (keyState & MK_SHIFT) |
|
373 horizontalScroll = true; |
|
374 |
|
375 // Set modifiers based on key state. |
|
376 if (keyState & MK_SHIFT) |
|
377 result.modifiers |= WebInputEvent::ShiftKey; |
|
378 if (keyState & MK_CONTROL) |
|
379 result.modifiers |= WebInputEvent::ControlKey; |
|
380 if (getKeyState(VK_MENU) & 0x8000) |
|
381 result.modifiers |= WebInputEvent::AltKey; |
|
382 if (keyState & MK_LBUTTON) |
|
383 result.modifiers |= WebInputEvent::LeftButtonDown; |
|
384 if (keyState & MK_MBUTTON) |
|
385 result.modifiers |= WebInputEvent::MiddleButtonDown; |
|
386 if (keyState & MK_RBUTTON) |
|
387 result.modifiers |= WebInputEvent::RightButtonDown; |
|
388 |
|
389 // Set coordinates by translating event coordinates from screen to client. |
|
390 POINT clientPoint = { result.globalX, result.globalY }; |
|
391 MapWindowPoints(0, hwnd, &clientPoint, 1); |
|
392 result.x = clientPoint.x; |
|
393 result.y = clientPoint.y; |
|
394 result.windowX = result.x; |
|
395 result.windowY = result.y; |
|
396 |
|
397 // Convert wheel delta amount to a number of pixels to scroll. |
|
398 // |
|
399 // How many pixels should we scroll per line? Gecko uses the height of the |
|
400 // current line, which means scroll distance changes as you go through the |
|
401 // page or go to different pages. IE 7 is ~50 px/line, although the value |
|
402 // seems to vary slightly by page and zoom level. Since IE 7 has a smoothing |
|
403 // algorithm on scrolling, it can get away with slightly larger scroll values |
|
404 // without feeling jerky. Here we use 100 px per three lines (the default |
|
405 // scroll amount is three lines per wheel tick). |
|
406 static const float scrollbarPixelsPerLine = 100.0f / 3.0f; |
|
407 wheelDelta /= WHEEL_DELTA; |
|
408 float scrollDelta = wheelDelta; |
|
409 if (horizontalScroll) { |
|
410 unsigned long scrollChars = defaultScrollCharsPerWheelDelta; |
|
411 SystemParametersInfo(SPI_GETWHEELSCROLLCHARS, 0, &scrollChars, 0); |
|
412 // TODO(pkasting): Should probably have a different multiplier |
|
413 // scrollbarPixelsPerChar here. |
|
414 scrollDelta *= static_cast<float>(scrollChars) * scrollbarPixelsPerLine; |
|
415 } else { |
|
416 unsigned long scrollLines = defaultScrollLinesPerWheelDelta; |
|
417 SystemParametersInfo(SPI_GETWHEELSCROLLLINES, 0, &scrollLines, 0); |
|
418 if (scrollLines == WHEEL_PAGESCROLL) |
|
419 result.scrollByPage = true; |
|
420 if (!result.scrollByPage) |
|
421 scrollDelta *= static_cast<float>(scrollLines) * scrollbarPixelsPerLine; |
|
422 } |
|
423 |
|
424 // Set scroll amount based on above calculations. WebKit expects positive |
|
425 // deltaY to mean "scroll up" and positive deltaX to mean "scroll left". |
|
426 if (horizontalScroll) { |
|
427 result.deltaX = scrollDelta; |
|
428 result.wheelTicksX = wheelDelta; |
|
429 } else { |
|
430 result.deltaY = scrollDelta; |
|
431 result.wheelTicksY = wheelDelta; |
|
432 } |
|
433 |
|
434 return result; |
|
435 } |
|
436 |
|
437 } // namespace WebKit |