• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 WebKit {
39 
40 static const unsigned long defaultScrollLinesPerWheelDelta = 3;
41 static const unsigned long defaultScrollCharsPerWheelDelta = 1;
42 
43 // WebKeyboardEvent -----------------------------------------------------------
44 
isKeyPad(WPARAM wparam,LPARAM lparam)45 static bool isKeyPad(WPARAM wparam, LPARAM lparam)
46 {
47     bool keypad = false;
48     switch (wparam) {
49     case VK_RETURN:
50         keypad = (lparam >> 16) & KF_EXTENDED;
51         break;
52     case VK_INSERT:
53     case VK_DELETE:
54     case VK_HOME:
55     case VK_END:
56     case VK_PRIOR:
57     case VK_NEXT:
58     case VK_UP:
59     case VK_DOWN:
60     case VK_LEFT:
61     case VK_RIGHT:
62         keypad = !((lparam >> 16) & KF_EXTENDED);
63         break;
64     case VK_NUMLOCK:
65     case VK_NUMPAD0:
66     case VK_NUMPAD1:
67     case VK_NUMPAD2:
68     case VK_NUMPAD3:
69     case VK_NUMPAD4:
70     case VK_NUMPAD5:
71     case VK_NUMPAD6:
72     case VK_NUMPAD7:
73     case VK_NUMPAD8:
74     case VK_NUMPAD9:
75     case VK_DIVIDE:
76     case VK_MULTIPLY:
77     case VK_SUBTRACT:
78     case VK_ADD:
79     case VK_DECIMAL:
80     case VK_CLEAR:
81         keypad = true;
82         break;
83     default:
84         keypad = false;
85     }
86     return keypad;
87 }
88 
89 // Loads the state for toggle keys into the event.
SetToggleKeyState(WebInputEvent * event)90 static void SetToggleKeyState(WebInputEvent* event)
91 {
92     // Low bit set from GetKeyState indicates "toggled".
93     if (::GetKeyState(VK_NUMLOCK) & 1)
94         event->modifiers |= WebInputEvent::NumLockOn;
95     if (::GetKeyState(VK_CAPITAL) & 1)
96         event->modifiers |= WebInputEvent::CapsLockOn;
97 }
98 
keyboardEvent(HWND hwnd,UINT message,WPARAM wparam,LPARAM lparam)99 WebKeyboardEvent WebInputEventFactory::keyboardEvent(HWND hwnd, UINT message,
100                                                      WPARAM wparam, LPARAM lparam)
101 {
102     WebKeyboardEvent result;
103 
104     // TODO(pkasting): http://b/1117926 Are we guaranteed that the message that
105     // GetMessageTime() refers to is the same one that we're passed in? Perhaps
106     // one of the construction parameters should be the time passed by the
107     // caller, who would know for sure.
108     result.timeStampSeconds = GetMessageTime() / 1000.0;
109 
110     result.windowsKeyCode = result.nativeKeyCode = static_cast<int>(wparam);
111 
112     switch (message) {
113     case WM_SYSKEYDOWN:
114         result.isSystemKey = true;
115     case WM_KEYDOWN:
116         result.type = WebInputEvent::RawKeyDown;
117         break;
118     case WM_SYSKEYUP:
119         result.isSystemKey = true;
120     case WM_KEYUP:
121         result.type = WebInputEvent::KeyUp;
122         break;
123     case WM_IME_CHAR:
124         result.type = WebInputEvent::Char;
125         break;
126     case WM_SYSCHAR:
127         result.isSystemKey = true;
128         result.type = WebInputEvent::Char;
129     case WM_CHAR:
130         result.type = WebInputEvent::Char;
131         break;
132     default:
133         ASSERT_NOT_REACHED();
134     }
135 
136     if (result.type == WebInputEvent::Char || result.type == WebInputEvent::RawKeyDown) {
137         result.text[0] = result.windowsKeyCode;
138         result.unmodifiedText[0] = result.windowsKeyCode;
139     }
140     if (result.type != WebInputEvent::Char)
141         result.setKeyIdentifierFromWindowsKeyCode();
142 
143     if (GetKeyState(VK_SHIFT) & 0x8000)
144         result.modifiers |= WebInputEvent::ShiftKey;
145     if (GetKeyState(VK_CONTROL) & 0x8000)
146         result.modifiers |= WebInputEvent::ControlKey;
147     if (GetKeyState(VK_MENU) & 0x8000)
148         result.modifiers |= WebInputEvent::AltKey;
149     // NOTE: There doesn't seem to be a way to query the mouse button state in
150     // this case.
151 
152     if (LOWORD(lparam) > 1)
153         result.modifiers |= WebInputEvent::IsAutoRepeat;
154     if (isKeyPad(wparam, lparam))
155         result.modifiers |= WebInputEvent::IsKeyPad;
156 
157     SetToggleKeyState(&result);
158     return result;
159 }
160 
161 // WebMouseEvent --------------------------------------------------------------
162 
163 static int gLastClickCount;
164 static double gLastClickTime;
165 
GetRelativeCursorPos(HWND hwnd)166 static LPARAM GetRelativeCursorPos(HWND hwnd)
167 {
168     POINT pos = {-1, -1};
169     GetCursorPos(&pos);
170     ScreenToClient(hwnd, &pos);
171     return MAKELPARAM(pos.x, pos.y);
172 }
173 
resetLastClickState()174 void WebInputEventFactory::resetLastClickState()
175 {
176     gLastClickTime = gLastClickCount = 0;
177 }
178 
mouseEvent(HWND hwnd,UINT message,WPARAM wparam,LPARAM lparam)179 WebMouseEvent WebInputEventFactory::mouseEvent(HWND hwnd, UINT message,
180                                                WPARAM wparam, LPARAM lparam)
181 {
182     WebMouseEvent result; //(WebInputEvent::Uninitialized());
183 
184     switch (message) {
185     case WM_MOUSEMOVE:
186         result.type = WebInputEvent::MouseMove;
187         if (wparam & MK_LBUTTON)
188             result.button = WebMouseEvent::ButtonLeft;
189         else if (wparam & MK_MBUTTON)
190             result.button = WebMouseEvent::ButtonMiddle;
191         else if (wparam & MK_RBUTTON)
192             result.button = WebMouseEvent::ButtonRight;
193         else
194             result.button = WebMouseEvent::ButtonNone;
195         break;
196     case WM_MOUSELEAVE:
197         result.type = WebInputEvent::MouseLeave;
198         result.button = WebMouseEvent::ButtonNone;
199         // set the current mouse position (relative to the client area of the
200         // current window) since none is specified for this event
201         lparam = GetRelativeCursorPos(hwnd);
202         break;
203     case WM_LBUTTONDOWN:
204     case WM_LBUTTONDBLCLK:
205         result.type = WebInputEvent::MouseDown;
206         result.button = WebMouseEvent::ButtonLeft;
207         break;
208     case WM_MBUTTONDOWN:
209     case WM_MBUTTONDBLCLK:
210         result.type = WebInputEvent::MouseDown;
211         result.button = WebMouseEvent::ButtonMiddle;
212         break;
213     case WM_RBUTTONDOWN:
214     case WM_RBUTTONDBLCLK:
215         result.type = WebInputEvent::MouseDown;
216         result.button = WebMouseEvent::ButtonRight;
217         break;
218     case WM_LBUTTONUP:
219         result.type = WebInputEvent::MouseUp;
220         result.button = WebMouseEvent::ButtonLeft;
221         break;
222     case WM_MBUTTONUP:
223         result.type = WebInputEvent::MouseUp;
224         result.button = WebMouseEvent::ButtonMiddle;
225         break;
226     case WM_RBUTTONUP:
227         result.type = WebInputEvent::MouseUp;
228         result.button = WebMouseEvent::ButtonRight;
229         break;
230     default:
231         ASSERT_NOT_REACHED();
232     }
233 
234     // TODO(pkasting): http://b/1117926 Are we guaranteed that the message that
235     // GetMessageTime() refers to is the same one that we're passed in? Perhaps
236     // one of the construction parameters should be the time passed by the
237     // caller, who would know for sure.
238     result.timeStampSeconds = GetMessageTime() / 1000.0;
239 
240     // set position fields:
241 
242     result.x = static_cast<short>(LOWORD(lparam));
243     result.y = static_cast<short>(HIWORD(lparam));
244     result.windowX = result.x;
245     result.windowY = result.y;
246 
247     POINT globalPoint = { result.x, result.y };
248     ClientToScreen(hwnd, &globalPoint);
249 
250     result.globalX = globalPoint.x;
251     result.globalY = globalPoint.y;
252 
253     // calculate number of clicks:
254 
255     // This differs slightly from the WebKit code in WebKit/win/WebView.cpp
256     // where their original code looks buggy.
257     static int lastClickPositionX;
258     static int lastClickPositionY;
259     static WebMouseEvent::Button lastClickButton = WebMouseEvent::ButtonLeft;
260 
261     double currentTime = result.timeStampSeconds;
262     bool cancelPreviousClick =
263         (abs(lastClickPositionX - result.x) > (GetSystemMetrics(SM_CXDOUBLECLK) / 2))
264         || (abs(lastClickPositionY - result.y) > (GetSystemMetrics(SM_CYDOUBLECLK) / 2))
265         || ((currentTime - gLastClickTime) * 1000.0 > GetDoubleClickTime());
266 
267     if (result.type == WebInputEvent::MouseDown) {
268         if (!cancelPreviousClick && (result.button == lastClickButton))
269             ++gLastClickCount;
270         else {
271             gLastClickCount = 1;
272             lastClickPositionX = result.x;
273             lastClickPositionY = result.y;
274         }
275         gLastClickTime = currentTime;
276         lastClickButton = result.button;
277     } else if (result.type == WebInputEvent::MouseMove
278                || result.type == WebInputEvent::MouseLeave) {
279         if (cancelPreviousClick) {
280             gLastClickCount = 0;
281             lastClickPositionX = 0;
282             lastClickPositionY = 0;
283             gLastClickTime = 0;
284         }
285     }
286     result.clickCount = gLastClickCount;
287 
288     // set modifiers:
289 
290     if (wparam & MK_CONTROL)
291         result.modifiers |= WebInputEvent::ControlKey;
292     if (wparam & MK_SHIFT)
293         result.modifiers |= WebInputEvent::ShiftKey;
294     if (GetKeyState(VK_MENU) & 0x8000)
295         result.modifiers |= WebInputEvent::AltKey;
296     if (wparam & MK_LBUTTON)
297         result.modifiers |= WebInputEvent::LeftButtonDown;
298     if (wparam & MK_MBUTTON)
299         result.modifiers |= WebInputEvent::MiddleButtonDown;
300     if (wparam & MK_RBUTTON)
301         result.modifiers |= WebInputEvent::RightButtonDown;
302 
303     SetToggleKeyState(&result);
304     return result;
305 }
306 
307 // WebMouseWheelEvent ---------------------------------------------------------
308 
mouseWheelEvent(HWND hwnd,UINT message,WPARAM wparam,LPARAM lparam)309 WebMouseWheelEvent WebInputEventFactory::mouseWheelEvent(HWND hwnd, UINT message,
310                                                          WPARAM wparam, LPARAM lparam)
311 {
312     WebMouseWheelEvent result; //(WebInputEvent::Uninitialized());
313 
314     result.type = WebInputEvent::MouseWheel;
315 
316     // TODO(pkasting): http://b/1117926 Are we guaranteed that the message that
317     // GetMessageTime() refers to is the same one that we're passed in? Perhaps
318     // one of the construction parameters should be the time passed by the
319     // caller, who would know for sure.
320     result.timeStampSeconds = GetMessageTime() / 1000.0;
321 
322     result.button = WebMouseEvent::ButtonNone;
323 
324     // Get key state, coordinates, and wheel delta from event.
325     typedef SHORT (WINAPI *GetKeyStateFunction)(int key);
326     GetKeyStateFunction getKeyState;
327     UINT keyState;
328     float wheelDelta;
329     bool horizontalScroll = false;
330     if ((message == WM_VSCROLL) || (message == WM_HSCROLL)) {
331         // Synthesize mousewheel event from a scroll event.  This is needed to
332         // simulate middle mouse scrolling in some laptops.  Use GetAsyncKeyState
333         // for key state since we are synthesizing the input event.
334         getKeyState = GetAsyncKeyState;
335         keyState = 0;
336         if (getKeyState(VK_SHIFT))
337             keyState |= MK_SHIFT;
338         if (getKeyState(VK_CONTROL))
339             keyState |= MK_CONTROL;
340         // NOTE: There doesn't seem to be a way to query the mouse button state
341         // in this case.
342 
343         POINT cursorPosition = {0};
344         GetCursorPos(&cursorPosition);
345         result.globalX = cursorPosition.x;
346         result.globalY = cursorPosition.y;
347 
348         switch (LOWORD(wparam)) {
349         case SB_LINEUP:    // == SB_LINELEFT
350             wheelDelta = WHEEL_DELTA;
351             break;
352         case SB_LINEDOWN:  // == SB_LINERIGHT
353             wheelDelta = -WHEEL_DELTA;
354             break;
355         case SB_PAGEUP:
356             wheelDelta = 1;
357             result.scrollByPage = true;
358             break;
359         case SB_PAGEDOWN:
360             wheelDelta = -1;
361             result.scrollByPage = true;
362             break;
363         default:  // We don't supoprt SB_THUMBPOSITION or SB_THUMBTRACK here.
364             wheelDelta = 0;
365             break;
366         }
367 
368         if (message == WM_HSCROLL)
369             horizontalScroll = true;
370     } else {
371         // Non-synthesized event; we can just read data off the event.
372         getKeyState = GetKeyState;
373         keyState = GET_KEYSTATE_WPARAM(wparam);
374 
375         result.globalX = static_cast<short>(LOWORD(lparam));
376         result.globalY = static_cast<short>(HIWORD(lparam));
377 
378         wheelDelta = static_cast<float>(GET_WHEEL_DELTA_WPARAM(wparam));
379         if (message == WM_MOUSEHWHEEL) {
380             horizontalScroll = true;
381             wheelDelta = -wheelDelta;  // Windows is <- -/+ ->, WebKit <- +/- ->.
382         }
383     }
384     if (keyState & MK_SHIFT)
385         horizontalScroll = true;
386 
387     // Set modifiers based on key state.
388     if (keyState & MK_SHIFT)
389         result.modifiers |= WebInputEvent::ShiftKey;
390     if (keyState & MK_CONTROL)
391         result.modifiers |= WebInputEvent::ControlKey;
392     if (getKeyState(VK_MENU) & 0x8000)
393         result.modifiers |= WebInputEvent::AltKey;
394     if (keyState & MK_LBUTTON)
395         result.modifiers |= WebInputEvent::LeftButtonDown;
396     if (keyState & MK_MBUTTON)
397         result.modifiers |= WebInputEvent::MiddleButtonDown;
398     if (keyState & MK_RBUTTON)
399         result.modifiers |= WebInputEvent::RightButtonDown;
400 
401     SetToggleKeyState(&result);
402 
403     // Set coordinates by translating event coordinates from screen to client.
404     POINT clientPoint = { result.globalX, result.globalY };
405     MapWindowPoints(0, hwnd, &clientPoint, 1);
406     result.x = clientPoint.x;
407     result.y = clientPoint.y;
408     result.windowX = result.x;
409     result.windowY = result.y;
410 
411     // Convert wheel delta amount to a number of pixels to scroll.
412     //
413     // How many pixels should we scroll per line?  Gecko uses the height of the
414     // current line, which means scroll distance changes as you go through the
415     // page or go to different pages.  IE 8 is ~60 px/line, although the value
416     // seems to vary slightly by page and zoom level.  Also, IE defaults to
417     // smooth scrolling while Firefox doesn't, so it can get away with somewhat
418     // larger scroll values without feeling as jerky.  Here we use 100 px per
419     // three lines (the default scroll amount is three lines per wheel tick).
420     // Even though we have smooth scrolling, we don't make this as large as IE
421     // because subjectively IE feels like it scrolls farther than you want while
422     // reading articles.
423     static const float scrollbarPixelsPerLine = 100.0f / 3.0f;
424     wheelDelta /= WHEEL_DELTA;
425     float scrollDelta = wheelDelta;
426     if (horizontalScroll) {
427         unsigned long scrollChars = defaultScrollCharsPerWheelDelta;
428         SystemParametersInfo(SPI_GETWHEELSCROLLCHARS, 0, &scrollChars, 0);
429         // TODO(pkasting): Should probably have a different multiplier
430         // scrollbarPixelsPerChar here.
431         scrollDelta *= static_cast<float>(scrollChars) * scrollbarPixelsPerLine;
432     } else {
433         unsigned long scrollLines = defaultScrollLinesPerWheelDelta;
434         SystemParametersInfo(SPI_GETWHEELSCROLLLINES, 0, &scrollLines, 0);
435         if (scrollLines == WHEEL_PAGESCROLL)
436             result.scrollByPage = true;
437         if (!result.scrollByPage)
438             scrollDelta *= static_cast<float>(scrollLines) * scrollbarPixelsPerLine;
439     }
440 
441     // Set scroll amount based on above calculations.  WebKit expects positive
442     // deltaY to mean "scroll up" and positive deltaX to mean "scroll left".
443     if (horizontalScroll) {
444         result.deltaX = scrollDelta;
445         result.wheelTicksX = wheelDelta;
446     } else {
447         result.deltaY = scrollDelta;
448         result.wheelTicksY = wheelDelta;
449     }
450 
451     return result;
452 }
453 
454 } // namespace WebKit
455