| /* |
| * Copyright (C) 2006-2009 Google Inc. All rights reserved. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions are |
| * met: |
| * |
| * * Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * * Redistributions in binary form must reproduce the above |
| * copyright notice, this list of conditions and the following disclaimer |
| * in the documentation and/or other materials provided with the |
| * distribution. |
| * * Neither the name of Google Inc. nor the names of its |
| * contributors may be used to endorse or promote products derived from |
| * this software without specific prior written permission. |
| * |
| * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
| * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
| * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
| * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
| * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
| * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
| * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
| * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
| * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| */ |
| |
| #include "config.h" |
| #include "WebInputEventFactory.h" |
| |
| #include "WebInputEvent.h" |
| |
| #include <wtf/Assertions.h> |
| |
| namespace WebKit { |
| |
| static const unsigned long defaultScrollLinesPerWheelDelta = 3; |
| static const unsigned long defaultScrollCharsPerWheelDelta = 1; |
| |
| // WebKeyboardEvent ----------------------------------------------------------- |
| |
| static bool isKeyPad(WPARAM wparam, LPARAM lparam) |
| { |
| bool keypad = false; |
| switch (wparam) { |
| case VK_RETURN: |
| keypad = (lparam >> 16) & KF_EXTENDED; |
| break; |
| case VK_INSERT: |
| case VK_DELETE: |
| case VK_HOME: |
| case VK_END: |
| case VK_PRIOR: |
| case VK_NEXT: |
| case VK_UP: |
| case VK_DOWN: |
| case VK_LEFT: |
| case VK_RIGHT: |
| keypad = !((lparam >> 16) & KF_EXTENDED); |
| break; |
| case VK_NUMLOCK: |
| case VK_NUMPAD0: |
| case VK_NUMPAD1: |
| case VK_NUMPAD2: |
| case VK_NUMPAD3: |
| case VK_NUMPAD4: |
| case VK_NUMPAD5: |
| case VK_NUMPAD6: |
| case VK_NUMPAD7: |
| case VK_NUMPAD8: |
| case VK_NUMPAD9: |
| case VK_DIVIDE: |
| case VK_MULTIPLY: |
| case VK_SUBTRACT: |
| case VK_ADD: |
| case VK_DECIMAL: |
| case VK_CLEAR: |
| keypad = true; |
| break; |
| default: |
| keypad = false; |
| } |
| return keypad; |
| } |
| |
| WebKeyboardEvent WebInputEventFactory::keyboardEvent(HWND hwnd, UINT message, |
| WPARAM wparam, LPARAM lparam) |
| { |
| WebKeyboardEvent result; |
| |
| // TODO(pkasting): http://b/1117926 Are we guaranteed that the message that |
| // GetMessageTime() refers to is the same one that we're passed in? Perhaps |
| // one of the construction parameters should be the time passed by the |
| // caller, who would know for sure. |
| result.timeStampSeconds = GetMessageTime() / 1000.0; |
| |
| result.windowsKeyCode = result.nativeKeyCode = static_cast<int>(wparam); |
| |
| switch (message) { |
| case WM_SYSKEYDOWN: |
| result.isSystemKey = true; |
| case WM_KEYDOWN: |
| result.type = WebInputEvent::RawKeyDown; |
| break; |
| case WM_SYSKEYUP: |
| result.isSystemKey = true; |
| case WM_KEYUP: |
| result.type = WebInputEvent::KeyUp; |
| break; |
| case WM_IME_CHAR: |
| result.type = WebInputEvent::Char; |
| break; |
| case WM_SYSCHAR: |
| result.isSystemKey = true; |
| result.type = WebInputEvent::Char; |
| case WM_CHAR: |
| result.type = WebInputEvent::Char; |
| break; |
| default: |
| ASSERT_NOT_REACHED(); |
| } |
| |
| if (result.type == WebInputEvent::Char || result.type == WebInputEvent::RawKeyDown) { |
| result.text[0] = result.windowsKeyCode; |
| result.unmodifiedText[0] = result.windowsKeyCode; |
| } |
| if (result.type != WebInputEvent::Char) |
| result.setKeyIdentifierFromWindowsKeyCode(); |
| |
| if (GetKeyState(VK_SHIFT) & 0x8000) |
| result.modifiers |= WebInputEvent::ShiftKey; |
| if (GetKeyState(VK_CONTROL) & 0x8000) |
| result.modifiers |= WebInputEvent::ControlKey; |
| if (GetKeyState(VK_MENU) & 0x8000) |
| result.modifiers |= WebInputEvent::AltKey; |
| // NOTE: There doesn't seem to be a way to query the mouse button state in |
| // this case. |
| |
| if (LOWORD(lparam) > 1) |
| result.modifiers |= WebInputEvent::IsAutoRepeat; |
| if (isKeyPad(wparam, lparam)) |
| result.modifiers |= WebInputEvent::IsKeyPad; |
| |
| return result; |
| } |
| |
| // WebMouseEvent -------------------------------------------------------------- |
| |
| static int gLastClickCount; |
| static double gLastClickTime; |
| |
| static LPARAM GetRelativeCursorPos(HWND hwnd) |
| { |
| POINT pos = {-1, -1}; |
| GetCursorPos(&pos); |
| ScreenToClient(hwnd, &pos); |
| return MAKELPARAM(pos.x, pos.y); |
| } |
| |
| void WebInputEventFactory::resetLastClickState() |
| { |
| gLastClickTime = gLastClickCount = 0; |
| } |
| |
| WebMouseEvent WebInputEventFactory::mouseEvent(HWND hwnd, UINT message, |
| WPARAM wparam, LPARAM lparam) |
| { |
| WebMouseEvent result; //(WebInputEvent::Uninitialized()); |
| |
| switch (message) { |
| case WM_MOUSEMOVE: |
| result.type = WebInputEvent::MouseMove; |
| if (wparam & MK_LBUTTON) |
| result.button = WebMouseEvent::ButtonLeft; |
| else if (wparam & MK_MBUTTON) |
| result.button = WebMouseEvent::ButtonMiddle; |
| else if (wparam & MK_RBUTTON) |
| result.button = WebMouseEvent::ButtonRight; |
| else |
| result.button = WebMouseEvent::ButtonNone; |
| break; |
| case WM_MOUSELEAVE: |
| result.type = WebInputEvent::MouseLeave; |
| result.button = WebMouseEvent::ButtonNone; |
| // set the current mouse position (relative to the client area of the |
| // current window) since none is specified for this event |
| lparam = GetRelativeCursorPos(hwnd); |
| break; |
| case WM_LBUTTONDOWN: |
| case WM_LBUTTONDBLCLK: |
| result.type = WebInputEvent::MouseDown; |
| result.button = WebMouseEvent::ButtonLeft; |
| break; |
| case WM_MBUTTONDOWN: |
| case WM_MBUTTONDBLCLK: |
| result.type = WebInputEvent::MouseDown; |
| result.button = WebMouseEvent::ButtonMiddle; |
| break; |
| case WM_RBUTTONDOWN: |
| case WM_RBUTTONDBLCLK: |
| result.type = WebInputEvent::MouseDown; |
| result.button = WebMouseEvent::ButtonRight; |
| break; |
| case WM_LBUTTONUP: |
| result.type = WebInputEvent::MouseUp; |
| result.button = WebMouseEvent::ButtonLeft; |
| break; |
| case WM_MBUTTONUP: |
| result.type = WebInputEvent::MouseUp; |
| result.button = WebMouseEvent::ButtonMiddle; |
| break; |
| case WM_RBUTTONUP: |
| result.type = WebInputEvent::MouseUp; |
| result.button = WebMouseEvent::ButtonRight; |
| break; |
| default: |
| ASSERT_NOT_REACHED(); |
| } |
| |
| // TODO(pkasting): http://b/1117926 Are we guaranteed that the message that |
| // GetMessageTime() refers to is the same one that we're passed in? Perhaps |
| // one of the construction parameters should be the time passed by the |
| // caller, who would know for sure. |
| result.timeStampSeconds = GetMessageTime() / 1000.0; |
| |
| // set position fields: |
| |
| result.x = static_cast<short>(LOWORD(lparam)); |
| result.y = static_cast<short>(HIWORD(lparam)); |
| result.windowX = result.x; |
| result.windowY = result.y; |
| |
| POINT globalPoint = { result.x, result.y }; |
| ClientToScreen(hwnd, &globalPoint); |
| |
| result.globalX = globalPoint.x; |
| result.globalY = globalPoint.y; |
| |
| // calculate number of clicks: |
| |
| // This differs slightly from the WebKit code in WebKit/win/WebView.cpp |
| // where their original code looks buggy. |
| static int lastClickPositionX; |
| static int lastClickPositionY; |
| static WebMouseEvent::Button lastClickButton = WebMouseEvent::ButtonLeft; |
| |
| double currentTime = result.timeStampSeconds; |
| bool cancelPreviousClick = |
| (abs(lastClickPositionX - result.x) > (GetSystemMetrics(SM_CXDOUBLECLK) / 2)) |
| || (abs(lastClickPositionY - result.y) > (GetSystemMetrics(SM_CYDOUBLECLK) / 2)) |
| || ((currentTime - gLastClickTime) * 1000.0 > GetDoubleClickTime()); |
| |
| if (result.type == WebInputEvent::MouseDown) { |
| if (!cancelPreviousClick && (result.button == lastClickButton)) |
| ++gLastClickCount; |
| else { |
| gLastClickCount = 1; |
| lastClickPositionX = result.x; |
| lastClickPositionY = result.y; |
| } |
| gLastClickTime = currentTime; |
| lastClickButton = result.button; |
| } else if (result.type == WebInputEvent::MouseMove |
| || result.type == WebInputEvent::MouseLeave) { |
| if (cancelPreviousClick) { |
| gLastClickCount = 0; |
| lastClickPositionX = 0; |
| lastClickPositionY = 0; |
| gLastClickTime = 0; |
| } |
| } |
| result.clickCount = gLastClickCount; |
| |
| // set modifiers: |
| |
| if (wparam & MK_CONTROL) |
| result.modifiers |= WebInputEvent::ControlKey; |
| if (wparam & MK_SHIFT) |
| result.modifiers |= WebInputEvent::ShiftKey; |
| if (GetKeyState(VK_MENU) & 0x8000) |
| result.modifiers |= WebInputEvent::AltKey; |
| if (wparam & MK_LBUTTON) |
| result.modifiers |= WebInputEvent::LeftButtonDown; |
| if (wparam & MK_MBUTTON) |
| result.modifiers |= WebInputEvent::MiddleButtonDown; |
| if (wparam & MK_RBUTTON) |
| result.modifiers |= WebInputEvent::RightButtonDown; |
| |
| return result; |
| } |
| |
| // WebMouseWheelEvent --------------------------------------------------------- |
| |
| WebMouseWheelEvent WebInputEventFactory::mouseWheelEvent(HWND hwnd, UINT message, |
| WPARAM wparam, LPARAM lparam) |
| { |
| WebMouseWheelEvent result; //(WebInputEvent::Uninitialized()); |
| |
| result.type = WebInputEvent::MouseWheel; |
| |
| // TODO(pkasting): http://b/1117926 Are we guaranteed that the message that |
| // GetMessageTime() refers to is the same one that we're passed in? Perhaps |
| // one of the construction parameters should be the time passed by the |
| // caller, who would know for sure. |
| result.timeStampSeconds = GetMessageTime() / 1000.0; |
| |
| result.button = WebMouseEvent::ButtonNone; |
| |
| // Get key state, coordinates, and wheel delta from event. |
| typedef SHORT (WINAPI *GetKeyStateFunction)(int key); |
| GetKeyStateFunction getKeyState; |
| UINT keyState; |
| float wheelDelta; |
| bool horizontalScroll = false; |
| if ((message == WM_VSCROLL) || (message == WM_HSCROLL)) { |
| // Synthesize mousewheel event from a scroll event. This is needed to |
| // simulate middle mouse scrolling in some laptops. Use GetAsyncKeyState |
| // for key state since we are synthesizing the input event. |
| getKeyState = GetAsyncKeyState; |
| keyState = 0; |
| if (getKeyState(VK_SHIFT)) |
| keyState |= MK_SHIFT; |
| if (getKeyState(VK_CONTROL)) |
| keyState |= MK_CONTROL; |
| // NOTE: There doesn't seem to be a way to query the mouse button state |
| // in this case. |
| |
| POINT cursorPosition = {0}; |
| GetCursorPos(&cursorPosition); |
| result.globalX = cursorPosition.x; |
| result.globalY = cursorPosition.y; |
| |
| switch (LOWORD(wparam)) { |
| case SB_LINEUP: // == SB_LINELEFT |
| wheelDelta = WHEEL_DELTA; |
| break; |
| case SB_LINEDOWN: // == SB_LINERIGHT |
| wheelDelta = -WHEEL_DELTA; |
| break; |
| case SB_PAGEUP: |
| wheelDelta = 1; |
| result.scrollByPage = true; |
| break; |
| case SB_PAGEDOWN: |
| wheelDelta = -1; |
| result.scrollByPage = true; |
| break; |
| default: // We don't supoprt SB_THUMBPOSITION or SB_THUMBTRACK here. |
| wheelDelta = 0; |
| break; |
| } |
| |
| if (message == WM_HSCROLL) |
| horizontalScroll = true; |
| } else { |
| // Non-synthesized event; we can just read data off the event. |
| getKeyState = GetKeyState; |
| keyState = GET_KEYSTATE_WPARAM(wparam); |
| |
| result.globalX = static_cast<short>(LOWORD(lparam)); |
| result.globalY = static_cast<short>(HIWORD(lparam)); |
| |
| wheelDelta = static_cast<float>(GET_WHEEL_DELTA_WPARAM(wparam)); |
| if (message == WM_MOUSEHWHEEL) { |
| horizontalScroll = true; |
| wheelDelta = -wheelDelta; // Windows is <- -/+ ->, WebKit <- +/- ->. |
| } |
| } |
| if (keyState & MK_SHIFT) |
| horizontalScroll = true; |
| |
| // Set modifiers based on key state. |
| if (keyState & MK_SHIFT) |
| result.modifiers |= WebInputEvent::ShiftKey; |
| if (keyState & MK_CONTROL) |
| result.modifiers |= WebInputEvent::ControlKey; |
| if (getKeyState(VK_MENU) & 0x8000) |
| result.modifiers |= WebInputEvent::AltKey; |
| if (keyState & MK_LBUTTON) |
| result.modifiers |= WebInputEvent::LeftButtonDown; |
| if (keyState & MK_MBUTTON) |
| result.modifiers |= WebInputEvent::MiddleButtonDown; |
| if (keyState & MK_RBUTTON) |
| result.modifiers |= WebInputEvent::RightButtonDown; |
| |
| // Set coordinates by translating event coordinates from screen to client. |
| POINT clientPoint = { result.globalX, result.globalY }; |
| MapWindowPoints(0, hwnd, &clientPoint, 1); |
| result.x = clientPoint.x; |
| result.y = clientPoint.y; |
| result.windowX = result.x; |
| result.windowY = result.y; |
| |
| // Convert wheel delta amount to a number of pixels to scroll. |
| // |
| // How many pixels should we scroll per line? Gecko uses the height of the |
| // current line, which means scroll distance changes as you go through the |
| // page or go to different pages. IE 7 is ~50 px/line, although the value |
| // seems to vary slightly by page and zoom level. Since IE 7 has a smoothing |
| // algorithm on scrolling, it can get away with slightly larger scroll values |
| // without feeling jerky. Here we use 100 px per three lines (the default |
| // scroll amount is three lines per wheel tick). |
| static const float scrollbarPixelsPerLine = 100.0f / 3.0f; |
| wheelDelta /= WHEEL_DELTA; |
| float scrollDelta = wheelDelta; |
| if (horizontalScroll) { |
| unsigned long scrollChars = defaultScrollCharsPerWheelDelta; |
| SystemParametersInfo(SPI_GETWHEELSCROLLCHARS, 0, &scrollChars, 0); |
| // TODO(pkasting): Should probably have a different multiplier |
| // scrollbarPixelsPerChar here. |
| scrollDelta *= static_cast<float>(scrollChars) * scrollbarPixelsPerLine; |
| } else { |
| unsigned long scrollLines = defaultScrollLinesPerWheelDelta; |
| SystemParametersInfo(SPI_GETWHEELSCROLLLINES, 0, &scrollLines, 0); |
| if (scrollLines == WHEEL_PAGESCROLL) |
| result.scrollByPage = true; |
| if (!result.scrollByPage) |
| scrollDelta *= static_cast<float>(scrollLines) * scrollbarPixelsPerLine; |
| } |
| |
| // Set scroll amount based on above calculations. WebKit expects positive |
| // deltaY to mean "scroll up" and positive deltaX to mean "scroll left". |
| if (horizontalScroll) { |
| result.deltaX = scrollDelta; |
| result.wheelTicksX = wheelDelta; |
| } else { |
| result.deltaY = scrollDelta; |
| result.wheelTicksY = wheelDelta; |
| } |
| |
| return result; |
| } |
| |
| } // namespace WebKit |