Upstream version 7.35.144.0
[platform/framework/web/crosswalk.git] / src / third_party / WebKit / Source / web / WebInputEventFactoryWin.cpp
1 /*
2  * Copyright (C) 2006-2009 Google Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions are
6  * met:
7  *
8  *     * Redistributions of source code must retain the above copyright
9  * notice, this list of conditions and the following disclaimer.
10  *     * Redistributions in binary form must reproduce the above
11  * copyright notice, this list of conditions and the following disclaimer
12  * in the documentation and/or other materials provided with the
13  * distribution.
14  *     * Neither the name of Google Inc. nor the names of its
15  * contributors may be used to endorse or promote products derived from
16  * this software without specific prior written permission.
17  *
18  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29  */
30
31 #include "config.h"
32 #include "WebInputEventFactory.h"
33
34 #include "WebInputEvent.h"
35
36 #include "wtf/Assertions.h"
37
38 namespace blink {
39
40 static const unsigned long defaultScrollLinesPerWheelDelta = 3;
41 static const unsigned long defaultScrollCharsPerWheelDelta = 1;
42
43 // WebKeyboardEvent -----------------------------------------------------------
44
45 static bool isKeyDown(WPARAM wparam)
46 {
47     return GetKeyState(wparam) & 0x8000;
48 }
49
50 static int getLocationModifier(WPARAM wparam, LPARAM lparam)
51 {
52     int modifier = 0;
53     switch (wparam) {
54     case VK_RETURN:
55         if ((lparam >> 16) & KF_EXTENDED)
56             modifier = WebInputEvent::IsKeyPad;
57         break;
58     case VK_INSERT:
59     case VK_DELETE:
60     case VK_HOME:
61     case VK_END:
62     case VK_PRIOR:
63     case VK_NEXT:
64     case VK_UP:
65     case VK_DOWN:
66     case VK_LEFT:
67     case VK_RIGHT:
68         if (!((lparam >> 16) & KF_EXTENDED))
69             modifier = WebInputEvent::IsKeyPad;
70         break;
71     case VK_NUMPAD0:
72     case VK_NUMPAD1:
73     case VK_NUMPAD2:
74     case VK_NUMPAD3:
75     case VK_NUMPAD4:
76     case VK_NUMPAD5:
77     case VK_NUMPAD6:
78     case VK_NUMPAD7:
79     case VK_NUMPAD8:
80     case VK_NUMPAD9:
81     case VK_DIVIDE:
82     case VK_MULTIPLY:
83     case VK_SUBTRACT:
84     case VK_ADD:
85     case VK_DECIMAL:
86     case VK_CLEAR:
87         modifier = WebInputEvent::IsKeyPad;
88         break;
89     case VK_SHIFT:
90         if (isKeyDown(VK_LSHIFT))
91             modifier = WebInputEvent::IsLeft;
92         else if (isKeyDown(VK_RSHIFT))
93             modifier = WebInputEvent::IsRight;
94         break;
95     case VK_CONTROL:
96         if (isKeyDown(VK_LCONTROL))
97             modifier = WebInputEvent::IsLeft;
98         else if (isKeyDown(VK_RCONTROL))
99             modifier = WebInputEvent::IsRight;
100         break;
101     case VK_MENU:
102         if (isKeyDown(VK_LMENU))
103             modifier = WebInputEvent::IsLeft;
104         else if (isKeyDown(VK_RMENU))
105             modifier = WebInputEvent::IsRight;
106         break;
107     case VK_LWIN:
108         modifier = WebInputEvent::IsLeft;
109         break;
110     case VK_RWIN:
111         modifier = WebInputEvent::IsRight;
112         break;
113     }
114
115     ASSERT(!modifier
116            || modifier == WebInputEvent::IsKeyPad
117            || modifier == WebInputEvent::IsLeft
118            || modifier == WebInputEvent::IsRight);
119     return modifier;
120 }
121
122 // Loads the state for toggle keys into the event.
123 static void SetToggleKeyState(WebInputEvent* event)
124 {
125     // Low bit set from GetKeyState indicates "toggled".
126     if (::GetKeyState(VK_NUMLOCK) & 1)
127         event->modifiers |= WebInputEvent::NumLockOn;
128     if (::GetKeyState(VK_CAPITAL) & 1)
129         event->modifiers |= WebInputEvent::CapsLockOn;
130 }
131
132 WebKeyboardEvent WebInputEventFactory::keyboardEvent(HWND hwnd, UINT message,
133                                                      WPARAM wparam, LPARAM lparam)
134 {
135     WebKeyboardEvent result;
136
137     // TODO(pkasting): http://b/1117926 Are we guaranteed that the message that
138     // GetMessageTime() refers to is the same one that we're passed in? Perhaps
139     // one of the construction parameters should be the time passed by the
140     // caller, who would know for sure.
141     result.timeStampSeconds = GetMessageTime() / 1000.0;
142
143     result.windowsKeyCode = static_cast<int>(wparam);
144     // Record the scan code (along with other context bits) for this key event.
145     result.nativeKeyCode = static_cast<int>(lparam);
146
147     switch (message) {
148     case WM_SYSKEYDOWN:
149         result.isSystemKey = true;
150     case WM_KEYDOWN:
151         result.type = WebInputEvent::RawKeyDown;
152         break;
153     case WM_SYSKEYUP:
154         result.isSystemKey = true;
155     case WM_KEYUP:
156         result.type = WebInputEvent::KeyUp;
157         break;
158     case WM_IME_CHAR:
159         result.type = WebInputEvent::Char;
160         break;
161     case WM_SYSCHAR:
162         result.isSystemKey = true;
163         result.type = WebInputEvent::Char;
164     case WM_CHAR:
165         result.type = WebInputEvent::Char;
166         break;
167     default:
168         ASSERT_NOT_REACHED();
169     }
170
171     if (result.type == WebInputEvent::Char || result.type == WebInputEvent::RawKeyDown) {
172         result.text[0] = result.windowsKeyCode;
173         result.unmodifiedText[0] = result.windowsKeyCode;
174     }
175     if (result.type != WebInputEvent::Char)
176         result.setKeyIdentifierFromWindowsKeyCode();
177
178     if (GetKeyState(VK_SHIFT) & 0x8000)
179         result.modifiers |= WebInputEvent::ShiftKey;
180     if (GetKeyState(VK_CONTROL) & 0x8000)
181         result.modifiers |= WebInputEvent::ControlKey;
182     if (GetKeyState(VK_MENU) & 0x8000)
183         result.modifiers |= WebInputEvent::AltKey;
184     // NOTE: There doesn't seem to be a way to query the mouse button state in
185     // this case.
186
187     if (LOWORD(lparam) > 1)
188         result.modifiers |= WebInputEvent::IsAutoRepeat;
189
190     result.modifiers |= getLocationModifier(wparam, lparam);
191
192     SetToggleKeyState(&result);
193     return result;
194 }
195
196 // WebMouseEvent --------------------------------------------------------------
197
198 static int gLastClickCount;
199 static double gLastClickTime;
200
201 static LPARAM GetRelativeCursorPos(HWND hwnd)
202 {
203     POINT pos = {-1, -1};
204     GetCursorPos(&pos);
205     ScreenToClient(hwnd, &pos);
206     return MAKELPARAM(pos.x, pos.y);
207 }
208
209 void WebInputEventFactory::resetLastClickState()
210 {
211     gLastClickTime = gLastClickCount = 0;
212 }
213
214 bool WebInputEventFactory::isSystemKeyEvent(const WebKeyboardEvent& event)
215 {
216     // According to MSDN:
217     // http://msdn.microsoft.com/en-us/library/ms646286(VS.85).aspx
218     // Key events with Alt modifier and F10 are system key events.
219     // We just emulate this behavior. It's necessary to prevent webkit from
220     // processing keypress event generated by alt-d, etc.
221     return event.modifiers & WebInputEvent::AltKey || event.windowsKeyCode == VK_F10;
222 }
223
224 WebMouseEvent WebInputEventFactory::mouseEvent(HWND hwnd, UINT message,
225                                                WPARAM wparam, LPARAM lparam)
226 {
227     WebMouseEvent result; //(WebInputEvent::Uninitialized());
228
229     switch (message) {
230     case WM_MOUSEMOVE:
231         result.type = WebInputEvent::MouseMove;
232         if (wparam & MK_LBUTTON)
233             result.button = WebMouseEvent::ButtonLeft;
234         else if (wparam & MK_MBUTTON)
235             result.button = WebMouseEvent::ButtonMiddle;
236         else if (wparam & MK_RBUTTON)
237             result.button = WebMouseEvent::ButtonRight;
238         else
239             result.button = WebMouseEvent::ButtonNone;
240         break;
241     case WM_MOUSELEAVE:
242         result.type = WebInputEvent::MouseLeave;
243         result.button = WebMouseEvent::ButtonNone;
244         // set the current mouse position (relative to the client area of the
245         // current window) since none is specified for this event
246         lparam = GetRelativeCursorPos(hwnd);
247         break;
248     case WM_LBUTTONDOWN:
249     case WM_LBUTTONDBLCLK:
250         result.type = WebInputEvent::MouseDown;
251         result.button = WebMouseEvent::ButtonLeft;
252         break;
253     case WM_MBUTTONDOWN:
254     case WM_MBUTTONDBLCLK:
255         result.type = WebInputEvent::MouseDown;
256         result.button = WebMouseEvent::ButtonMiddle;
257         break;
258     case WM_RBUTTONDOWN:
259     case WM_RBUTTONDBLCLK:
260         result.type = WebInputEvent::MouseDown;
261         result.button = WebMouseEvent::ButtonRight;
262         break;
263     case WM_LBUTTONUP:
264         result.type = WebInputEvent::MouseUp;
265         result.button = WebMouseEvent::ButtonLeft;
266         break;
267     case WM_MBUTTONUP:
268         result.type = WebInputEvent::MouseUp;
269         result.button = WebMouseEvent::ButtonMiddle;
270         break;
271     case WM_RBUTTONUP:
272         result.type = WebInputEvent::MouseUp;
273         result.button = WebMouseEvent::ButtonRight;
274         break;
275     default:
276         ASSERT_NOT_REACHED();
277     }
278
279     // TODO(pkasting): http://b/1117926 Are we guaranteed that the message that
280     // GetMessageTime() refers to is the same one that we're passed in? Perhaps
281     // one of the construction parameters should be the time passed by the
282     // caller, who would know for sure.
283     result.timeStampSeconds = GetMessageTime() / 1000.0;
284
285     // set position fields:
286
287     result.x = static_cast<short>(LOWORD(lparam));
288     result.y = static_cast<short>(HIWORD(lparam));
289     result.windowX = result.x;
290     result.windowY = result.y;
291
292     POINT globalPoint = { result.x, result.y };
293     ClientToScreen(hwnd, &globalPoint);
294
295     result.globalX = globalPoint.x;
296     result.globalY = globalPoint.y;
297
298     // calculate number of clicks:
299
300     // This differs slightly from the WebKit code in WebKit/win/WebView.cpp
301     // where their original code looks buggy.
302     static int lastClickPositionX;
303     static int lastClickPositionY;
304     static WebMouseEvent::Button lastClickButton = WebMouseEvent::ButtonLeft;
305
306     double currentTime = result.timeStampSeconds;
307     bool cancelPreviousClick =
308         (abs(lastClickPositionX - result.x) > (GetSystemMetrics(SM_CXDOUBLECLK) / 2))
309         || (abs(lastClickPositionY - result.y) > (GetSystemMetrics(SM_CYDOUBLECLK) / 2))
310         || ((currentTime - gLastClickTime) * 1000.0 > GetDoubleClickTime());
311
312     if (result.type == WebInputEvent::MouseDown) {
313         if (!cancelPreviousClick && (result.button == lastClickButton))
314             ++gLastClickCount;
315         else {
316             gLastClickCount = 1;
317             lastClickPositionX = result.x;
318             lastClickPositionY = result.y;
319         }
320         gLastClickTime = currentTime;
321         lastClickButton = result.button;
322     } else if (result.type == WebInputEvent::MouseMove
323                || result.type == WebInputEvent::MouseLeave) {
324         if (cancelPreviousClick) {
325             gLastClickCount = 0;
326             lastClickPositionX = 0;
327             lastClickPositionY = 0;
328             gLastClickTime = 0;
329         }
330     }
331     result.clickCount = gLastClickCount;
332
333     // set modifiers:
334
335     if (wparam & MK_CONTROL)
336         result.modifiers |= WebInputEvent::ControlKey;
337     if (wparam & MK_SHIFT)
338         result.modifiers |= WebInputEvent::ShiftKey;
339     if (GetKeyState(VK_MENU) & 0x8000)
340         result.modifiers |= WebInputEvent::AltKey;
341     if (wparam & MK_LBUTTON)
342         result.modifiers |= WebInputEvent::LeftButtonDown;
343     if (wparam & MK_MBUTTON)
344         result.modifiers |= WebInputEvent::MiddleButtonDown;
345     if (wparam & MK_RBUTTON)
346         result.modifiers |= WebInputEvent::RightButtonDown;
347
348     SetToggleKeyState(&result);
349     return result;
350 }
351
352 // WebMouseWheelEvent ---------------------------------------------------------
353
354 WebMouseWheelEvent WebInputEventFactory::mouseWheelEvent(HWND hwnd, UINT message,
355                                                          WPARAM wparam, LPARAM lparam)
356 {
357     WebMouseWheelEvent result; //(WebInputEvent::Uninitialized());
358
359     result.type = WebInputEvent::MouseWheel;
360
361     // TODO(pkasting): http://b/1117926 Are we guaranteed that the message that
362     // GetMessageTime() refers to is the same one that we're passed in? Perhaps
363     // one of the construction parameters should be the time passed by the
364     // caller, who would know for sure.
365     result.timeStampSeconds = GetMessageTime() / 1000.0;
366
367     result.button = WebMouseEvent::ButtonNone;
368
369     // Get key state, coordinates, and wheel delta from event.
370     typedef SHORT (WINAPI *GetKeyStateFunction)(int key);
371     GetKeyStateFunction getKeyState;
372     UINT keyState;
373     float wheelDelta;
374     bool horizontalScroll = false;
375     if ((message == WM_VSCROLL) || (message == WM_HSCROLL)) {
376         // Synthesize mousewheel event from a scroll event.  This is needed to
377         // simulate middle mouse scrolling in some laptops.  Use GetAsyncKeyState
378         // for key state since we are synthesizing the input event.
379         getKeyState = GetAsyncKeyState;
380         keyState = 0;
381         if (getKeyState(VK_SHIFT))
382             keyState |= MK_SHIFT;
383         if (getKeyState(VK_CONTROL))
384             keyState |= MK_CONTROL;
385         // NOTE: There doesn't seem to be a way to query the mouse button state
386         // in this case.
387
388         POINT cursorPosition = {0};
389         GetCursorPos(&cursorPosition);
390         result.globalX = cursorPosition.x;
391         result.globalY = cursorPosition.y;
392
393         switch (LOWORD(wparam)) {
394         case SB_LINEUP:    // == SB_LINELEFT
395             wheelDelta = WHEEL_DELTA;
396             break;
397         case SB_LINEDOWN:  // == SB_LINERIGHT
398             wheelDelta = -WHEEL_DELTA;
399             break;
400         case SB_PAGEUP:
401             wheelDelta = 1;
402             result.scrollByPage = true;
403             break;
404         case SB_PAGEDOWN:
405             wheelDelta = -1;
406             result.scrollByPage = true;
407             break;
408         default:  // We don't supoprt SB_THUMBPOSITION or SB_THUMBTRACK here.
409             wheelDelta = 0;
410             break;
411         }
412
413         if (message == WM_HSCROLL)
414             horizontalScroll = true;
415     } else {
416         // Non-synthesized event; we can just read data off the event.
417         getKeyState = GetKeyState;
418         keyState = GET_KEYSTATE_WPARAM(wparam);
419
420         result.globalX = static_cast<short>(LOWORD(lparam));
421         result.globalY = static_cast<short>(HIWORD(lparam));
422
423         wheelDelta = static_cast<float>(GET_WHEEL_DELTA_WPARAM(wparam));
424         if (message == WM_MOUSEHWHEEL) {
425             horizontalScroll = true;
426             wheelDelta = -wheelDelta;  // Windows is <- -/+ ->, WebKit <- +/- ->.
427         }
428     }
429     if (keyState & MK_SHIFT)
430         horizontalScroll = true;
431
432     // Set modifiers based on key state.
433     if (keyState & MK_SHIFT)
434         result.modifiers |= WebInputEvent::ShiftKey;
435     if (keyState & MK_CONTROL)
436         result.modifiers |= WebInputEvent::ControlKey;
437     if (getKeyState(VK_MENU) & 0x8000)
438         result.modifiers |= WebInputEvent::AltKey;
439     if (keyState & MK_LBUTTON)
440         result.modifiers |= WebInputEvent::LeftButtonDown;
441     if (keyState & MK_MBUTTON)
442         result.modifiers |= WebInputEvent::MiddleButtonDown;
443     if (keyState & MK_RBUTTON)
444         result.modifiers |= WebInputEvent::RightButtonDown;
445
446     SetToggleKeyState(&result);
447
448     // Set coordinates by translating event coordinates from screen to client.
449     POINT clientPoint = { result.globalX, result.globalY };
450     MapWindowPoints(0, hwnd, &clientPoint, 1);
451     result.x = clientPoint.x;
452     result.y = clientPoint.y;
453     result.windowX = result.x;
454     result.windowY = result.y;
455
456     // Convert wheel delta amount to a number of pixels to scroll.
457     //
458     // How many pixels should we scroll per line?  Gecko uses the height of the
459     // current line, which means scroll distance changes as you go through the
460     // page or go to different pages.  IE 8 is ~60 px/line, although the value
461     // seems to vary slightly by page and zoom level.  Also, IE defaults to
462     // smooth scrolling while Firefox doesn't, so it can get away with somewhat
463     // larger scroll values without feeling as jerky.  Here we use 100 px per
464     // three lines (the default scroll amount is three lines per wheel tick).
465     // Even though we have smooth scrolling, we don't make this as large as IE
466     // because subjectively IE feels like it scrolls farther than you want while
467     // reading articles.
468     static const float scrollbarPixelsPerLine = 100.0f / 3.0f;
469     wheelDelta /= WHEEL_DELTA;
470     float scrollDelta = wheelDelta;
471     if (horizontalScroll) {
472         unsigned long scrollChars = defaultScrollCharsPerWheelDelta;
473         SystemParametersInfo(SPI_GETWHEELSCROLLCHARS, 0, &scrollChars, 0);
474         // TODO(pkasting): Should probably have a different multiplier
475         // scrollbarPixelsPerChar here.
476         scrollDelta *= static_cast<float>(scrollChars) * scrollbarPixelsPerLine;
477     } else {
478         unsigned long scrollLines = defaultScrollLinesPerWheelDelta;
479         SystemParametersInfo(SPI_GETWHEELSCROLLLINES, 0, &scrollLines, 0);
480         if (scrollLines == WHEEL_PAGESCROLL)
481             result.scrollByPage = true;
482         if (!result.scrollByPage)
483             scrollDelta *= static_cast<float>(scrollLines) * scrollbarPixelsPerLine;
484     }
485
486     // Set scroll amount based on above calculations.  WebKit expects positive
487     // deltaY to mean "scroll up" and positive deltaX to mean "scroll left".
488     if (horizontalScroll) {
489         result.deltaX = scrollDelta;
490         result.wheelTicksX = wheelDelta;
491     } else {
492         result.deltaY = scrollDelta;
493         result.wheelTicksY = wheelDelta;
494     }
495
496     return result;
497 }
498
499 } // namespace blink