Home | History | Annotate | Download | only in gtk
      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 
     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 
     60 static double gdkEventTimeToWebEventTime(guint32 time)
     61 {
     62     // Convert from time in ms to time in sec.
     63     return time / 1000.0;
     64 }
     65 
     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 
     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.
    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 
    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 
    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 
    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 
    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 
    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 
    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