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