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