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