2 * Copyright (C) 2006-2009 Google Inc. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are
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
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.
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.
32 #include "WebInputEventFactory.h"
34 #include "WebInputEvent.h"
36 #include "wtf/Assertions.h"
40 static const unsigned long defaultScrollLinesPerWheelDelta = 3;
41 static const unsigned long defaultScrollCharsPerWheelDelta = 1;
43 // WebKeyboardEvent -----------------------------------------------------------
45 static bool isKeyDown(WPARAM wparam)
47 return GetKeyState(wparam) & 0x8000;
50 static int getLocationModifier(WPARAM wparam, LPARAM lparam)
55 if ((lparam >> 16) & KF_EXTENDED)
56 modifier = WebInputEvent::IsKeyPad;
68 if (!((lparam >> 16) & KF_EXTENDED))
69 modifier = WebInputEvent::IsKeyPad;
87 modifier = WebInputEvent::IsKeyPad;
90 if (isKeyDown(VK_LSHIFT))
91 modifier = WebInputEvent::IsLeft;
92 else if (isKeyDown(VK_RSHIFT))
93 modifier = WebInputEvent::IsRight;
96 if (isKeyDown(VK_LCONTROL))
97 modifier = WebInputEvent::IsLeft;
98 else if (isKeyDown(VK_RCONTROL))
99 modifier = WebInputEvent::IsRight;
102 if (isKeyDown(VK_LMENU))
103 modifier = WebInputEvent::IsLeft;
104 else if (isKeyDown(VK_RMENU))
105 modifier = WebInputEvent::IsRight;
108 modifier = WebInputEvent::IsLeft;
111 modifier = WebInputEvent::IsRight;
116 || modifier == WebInputEvent::IsKeyPad
117 || modifier == WebInputEvent::IsLeft
118 || modifier == WebInputEvent::IsRight);
122 // Loads the state for toggle keys into the event.
123 static void SetToggleKeyState(WebInputEvent* event)
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;
132 WebKeyboardEvent WebInputEventFactory::keyboardEvent(HWND hwnd, UINT message,
133 WPARAM wparam, LPARAM lparam)
135 WebKeyboardEvent result;
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;
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);
149 result.isSystemKey = true;
151 result.type = WebInputEvent::RawKeyDown;
154 result.isSystemKey = true;
156 result.type = WebInputEvent::KeyUp;
159 result.type = WebInputEvent::Char;
162 result.isSystemKey = true;
163 result.type = WebInputEvent::Char;
165 result.type = WebInputEvent::Char;
168 ASSERT_NOT_REACHED();
171 if (result.type == WebInputEvent::Char || result.type == WebInputEvent::RawKeyDown) {
172 result.text[0] = result.windowsKeyCode;
173 result.unmodifiedText[0] = result.windowsKeyCode;
175 if (result.type != WebInputEvent::Char)
176 result.setKeyIdentifierFromWindowsKeyCode();
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
187 if (LOWORD(lparam) > 1)
188 result.modifiers |= WebInputEvent::IsAutoRepeat;
190 result.modifiers |= getLocationModifier(wparam, lparam);
192 SetToggleKeyState(&result);
196 // WebMouseEvent --------------------------------------------------------------
198 static int gLastClickCount;
199 static double gLastClickTime;
201 static LPARAM GetRelativeCursorPos(HWND hwnd)
203 POINT pos = {-1, -1};
205 ScreenToClient(hwnd, &pos);
206 return MAKELPARAM(pos.x, pos.y);
209 void WebInputEventFactory::resetLastClickState()
211 gLastClickTime = gLastClickCount = 0;
214 bool WebInputEventFactory::isSystemKeyEvent(const WebKeyboardEvent& event)
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;
224 WebMouseEvent WebInputEventFactory::mouseEvent(HWND hwnd, UINT message,
225 WPARAM wparam, LPARAM lparam)
227 WebMouseEvent result; //(WebInputEvent::Uninitialized());
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;
239 result.button = WebMouseEvent::ButtonNone;
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);
249 case WM_LBUTTONDBLCLK:
250 result.type = WebInputEvent::MouseDown;
251 result.button = WebMouseEvent::ButtonLeft;
254 case WM_MBUTTONDBLCLK:
255 result.type = WebInputEvent::MouseDown;
256 result.button = WebMouseEvent::ButtonMiddle;
259 case WM_RBUTTONDBLCLK:
260 result.type = WebInputEvent::MouseDown;
261 result.button = WebMouseEvent::ButtonRight;
264 result.type = WebInputEvent::MouseUp;
265 result.button = WebMouseEvent::ButtonLeft;
268 result.type = WebInputEvent::MouseUp;
269 result.button = WebMouseEvent::ButtonMiddle;
272 result.type = WebInputEvent::MouseUp;
273 result.button = WebMouseEvent::ButtonRight;
276 ASSERT_NOT_REACHED();
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;
285 // set position fields:
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;
292 POINT globalPoint = { result.x, result.y };
293 ClientToScreen(hwnd, &globalPoint);
295 result.globalX = globalPoint.x;
296 result.globalY = globalPoint.y;
298 // calculate number of clicks:
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;
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());
312 if (result.type == WebInputEvent::MouseDown) {
313 if (!cancelPreviousClick && (result.button == lastClickButton))
317 lastClickPositionX = result.x;
318 lastClickPositionY = result.y;
320 gLastClickTime = currentTime;
321 lastClickButton = result.button;
322 } else if (result.type == WebInputEvent::MouseMove
323 || result.type == WebInputEvent::MouseLeave) {
324 if (cancelPreviousClick) {
326 lastClickPositionX = 0;
327 lastClickPositionY = 0;
331 result.clickCount = gLastClickCount;
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;
348 SetToggleKeyState(&result);
352 // WebMouseWheelEvent ---------------------------------------------------------
354 WebMouseWheelEvent WebInputEventFactory::mouseWheelEvent(HWND hwnd, UINT message,
355 WPARAM wparam, LPARAM lparam)
357 WebMouseWheelEvent result; //(WebInputEvent::Uninitialized());
359 result.type = WebInputEvent::MouseWheel;
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;
367 result.button = WebMouseEvent::ButtonNone;
369 // Get key state, coordinates, and wheel delta from event.
370 typedef SHORT (WINAPI *GetKeyStateFunction)(int key);
371 GetKeyStateFunction getKeyState;
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;
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
388 POINT cursorPosition = {0};
389 GetCursorPos(&cursorPosition);
390 result.globalX = cursorPosition.x;
391 result.globalY = cursorPosition.y;
393 switch (LOWORD(wparam)) {
394 case SB_LINEUP: // == SB_LINELEFT
395 wheelDelta = WHEEL_DELTA;
397 case SB_LINEDOWN: // == SB_LINERIGHT
398 wheelDelta = -WHEEL_DELTA;
402 result.scrollByPage = true;
406 result.scrollByPage = true;
408 default: // We don't supoprt SB_THUMBPOSITION or SB_THUMBTRACK here.
413 if (message == WM_HSCROLL)
414 horizontalScroll = true;
416 // Non-synthesized event; we can just read data off the event.
417 getKeyState = GetKeyState;
418 keyState = GET_KEYSTATE_WPARAM(wparam);
420 result.globalX = static_cast<short>(LOWORD(lparam));
421 result.globalY = static_cast<short>(HIWORD(lparam));
423 wheelDelta = static_cast<float>(GET_WHEEL_DELTA_WPARAM(wparam));
424 if (message == WM_MOUSEHWHEEL) {
425 horizontalScroll = true;
426 wheelDelta = -wheelDelta; // Windows is <- -/+ ->, WebKit <- +/- ->.
429 if (keyState & MK_SHIFT)
430 horizontalScroll = true;
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;
446 SetToggleKeyState(&result);
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;
456 // Convert wheel delta amount to a number of pixels to scroll.
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
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;
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;
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;
492 result.deltaY = scrollDelta;
493 result.wheelTicksY = wheelDelta;