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