OLD | NEW |
(Empty) | |
| 1 // Copyright (c) 2013 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. |
| 4 |
| 5 #include "content/browser/renderer_host/input/web_input_event_builders_win.h" |
| 6 |
| 7 #include "base/logging.h" |
| 8 |
| 9 using WebKit::WebInputEvent; |
| 10 using WebKit::WebKeyboardEvent; |
| 11 using WebKit::WebMouseEvent; |
| 12 using WebKit::WebMouseWheelEvent; |
| 13 |
| 14 namespace content { |
| 15 |
| 16 static const unsigned long kDefaultScrollLinesPerWheelDelta = 3; |
| 17 static const unsigned long kDefaultScrollCharsPerWheelDelta = 1; |
| 18 |
| 19 static bool IsKeyDown(WPARAM wparam) { |
| 20 return (GetKeyState(wparam) & 0x8000) != 0; |
| 21 } |
| 22 |
| 23 static int GetLocationModifier(WPARAM wparam, LPARAM lparam) { |
| 24 int modifier = 0; |
| 25 switch (wparam) { |
| 26 case VK_RETURN: |
| 27 if ((lparam >> 16) & KF_EXTENDED) |
| 28 modifier = WebInputEvent::IsKeyPad; |
| 29 break; |
| 30 case VK_INSERT: |
| 31 case VK_DELETE: |
| 32 case VK_HOME: |
| 33 case VK_END: |
| 34 case VK_PRIOR: |
| 35 case VK_NEXT: |
| 36 case VK_UP: |
| 37 case VK_DOWN: |
| 38 case VK_LEFT: |
| 39 case VK_RIGHT: |
| 40 if (!((lparam >> 16) & KF_EXTENDED)) |
| 41 modifier = WebInputEvent::IsKeyPad; |
| 42 break; |
| 43 case VK_NUMLOCK: |
| 44 case VK_NUMPAD0: |
| 45 case VK_NUMPAD1: |
| 46 case VK_NUMPAD2: |
| 47 case VK_NUMPAD3: |
| 48 case VK_NUMPAD4: |
| 49 case VK_NUMPAD5: |
| 50 case VK_NUMPAD6: |
| 51 case VK_NUMPAD7: |
| 52 case VK_NUMPAD8: |
| 53 case VK_NUMPAD9: |
| 54 case VK_DIVIDE: |
| 55 case VK_MULTIPLY: |
| 56 case VK_SUBTRACT: |
| 57 case VK_ADD: |
| 58 case VK_DECIMAL: |
| 59 case VK_CLEAR: |
| 60 modifier = WebInputEvent::IsKeyPad; |
| 61 break; |
| 62 case VK_SHIFT: |
| 63 if (IsKeyDown(VK_LSHIFT)) |
| 64 modifier = WebInputEvent::IsLeft; |
| 65 else if (IsKeyDown(VK_RSHIFT)) |
| 66 modifier = WebInputEvent::IsRight; |
| 67 break; |
| 68 case VK_CONTROL: |
| 69 if (IsKeyDown(VK_LCONTROL)) |
| 70 modifier = WebInputEvent::IsLeft; |
| 71 else if (IsKeyDown(VK_RCONTROL)) |
| 72 modifier = WebInputEvent::IsRight; |
| 73 break; |
| 74 case VK_MENU: |
| 75 if (IsKeyDown(VK_LMENU)) |
| 76 modifier = WebInputEvent::IsLeft; |
| 77 else if (IsKeyDown(VK_RMENU)) |
| 78 modifier = WebInputEvent::IsRight; |
| 79 break; |
| 80 case VK_LWIN: |
| 81 modifier = WebInputEvent::IsLeft; |
| 82 break; |
| 83 case VK_RWIN: |
| 84 modifier = WebInputEvent::IsRight; |
| 85 break; |
| 86 } |
| 87 |
| 88 DCHECK(!modifier |
| 89 || modifier == WebInputEvent::IsKeyPad |
| 90 || modifier == WebInputEvent::IsLeft |
| 91 || modifier == WebInputEvent::IsRight); |
| 92 return modifier; |
| 93 } |
| 94 |
| 95 // Loads the state for toggle keys into the event. |
| 96 static void SetToggleKeyState(WebInputEvent* event) { |
| 97 // Low bit set from GetKeyState indicates "toggled". |
| 98 if (::GetKeyState(VK_NUMLOCK) & 1) |
| 99 event->modifiers |= WebInputEvent::NumLockOn; |
| 100 if (::GetKeyState(VK_CAPITAL) & 1) |
| 101 event->modifiers |= WebInputEvent::CapsLockOn; |
| 102 } |
| 103 |
| 104 WebKeyboardEvent WebKeyboardEventBuilder::Build(HWND hwnd, UINT message, |
| 105 WPARAM wparam, LPARAM lparam) { |
| 106 WebKeyboardEvent result; |
| 107 |
| 108 // TODO(pkasting): http://b/1117926 Are we guaranteed that the message that |
| 109 // GetMessageTime() refers to is the same one that we're passed in? Perhaps |
| 110 // one of the construction parameters should be the time passed by the |
| 111 // caller, who would know for sure. |
| 112 result.timeStampSeconds = ::GetMessageTime() / 1000.0; |
| 113 |
| 114 result.windowsKeyCode = static_cast<int>(wparam); |
| 115 // Record the scan code (along with other context bits) for this key event. |
| 116 result.nativeKeyCode = static_cast<int>(lparam); |
| 117 |
| 118 switch (message) { |
| 119 case WM_SYSKEYDOWN: |
| 120 result.isSystemKey = true; |
| 121 case WM_KEYDOWN: |
| 122 result.type = WebInputEvent::RawKeyDown; |
| 123 break; |
| 124 case WM_SYSKEYUP: |
| 125 result.isSystemKey = true; |
| 126 case WM_KEYUP: |
| 127 result.type = WebInputEvent::KeyUp; |
| 128 break; |
| 129 case WM_IME_CHAR: |
| 130 result.type = WebInputEvent::Char; |
| 131 break; |
| 132 case WM_SYSCHAR: |
| 133 result.isSystemKey = true; |
| 134 result.type = WebInputEvent::Char; |
| 135 case WM_CHAR: |
| 136 result.type = WebInputEvent::Char; |
| 137 break; |
| 138 default: |
| 139 NOTREACHED(); |
| 140 } |
| 141 |
| 142 if (result.type == WebInputEvent::Char |
| 143 || result.type == WebInputEvent::RawKeyDown) { |
| 144 result.text[0] = result.windowsKeyCode; |
| 145 result.unmodifiedText[0] = result.windowsKeyCode; |
| 146 } |
| 147 if (result.type != WebInputEvent::Char) |
| 148 result.setKeyIdentifierFromWindowsKeyCode(); |
| 149 |
| 150 if (::GetKeyState(VK_SHIFT) & 0x8000) |
| 151 result.modifiers |= WebInputEvent::ShiftKey; |
| 152 if (::GetKeyState(VK_CONTROL) & 0x8000) |
| 153 result.modifiers |= WebInputEvent::ControlKey; |
| 154 if (::GetKeyState(VK_MENU) & 0x8000) |
| 155 result.modifiers |= WebInputEvent::AltKey; |
| 156 // NOTE: There doesn't seem to be a way to query the mouse button state in |
| 157 // this case. |
| 158 |
| 159 if (LOWORD(lparam) > 1) |
| 160 result.modifiers |= WebInputEvent::IsAutoRepeat; |
| 161 |
| 162 result.modifiers |= GetLocationModifier(wparam, lparam); |
| 163 |
| 164 SetToggleKeyState(&result); |
| 165 return result; |
| 166 } |
| 167 |
| 168 // WebMouseEvent -------------------------------------------------------------- |
| 169 |
| 170 static int g_last_click_count = 0; |
| 171 static double g_last_click_time = 0; |
| 172 |
| 173 static LPARAM GetRelativeCursorPos(HWND hwnd) { |
| 174 POINT pos = {-1, -1}; |
| 175 GetCursorPos(&pos); |
| 176 ScreenToClient(hwnd, &pos); |
| 177 return MAKELPARAM(pos.x, pos.y); |
| 178 } |
| 179 |
| 180 WebMouseEvent WebMouseEventBuilder::Build(HWND hwnd, UINT message, |
| 181 WPARAM wparam, LPARAM lparam) { |
| 182 WebMouseEvent result; |
| 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 NOTREACHED(); |
| 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 global_point = { result.x, result.y }; |
| 248 ClientToScreen(hwnd, &global_point); |
| 249 |
| 250 result.globalX = global_point.x; |
| 251 result.globalY = global_point.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 last_click_position_x; |
| 258 static int last_click_position_Y; |
| 259 static WebMouseEvent::Button last_click_button = WebMouseEvent::ButtonLeft; |
| 260 |
| 261 double current_time = result.timeStampSeconds; |
| 262 bool cancel_previous_click = |
| 263 (abs(last_click_position_x - result.x) > |
| 264 (::GetSystemMetrics(SM_CXDOUBLECLK) / 2)) |
| 265 || (abs(last_click_position_x - result.y) > |
| 266 (::GetSystemMetrics(SM_CYDOUBLECLK) / 2)) |
| 267 || ((current_time - g_last_click_time) * 1000.0 > ::GetDoubleClickTime()); |
| 268 |
| 269 if (result.type == WebInputEvent::MouseDown) { |
| 270 if (!cancel_previous_click && (result.button == last_click_button)) { |
| 271 ++g_last_click_count; |
| 272 } else { |
| 273 g_last_click_count = 1; |
| 274 last_click_position_x = result.x; |
| 275 last_click_position_x = result.y; |
| 276 } |
| 277 g_last_click_time = current_time; |
| 278 last_click_button = result.button; |
| 279 } else if (result.type == WebInputEvent::MouseMove |
| 280 || result.type == WebInputEvent::MouseLeave) { |
| 281 if (cancel_previous_click) { |
| 282 g_last_click_count = 0; |
| 283 last_click_position_x = 0; |
| 284 last_click_position_x = 0; |
| 285 g_last_click_time = 0; |
| 286 } |
| 287 } |
| 288 result.clickCount = g_last_click_count; |
| 289 |
| 290 // set modifiers: |
| 291 |
| 292 if (wparam & MK_CONTROL) |
| 293 result.modifiers |= WebInputEvent::ControlKey; |
| 294 if (wparam & MK_SHIFT) |
| 295 result.modifiers |= WebInputEvent::ShiftKey; |
| 296 if (::GetKeyState(VK_MENU) & 0x8000) |
| 297 result.modifiers |= WebInputEvent::AltKey; |
| 298 if (wparam & MK_LBUTTON) |
| 299 result.modifiers |= WebInputEvent::LeftButtonDown; |
| 300 if (wparam & MK_MBUTTON) |
| 301 result.modifiers |= WebInputEvent::MiddleButtonDown; |
| 302 if (wparam & MK_RBUTTON) |
| 303 result.modifiers |= WebInputEvent::RightButtonDown; |
| 304 |
| 305 SetToggleKeyState(&result); |
| 306 return result; |
| 307 } |
| 308 |
| 309 // WebMouseWheelEvent --------------------------------------------------------- |
| 310 |
| 311 WebMouseWheelEvent |
| 312 WebMouseWheelEventBuilder::Build(HWND hwnd, UINT message, |
| 313 WPARAM wparam, LPARAM lparam) { |
| 314 WebMouseWheelEvent result; |
| 315 |
| 316 result.type = WebInputEvent::MouseWheel; |
| 317 |
| 318 // TODO(pkasting): http://b/1117926 Are we guaranteed that the message that |
| 319 // GetMessageTime() refers to is the same one that we're passed in? Perhaps |
| 320 // one of the construction parameters should be the time passed by the |
| 321 // caller, who would know for sure. |
| 322 result.timeStampSeconds = ::GetMessageTime() / 1000.0; |
| 323 |
| 324 result.button = WebMouseEvent::ButtonNone; |
| 325 |
| 326 // Get key state, coordinates, and wheel delta from event. |
| 327 typedef SHORT (WINAPI *GetKeyStateFunction)(int key); |
| 328 GetKeyStateFunction get_key_state_func; |
| 329 UINT key_state; |
| 330 float wheel_delta; |
| 331 bool horizontal_scroll = false; |
| 332 if ((message == WM_VSCROLL) || (message == WM_HSCROLL)) { |
| 333 // Synthesize mousewheel event from a scroll event. This is needed to |
| 334 // simulate middle mouse scrolling in some laptops. Use GetAsyncKeyState |
| 335 // for key state since we are synthesizing the input event. |
| 336 get_key_state_func = GetAsyncKeyState; |
| 337 key_state = 0; |
| 338 if (get_key_state_func(VK_SHIFT)) |
| 339 key_state |= MK_SHIFT; |
| 340 if (get_key_state_func(VK_CONTROL)) |
| 341 key_state |= MK_CONTROL; |
| 342 // NOTE: There doesn't seem to be a way to query the mouse button state |
| 343 // in this case. |
| 344 |
| 345 POINT cursor_position = {0}; |
| 346 GetCursorPos(&cursor_position); |
| 347 result.globalX = cursor_position.x; |
| 348 result.globalY = cursor_position.y; |
| 349 |
| 350 switch (LOWORD(wparam)) { |
| 351 case SB_LINEUP: // == SB_LINELEFT |
| 352 wheel_delta = WHEEL_DELTA; |
| 353 break; |
| 354 case SB_LINEDOWN: // == SB_LINERIGHT |
| 355 wheel_delta = -WHEEL_DELTA; |
| 356 break; |
| 357 case SB_PAGEUP: |
| 358 wheel_delta = 1; |
| 359 result.scrollByPage = true; |
| 360 break; |
| 361 case SB_PAGEDOWN: |
| 362 wheel_delta = -1; |
| 363 result.scrollByPage = true; |
| 364 break; |
| 365 default: // We don't supoprt SB_THUMBPOSITION or SB_THUMBTRACK here. |
| 366 wheel_delta = 0; |
| 367 break; |
| 368 } |
| 369 |
| 370 if (message == WM_HSCROLL) |
| 371 horizontal_scroll = true; |
| 372 } else { |
| 373 // Non-synthesized event; we can just read data off the event. |
| 374 get_key_state_func = ::GetKeyState; |
| 375 key_state = GET_KEYSTATE_WPARAM(wparam); |
| 376 |
| 377 result.globalX = static_cast<short>(LOWORD(lparam)); |
| 378 result.globalY = static_cast<short>(HIWORD(lparam)); |
| 379 |
| 380 wheel_delta = static_cast<float>(GET_WHEEL_DELTA_WPARAM(wparam)); |
| 381 if (message == WM_MOUSEHWHEEL) { |
| 382 horizontal_scroll = true; |
| 383 wheel_delta = -wheel_delta; // Windows is <- -/+ ->, WebKit <- +/- ->. |
| 384 } |
| 385 } |
| 386 if (key_state & MK_SHIFT) |
| 387 horizontal_scroll = true; |
| 388 |
| 389 // Set modifiers based on key state. |
| 390 if (key_state & MK_SHIFT) |
| 391 result.modifiers |= WebInputEvent::ShiftKey; |
| 392 if (key_state & MK_CONTROL) |
| 393 result.modifiers |= WebInputEvent::ControlKey; |
| 394 if (get_key_state_func(VK_MENU) & 0x8000) |
| 395 result.modifiers |= WebInputEvent::AltKey; |
| 396 if (key_state & MK_LBUTTON) |
| 397 result.modifiers |= WebInputEvent::LeftButtonDown; |
| 398 if (key_state & MK_MBUTTON) |
| 399 result.modifiers |= WebInputEvent::MiddleButtonDown; |
| 400 if (key_state & MK_RBUTTON) |
| 401 result.modifiers |= WebInputEvent::RightButtonDown; |
| 402 |
| 403 SetToggleKeyState(&result); |
| 404 |
| 405 // Set coordinates by translating event coordinates from screen to client. |
| 406 POINT client_point = { result.globalX, result.globalY }; |
| 407 MapWindowPoints(0, hwnd, &client_point, 1); |
| 408 result.x = client_point.x; |
| 409 result.y = client_point.y; |
| 410 result.windowX = result.x; |
| 411 result.windowY = result.y; |
| 412 |
| 413 // Convert wheel delta amount to a number of pixels to scroll. |
| 414 // |
| 415 // How many pixels should we scroll per line? Gecko uses the height of the |
| 416 // current line, which means scroll distance changes as you go through the |
| 417 // page or go to different pages. IE 8 is ~60 px/line, although the value |
| 418 // seems to vary slightly by page and zoom level. Also, IE defaults to |
| 419 // smooth scrolling while Firefox doesn't, so it can get away with somewhat |
| 420 // larger scroll values without feeling as jerky. Here we use 100 px per |
| 421 // three lines (the default scroll amount is three lines per wheel tick). |
| 422 // Even though we have smooth scrolling, we don't make this as large as IE |
| 423 // because subjectively IE feels like it scrolls farther than you want while |
| 424 // reading articles. |
| 425 static const float kScrollbarPixelsPerLine = 100.0f / 3.0f; |
| 426 wheel_delta /= WHEEL_DELTA; |
| 427 float scroll_delta = wheel_delta * kScrollbarPixelsPerLine; |
| 428 if (horizontal_scroll) { |
| 429 unsigned long scroll_chars = kDefaultScrollCharsPerWheelDelta; |
| 430 SystemParametersInfo(SPI_GETWHEELSCROLLCHARS, 0, &scroll_chars, 0); |
| 431 // TODO(pkasting): Should probably have a different multiplier |
| 432 // scrollbarPixelsPerChar here. |
| 433 scroll_delta *= static_cast<float>(scroll_chars); |
| 434 } else { |
| 435 unsigned long scroll_lines = kDefaultScrollLinesPerWheelDelta; |
| 436 SystemParametersInfo(SPI_GETWHEELSCROLLLINES, 0, &scroll_lines, 0); |
| 437 if (scroll_lines == WHEEL_PAGESCROLL) |
| 438 result.scrollByPage = true; |
| 439 if (!result.scrollByPage) |
| 440 scroll_delta *= static_cast<float>(scroll_lines); |
| 441 } |
| 442 |
| 443 // Set scroll amount based on above calculations. WebKit expects positive |
| 444 // deltaY to mean "scroll up" and positive deltaX to mean "scroll left". |
| 445 if (horizontal_scroll) { |
| 446 result.deltaX = scroll_delta; |
| 447 result.wheelTicksX = wheel_delta; |
| 448 } else { |
| 449 result.deltaY = scroll_delta; |
| 450 result.wheelTicksY = wheel_delta; |
| 451 } |
| 452 |
| 453 return result; |
| 454 } |
| 455 |
| 456 } // namespace content |
OLD | NEW |