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 // 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
);
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};
182 ScreenToClient(hwnd
, &pos
);
183 return MAKELPARAM(pos
.x
, pos
.y
);
186 WebMouseEvent
WebMouseEventBuilder::Build(HWND hwnd
,
191 WebMouseEvent result
;
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
;
203 result
.button
= WebMouseEvent::ButtonNone
;
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
);
213 case WM_LBUTTONDBLCLK
:
214 result
.type
= WebInputEvent::MouseDown
;
215 result
.button
= WebMouseEvent::ButtonLeft
;
218 case WM_MBUTTONDBLCLK
:
219 result
.type
= WebInputEvent::MouseDown
;
220 result
.button
= WebMouseEvent::ButtonMiddle
;
223 case WM_RBUTTONDBLCLK
:
224 result
.type
= WebInputEvent::MouseDown
;
225 result
.button
= WebMouseEvent::ButtonRight
;
228 result
.type
= WebInputEvent::MouseUp
;
229 result
.button
= WebMouseEvent::ButtonLeft
;
232 result
.type
= WebInputEvent::MouseUp
;
233 result
.button
= WebMouseEvent::ButtonMiddle
;
236 result
.type
= WebInputEvent::MouseUp
;
237 result
.button
= WebMouseEvent::ButtonRight
;
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
;
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
;
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
);
315 // WebMouseWheelEvent ---------------------------------------------------------
317 WebMouseWheelEvent
WebMouseWheelEventBuilder::Build(HWND hwnd
,
322 WebMouseWheelEvent result
;
324 result
.type
= WebInputEvent::MouseWheel
;
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
;
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
;
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
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
;
359 case SB_LINEDOWN
: // == SB_LINERIGHT
360 wheel_delta
= -WHEEL_DELTA
;
364 result
.scrollByPage
= true;
368 result
.scrollByPage
= true;
370 default: // We don't supoprt SB_THUMBPOSITION or SB_THUMBTRACK here.
375 if (message
== WM_HSCROLL
)
376 horizontal_scroll
= true;
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
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
;
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
) {
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
;
456 result
.deltaY
= scroll_delta
;
457 result
.wheelTicksY
= wheel_delta
;
463 } // namespace content