Add ICU message format support
[chromium-blink-merge.git] / content / browser / renderer_host / input / web_input_event_builders_win.cc
blobf3eec13ee9c8730c3a07d651ddfb308d2581d91c
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 // Loads the state for toggle keys into the event.
22 static void SetToggleKeyState(WebInputEvent* event) {
23 // Low bit set from GetKeyState indicates "toggled".
24 if (::GetKeyState(VK_NUMLOCK) & 1)
25 event->modifiers |= WebInputEvent::NumLockOn;
26 if (::GetKeyState(VK_CAPITAL) & 1)
27 event->modifiers |= WebInputEvent::CapsLockOn;
30 WebKeyboardEvent WebKeyboardEventBuilder::Build(HWND hwnd,
31 UINT message,
32 WPARAM wparam,
33 LPARAM lparam,
34 DWORD time_ms) {
35 WebKeyboardEvent result;
37 result.timeStampSeconds = time_ms / 1000.0;
39 result.windowsKeyCode = static_cast<int>(wparam);
40 // Record the scan code (along with other context bits) for this key event.
41 result.nativeKeyCode = static_cast<int>(lparam);
43 switch (message) {
44 case WM_SYSKEYDOWN:
45 result.isSystemKey = true;
46 case WM_KEYDOWN:
47 result.type = WebInputEvent::RawKeyDown;
48 break;
49 case WM_SYSKEYUP:
50 result.isSystemKey = true;
51 case WM_KEYUP:
52 result.type = WebInputEvent::KeyUp;
53 break;
54 case WM_IME_CHAR:
55 result.type = WebInputEvent::Char;
56 break;
57 case WM_SYSCHAR:
58 result.isSystemKey = true;
59 result.type = WebInputEvent::Char;
60 case WM_CHAR:
61 result.type = WebInputEvent::Char;
62 break;
63 default:
64 NOTREACHED();
67 if (result.type == WebInputEvent::Char
68 || result.type == WebInputEvent::RawKeyDown) {
69 result.text[0] = result.windowsKeyCode;
70 result.unmodifiedText[0] = result.windowsKeyCode;
72 if (result.type != WebInputEvent::Char) {
73 UpdateWindowsKeyCodeAndKeyIdentifier(
74 &result,
75 static_cast<ui::KeyboardCode>(result.windowsKeyCode));
78 if (::GetKeyState(VK_SHIFT) & 0x8000)
79 result.modifiers |= WebInputEvent::ShiftKey;
80 if (::GetKeyState(VK_CONTROL) & 0x8000)
81 result.modifiers |= WebInputEvent::ControlKey;
82 if (::GetKeyState(VK_MENU) & 0x8000)
83 result.modifiers |= WebInputEvent::AltKey;
84 // NOTE: There doesn't seem to be a way to query the mouse button state in
85 // this case.
87 // Bit 30 of lParam represents the "previous key state". If set, the key was
88 // already down, therefore this is an auto-repeat. Only apply this to key
89 // down events, to match DOM semantics.
90 if ((result.type == WebInputEvent::RawKeyDown) && (lparam & 0x40000000))
91 result.modifiers |= WebInputEvent::IsAutoRepeat;
93 SetToggleKeyState(&result);
94 return result;
97 // WebMouseEvent --------------------------------------------------------------
99 static int g_last_click_count = 0;
100 static double g_last_click_time = 0;
102 static LPARAM GetRelativeCursorPos(HWND hwnd) {
103 POINT pos = {-1, -1};
104 GetCursorPos(&pos);
105 ScreenToClient(hwnd, &pos);
106 return MAKELPARAM(pos.x, pos.y);
109 WebMouseEvent WebMouseEventBuilder::Build(HWND hwnd,
110 UINT message,
111 WPARAM wparam,
112 LPARAM lparam,
113 DWORD time_ms) {
114 WebMouseEvent result;
116 switch (message) {
117 case WM_MOUSEMOVE:
118 result.type = WebInputEvent::MouseMove;
119 if (wparam & MK_LBUTTON)
120 result.button = WebMouseEvent::ButtonLeft;
121 else if (wparam & MK_MBUTTON)
122 result.button = WebMouseEvent::ButtonMiddle;
123 else if (wparam & MK_RBUTTON)
124 result.button = WebMouseEvent::ButtonRight;
125 else
126 result.button = WebMouseEvent::ButtonNone;
127 break;
128 case WM_MOUSELEAVE:
129 // TODO(rbyers): This should be MouseLeave but is disabled temporarily.
130 // See http://crbug.com/450631
131 result.type = WebInputEvent::MouseMove;
132 result.button = WebMouseEvent::ButtonNone;
133 // set the current mouse position (relative to the client area of the
134 // current window) since none is specified for this event
135 lparam = GetRelativeCursorPos(hwnd);
136 break;
137 case WM_LBUTTONDOWN:
138 case WM_LBUTTONDBLCLK:
139 result.type = WebInputEvent::MouseDown;
140 result.button = WebMouseEvent::ButtonLeft;
141 break;
142 case WM_MBUTTONDOWN:
143 case WM_MBUTTONDBLCLK:
144 result.type = WebInputEvent::MouseDown;
145 result.button = WebMouseEvent::ButtonMiddle;
146 break;
147 case WM_RBUTTONDOWN:
148 case WM_RBUTTONDBLCLK:
149 result.type = WebInputEvent::MouseDown;
150 result.button = WebMouseEvent::ButtonRight;
151 break;
152 case WM_LBUTTONUP:
153 result.type = WebInputEvent::MouseUp;
154 result.button = WebMouseEvent::ButtonLeft;
155 break;
156 case WM_MBUTTONUP:
157 result.type = WebInputEvent::MouseUp;
158 result.button = WebMouseEvent::ButtonMiddle;
159 break;
160 case WM_RBUTTONUP:
161 result.type = WebInputEvent::MouseUp;
162 result.button = WebMouseEvent::ButtonRight;
163 break;
164 default:
165 NOTREACHED();
168 result.timeStampSeconds = time_ms / 1000.0;
170 // set position fields:
172 result.x = static_cast<short>(LOWORD(lparam));
173 result.y = static_cast<short>(HIWORD(lparam));
174 result.windowX = result.x;
175 result.windowY = result.y;
177 POINT global_point = { result.x, result.y };
178 ClientToScreen(hwnd, &global_point);
180 // We need to convert the global point back to DIP before using it.
181 gfx::Point dip_global_point = gfx::win::ScreenToDIPPoint(
182 gfx::Point(global_point.x, global_point.y));
184 result.globalX = dip_global_point.x();
185 result.globalY = dip_global_point.y();
187 // calculate number of clicks:
189 // This differs slightly from the WebKit code in WebKit/win/WebView.cpp
190 // where their original code looks buggy.
191 static int last_click_position_x;
192 static int last_click_position_y;
193 static WebMouseEvent::Button last_click_button = WebMouseEvent::ButtonLeft;
195 double current_time = result.timeStampSeconds;
196 bool cancel_previous_click =
197 (abs(last_click_position_x - result.x) >
198 (::GetSystemMetrics(SM_CXDOUBLECLK) / 2))
199 || (abs(last_click_position_y - result.y) >
200 (::GetSystemMetrics(SM_CYDOUBLECLK) / 2))
201 || ((current_time - g_last_click_time) * 1000.0 > ::GetDoubleClickTime());
203 if (result.type == WebInputEvent::MouseDown) {
204 if (!cancel_previous_click && (result.button == last_click_button)) {
205 ++g_last_click_count;
206 } else {
207 g_last_click_count = 1;
208 last_click_position_x = result.x;
209 last_click_position_y = result.y;
211 g_last_click_time = current_time;
212 last_click_button = result.button;
213 } else if (result.type == WebInputEvent::MouseMove
214 || result.type == WebInputEvent::MouseLeave) {
215 if (cancel_previous_click) {
216 g_last_click_count = 0;
217 last_click_position_x = 0;
218 last_click_position_y = 0;
219 g_last_click_time = 0;
222 result.clickCount = g_last_click_count;
224 // set modifiers:
226 if (wparam & MK_CONTROL)
227 result.modifiers |= WebInputEvent::ControlKey;
228 if (wparam & MK_SHIFT)
229 result.modifiers |= WebInputEvent::ShiftKey;
230 if (::GetKeyState(VK_MENU) & 0x8000)
231 result.modifiers |= WebInputEvent::AltKey;
232 if (wparam & MK_LBUTTON)
233 result.modifiers |= WebInputEvent::LeftButtonDown;
234 if (wparam & MK_MBUTTON)
235 result.modifiers |= WebInputEvent::MiddleButtonDown;
236 if (wparam & MK_RBUTTON)
237 result.modifiers |= WebInputEvent::RightButtonDown;
239 SetToggleKeyState(&result);
240 return result;
243 // WebMouseWheelEvent ---------------------------------------------------------
245 WebMouseWheelEvent WebMouseWheelEventBuilder::Build(HWND hwnd,
246 UINT message,
247 WPARAM wparam,
248 LPARAM lparam,
249 DWORD time_ms) {
250 WebMouseWheelEvent result;
252 result.type = WebInputEvent::MouseWheel;
254 result.timeStampSeconds = time_ms / 1000.0;
256 result.button = WebMouseEvent::ButtonNone;
258 // Get key state, coordinates, and wheel delta from event.
259 typedef SHORT (WINAPI *GetKeyStateFunction)(int key);
260 GetKeyStateFunction get_key_state_func;
261 UINT key_state;
262 float wheel_delta;
263 bool horizontal_scroll = false;
264 if ((message == WM_VSCROLL) || (message == WM_HSCROLL)) {
265 // Synthesize mousewheel event from a scroll event. This is needed to
266 // simulate middle mouse scrolling in some laptops. Use GetAsyncKeyState
267 // for key state since we are synthesizing the input event.
268 get_key_state_func = GetAsyncKeyState;
269 key_state = 0;
270 if (get_key_state_func(VK_SHIFT) & 0x8000)
271 key_state |= MK_SHIFT;
272 if (get_key_state_func(VK_CONTROL) & 0x8000)
273 key_state |= MK_CONTROL;
274 // NOTE: There doesn't seem to be a way to query the mouse button state
275 // in this case.
277 POINT cursor_position = {0};
278 GetCursorPos(&cursor_position);
279 result.globalX = cursor_position.x;
280 result.globalY = cursor_position.y;
282 switch (LOWORD(wparam)) {
283 case SB_LINEUP: // == SB_LINELEFT
284 wheel_delta = WHEEL_DELTA;
285 break;
286 case SB_LINEDOWN: // == SB_LINERIGHT
287 wheel_delta = -WHEEL_DELTA;
288 break;
289 case SB_PAGEUP:
290 wheel_delta = 1;
291 result.scrollByPage = true;
292 break;
293 case SB_PAGEDOWN:
294 wheel_delta = -1;
295 result.scrollByPage = true;
296 break;
297 default: // We don't supoprt SB_THUMBPOSITION or SB_THUMBTRACK here.
298 wheel_delta = 0;
299 break;
302 if (message == WM_HSCROLL)
303 horizontal_scroll = true;
304 } else {
305 // Non-synthesized event; we can just read data off the event.
306 get_key_state_func = ::GetKeyState;
307 key_state = GET_KEYSTATE_WPARAM(wparam);
309 result.globalX = static_cast<short>(LOWORD(lparam));
310 result.globalY = static_cast<short>(HIWORD(lparam));
312 wheel_delta = static_cast<float>(GET_WHEEL_DELTA_WPARAM(wparam));
313 if (message == WM_MOUSEHWHEEL) {
314 horizontal_scroll = true;
315 wheel_delta = -wheel_delta; // Windows is <- -/+ ->, WebKit <- +/- ->.
318 if (key_state & MK_SHIFT)
319 horizontal_scroll = true;
321 // Set modifiers based on key state.
322 if (key_state & MK_SHIFT)
323 result.modifiers |= WebInputEvent::ShiftKey;
324 if (key_state & MK_CONTROL)
325 result.modifiers |= WebInputEvent::ControlKey;
326 if (get_key_state_func(VK_MENU) & 0x8000)
327 result.modifiers |= WebInputEvent::AltKey;
328 if (key_state & MK_LBUTTON)
329 result.modifiers |= WebInputEvent::LeftButtonDown;
330 if (key_state & MK_MBUTTON)
331 result.modifiers |= WebInputEvent::MiddleButtonDown;
332 if (key_state & MK_RBUTTON)
333 result.modifiers |= WebInputEvent::RightButtonDown;
335 SetToggleKeyState(&result);
337 // Set coordinates by translating event coordinates from screen to client.
338 POINT client_point = { result.globalX, result.globalY };
339 MapWindowPoints(0, hwnd, &client_point, 1);
340 result.x = client_point.x;
341 result.y = client_point.y;
342 result.windowX = result.x;
343 result.windowY = result.y;
345 // Convert wheel delta amount to a number of pixels to scroll.
347 // How many pixels should we scroll per line? Gecko uses the height of the
348 // current line, which means scroll distance changes as you go through the
349 // page or go to different pages. IE 8 is ~60 px/line, although the value
350 // seems to vary slightly by page and zoom level. Also, IE defaults to
351 // smooth scrolling while Firefox doesn't, so it can get away with somewhat
352 // larger scroll values without feeling as jerky. Here we use 100 px per
353 // three lines (the default scroll amount is three lines per wheel tick).
354 // Even though we have smooth scrolling, we don't make this as large as IE
355 // because subjectively IE feels like it scrolls farther than you want while
356 // reading articles.
357 static const float kScrollbarPixelsPerLine = 100.0f / 3.0f;
358 wheel_delta /= WHEEL_DELTA;
359 float scroll_delta = wheel_delta;
360 if (horizontal_scroll) {
361 unsigned long scroll_chars = kDefaultScrollCharsPerWheelDelta;
362 SystemParametersInfo(SPI_GETWHEELSCROLLCHARS, 0, &scroll_chars, 0);
363 // TODO(pkasting): Should probably have a different multiplier
364 // scrollbarPixelsPerChar here.
365 scroll_delta *= static_cast<float>(scroll_chars) * kScrollbarPixelsPerLine;
366 } else {
367 unsigned long scroll_lines = kDefaultScrollLinesPerWheelDelta;
368 SystemParametersInfo(SPI_GETWHEELSCROLLLINES, 0, &scroll_lines, 0);
369 if (scroll_lines == WHEEL_PAGESCROLL)
370 result.scrollByPage = true;
371 if (!result.scrollByPage) {
372 scroll_delta *=
373 static_cast<float>(scroll_lines) * kScrollbarPixelsPerLine;
377 // Set scroll amount based on above calculations. WebKit expects positive
378 // deltaY to mean "scroll up" and positive deltaX to mean "scroll left".
379 if (horizontal_scroll) {
380 result.deltaX = scroll_delta;
381 result.wheelTicksX = wheel_delta;
382 } else {
383 result.deltaY = scroll_delta;
384 result.wheelTicksY = wheel_delta;
387 return result;
390 } // namespace content