Home | History | Annotate | Download | only in win
      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 
     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 
     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 
    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 
    163 void WebInputEventFactory::resetLastClickState()
    164 {
    165     gLastClickTime = gLastClickCount = 0;
    166 }
    167 
    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 
    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