blob: 3ad46599d9c0bd3a7f78d5c251d67af916c318dc [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
Torne (Richard Coles)51b29062013-11-28 11:56:03 +000038namespace blink {
Torne (Richard Coles)5c87bf82012-11-14 11:46:17 +000039
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;
Torne (Richard Coles)5c87bf82012-11-14 11:46:17 +000071 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.
123static 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
132WebKeyboardEvent WebInputEventFactory::keyboardEvent(HWND hwnd, UINT message,
133 WPARAM wparam, LPARAM lparam)
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::RawKeyDown) {
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
198static int gLastClickCount;
199static double gLastClickTime;
200
201static 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
209void WebInputEventFactory::resetLastClickState()
210{
211 gLastClickTime = gLastClickCount = 0;
212}
213
Torne (Richard Coles)51b29062013-11-28 11:56:03 +0000214bool WebInputEventFactory::isSystemKeyEvent(const WebKeyboardEvent& event)
215{
216 // According to MSDN:
217 // http://msdn.microsoft.com/en-us/library/ms646286(VS.85).aspx
218 // Key events with Alt modifier and F10 are system key events.
219 // We just emulate this behavior. It's necessary to prevent webkit from
220 // processing keypress event generated by alt-d, etc.
221 return event.modifiers & WebInputEvent::AltKey || event.windowsKeyCode == VK_F10;
222}
223
Torne (Richard Coles)5c87bf82012-11-14 11:46:17 +0000224WebMouseEvent WebInputEventFactory::mouseEvent(HWND hwnd, UINT message,
225 WPARAM wparam, LPARAM lparam)
226{
227 WebMouseEvent result; //(WebInputEvent::Uninitialized());
228
229 switch (message) {
230 case WM_MOUSEMOVE:
231 result.type = WebInputEvent::MouseMove;
232 if (wparam & MK_LBUTTON)
233 result.button = WebMouseEvent::ButtonLeft;
234 else if (wparam & MK_MBUTTON)
235 result.button = WebMouseEvent::ButtonMiddle;
236 else if (wparam & MK_RBUTTON)
237 result.button = WebMouseEvent::ButtonRight;
238 else
239 result.button = WebMouseEvent::ButtonNone;
240 break;
241 case WM_MOUSELEAVE:
242 result.type = WebInputEvent::MouseLeave;
243 result.button = WebMouseEvent::ButtonNone;
244 // set the current mouse position (relative to the client area of the
245 // current window) since none is specified for this event
246 lparam = GetRelativeCursorPos(hwnd);
247 break;
248 case WM_LBUTTONDOWN:
249 case WM_LBUTTONDBLCLK:
250 result.type = WebInputEvent::MouseDown;
251 result.button = WebMouseEvent::ButtonLeft;
252 break;
253 case WM_MBUTTONDOWN:
254 case WM_MBUTTONDBLCLK:
255 result.type = WebInputEvent::MouseDown;
256 result.button = WebMouseEvent::ButtonMiddle;
257 break;
258 case WM_RBUTTONDOWN:
259 case WM_RBUTTONDBLCLK:
260 result.type = WebInputEvent::MouseDown;
261 result.button = WebMouseEvent::ButtonRight;
262 break;
263 case WM_LBUTTONUP:
264 result.type = WebInputEvent::MouseUp;
265 result.button = WebMouseEvent::ButtonLeft;
266 break;
267 case WM_MBUTTONUP:
268 result.type = WebInputEvent::MouseUp;
269 result.button = WebMouseEvent::ButtonMiddle;
270 break;
271 case WM_RBUTTONUP:
272 result.type = WebInputEvent::MouseUp;
273 result.button = WebMouseEvent::ButtonRight;
274 break;
275 default:
276 ASSERT_NOT_REACHED();
277 }
278
279 // TODO(pkasting): http://b/1117926 Are we guaranteed that the message that
280 // GetMessageTime() refers to is the same one that we're passed in? Perhaps
281 // one of the construction parameters should be the time passed by the
282 // caller, who would know for sure.
283 result.timeStampSeconds = GetMessageTime() / 1000.0;
284
285 // set position fields:
286
287 result.x = static_cast<short>(LOWORD(lparam));
288 result.y = static_cast<short>(HIWORD(lparam));
289 result.windowX = result.x;
290 result.windowY = result.y;
291
292 POINT globalPoint = { result.x, result.y };
293 ClientToScreen(hwnd, &globalPoint);
294
295 result.globalX = globalPoint.x;
296 result.globalY = globalPoint.y;
297
298 // calculate number of clicks:
299
300 // This differs slightly from the WebKit code in WebKit/win/WebView.cpp
301 // where their original code looks buggy.
302 static int lastClickPositionX;
303 static int lastClickPositionY;
304 static WebMouseEvent::Button lastClickButton = WebMouseEvent::ButtonLeft;
305
306 double currentTime = result.timeStampSeconds;
307 bool cancelPreviousClick =
308 (abs(lastClickPositionX - result.x) > (GetSystemMetrics(SM_CXDOUBLECLK) / 2))
309 || (abs(lastClickPositionY - result.y) > (GetSystemMetrics(SM_CYDOUBLECLK) / 2))
310 || ((currentTime - gLastClickTime) * 1000.0 > GetDoubleClickTime());
311
312 if (result.type == WebInputEvent::MouseDown) {
313 if (!cancelPreviousClick && (result.button == lastClickButton))
314 ++gLastClickCount;
315 else {
316 gLastClickCount = 1;
317 lastClickPositionX = result.x;
318 lastClickPositionY = result.y;
319 }
320 gLastClickTime = currentTime;
321 lastClickButton = result.button;
322 } else if (result.type == WebInputEvent::MouseMove
323 || result.type == WebInputEvent::MouseLeave) {
324 if (cancelPreviousClick) {
325 gLastClickCount = 0;
326 lastClickPositionX = 0;
327 lastClickPositionY = 0;
328 gLastClickTime = 0;
329 }
330 }
331 result.clickCount = gLastClickCount;
332
333 // set modifiers:
334
335 if (wparam & MK_CONTROL)
336 result.modifiers |= WebInputEvent::ControlKey;
337 if (wparam & MK_SHIFT)
338 result.modifiers |= WebInputEvent::ShiftKey;
339 if (GetKeyState(VK_MENU) & 0x8000)
340 result.modifiers |= WebInputEvent::AltKey;
341 if (wparam & MK_LBUTTON)
342 result.modifiers |= WebInputEvent::LeftButtonDown;
343 if (wparam & MK_MBUTTON)
344 result.modifiers |= WebInputEvent::MiddleButtonDown;
345 if (wparam & MK_RBUTTON)
346 result.modifiers |= WebInputEvent::RightButtonDown;
347
348 SetToggleKeyState(&result);
349 return result;
350}
351
352// WebMouseWheelEvent ---------------------------------------------------------
353
354WebMouseWheelEvent WebInputEventFactory::mouseWheelEvent(HWND hwnd, UINT message,
355 WPARAM wparam, LPARAM lparam)
356{
357 WebMouseWheelEvent result; //(WebInputEvent::Uninitialized());
358
359 result.type = WebInputEvent::MouseWheel;
360
361 // TODO(pkasting): http://b/1117926 Are we guaranteed that the message that
362 // GetMessageTime() refers to is the same one that we're passed in? Perhaps
363 // one of the construction parameters should be the time passed by the
364 // caller, who would know for sure.
365 result.timeStampSeconds = GetMessageTime() / 1000.0;
366
367 result.button = WebMouseEvent::ButtonNone;
368
369 // Get key state, coordinates, and wheel delta from event.
370 typedef SHORT (WINAPI *GetKeyStateFunction)(int key);
371 GetKeyStateFunction getKeyState;
372 UINT keyState;
373 float wheelDelta;
374 bool horizontalScroll = false;
375 if ((message == WM_VSCROLL) || (message == WM_HSCROLL)) {
376 // Synthesize mousewheel event from a scroll event. This is needed to
377 // simulate middle mouse scrolling in some laptops. Use GetAsyncKeyState
378 // for key state since we are synthesizing the input event.
379 getKeyState = GetAsyncKeyState;
380 keyState = 0;
381 if (getKeyState(VK_SHIFT))
382 keyState |= MK_SHIFT;
383 if (getKeyState(VK_CONTROL))
384 keyState |= MK_CONTROL;
385 // NOTE: There doesn't seem to be a way to query the mouse button state
386 // in this case.
387
388 POINT cursorPosition = {0};
389 GetCursorPos(&cursorPosition);
390 result.globalX = cursorPosition.x;
391 result.globalY = cursorPosition.y;
392
393 switch (LOWORD(wparam)) {
394 case SB_LINEUP: // == SB_LINELEFT
395 wheelDelta = WHEEL_DELTA;
396 break;
397 case SB_LINEDOWN: // == SB_LINERIGHT
398 wheelDelta = -WHEEL_DELTA;
399 break;
400 case SB_PAGEUP:
401 wheelDelta = 1;
402 result.scrollByPage = true;
403 break;
404 case SB_PAGEDOWN:
405 wheelDelta = -1;
406 result.scrollByPage = true;
407 break;
408 default: // We don't supoprt SB_THUMBPOSITION or SB_THUMBTRACK here.
409 wheelDelta = 0;
410 break;
411 }
412
413 if (message == WM_HSCROLL)
414 horizontalScroll = true;
415 } else {
416 // Non-synthesized event; we can just read data off the event.
417 getKeyState = GetKeyState;
418 keyState = GET_KEYSTATE_WPARAM(wparam);
419
420 result.globalX = static_cast<short>(LOWORD(lparam));
421 result.globalY = static_cast<short>(HIWORD(lparam));
422
423 wheelDelta = static_cast<float>(GET_WHEEL_DELTA_WPARAM(wparam));
424 if (message == WM_MOUSEHWHEEL) {
425 horizontalScroll = true;
426 wheelDelta = -wheelDelta; // Windows is <- -/+ ->, WebKit <- +/- ->.
427 }
428 }
429 if (keyState & MK_SHIFT)
430 horizontalScroll = true;
431
432 // Set modifiers based on key state.
433 if (keyState & MK_SHIFT)
434 result.modifiers |= WebInputEvent::ShiftKey;
435 if (keyState & MK_CONTROL)
436 result.modifiers |= WebInputEvent::ControlKey;
437 if (getKeyState(VK_MENU) & 0x8000)
438 result.modifiers |= WebInputEvent::AltKey;
439 if (keyState & MK_LBUTTON)
440 result.modifiers |= WebInputEvent::LeftButtonDown;
441 if (keyState & MK_MBUTTON)
442 result.modifiers |= WebInputEvent::MiddleButtonDown;
443 if (keyState & MK_RBUTTON)
444 result.modifiers |= WebInputEvent::RightButtonDown;
445
446 SetToggleKeyState(&result);
447
448 // Set coordinates by translating event coordinates from screen to client.
449 POINT clientPoint = { result.globalX, result.globalY };
450 MapWindowPoints(0, hwnd, &clientPoint, 1);
451 result.x = clientPoint.x;
452 result.y = clientPoint.y;
453 result.windowX = result.x;
454 result.windowY = result.y;
455
456 // Convert wheel delta amount to a number of pixels to scroll.
457 //
458 // How many pixels should we scroll per line? Gecko uses the height of the
459 // current line, which means scroll distance changes as you go through the
460 // page or go to different pages. IE 8 is ~60 px/line, although the value
461 // seems to vary slightly by page and zoom level. Also, IE defaults to
462 // smooth scrolling while Firefox doesn't, so it can get away with somewhat
463 // larger scroll values without feeling as jerky. Here we use 100 px per
464 // three lines (the default scroll amount is three lines per wheel tick).
465 // Even though we have smooth scrolling, we don't make this as large as IE
466 // because subjectively IE feels like it scrolls farther than you want while
467 // reading articles.
468 static const float scrollbarPixelsPerLine = 100.0f / 3.0f;
469 wheelDelta /= WHEEL_DELTA;
470 float scrollDelta = wheelDelta;
471 if (horizontalScroll) {
472 unsigned long scrollChars = defaultScrollCharsPerWheelDelta;
473 SystemParametersInfo(SPI_GETWHEELSCROLLCHARS, 0, &scrollChars, 0);
474 // TODO(pkasting): Should probably have a different multiplier
475 // scrollbarPixelsPerChar here.
476 scrollDelta *= static_cast<float>(scrollChars) * scrollbarPixelsPerLine;
477 } else {
478 unsigned long scrollLines = defaultScrollLinesPerWheelDelta;
479 SystemParametersInfo(SPI_GETWHEELSCROLLLINES, 0, &scrollLines, 0);
480 if (scrollLines == WHEEL_PAGESCROLL)
481 result.scrollByPage = true;
482 if (!result.scrollByPage)
483 scrollDelta *= static_cast<float>(scrollLines) * scrollbarPixelsPerLine;
484 }
485
486 // Set scroll amount based on above calculations. WebKit expects positive
487 // deltaY to mean "scroll up" and positive deltaX to mean "scroll left".
488 if (horizontalScroll) {
489 result.deltaX = scrollDelta;
490 result.wheelTicksX = wheelDelta;
491 } else {
492 result.deltaY = scrollDelta;
493 result.wheelTicksY = wheelDelta;
494 }
495
496 return result;
497}
498
Torne (Richard Coles)51b29062013-11-28 11:56:03 +0000499} // namespace blink