- add sources.
[platform/framework/web/crosswalk.git] / src / content / browser / renderer_host / input / web_input_event_builders_win.cc
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.
4
5 #include "content/browser/renderer_host/input/web_input_event_builders_win.h"
6
7 #include "base/logging.h"
8 #include "content/browser/renderer_host/input/web_input_event_util.h"
9
10 using WebKit::WebInputEvent;
11 using WebKit::WebKeyboardEvent;
12 using WebKit::WebMouseEvent;
13 using WebKit::WebMouseWheelEvent;
14
15 namespace content {
16
17 static const unsigned long kDefaultScrollLinesPerWheelDelta = 3;
18 static const unsigned long kDefaultScrollCharsPerWheelDelta = 1;
19
20 static bool IsKeyDown(WPARAM wparam) {
21   return (GetKeyState(wparam) & 0x8000) != 0;
22 }
23
24 static int GetLocationModifier(WPARAM wparam, LPARAM lparam) {
25   int modifier = 0;
26   switch (wparam) {
27   case VK_RETURN:
28     if ((lparam >> 16) & KF_EXTENDED)
29       modifier = WebInputEvent::IsKeyPad;
30     break;
31   case VK_INSERT:
32   case VK_DELETE:
33   case VK_HOME:
34   case VK_END:
35   case VK_PRIOR:
36   case VK_NEXT:
37   case VK_UP:
38   case VK_DOWN:
39   case VK_LEFT:
40   case VK_RIGHT:
41     if (!((lparam >> 16) & KF_EXTENDED))
42       modifier = WebInputEvent::IsKeyPad;
43     break;
44   case VK_NUMLOCK:
45   case VK_NUMPAD0:
46   case VK_NUMPAD1:
47   case VK_NUMPAD2:
48   case VK_NUMPAD3:
49   case VK_NUMPAD4:
50   case VK_NUMPAD5:
51   case VK_NUMPAD6:
52   case VK_NUMPAD7:
53   case VK_NUMPAD8:
54   case VK_NUMPAD9:
55   case VK_DIVIDE:
56   case VK_MULTIPLY:
57   case VK_SUBTRACT:
58   case VK_ADD:
59   case VK_DECIMAL:
60   case VK_CLEAR:
61     modifier = WebInputEvent::IsKeyPad;
62     break;
63   case VK_SHIFT:
64     if (IsKeyDown(VK_LSHIFT))
65       modifier = WebInputEvent::IsLeft;
66     else if (IsKeyDown(VK_RSHIFT))
67       modifier = WebInputEvent::IsRight;
68     break;
69   case VK_CONTROL:
70     if (IsKeyDown(VK_LCONTROL))
71       modifier = WebInputEvent::IsLeft;
72     else if (IsKeyDown(VK_RCONTROL))
73       modifier = WebInputEvent::IsRight;
74     break;
75   case VK_MENU:
76     if (IsKeyDown(VK_LMENU))
77       modifier = WebInputEvent::IsLeft;
78     else if (IsKeyDown(VK_RMENU))
79       modifier = WebInputEvent::IsRight;
80     break;
81   case VK_LWIN:
82     modifier = WebInputEvent::IsLeft;
83     break;
84   case VK_RWIN:
85     modifier = WebInputEvent::IsRight;
86     break;
87   }
88
89   DCHECK(!modifier
90          || modifier == WebInputEvent::IsKeyPad
91          || modifier == WebInputEvent::IsLeft
92          || modifier == WebInputEvent::IsRight);
93   return modifier;
94 }
95
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;
103 }
104
105 WebKeyboardEvent WebKeyboardEventBuilder::Build(HWND hwnd, UINT message,
106                                                 WPARAM wparam, LPARAM lparam) {
107   WebKeyboardEvent result;
108
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;
114
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);
118
119   switch (message) {
120   case WM_SYSKEYDOWN:
121     result.isSystemKey = true;
122   case WM_KEYDOWN:
123     result.type = WebInputEvent::RawKeyDown;
124     break;
125   case WM_SYSKEYUP:
126     result.isSystemKey = true;
127   case WM_KEYUP:
128     result.type = WebInputEvent::KeyUp;
129     break;
130   case WM_IME_CHAR:
131     result.type = WebInputEvent::Char;
132     break;
133   case WM_SYSCHAR:
134     result.isSystemKey = true;
135     result.type = WebInputEvent::Char;
136   case WM_CHAR:
137     result.type = WebInputEvent::Char;
138     break;
139   default:
140     NOTREACHED();
141   }
142
143   if (result.type == WebInputEvent::Char
144    || result.type == WebInputEvent::RawKeyDown) {
145     result.text[0] = result.windowsKeyCode;
146     result.unmodifiedText[0] = result.windowsKeyCode;
147   }
148   if (result.type != WebInputEvent::Char) {
149     UpdateWindowsKeyCodeAndKeyIdentifier(
150         &result,
151         static_cast<ui::KeyboardCode>(result.windowsKeyCode));
152   }
153
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
161   // this case.
162
163   if (LOWORD(lparam) > 1)
164     result.modifiers |= WebInputEvent::IsAutoRepeat;
165
166   result.modifiers |= GetLocationModifier(wparam, lparam);
167
168   SetToggleKeyState(&result);
169   return result;
170 }
171
172 // WebMouseEvent --------------------------------------------------------------
173
174 static int g_last_click_count = 0;
175 static double g_last_click_time = 0;
176
177 static LPARAM GetRelativeCursorPos(HWND hwnd) {
178   POINT pos = {-1, -1};
179   GetCursorPos(&pos);
180   ScreenToClient(hwnd, &pos);
181   return MAKELPARAM(pos.x, pos.y);
182 }
183
184 WebMouseEvent WebMouseEventBuilder::Build(HWND hwnd, UINT message,
185                                           WPARAM wparam, LPARAM lparam) {
186   WebMouseEvent result;
187
188   switch (message) {
189   case WM_MOUSEMOVE:
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;
197     else
198       result.button = WebMouseEvent::ButtonNone;
199     break;
200   case WM_MOUSELEAVE:
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);
206     break;
207   case WM_LBUTTONDOWN:
208   case WM_LBUTTONDBLCLK:
209     result.type = WebInputEvent::MouseDown;
210     result.button = WebMouseEvent::ButtonLeft;
211     break;
212   case WM_MBUTTONDOWN:
213   case WM_MBUTTONDBLCLK:
214     result.type = WebInputEvent::MouseDown;
215     result.button = WebMouseEvent::ButtonMiddle;
216     break;
217   case WM_RBUTTONDOWN:
218   case WM_RBUTTONDBLCLK:
219     result.type = WebInputEvent::MouseDown;
220     result.button = WebMouseEvent::ButtonRight;
221     break;
222   case WM_LBUTTONUP:
223     result.type = WebInputEvent::MouseUp;
224     result.button = WebMouseEvent::ButtonLeft;
225     break;
226   case WM_MBUTTONUP:
227     result.type = WebInputEvent::MouseUp;
228     result.button = WebMouseEvent::ButtonMiddle;
229     break;
230   case WM_RBUTTONUP:
231     result.type = WebInputEvent::MouseUp;
232     result.button = WebMouseEvent::ButtonRight;
233     break;
234   default:
235     NOTREACHED();
236   }
237
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;
243
244   // set position fields:
245
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;
250
251   POINT global_point = { result.x, result.y };
252   ClientToScreen(hwnd, &global_point);
253
254   result.globalX = global_point.x;
255   result.globalY = global_point.y;
256
257   // calculate number of clicks:
258
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;
264
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());
272
273   if (result.type == WebInputEvent::MouseDown) {
274     if (!cancel_previous_click && (result.button == last_click_button)) {
275       ++g_last_click_count;
276     } else {
277       g_last_click_count = 1;
278       last_click_position_x = result.x;
279       last_click_position_y = result.y;
280     }
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;
290     }
291   }
292   result.clickCount = g_last_click_count;
293
294   // set modifiers:
295
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;
308
309   SetToggleKeyState(&result);
310   return result;
311 }
312
313 // WebMouseWheelEvent ---------------------------------------------------------
314
315 WebMouseWheelEvent
316 WebMouseWheelEventBuilder::Build(HWND hwnd, UINT message,
317                                  WPARAM wparam, LPARAM lparam) {
318   WebMouseWheelEvent result;
319
320   result.type = WebInputEvent::MouseWheel;
321
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;
327
328   result.button = WebMouseEvent::ButtonNone;
329
330   // Get key state, coordinates, and wheel delta from event.
331   typedef SHORT (WINAPI *GetKeyStateFunction)(int key);
332   GetKeyStateFunction get_key_state_func;
333   UINT key_state;
334   float wheel_delta;
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;
341     key_state = 0;
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
347     // in this case.
348
349     POINT cursor_position = {0};
350     GetCursorPos(&cursor_position);
351     result.globalX = cursor_position.x;
352     result.globalY = cursor_position.y;
353
354     switch (LOWORD(wparam)) {
355       case SB_LINEUP:    // == SB_LINELEFT
356         wheel_delta = WHEEL_DELTA;
357         break;
358       case SB_LINEDOWN:  // == SB_LINERIGHT
359         wheel_delta = -WHEEL_DELTA;
360         break;
361       case SB_PAGEUP:
362         wheel_delta = 1;
363         result.scrollByPage = true;
364         break;
365       case SB_PAGEDOWN:
366         wheel_delta = -1;
367         result.scrollByPage = true;
368         break;
369       default:  // We don't supoprt SB_THUMBPOSITION or SB_THUMBTRACK here.
370         wheel_delta = 0;
371         break;
372     }
373
374     if (message == WM_HSCROLL)
375       horizontal_scroll = true;
376   } else {
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);
380
381     result.globalX = static_cast<short>(LOWORD(lparam));
382     result.globalY = static_cast<short>(HIWORD(lparam));
383
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 <- +/- ->.
388     }
389   }
390   if (key_state & MK_SHIFT)
391     horizontal_scroll = true;
392
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;
406
407   SetToggleKeyState(&result);
408
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;
416
417   // Convert wheel delta amount to a number of pixels to scroll.
418   //
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
428   // reading articles.
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;
438   } else {
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) {
444       scroll_delta *=
445           static_cast<float>(scroll_lines) * kScrollbarPixelsPerLine;
446     }
447   }
448
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;
454   } else {
455     result.deltaY = scroll_delta;
456     result.wheelTicksY = wheel_delta;
457   }
458
459   return result;
460 }
461
462 }  // namespace content