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
,
110 WebKeyboardEvent result
;
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
);
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
,
189 WebMouseEvent result
;
193 result
.type
= WebInputEvent::MouseMove
;
194 if (wparam
& MK_LBUTTON
)
195 result
.button
= WebMouseEvent::ButtonLeft
;
196 else if (wparam
& MK_MBUTTON
)
197 result
.button
= WebMouseEvent::ButtonMiddle
;
198 else if (wparam
& MK_RBUTTON
)
199 result
.button
= WebMouseEvent::ButtonRight
;
201 result
.button
= WebMouseEvent::ButtonNone
;
204 result
.type
= WebInputEvent::MouseLeave
;
205 result
.button
= WebMouseEvent::ButtonNone
;
206 // set the current mouse position (relative to the client area of the
207 // current window) since none is specified for this event
208 lparam
= GetRelativeCursorPos(hwnd
);
211 case WM_LBUTTONDBLCLK
:
212 result
.type
= WebInputEvent::MouseDown
;
213 result
.button
= WebMouseEvent::ButtonLeft
;
216 case WM_MBUTTONDBLCLK
:
217 result
.type
= WebInputEvent::MouseDown
;
218 result
.button
= WebMouseEvent::ButtonMiddle
;
221 case WM_RBUTTONDBLCLK
:
222 result
.type
= WebInputEvent::MouseDown
;
223 result
.button
= WebMouseEvent::ButtonRight
;
226 result
.type
= WebInputEvent::MouseUp
;
227 result
.button
= WebMouseEvent::ButtonLeft
;
230 result
.type
= WebInputEvent::MouseUp
;
231 result
.button
= WebMouseEvent::ButtonMiddle
;
234 result
.type
= WebInputEvent::MouseUp
;
235 result
.button
= WebMouseEvent::ButtonRight
;
242 result
.timeStampSeconds
= time_ms
/ 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 ---------------------------------------------------------
315 WebMouseWheelEvent
WebMouseWheelEventBuilder::Build(HWND hwnd
,
320 WebMouseWheelEvent result
;
322 result
.type
= WebInputEvent::MouseWheel
;
325 result
.timeStampSeconds
= time_ms
/ 1000.0;
327 result
.button
= WebMouseEvent::ButtonNone
;
329 // Get key state, coordinates, and wheel delta from event.
330 typedef SHORT (WINAPI
*GetKeyStateFunction
)(int key
);
331 GetKeyStateFunction get_key_state_func
;
334 bool horizontal_scroll
= false;
335 if ((message
== WM_VSCROLL
) || (message
== WM_HSCROLL
)) {
336 // Synthesize mousewheel event from a scroll event. This is needed to
337 // simulate middle mouse scrolling in some laptops. Use GetAsyncKeyState
338 // for key state since we are synthesizing the input event.
339 get_key_state_func
= GetAsyncKeyState
;
341 if (get_key_state_func(VK_SHIFT
) & 0x8000)
342 key_state
|= MK_SHIFT
;
343 if (get_key_state_func(VK_CONTROL
) & 0x8000)
344 key_state
|= MK_CONTROL
;
345 // NOTE: There doesn't seem to be a way to query the mouse button state
348 POINT cursor_position
= {0};
349 GetCursorPos(&cursor_position
);
350 result
.globalX
= cursor_position
.x
;
351 result
.globalY
= cursor_position
.y
;
353 switch (LOWORD(wparam
)) {
354 case SB_LINEUP
: // == SB_LINELEFT
355 wheel_delta
= WHEEL_DELTA
;
357 case SB_LINEDOWN
: // == SB_LINERIGHT
358 wheel_delta
= -WHEEL_DELTA
;
362 result
.scrollByPage
= true;
366 result
.scrollByPage
= true;
368 default: // We don't supoprt SB_THUMBPOSITION or SB_THUMBTRACK here.
373 if (message
== WM_HSCROLL
)
374 horizontal_scroll
= true;
376 // Non-synthesized event; we can just read data off the event.
377 get_key_state_func
= ::GetKeyState
;
378 key_state
= GET_KEYSTATE_WPARAM(wparam
);
380 result
.globalX
= static_cast<short>(LOWORD(lparam
));
381 result
.globalY
= static_cast<short>(HIWORD(lparam
));
383 wheel_delta
= static_cast<float>(GET_WHEEL_DELTA_WPARAM(wparam
));
384 if (message
== WM_MOUSEHWHEEL
) {
385 horizontal_scroll
= true;
386 wheel_delta
= -wheel_delta
; // Windows is <- -/+ ->, WebKit <- +/- ->.
389 if (key_state
& MK_SHIFT
)
390 horizontal_scroll
= true;
392 // Set modifiers based on key state.
393 if (key_state
& MK_SHIFT
)
394 result
.modifiers
|= WebInputEvent::ShiftKey
;
395 if (key_state
& MK_CONTROL
)
396 result
.modifiers
|= WebInputEvent::ControlKey
;
397 if (get_key_state_func(VK_MENU
) & 0x8000)
398 result
.modifiers
|= WebInputEvent::AltKey
;
399 if (key_state
& MK_LBUTTON
)
400 result
.modifiers
|= WebInputEvent::LeftButtonDown
;
401 if (key_state
& MK_MBUTTON
)
402 result
.modifiers
|= WebInputEvent::MiddleButtonDown
;
403 if (key_state
& MK_RBUTTON
)
404 result
.modifiers
|= WebInputEvent::RightButtonDown
;
406 SetToggleKeyState(&result
);
408 // Set coordinates by translating event coordinates from screen to client.
409 POINT client_point
= { result
.globalX
, result
.globalY
};
410 MapWindowPoints(0, hwnd
, &client_point
, 1);
411 result
.x
= client_point
.x
;
412 result
.y
= client_point
.y
;
413 result
.windowX
= result
.x
;
414 result
.windowY
= result
.y
;
416 // Convert wheel delta amount to a number of pixels to scroll.
418 // How many pixels should we scroll per line? Gecko uses the height of the
419 // current line, which means scroll distance changes as you go through the
420 // page or go to different pages. IE 8 is ~60 px/line, although the value
421 // seems to vary slightly by page and zoom level. Also, IE defaults to
422 // smooth scrolling while Firefox doesn't, so it can get away with somewhat
423 // larger scroll values without feeling as jerky. Here we use 100 px per
424 // three lines (the default scroll amount is three lines per wheel tick).
425 // Even though we have smooth scrolling, we don't make this as large as IE
426 // because subjectively IE feels like it scrolls farther than you want while
428 static const float kScrollbarPixelsPerLine
= 100.0f
/ 3.0f
;
429 wheel_delta
/= WHEEL_DELTA
;
430 float scroll_delta
= wheel_delta
;
431 if (horizontal_scroll
) {
432 unsigned long scroll_chars
= kDefaultScrollCharsPerWheelDelta
;
433 SystemParametersInfo(SPI_GETWHEELSCROLLCHARS
, 0, &scroll_chars
, 0);
434 // TODO(pkasting): Should probably have a different multiplier
435 // scrollbarPixelsPerChar here.
436 scroll_delta
*= static_cast<float>(scroll_chars
) * kScrollbarPixelsPerLine
;
438 unsigned long scroll_lines
= kDefaultScrollLinesPerWheelDelta
;
439 SystemParametersInfo(SPI_GETWHEELSCROLLLINES
, 0, &scroll_lines
, 0);
440 if (scroll_lines
== WHEEL_PAGESCROLL
)
441 result
.scrollByPage
= true;
442 if (!result
.scrollByPage
) {
444 static_cast<float>(scroll_lines
) * kScrollbarPixelsPerLine
;
448 // Set scroll amount based on above calculations. WebKit expects positive
449 // deltaY to mean "scroll up" and positive deltaX to mean "scroll left".
450 if (horizontal_scroll
) {
451 result
.deltaX
= scroll_delta
;
452 result
.wheelTicksX
= wheel_delta
;
454 result
.deltaY
= scroll_delta
;
455 result
.wheelTicksY
= wheel_delta
;
461 } // namespace content