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