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
;
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
) {
29 if ((lparam
>> 16) & KF_EXTENDED
)
30 modifier
= WebInputEvent::IsKeyPad
;
42 if (!((lparam
>> 16) & KF_EXTENDED
))
43 modifier
= WebInputEvent::IsKeyPad
;
62 modifier
= WebInputEvent::IsKeyPad
;
65 if (IsKeyDown(VK_LSHIFT
))
66 modifier
= WebInputEvent::IsLeft
;
67 else if (IsKeyDown(VK_RSHIFT
))
68 modifier
= WebInputEvent::IsRight
;
71 if (IsKeyDown(VK_LCONTROL
))
72 modifier
= WebInputEvent::IsLeft
;
73 else if (IsKeyDown(VK_RCONTROL
))
74 modifier
= WebInputEvent::IsRight
;
77 if (IsKeyDown(VK_LMENU
))
78 modifier
= WebInputEvent::IsLeft
;
79 else if (IsKeyDown(VK_RMENU
))
80 modifier
= WebInputEvent::IsRight
;
83 modifier
= WebInputEvent::IsLeft
;
86 modifier
= WebInputEvent::IsRight
;
91 || modifier
== WebInputEvent::IsKeyPad
92 || modifier
== WebInputEvent::IsLeft
93 || modifier
== WebInputEvent::IsRight
);
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
,
111 WebKeyboardEvent result
;
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
);
122 result
.isSystemKey
= true;
124 result
.type
= WebInputEvent::RawKeyDown
;
127 result
.isSystemKey
= true;
129 result
.type
= WebInputEvent::KeyUp
;
132 result
.type
= WebInputEvent::Char
;
135 result
.isSystemKey
= true;
136 result
.type
= WebInputEvent::Char
;
138 result
.type
= WebInputEvent::Char
;
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(
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
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
);
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};
183 ScreenToClient(hwnd
, &pos
);
184 return MAKELPARAM(pos
.x
, pos
.y
);
187 WebMouseEvent
WebMouseEventBuilder::Build(HWND hwnd
,
192 WebMouseEvent result
;
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
;
204 result
.button
= WebMouseEvent::ButtonNone
;
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
);
214 case WM_LBUTTONDBLCLK
:
215 result
.type
= WebInputEvent::MouseDown
;
216 result
.button
= WebMouseEvent::ButtonLeft
;
219 case WM_MBUTTONDBLCLK
:
220 result
.type
= WebInputEvent::MouseDown
;
221 result
.button
= WebMouseEvent::ButtonMiddle
;
224 case WM_RBUTTONDBLCLK
:
225 result
.type
= WebInputEvent::MouseDown
;
226 result
.button
= WebMouseEvent::ButtonRight
;
229 result
.type
= WebInputEvent::MouseUp
;
230 result
.button
= WebMouseEvent::ButtonLeft
;
233 result
.type
= WebInputEvent::MouseUp
;
234 result
.button
= WebMouseEvent::ButtonMiddle
;
237 result
.type
= WebInputEvent::MouseUp
;
238 result
.button
= WebMouseEvent::ButtonRight
;
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
;
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
;
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
);
320 // WebMouseWheelEvent ---------------------------------------------------------
322 WebMouseWheelEvent
WebMouseWheelEventBuilder::Build(HWND hwnd
,
327 WebMouseWheelEvent result
;
329 result
.type
= WebInputEvent::MouseWheel
;
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
;
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
;
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
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
;
364 case SB_LINEDOWN
: // == SB_LINERIGHT
365 wheel_delta
= -WHEEL_DELTA
;
369 result
.scrollByPage
= true;
373 result
.scrollByPage
= true;
375 default: // We don't supoprt SB_THUMBPOSITION or SB_THUMBTRACK here.
380 if (message
== WM_HSCROLL
)
381 horizontal_scroll
= true;
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
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
;
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
) {
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
;
461 result
.deltaY
= scroll_delta
;
462 result
.wheelTicksY
= wheel_delta
;
468 } // namespace content