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