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 "KeyboardCodes.h"
35 #include "KeyCodeConversion.h"
36
37 #include "WebInputEvent.h"
38
39 #include <gdk/gdk.h>
40 #include <gdk/gdkkeysyms.h>
41 #include <gtk/gtk.h>
42 #include <gtk/gtkversion.h>
43
44 #include <wtf/Assertions.h>
45
46 namespace {
47
getDoubleClickTime()48 gint getDoubleClickTime()
49 {
50 static GtkSettings* settings = gtk_settings_get_default();
51 gint doubleClickTime = 250;
52 g_object_get(G_OBJECT(settings), "gtk-double-click-time", &doubleClickTime, 0);
53 return doubleClickTime;
54 }
55
56 } // namespace
57
58 namespace WebKit {
59
gdkEventTimeToWebEventTime(guint32 time)60 static double gdkEventTimeToWebEventTime(guint32 time)
61 {
62 // Convert from time in ms to time in sec.
63 return time / 1000.0;
64 }
65
gdkStateToWebEventModifiers(guint state)66 static int gdkStateToWebEventModifiers(guint state)
67 {
68 int modifiers = 0;
69 if (state & GDK_SHIFT_MASK)
70 modifiers |= WebInputEvent::ShiftKey;
71 if (state & GDK_CONTROL_MASK)
72 modifiers |= WebInputEvent::ControlKey;
73 if (state & GDK_MOD1_MASK)
74 modifiers |= WebInputEvent::AltKey;
75 #if GTK_CHECK_VERSION(2, 10, 0)
76 if (state & GDK_META_MASK)
77 modifiers |= WebInputEvent::MetaKey;
78 #endif
79 if (state & GDK_BUTTON1_MASK)
80 modifiers |= WebInputEvent::LeftButtonDown;
81 if (state & GDK_BUTTON2_MASK)
82 modifiers |= WebInputEvent::MiddleButtonDown;
83 if (state & GDK_BUTTON3_MASK)
84 modifiers |= WebInputEvent::RightButtonDown;
85 return modifiers;
86 }
87
gdkEventToWindowsKeyCode(const GdkEventKey * event)88 static int gdkEventToWindowsKeyCode(const GdkEventKey* event)
89 {
90 static const unsigned int hardwareCodeToGDKKeyval[] = {
91 0, // 0x00:
92 0, // 0x01:
93 0, // 0x02:
94 0, // 0x03:
95 0, // 0x04:
96 0, // 0x05:
97 0, // 0x06:
98 0, // 0x07:
99 0, // 0x08:
100 0, // 0x09: GDK_Escape
101 GDK_1, // 0x0A: GDK_1
102 GDK_2, // 0x0B: GDK_2
103 GDK_3, // 0x0C: GDK_3
104 GDK_4, // 0x0D: GDK_4
105 GDK_5, // 0x0E: GDK_5
106 GDK_6, // 0x0F: GDK_6
107 GDK_7, // 0x10: GDK_7
108 GDK_8, // 0x11: GDK_8
109 GDK_9, // 0x12: GDK_9
110 GDK_0, // 0x13: GDK_0
111 GDK_minus, // 0x14: GDK_minus
112 GDK_equal, // 0x15: GDK_equal
113 0, // 0x16: GDK_BackSpace
114 0, // 0x17: GDK_Tab
115 GDK_q, // 0x18: GDK_q
116 GDK_w, // 0x19: GDK_w
117 GDK_e, // 0x1A: GDK_e
118 GDK_r, // 0x1B: GDK_r
119 GDK_t, // 0x1C: GDK_t
120 GDK_y, // 0x1D: GDK_y
121 GDK_u, // 0x1E: GDK_u
122 GDK_i, // 0x1F: GDK_i
123 GDK_o, // 0x20: GDK_o
124 GDK_p, // 0x21: GDK_p
125 GDK_bracketleft, // 0x22: GDK_bracketleft
126 GDK_bracketright, // 0x23: GDK_bracketright
127 0, // 0x24: GDK_Return
128 0, // 0x25: GDK_Control_L
129 GDK_a, // 0x26: GDK_a
130 GDK_s, // 0x27: GDK_s
131 GDK_d, // 0x28: GDK_d
132 GDK_f, // 0x29: GDK_f
133 GDK_g, // 0x2A: GDK_g
134 GDK_h, // 0x2B: GDK_h
135 GDK_j, // 0x2C: GDK_j
136 GDK_k, // 0x2D: GDK_k
137 GDK_l, // 0x2E: GDK_l
138 GDK_semicolon, // 0x2F: GDK_semicolon
139 GDK_apostrophe, // 0x30: GDK_apostrophe
140 GDK_grave, // 0x31: GDK_grave
141 0, // 0x32: GDK_Shift_L
142 GDK_backslash, // 0x33: GDK_backslash
143 GDK_z, // 0x34: GDK_z
144 GDK_x, // 0x35: GDK_x
145 GDK_c, // 0x36: GDK_c
146 GDK_v, // 0x37: GDK_v
147 GDK_b, // 0x38: GDK_b
148 GDK_n, // 0x39: GDK_n
149 GDK_m, // 0x3A: GDK_m
150 GDK_comma, // 0x3B: GDK_comma
151 GDK_period, // 0x3C: GDK_period
152 GDK_slash, // 0x3D: GDK_slash
153 0, // 0x3E: GDK_Shift_R
154 };
155
156 // |windowsKeyCode| has to include a valid virtual-key code even when we
157 // use non-US layouts, e.g. even when we type an 'A' key of a US keyboard
158 // on the Hebrew layout, |windowsKeyCode| should be VK_A.
159 // On the other hand, |event->keyval| value depends on the current
160 // GdkKeymap object, i.e. when we type an 'A' key of a US keyboard on
161 // the Hebrew layout, |event->keyval| becomes GDK_hebrew_shin and this
162 // WebCore::windowsKeyCodeForKeyEvent() call returns 0.
163 // To improve compatibilty with Windows, we use |event->hardware_keycode|
164 // for retrieving its Windows key-code for the keys when the
165 // WebCore::windowsKeyCodeForEvent() call returns 0.
166 // We shouldn't use |event->hardware_keycode| for keys that GdkKeymap
167 // objects cannot change because |event->hardware_keycode| doesn't change
168 // even when we change the layout options, e.g. when we swap a control
169 // key and a caps-lock key, GTK doesn't swap their
170 // |event->hardware_keycode| values but swap their |event->keyval| values.
171 int windowsKeyCode = WebCore::windowsKeyCodeForKeyEvent(event->keyval);
172 if (windowsKeyCode)
173 return windowsKeyCode;
174
175 const int tableSize = sizeof(hardwareCodeToGDKKeyval) / sizeof(hardwareCodeToGDKKeyval[0]);
176 if (event->hardware_keycode < tableSize) {
177 int keyval = hardwareCodeToGDKKeyval[event->hardware_keycode];
178 if (keyval)
179 return WebCore::windowsKeyCodeForKeyEvent(keyval);
180 }
181
182 // This key is one that keyboard-layout drivers cannot change.
183 // Use |event->keyval| to retrieve its |windowsKeyCode| value.
184 return WebCore::windowsKeyCodeForKeyEvent(event->keyval);
185 }
186
187 // Gets the corresponding control character of a specified key code. See:
188 // http://en.wikipedia.org/wiki/Control_characters
189 // We emulate Windows behavior here.
getControlCharacter(int windowsKeyCode,bool shift)190 static WebUChar getControlCharacter(int windowsKeyCode, bool shift)
191 {
192 if (windowsKeyCode >= WebCore::VKEY_A && windowsKeyCode <= WebCore::VKEY_Z) {
193 // ctrl-A ~ ctrl-Z map to \x01 ~ \x1A
194 return windowsKeyCode - WebCore::VKEY_A + 1;
195 }
196 if (shift) {
197 // following graphics chars require shift key to input.
198 switch (windowsKeyCode) {
199 // ctrl-@ maps to \x00 (Null byte)
200 case WebCore::VKEY_2:
201 return 0;
202 // ctrl-^ maps to \x1E (Record separator, Information separator two)
203 case WebCore::VKEY_6:
204 return 0x1E;
205 // ctrl-_ maps to \x1F (Unit separator, Information separator one)
206 case WebCore::VKEY_OEM_MINUS:
207 return 0x1F;
208 // Returns 0 for all other keys to avoid inputting unexpected chars.
209 default:
210 return 0;
211 }
212 } else {
213 switch (windowsKeyCode) {
214 // ctrl-[ maps to \x1B (Escape)
215 case WebCore::VKEY_OEM_4:
216 return 0x1B;
217 // ctrl-\ maps to \x1C (File separator, Information separator four)
218 case WebCore::VKEY_OEM_5:
219 return 0x1C;
220 // ctrl-] maps to \x1D (Group separator, Information separator three)
221 case WebCore::VKEY_OEM_6:
222 return 0x1D;
223 // ctrl-Enter maps to \x0A (Line feed)
224 case WebCore::VKEY_RETURN:
225 return 0x0A;
226 // Returns 0 for all other keys to avoid inputting unexpected chars.
227 default:
228 return 0;
229 }
230 }
231 }
232
233 // WebKeyboardEvent -----------------------------------------------------------
234
keyboardEvent(const GdkEventKey * event)235 WebKeyboardEvent WebInputEventFactory::keyboardEvent(const GdkEventKey* event)
236 {
237 WebKeyboardEvent result;
238
239 result.timeStampSeconds = gdkEventTimeToWebEventTime(event->time);
240 result.modifiers = gdkStateToWebEventModifiers(event->state);
241
242 switch (event->type) {
243 case GDK_KEY_RELEASE:
244 result.type = WebInputEvent::KeyUp;
245 break;
246 case GDK_KEY_PRESS:
247 result.type = WebInputEvent::RawKeyDown;
248 break;
249 default:
250 ASSERT_NOT_REACHED();
251 }
252
253 // According to MSDN:
254 // http://msdn.microsoft.com/en-us/library/ms646286(VS.85).aspx
255 // Key events with Alt modifier and F10 are system key events.
256 // We just emulate this behavior. It's necessary to prevent webkit from
257 // processing keypress event generated by alt-d, etc.
258 // F10 is not special on Linux, so don't treat it as system key.
259 if (result.modifiers & WebInputEvent::AltKey)
260 result.isSystemKey = true;
261
262 // The key code tells us which physical key was pressed (for example, the
263 // A key went down or up). It does not determine whether A should be lower
264 // or upper case. This is what text does, which should be the keyval.
265 result.windowsKeyCode = gdkEventToWindowsKeyCode(event);
266 result.nativeKeyCode = event->hardware_keycode;
267
268 if (result.windowsKeyCode == WebCore::VKEY_RETURN)
269 // We need to treat the enter key as a key press of character \r. This
270 // is apparently just how webkit handles it and what it expects.
271 result.unmodifiedText[0] = '\r';
272 else
273 // FIXME: fix for non BMP chars
274 result.unmodifiedText[0] =
275 static_cast<WebUChar>(gdk_keyval_to_unicode(event->keyval));
276
277 // If ctrl key is pressed down, then control character shall be input.
278 if (result.modifiers & WebInputEvent::ControlKey)
279 result.text[0] = getControlCharacter(
280 result.windowsKeyCode, result.modifiers & WebInputEvent::ShiftKey);
281 else
282 result.text[0] = result.unmodifiedText[0];
283
284 result.setKeyIdentifierFromWindowsKeyCode();
285
286 // FIXME: Do we need to set IsAutoRepeat or IsKeyPad?
287
288 return result;
289 }
290
keyboardEvent(wchar_t character,int state,double timeStampSeconds)291 WebKeyboardEvent WebInputEventFactory::keyboardEvent(wchar_t character, int state, double timeStampSeconds)
292 {
293 // keyboardEvent(const GdkEventKey*) depends on the GdkEventKey object and
294 // it is hard to use/ it from signal handlers which don't use GdkEventKey
295 // objects (e.g. GtkIMContext signal handlers.) For such handlers, this
296 // function creates a WebInputEvent::Char event without using a
297 // GdkEventKey object.
298 WebKeyboardEvent result;
299 result.type = WebKit::WebInputEvent::Char;
300 result.timeStampSeconds = timeStampSeconds;
301 result.modifiers = gdkStateToWebEventModifiers(state);
302 result.windowsKeyCode = character;
303 result.nativeKeyCode = character;
304 result.text[0] = character;
305 result.unmodifiedText[0] = character;
306
307 // According to MSDN:
308 // http://msdn.microsoft.com/en-us/library/ms646286(VS.85).aspx
309 // Key events with Alt modifier and F10 are system key events.
310 // We just emulate this behavior. It's necessary to prevent webkit from
311 // processing keypress event generated by alt-d, etc.
312 // F10 is not special on Linux, so don't treat it as system key.
313 if (result.modifiers & WebInputEvent::AltKey)
314 result.isSystemKey = true;
315
316 return result;
317 }
318
319 // WebMouseEvent --------------------------------------------------------------
320
mouseEvent(const GdkEventButton * event)321 WebMouseEvent WebInputEventFactory::mouseEvent(const GdkEventButton* event)
322 {
323 WebMouseEvent result;
324
325 result.timeStampSeconds = gdkEventTimeToWebEventTime(event->time);
326
327 result.modifiers = gdkStateToWebEventModifiers(event->state);
328 result.x = static_cast<int>(event->x);
329 result.y = static_cast<int>(event->y);
330 result.windowX = result.x;
331 result.windowY = result.y;
332 result.globalX = static_cast<int>(event->x_root);
333 result.globalY = static_cast<int>(event->y_root);
334 result.clickCount = 0;
335
336 switch (event->type) {
337 case GDK_BUTTON_PRESS:
338 result.type = WebInputEvent::MouseDown;
339 break;
340 case GDK_BUTTON_RELEASE:
341 result.type = WebInputEvent::MouseUp;
342 break;
343 case GDK_3BUTTON_PRESS:
344 case GDK_2BUTTON_PRESS:
345 default:
346 ASSERT_NOT_REACHED();
347 };
348
349 if (GDK_BUTTON_PRESS == event->type) {
350 static int numClicks = 0;
351 static GdkWindow* eventWindow = 0;
352 static gint lastLeftClickTime = 0;
353
354 gint time_diff = event->time - lastLeftClickTime;
355 if (eventWindow == event->window && time_diff < getDoubleClickTime())
356 numClicks++;
357 else
358 numClicks = 1;
359
360 result.clickCount = numClicks;
361 eventWindow = event->window;
362 lastLeftClickTime = event->time;
363 }
364
365 result.button = WebMouseEvent::ButtonNone;
366 if (event->button == 1)
367 result.button = WebMouseEvent::ButtonLeft;
368 else if (event->button == 2)
369 result.button = WebMouseEvent::ButtonMiddle;
370 else if (event->button == 3)
371 result.button = WebMouseEvent::ButtonRight;
372
373 return result;
374 }
375
mouseEvent(const GdkEventMotion * event)376 WebMouseEvent WebInputEventFactory::mouseEvent(const GdkEventMotion* event)
377 {
378 WebMouseEvent result;
379
380 result.timeStampSeconds = gdkEventTimeToWebEventTime(event->time);
381 result.modifiers = gdkStateToWebEventModifiers(event->state);
382 result.x = static_cast<int>(event->x);
383 result.y = static_cast<int>(event->y);
384 result.windowX = result.x;
385 result.windowY = result.y;
386 result.globalX = static_cast<int>(event->x_root);
387 result.globalY = static_cast<int>(event->y_root);
388
389 switch (event->type) {
390 case GDK_MOTION_NOTIFY:
391 result.type = WebInputEvent::MouseMove;
392 break;
393 default:
394 ASSERT_NOT_REACHED();
395 }
396
397 result.button = WebMouseEvent::ButtonNone;
398 if (event->state & GDK_BUTTON1_MASK)
399 result.button = WebMouseEvent::ButtonLeft;
400 else if (event->state & GDK_BUTTON2_MASK)
401 result.button = WebMouseEvent::ButtonMiddle;
402 else if (event->state & GDK_BUTTON3_MASK)
403 result.button = WebMouseEvent::ButtonRight;
404
405 return result;
406 }
407
mouseEvent(const GdkEventCrossing * event)408 WebMouseEvent WebInputEventFactory::mouseEvent(const GdkEventCrossing* event)
409 {
410 WebMouseEvent result;
411
412 result.timeStampSeconds = gdkEventTimeToWebEventTime(event->time);
413 result.modifiers = gdkStateToWebEventModifiers(event->state);
414 result.x = static_cast<int>(event->x);
415 result.y = static_cast<int>(event->y);
416 result.windowX = result.x;
417 result.windowY = result.y;
418 result.globalX = static_cast<int>(event->x_root);
419 result.globalY = static_cast<int>(event->y_root);
420
421 switch (event->type) {
422 case GDK_ENTER_NOTIFY:
423 case GDK_LEAVE_NOTIFY:
424 // Note that if we sent MouseEnter or MouseLeave to WebKit, it
425 // wouldn't work - they don't result in the proper JavaScript events.
426 // MouseMove does the right thing.
427 result.type = WebInputEvent::MouseMove;
428 break;
429 default:
430 ASSERT_NOT_REACHED();
431 }
432
433 result.button = WebMouseEvent::ButtonNone;
434 if (event->state & GDK_BUTTON1_MASK)
435 result.button = WebMouseEvent::ButtonLeft;
436 else if (event->state & GDK_BUTTON2_MASK)
437 result.button = WebMouseEvent::ButtonMiddle;
438 else if (event->state & GDK_BUTTON3_MASK)
439 result.button = WebMouseEvent::ButtonRight;
440
441 return result;
442 }
443
444 // WebMouseWheelEvent ---------------------------------------------------------
445
mouseWheelEvent(const GdkEventScroll * event)446 WebMouseWheelEvent WebInputEventFactory::mouseWheelEvent(const GdkEventScroll* event)
447 {
448 WebMouseWheelEvent result;
449
450 result.type = WebInputEvent::MouseWheel;
451 result.button = WebMouseEvent::ButtonNone;
452
453 result.timeStampSeconds = gdkEventTimeToWebEventTime(event->time);
454 result.modifiers = gdkStateToWebEventModifiers(event->state);
455 result.x = static_cast<int>(event->x);
456 result.y = static_cast<int>(event->y);
457 result.windowX = result.x;
458 result.windowY = result.y;
459 result.globalX = static_cast<int>(event->x_root);
460 result.globalY = static_cast<int>(event->y_root);
461
462 // How much should we scroll per mouse wheel event?
463 // - Windows uses 3 lines by default and obeys a system setting.
464 // - Mozilla has a pref that lets you either use the "system" number of lines
465 // to scroll, or lets the user override it.
466 // For the "system" number of lines, it appears they've hardcoded 3.
467 // See case NS_MOUSE_SCROLL in content/events/src/nsEventStateManager.cpp
468 // and InitMouseScrollEvent in widget/src/gtk2/nsCommonWidget.cpp .
469 // - Gtk makes the scroll amount a function of the size of the scroll bar,
470 // which is not available to us here.
471 // Instead, we pick a number that empirically matches Firefox's behavior.
472 static const float scrollbarPixelsPerTick = 160.0f / 3.0f;
473
474 switch (event->direction) {
475 case GDK_SCROLL_UP:
476 result.deltaY = scrollbarPixelsPerTick;
477 result.wheelTicksY = 1;
478 break;
479 case GDK_SCROLL_DOWN:
480 result.deltaY = -scrollbarPixelsPerTick;
481 result.wheelTicksY = -1;
482 break;
483 case GDK_SCROLL_LEFT:
484 result.deltaX = scrollbarPixelsPerTick;
485 result.wheelTicksX = 1;
486 break;
487 case GDK_SCROLL_RIGHT:
488 result.deltaX = -scrollbarPixelsPerTick;
489 result.wheelTicksX = -1;
490 break;
491 }
492
493 return result;
494 }
495
496 } // namespace WebKit
497