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