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
isKeyPad(WPARAM wparam,LPARAM lparam)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
keyboardEvent(HWND hwnd,UINT message,WPARAM wparam,LPARAM lparam)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
GetRelativeCursorPos(HWND hwnd)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
resetLastClickState()163 void WebInputEventFactory::resetLastClickState()
164 {
165 gLastClickTime = gLastClickCount = 0;
166 }
167
mouseEvent(HWND hwnd,UINT message,WPARAM wparam,LPARAM lparam)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
mouseWheelEvent(HWND hwnd,UINT message,WPARAM wparam,LPARAM lparam)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
438