handoff: Fix the origin so that it correctly reflects the sender.
[chromium-blink-merge.git] / content / browser / renderer_host / input / web_input_event_builders_win.cc
blob30034ee0119c1585e79ab80bfbefce4bd5649bf1
1 // Copyright 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.
5 #include "content/browser/renderer_host/input/web_input_event_builders_win.h"
7 #include "base/logging.h"
8 #include "content/browser/renderer_host/input/web_input_event_util.h"
9 #include "ui/gfx/win/dpi.h"
11 using blink::WebInputEvent;
12 using blink::WebKeyboardEvent;
13 using blink::WebMouseEvent;
14 using blink::WebMouseWheelEvent;
16 namespace content {
18 static const unsigned long kDefaultScrollLinesPerWheelDelta = 3;
19 static const unsigned long kDefaultScrollCharsPerWheelDelta = 1;
21 static bool IsKeyDown(WPARAM wparam) {
22 return (GetKeyState(wparam) & 0x8000) != 0;
25 static int GetLocationModifier(WPARAM wparam, LPARAM lparam) {
26 int modifier = 0;
27 switch (wparam) {
28 case VK_RETURN:
29 if ((lparam >> 16) & KF_EXTENDED)
30 modifier = WebInputEvent::IsKeyPad;
31 break;
32 case VK_INSERT:
33 case VK_DELETE:
34 case VK_HOME:
35 case VK_END:
36 case VK_PRIOR:
37 case VK_NEXT:
38 case VK_UP:
39 case VK_DOWN:
40 case VK_LEFT:
41 case VK_RIGHT:
42 if (!((lparam >> 16) & KF_EXTENDED))
43 modifier = WebInputEvent::IsKeyPad;
44 break;
45 case VK_NUMLOCK:
46 case VK_NUMPAD0:
47 case VK_NUMPAD1:
48 case VK_NUMPAD2:
49 case VK_NUMPAD3:
50 case VK_NUMPAD4:
51 case VK_NUMPAD5:
52 case VK_NUMPAD6:
53 case VK_NUMPAD7:
54 case VK_NUMPAD8:
55 case VK_NUMPAD9:
56 case VK_DIVIDE:
57 case VK_MULTIPLY:
58 case VK_SUBTRACT:
59 case VK_ADD:
60 case VK_DECIMAL:
61 case VK_CLEAR:
62 modifier = WebInputEvent::IsKeyPad;
63 break;
64 case VK_SHIFT:
65 if (IsKeyDown(VK_LSHIFT))
66 modifier = WebInputEvent::IsLeft;
67 else if (IsKeyDown(VK_RSHIFT))
68 modifier = WebInputEvent::IsRight;
69 break;
70 case VK_CONTROL:
71 if (IsKeyDown(VK_LCONTROL))
72 modifier = WebInputEvent::IsLeft;
73 else if (IsKeyDown(VK_RCONTROL))
74 modifier = WebInputEvent::IsRight;
75 break;
76 case VK_MENU:
77 if (IsKeyDown(VK_LMENU))
78 modifier = WebInputEvent::IsLeft;
79 else if (IsKeyDown(VK_RMENU))
80 modifier = WebInputEvent::IsRight;
81 break;
82 case VK_LWIN:
83 modifier = WebInputEvent::IsLeft;
84 break;
85 case VK_RWIN:
86 modifier = WebInputEvent::IsRight;
87 break;
90 DCHECK(!modifier
91 || modifier == WebInputEvent::IsKeyPad
92 || modifier == WebInputEvent::IsLeft
93 || modifier == WebInputEvent::IsRight);
94 return modifier;
97 // Loads the state for toggle keys into the event.
98 static void SetToggleKeyState(WebInputEvent* event) {
99 // Low bit set from GetKeyState indicates "toggled".
100 if (::GetKeyState(VK_NUMLOCK) & 1)
101 event->modifiers |= WebInputEvent::NumLockOn;
102 if (::GetKeyState(VK_CAPITAL) & 1)
103 event->modifiers |= WebInputEvent::CapsLockOn;
106 WebKeyboardEvent WebKeyboardEventBuilder::Build(HWND hwnd,
107 UINT message,
108 WPARAM wparam,
109 LPARAM lparam,
110 DWORD time_ms) {
111 WebKeyboardEvent result;
113 DCHECK(time_ms);
114 result.timeStampSeconds = time_ms / 1000.0;
116 result.windowsKeyCode = static_cast<int>(wparam);
117 // Record the scan code (along with other context bits) for this key event.
118 result.nativeKeyCode = static_cast<int>(lparam);
120 switch (message) {
121 case WM_SYSKEYDOWN:
122 result.isSystemKey = true;
123 case WM_KEYDOWN:
124 result.type = WebInputEvent::RawKeyDown;
125 break;
126 case WM_SYSKEYUP:
127 result.isSystemKey = true;
128 case WM_KEYUP:
129 result.type = WebInputEvent::KeyUp;
130 break;
131 case WM_IME_CHAR:
132 result.type = WebInputEvent::Char;
133 break;
134 case WM_SYSCHAR:
135 result.isSystemKey = true;
136 result.type = WebInputEvent::Char;
137 case WM_CHAR:
138 result.type = WebInputEvent::Char;
139 break;
140 default:
141 NOTREACHED();
144 if (result.type == WebInputEvent::Char
145 || result.type == WebInputEvent::RawKeyDown) {
146 result.text[0] = result.windowsKeyCode;
147 result.unmodifiedText[0] = result.windowsKeyCode;
149 if (result.type != WebInputEvent::Char) {
150 UpdateWindowsKeyCodeAndKeyIdentifier(
151 &result,
152 static_cast<ui::KeyboardCode>(result.windowsKeyCode));
155 if (::GetKeyState(VK_SHIFT) & 0x8000)
156 result.modifiers |= WebInputEvent::ShiftKey;
157 if (::GetKeyState(VK_CONTROL) & 0x8000)
158 result.modifiers |= WebInputEvent::ControlKey;
159 if (::GetKeyState(VK_MENU) & 0x8000)
160 result.modifiers |= WebInputEvent::AltKey;
161 // NOTE: There doesn't seem to be a way to query the mouse button state in
162 // this case.
164 // Bit 30 of lParam represents the "previous key state". If set, the key was
165 // already down, therefore this is an auto-repeat.
166 if (lparam & 0x40000000)
167 result.modifiers |= WebInputEvent::IsAutoRepeat;
169 result.modifiers |= GetLocationModifier(wparam, lparam);
171 SetToggleKeyState(&result);
172 return result;
175 // WebMouseEvent --------------------------------------------------------------
177 static int g_last_click_count = 0;
178 static double g_last_click_time = 0;
180 static LPARAM GetRelativeCursorPos(HWND hwnd) {
181 POINT pos = {-1, -1};
182 GetCursorPos(&pos);
183 ScreenToClient(hwnd, &pos);
184 return MAKELPARAM(pos.x, pos.y);
187 WebMouseEvent WebMouseEventBuilder::Build(HWND hwnd,
188 UINT message,
189 WPARAM wparam,
190 LPARAM lparam,
191 DWORD time_ms) {
192 WebMouseEvent result;
194 switch (message) {
195 case WM_MOUSEMOVE:
196 result.type = WebInputEvent::MouseMove;
197 if (wparam & MK_LBUTTON)
198 result.button = WebMouseEvent::ButtonLeft;
199 else if (wparam & MK_MBUTTON)
200 result.button = WebMouseEvent::ButtonMiddle;
201 else if (wparam & MK_RBUTTON)
202 result.button = WebMouseEvent::ButtonRight;
203 else
204 result.button = WebMouseEvent::ButtonNone;
205 break;
206 case WM_MOUSELEAVE:
207 result.type = WebInputEvent::MouseLeave;
208 result.button = WebMouseEvent::ButtonNone;
209 // set the current mouse position (relative to the client area of the
210 // current window) since none is specified for this event
211 lparam = GetRelativeCursorPos(hwnd);
212 break;
213 case WM_LBUTTONDOWN:
214 case WM_LBUTTONDBLCLK:
215 result.type = WebInputEvent::MouseDown;
216 result.button = WebMouseEvent::ButtonLeft;
217 break;
218 case WM_MBUTTONDOWN:
219 case WM_MBUTTONDBLCLK:
220 result.type = WebInputEvent::MouseDown;
221 result.button = WebMouseEvent::ButtonMiddle;
222 break;
223 case WM_RBUTTONDOWN:
224 case WM_RBUTTONDBLCLK:
225 result.type = WebInputEvent::MouseDown;
226 result.button = WebMouseEvent::ButtonRight;
227 break;
228 case WM_LBUTTONUP:
229 result.type = WebInputEvent::MouseUp;
230 result.button = WebMouseEvent::ButtonLeft;
231 break;
232 case WM_MBUTTONUP:
233 result.type = WebInputEvent::MouseUp;
234 result.button = WebMouseEvent::ButtonMiddle;
235 break;
236 case WM_RBUTTONUP:
237 result.type = WebInputEvent::MouseUp;
238 result.button = WebMouseEvent::ButtonRight;
239 break;
240 default:
241 NOTREACHED();
244 DCHECK(time_ms);
245 result.timeStampSeconds = time_ms / 1000.0;
247 // set position fields:
249 result.x = static_cast<short>(LOWORD(lparam));
250 result.y = static_cast<short>(HIWORD(lparam));
251 result.windowX = result.x;
252 result.windowY = result.y;
254 POINT global_point = { result.x, result.y };
255 ClientToScreen(hwnd, &global_point);
257 // We need to convert the global point back to DIP before using it.
258 gfx::Point dip_global_point = gfx::win::ScreenToDIPPoint(
259 gfx::Point(global_point.x, global_point.y));
261 result.globalX = dip_global_point.x();
262 result.globalY = dip_global_point.y();
264 // calculate number of clicks:
266 // This differs slightly from the WebKit code in WebKit/win/WebView.cpp
267 // where their original code looks buggy.
268 static int last_click_position_x;
269 static int last_click_position_y;
270 static WebMouseEvent::Button last_click_button = WebMouseEvent::ButtonLeft;
272 double current_time = result.timeStampSeconds;
273 bool cancel_previous_click =
274 (abs(last_click_position_x - result.x) >
275 (::GetSystemMetrics(SM_CXDOUBLECLK) / 2))
276 || (abs(last_click_position_y - result.y) >
277 (::GetSystemMetrics(SM_CYDOUBLECLK) / 2))
278 || ((current_time - g_last_click_time) * 1000.0 > ::GetDoubleClickTime());
280 if (result.type == WebInputEvent::MouseDown) {
281 if (!cancel_previous_click && (result.button == last_click_button)) {
282 ++g_last_click_count;
283 } else {
284 g_last_click_count = 1;
285 last_click_position_x = result.x;
286 last_click_position_y = result.y;
288 g_last_click_time = current_time;
289 last_click_button = result.button;
290 } else if (result.type == WebInputEvent::MouseMove
291 || result.type == WebInputEvent::MouseLeave) {
292 if (cancel_previous_click) {
293 g_last_click_count = 0;
294 last_click_position_x = 0;
295 last_click_position_y = 0;
296 g_last_click_time = 0;
299 result.clickCount = g_last_click_count;
301 // set modifiers:
303 if (wparam & MK_CONTROL)
304 result.modifiers |= WebInputEvent::ControlKey;
305 if (wparam & MK_SHIFT)
306 result.modifiers |= WebInputEvent::ShiftKey;
307 if (::GetKeyState(VK_MENU) & 0x8000)
308 result.modifiers |= WebInputEvent::AltKey;
309 if (wparam & MK_LBUTTON)
310 result.modifiers |= WebInputEvent::LeftButtonDown;
311 if (wparam & MK_MBUTTON)
312 result.modifiers |= WebInputEvent::MiddleButtonDown;
313 if (wparam & MK_RBUTTON)
314 result.modifiers |= WebInputEvent::RightButtonDown;
316 SetToggleKeyState(&result);
317 return result;
320 // WebMouseWheelEvent ---------------------------------------------------------
322 WebMouseWheelEvent WebMouseWheelEventBuilder::Build(HWND hwnd,
323 UINT message,
324 WPARAM wparam,
325 LPARAM lparam,
326 DWORD time_ms) {
327 WebMouseWheelEvent result;
329 result.type = WebInputEvent::MouseWheel;
331 DCHECK(time_ms);
332 result.timeStampSeconds = time_ms / 1000.0;
334 result.button = WebMouseEvent::ButtonNone;
336 // Get key state, coordinates, and wheel delta from event.
337 typedef SHORT (WINAPI *GetKeyStateFunction)(int key);
338 GetKeyStateFunction get_key_state_func;
339 UINT key_state;
340 float wheel_delta;
341 bool horizontal_scroll = false;
342 if ((message == WM_VSCROLL) || (message == WM_HSCROLL)) {
343 // Synthesize mousewheel event from a scroll event. This is needed to
344 // simulate middle mouse scrolling in some laptops. Use GetAsyncKeyState
345 // for key state since we are synthesizing the input event.
346 get_key_state_func = GetAsyncKeyState;
347 key_state = 0;
348 if (get_key_state_func(VK_SHIFT) & 0x8000)
349 key_state |= MK_SHIFT;
350 if (get_key_state_func(VK_CONTROL) & 0x8000)
351 key_state |= MK_CONTROL;
352 // NOTE: There doesn't seem to be a way to query the mouse button state
353 // in this case.
355 POINT cursor_position = {0};
356 GetCursorPos(&cursor_position);
357 result.globalX = cursor_position.x;
358 result.globalY = cursor_position.y;
360 switch (LOWORD(wparam)) {
361 case SB_LINEUP: // == SB_LINELEFT
362 wheel_delta = WHEEL_DELTA;
363 break;
364 case SB_LINEDOWN: // == SB_LINERIGHT
365 wheel_delta = -WHEEL_DELTA;
366 break;
367 case SB_PAGEUP:
368 wheel_delta = 1;
369 result.scrollByPage = true;
370 break;
371 case SB_PAGEDOWN:
372 wheel_delta = -1;
373 result.scrollByPage = true;
374 break;
375 default: // We don't supoprt SB_THUMBPOSITION or SB_THUMBTRACK here.
376 wheel_delta = 0;
377 break;
380 if (message == WM_HSCROLL)
381 horizontal_scroll = true;
382 } else {
383 // Non-synthesized event; we can just read data off the event.
384 get_key_state_func = ::GetKeyState;
385 key_state = GET_KEYSTATE_WPARAM(wparam);
387 result.globalX = static_cast<short>(LOWORD(lparam));
388 result.globalY = static_cast<short>(HIWORD(lparam));
390 wheel_delta = static_cast<float>(GET_WHEEL_DELTA_WPARAM(wparam));
391 if (message == WM_MOUSEHWHEEL) {
392 horizontal_scroll = true;
393 wheel_delta = -wheel_delta; // Windows is <- -/+ ->, WebKit <- +/- ->.
396 if (key_state & MK_SHIFT)
397 horizontal_scroll = true;
399 // Set modifiers based on key state.
400 if (key_state & MK_SHIFT)
401 result.modifiers |= WebInputEvent::ShiftKey;
402 if (key_state & MK_CONTROL)
403 result.modifiers |= WebInputEvent::ControlKey;
404 if (get_key_state_func(VK_MENU) & 0x8000)
405 result.modifiers |= WebInputEvent::AltKey;
406 if (key_state & MK_LBUTTON)
407 result.modifiers |= WebInputEvent::LeftButtonDown;
408 if (key_state & MK_MBUTTON)
409 result.modifiers |= WebInputEvent::MiddleButtonDown;
410 if (key_state & MK_RBUTTON)
411 result.modifiers |= WebInputEvent::RightButtonDown;
413 SetToggleKeyState(&result);
415 // Set coordinates by translating event coordinates from screen to client.
416 POINT client_point = { result.globalX, result.globalY };
417 MapWindowPoints(0, hwnd, &client_point, 1);
418 result.x = client_point.x;
419 result.y = client_point.y;
420 result.windowX = result.x;
421 result.windowY = result.y;
423 // Convert wheel delta amount to a number of pixels to scroll.
425 // How many pixels should we scroll per line? Gecko uses the height of the
426 // current line, which means scroll distance changes as you go through the
427 // page or go to different pages. IE 8 is ~60 px/line, although the value
428 // seems to vary slightly by page and zoom level. Also, IE defaults to
429 // smooth scrolling while Firefox doesn't, so it can get away with somewhat
430 // larger scroll values without feeling as jerky. Here we use 100 px per
431 // three lines (the default scroll amount is three lines per wheel tick).
432 // Even though we have smooth scrolling, we don't make this as large as IE
433 // because subjectively IE feels like it scrolls farther than you want while
434 // reading articles.
435 static const float kScrollbarPixelsPerLine = 100.0f / 3.0f;
436 wheel_delta /= WHEEL_DELTA;
437 float scroll_delta = wheel_delta;
438 if (horizontal_scroll) {
439 unsigned long scroll_chars = kDefaultScrollCharsPerWheelDelta;
440 SystemParametersInfo(SPI_GETWHEELSCROLLCHARS, 0, &scroll_chars, 0);
441 // TODO(pkasting): Should probably have a different multiplier
442 // scrollbarPixelsPerChar here.
443 scroll_delta *= static_cast<float>(scroll_chars) * kScrollbarPixelsPerLine;
444 } else {
445 unsigned long scroll_lines = kDefaultScrollLinesPerWheelDelta;
446 SystemParametersInfo(SPI_GETWHEELSCROLLLINES, 0, &scroll_lines, 0);
447 if (scroll_lines == WHEEL_PAGESCROLL)
448 result.scrollByPage = true;
449 if (!result.scrollByPage) {
450 scroll_delta *=
451 static_cast<float>(scroll_lines) * kScrollbarPixelsPerLine;
455 // Set scroll amount based on above calculations. WebKit expects positive
456 // deltaY to mean "scroll up" and positive deltaX to mean "scroll left".
457 if (horizontal_scroll) {
458 result.deltaX = scroll_delta;
459 result.wheelTicksX = wheel_delta;
460 } else {
461 result.deltaY = scroll_delta;
462 result.wheelTicksY = wheel_delta;
465 return result;
468 } // namespace content