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