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
;
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
) {
28 if ((lparam
>> 16) & KF_EXTENDED
)
29 modifier
= WebInputEvent::IsKeyPad
;
41 if (!((lparam
>> 16) & KF_EXTENDED
))
42 modifier
= WebInputEvent::IsKeyPad
;
61 modifier
= WebInputEvent::IsKeyPad
;
64 if (IsKeyDown(VK_LSHIFT
))
65 modifier
= WebInputEvent::IsLeft
;
66 else if (IsKeyDown(VK_RSHIFT
))
67 modifier
= WebInputEvent::IsRight
;
70 if (IsKeyDown(VK_LCONTROL
))
71 modifier
= WebInputEvent::IsLeft
;
72 else if (IsKeyDown(VK_RCONTROL
))
73 modifier
= WebInputEvent::IsRight
;
76 if (IsKeyDown(VK_LMENU
))
77 modifier
= WebInputEvent::IsLeft
;
78 else if (IsKeyDown(VK_RMENU
))
79 modifier
= WebInputEvent::IsRight
;
82 modifier
= WebInputEvent::IsLeft
;
85 modifier
= WebInputEvent::IsRight
;
90 || modifier
== WebInputEvent::IsKeyPad
91 || modifier
== WebInputEvent::IsLeft
92 || modifier
== WebInputEvent::IsRight
);
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
, UINT message
,
106 WPARAM wparam
, LPARAM lparam
) {
107 WebKeyboardEvent result
;
109 // TODO(pkasting): http://b/1117926 Are we guaranteed that the message that
110 // GetMessageTime() refers to is the same one that we're passed in? Perhaps
111 // one of the construction parameters should be the time passed by the
112 // caller, who would know for sure.
113 result
.timeStampSeconds
= ::GetMessageTime() / 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
);
121 result
.isSystemKey
= true;
123 result
.type
= WebInputEvent::RawKeyDown
;
126 result
.isSystemKey
= true;
128 result
.type
= WebInputEvent::KeyUp
;
131 result
.type
= WebInputEvent::Char
;
134 result
.isSystemKey
= true;
135 result
.type
= WebInputEvent::Char
;
137 result
.type
= WebInputEvent::Char
;
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(
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
163 if (LOWORD(lparam
) > 1)
164 result
.modifiers
|= WebInputEvent::IsAutoRepeat
;
166 result
.modifiers
|= GetLocationModifier(wparam
, lparam
);
168 SetToggleKeyState(&result
);
172 // WebMouseEvent --------------------------------------------------------------
174 static int g_last_click_count
= 0;
175 static double g_last_click_time
= 0;
177 static LPARAM
GetRelativeCursorPos(HWND hwnd
) {
178 POINT pos
= {-1, -1};
180 ScreenToClient(hwnd
, &pos
);
181 return MAKELPARAM(pos
.x
, pos
.y
);
184 WebMouseEvent
WebMouseEventBuilder::Build(HWND hwnd
, UINT message
,
185 WPARAM wparam
, LPARAM lparam
) {
186 WebMouseEvent result
;
190 result
.type
= WebInputEvent::MouseMove
;
191 if (wparam
& MK_LBUTTON
)
192 result
.button
= WebMouseEvent::ButtonLeft
;
193 else if (wparam
& MK_MBUTTON
)
194 result
.button
= WebMouseEvent::ButtonMiddle
;
195 else if (wparam
& MK_RBUTTON
)
196 result
.button
= WebMouseEvent::ButtonRight
;
198 result
.button
= WebMouseEvent::ButtonNone
;
201 result
.type
= WebInputEvent::MouseLeave
;
202 result
.button
= WebMouseEvent::ButtonNone
;
203 // set the current mouse position (relative to the client area of the
204 // current window) since none is specified for this event
205 lparam
= GetRelativeCursorPos(hwnd
);
208 case WM_LBUTTONDBLCLK
:
209 result
.type
= WebInputEvent::MouseDown
;
210 result
.button
= WebMouseEvent::ButtonLeft
;
213 case WM_MBUTTONDBLCLK
:
214 result
.type
= WebInputEvent::MouseDown
;
215 result
.button
= WebMouseEvent::ButtonMiddle
;
218 case WM_RBUTTONDBLCLK
:
219 result
.type
= WebInputEvent::MouseDown
;
220 result
.button
= WebMouseEvent::ButtonRight
;
223 result
.type
= WebInputEvent::MouseUp
;
224 result
.button
= WebMouseEvent::ButtonLeft
;
227 result
.type
= WebInputEvent::MouseUp
;
228 result
.button
= WebMouseEvent::ButtonMiddle
;
231 result
.type
= WebInputEvent::MouseUp
;
232 result
.button
= WebMouseEvent::ButtonRight
;
238 // TODO(pkasting): http://b/1117926 Are we guaranteed that the message that
239 // GetMessageTime() refers to is the same one that we're passed in? Perhaps
240 // one of the construction parameters should be the time passed by the
241 // caller, who would know for sure.
242 result
.timeStampSeconds
= ::GetMessageTime() / 1000.0;
244 // set position fields:
246 result
.x
= static_cast<short>(LOWORD(lparam
));
247 result
.y
= static_cast<short>(HIWORD(lparam
));
248 result
.windowX
= result
.x
;
249 result
.windowY
= result
.y
;
251 POINT global_point
= { result
.x
, result
.y
};
252 ClientToScreen(hwnd
, &global_point
);
254 result
.globalX
= global_point
.x
;
255 result
.globalY
= global_point
.y
;
257 // calculate number of clicks:
259 // This differs slightly from the WebKit code in WebKit/win/WebView.cpp
260 // where their original code looks buggy.
261 static int last_click_position_x
;
262 static int last_click_position_y
;
263 static WebMouseEvent::Button last_click_button
= WebMouseEvent::ButtonLeft
;
265 double current_time
= result
.timeStampSeconds
;
266 bool cancel_previous_click
=
267 (abs(last_click_position_x
- result
.x
) >
268 (::GetSystemMetrics(SM_CXDOUBLECLK
) / 2))
269 || (abs(last_click_position_y
- result
.y
) >
270 (::GetSystemMetrics(SM_CYDOUBLECLK
) / 2))
271 || ((current_time
- g_last_click_time
) * 1000.0 > ::GetDoubleClickTime());
273 if (result
.type
== WebInputEvent::MouseDown
) {
274 if (!cancel_previous_click
&& (result
.button
== last_click_button
)) {
275 ++g_last_click_count
;
277 g_last_click_count
= 1;
278 last_click_position_x
= result
.x
;
279 last_click_position_y
= result
.y
;
281 g_last_click_time
= current_time
;
282 last_click_button
= result
.button
;
283 } else if (result
.type
== WebInputEvent::MouseMove
284 || result
.type
== WebInputEvent::MouseLeave
) {
285 if (cancel_previous_click
) {
286 g_last_click_count
= 0;
287 last_click_position_x
= 0;
288 last_click_position_y
= 0;
289 g_last_click_time
= 0;
292 result
.clickCount
= g_last_click_count
;
296 if (wparam
& MK_CONTROL
)
297 result
.modifiers
|= WebInputEvent::ControlKey
;
298 if (wparam
& MK_SHIFT
)
299 result
.modifiers
|= WebInputEvent::ShiftKey
;
300 if (::GetKeyState(VK_MENU
) & 0x8000)
301 result
.modifiers
|= WebInputEvent::AltKey
;
302 if (wparam
& MK_LBUTTON
)
303 result
.modifiers
|= WebInputEvent::LeftButtonDown
;
304 if (wparam
& MK_MBUTTON
)
305 result
.modifiers
|= WebInputEvent::MiddleButtonDown
;
306 if (wparam
& MK_RBUTTON
)
307 result
.modifiers
|= WebInputEvent::RightButtonDown
;
309 SetToggleKeyState(&result
);
313 // WebMouseWheelEvent ---------------------------------------------------------
316 WebMouseWheelEventBuilder::Build(HWND hwnd
, UINT message
,
317 WPARAM wparam
, LPARAM lparam
) {
318 WebMouseWheelEvent result
;
320 result
.type
= WebInputEvent::MouseWheel
;
322 // TODO(pkasting): http://b/1117926 Are we guaranteed that the message that
323 // GetMessageTime() refers to is the same one that we're passed in? Perhaps
324 // one of the construction parameters should be the time passed by the
325 // caller, who would know for sure.
326 result
.timeStampSeconds
= ::GetMessageTime() / 1000.0;
328 result
.button
= WebMouseEvent::ButtonNone
;
330 // Get key state, coordinates, and wheel delta from event.
331 typedef SHORT (WINAPI
*GetKeyStateFunction
)(int key
);
332 GetKeyStateFunction get_key_state_func
;
335 bool horizontal_scroll
= false;
336 if ((message
== WM_VSCROLL
) || (message
== WM_HSCROLL
)) {
337 // Synthesize mousewheel event from a scroll event. This is needed to
338 // simulate middle mouse scrolling in some laptops. Use GetAsyncKeyState
339 // for key state since we are synthesizing the input event.
340 get_key_state_func
= GetAsyncKeyState
;
342 if (get_key_state_func(VK_SHIFT
))
343 key_state
|= MK_SHIFT
;
344 if (get_key_state_func(VK_CONTROL
))
345 key_state
|= MK_CONTROL
;
346 // NOTE: There doesn't seem to be a way to query the mouse button state
349 POINT cursor_position
= {0};
350 GetCursorPos(&cursor_position
);
351 result
.globalX
= cursor_position
.x
;
352 result
.globalY
= cursor_position
.y
;
354 switch (LOWORD(wparam
)) {
355 case SB_LINEUP
: // == SB_LINELEFT
356 wheel_delta
= WHEEL_DELTA
;
358 case SB_LINEDOWN
: // == SB_LINERIGHT
359 wheel_delta
= -WHEEL_DELTA
;
363 result
.scrollByPage
= true;
367 result
.scrollByPage
= true;
369 default: // We don't supoprt SB_THUMBPOSITION or SB_THUMBTRACK here.
374 if (message
== WM_HSCROLL
)
375 horizontal_scroll
= true;
377 // Non-synthesized event; we can just read data off the event.
378 get_key_state_func
= ::GetKeyState
;
379 key_state
= GET_KEYSTATE_WPARAM(wparam
);
381 result
.globalX
= static_cast<short>(LOWORD(lparam
));
382 result
.globalY
= static_cast<short>(HIWORD(lparam
));
384 wheel_delta
= static_cast<float>(GET_WHEEL_DELTA_WPARAM(wparam
));
385 if (message
== WM_MOUSEHWHEEL
) {
386 horizontal_scroll
= true;
387 wheel_delta
= -wheel_delta
; // Windows is <- -/+ ->, WebKit <- +/- ->.
390 if (key_state
& MK_SHIFT
)
391 horizontal_scroll
= true;
393 // Set modifiers based on key state.
394 if (key_state
& MK_SHIFT
)
395 result
.modifiers
|= WebInputEvent::ShiftKey
;
396 if (key_state
& MK_CONTROL
)
397 result
.modifiers
|= WebInputEvent::ControlKey
;
398 if (get_key_state_func(VK_MENU
) & 0x8000)
399 result
.modifiers
|= WebInputEvent::AltKey
;
400 if (key_state
& MK_LBUTTON
)
401 result
.modifiers
|= WebInputEvent::LeftButtonDown
;
402 if (key_state
& MK_MBUTTON
)
403 result
.modifiers
|= WebInputEvent::MiddleButtonDown
;
404 if (key_state
& MK_RBUTTON
)
405 result
.modifiers
|= WebInputEvent::RightButtonDown
;
407 SetToggleKeyState(&result
);
409 // Set coordinates by translating event coordinates from screen to client.
410 POINT client_point
= { result
.globalX
, result
.globalY
};
411 MapWindowPoints(0, hwnd
, &client_point
, 1);
412 result
.x
= client_point
.x
;
413 result
.y
= client_point
.y
;
414 result
.windowX
= result
.x
;
415 result
.windowY
= result
.y
;
417 // Convert wheel delta amount to a number of pixels to scroll.
419 // How many pixels should we scroll per line? Gecko uses the height of the
420 // current line, which means scroll distance changes as you go through the
421 // page or go to different pages. IE 8 is ~60 px/line, although the value
422 // seems to vary slightly by page and zoom level. Also, IE defaults to
423 // smooth scrolling while Firefox doesn't, so it can get away with somewhat
424 // larger scroll values without feeling as jerky. Here we use 100 px per
425 // three lines (the default scroll amount is three lines per wheel tick).
426 // Even though we have smooth scrolling, we don't make this as large as IE
427 // because subjectively IE feels like it scrolls farther than you want while
429 static const float kScrollbarPixelsPerLine
= 100.0f
/ 3.0f
;
430 wheel_delta
/= WHEEL_DELTA
;
431 float scroll_delta
= wheel_delta
;
432 if (horizontal_scroll
) {
433 unsigned long scroll_chars
= kDefaultScrollCharsPerWheelDelta
;
434 SystemParametersInfo(SPI_GETWHEELSCROLLCHARS
, 0, &scroll_chars
, 0);
435 // TODO(pkasting): Should probably have a different multiplier
436 // scrollbarPixelsPerChar here.
437 scroll_delta
*= static_cast<float>(scroll_chars
) * kScrollbarPixelsPerLine
;
439 unsigned long scroll_lines
= kDefaultScrollLinesPerWheelDelta
;
440 SystemParametersInfo(SPI_GETWHEELSCROLLLINES
, 0, &scroll_lines
, 0);
441 if (scroll_lines
== WHEEL_PAGESCROLL
)
442 result
.scrollByPage
= true;
443 if (!result
.scrollByPage
) {
445 static_cast<float>(scroll_lines
) * kScrollbarPixelsPerLine
;
449 // Set scroll amount based on above calculations. WebKit expects positive
450 // deltaY to mean "scroll up" and positive deltaX to mean "scroll left".
451 if (horizontal_scroll
) {
452 result
.deltaX
= scroll_delta
;
453 result
.wheelTicksX
= wheel_delta
;
455 result
.deltaY
= scroll_delta
;
456 result
.wheelTicksY
= wheel_delta
;
462 } // namespace content