• 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 "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