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 "core/platform/chromium/KeyCodeConversion.h"
     35 #include "core/platform/chromium/KeyboardCodes.h"
     36 
     37 #include "WebInputEvent.h"
     38 
     39 #include <gdk/gdk.h>
     40 #include <gdk/gdkkeysyms.h>
     41 #include <gtk/gtk.h>
     42 
     43 #include "wtf/Assertions.h"
     44 
     45 namespace {
     46 
     47 // For click count tracking.
     48 static int gNumClicks = 0;
     49 static GdkWindow* gLastClickEventWindow = 0;
     50 static gint gLastClickTime = 0;
     51 static gint gLastClickX = 0;
     52 static gint gLastClickY = 0;
     53 static WebKit::WebMouseEvent::Button gLastClickButton = WebKit::WebMouseEvent::ButtonNone;
     54 
     55 bool shouldForgetPreviousClick(GdkWindow* window, gint time, gint x, gint y)
     56 {
     57     static GtkSettings* settings = gtk_settings_get_default();
     58 
     59     if (window != gLastClickEventWindow)
     60       return true;
     61 
     62     gint doubleClickTime = 250;
     63     gint doubleClickDistance = 5;
     64     g_object_get(G_OBJECT(settings),
     65                  "gtk-double-click-time", &doubleClickTime,
     66                  "gtk-double-click-distance", &doubleClickDistance, NULL);
     67     return (time - gLastClickTime) > doubleClickTime
     68            || abs(x - gLastClickX) > doubleClickDistance
     69            || abs(y - gLastClickY) > doubleClickDistance;
     70 }
     71 
     72 void resetClickCountState()
     73 {
     74     gNumClicks = 0;
     75     gLastClickEventWindow = 0;
     76     gLastClickTime = 0;
     77     gLastClickX = 0;
     78     gLastClickY = 0;
     79     gLastClickButton = WebKit::WebMouseEvent::ButtonNone;
     80 }
     81 
     82 bool isKeyPadKeyval(guint keyval)
     83 {
     84     // Keypad keyvals all fall into one range.
     85     return keyval >= GDK_KP_Space && keyval <= GDK_KP_9;
     86 }
     87 
     88 }  // namespace
     89 
     90 namespace WebKit {
     91 
     92 static double gdkEventTimeToWebEventTime(guint32 time)
     93 {
     94     // Convert from time in ms to time in sec.
     95     return time / 1000.0;
     96 }
     97 
     98 static int gdkStateToWebEventModifiers(guint state)
     99 {
    100     int modifiers = 0;
    101     if (state & GDK_SHIFT_MASK)
    102         modifiers |= WebInputEvent::ShiftKey;
    103     if (state & GDK_CONTROL_MASK)
    104         modifiers |= WebInputEvent::ControlKey;
    105     if (state & GDK_MOD1_MASK)
    106         modifiers |= WebInputEvent::AltKey;
    107     if (state & GDK_META_MASK)
    108         modifiers |= WebInputEvent::MetaKey;
    109     if (state & GDK_BUTTON1_MASK)
    110         modifiers |= WebInputEvent::LeftButtonDown;
    111     if (state & GDK_BUTTON2_MASK)
    112         modifiers |= WebInputEvent::MiddleButtonDown;
    113     if (state & GDK_BUTTON3_MASK)
    114         modifiers |= WebInputEvent::RightButtonDown;
    115     if (state & GDK_LOCK_MASK)
    116         modifiers |= WebInputEvent::CapsLockOn;
    117     if (state & GDK_MOD2_MASK)
    118         modifiers |= WebInputEvent::NumLockOn;
    119     return modifiers;
    120 }
    121 
    122 static int gdkEventToWindowsKeyCode(const GdkEventKey* event)
    123 {
    124     static const unsigned int hardwareCodeToGDKKeyval[] = {
    125         0,                 // 0x00:
    126         0,                 // 0x01:
    127         0,                 // 0x02:
    128         0,                 // 0x03:
    129         0,                 // 0x04:
    130         0,                 // 0x05:
    131         0,                 // 0x06:
    132         0,                 // 0x07:
    133         0,                 // 0x08:
    134         0,                 // 0x09: GDK_Escape
    135         GDK_1,             // 0x0A: GDK_1
    136         GDK_2,             // 0x0B: GDK_2
    137         GDK_3,             // 0x0C: GDK_3
    138         GDK_4,             // 0x0D: GDK_4
    139         GDK_5,             // 0x0E: GDK_5
    140         GDK_6,             // 0x0F: GDK_6
    141         GDK_7,             // 0x10: GDK_7
    142         GDK_8,             // 0x11: GDK_8
    143         GDK_9,             // 0x12: GDK_9
    144         GDK_0,             // 0x13: GDK_0
    145         GDK_minus,         // 0x14: GDK_minus
    146         GDK_equal,         // 0x15: GDK_equal
    147         0,                 // 0x16: GDK_BackSpace
    148         0,                 // 0x17: GDK_Tab
    149         GDK_q,             // 0x18: GDK_q
    150         GDK_w,             // 0x19: GDK_w
    151         GDK_e,             // 0x1A: GDK_e
    152         GDK_r,             // 0x1B: GDK_r
    153         GDK_t,             // 0x1C: GDK_t
    154         GDK_y,             // 0x1D: GDK_y
    155         GDK_u,             // 0x1E: GDK_u
    156         GDK_i,             // 0x1F: GDK_i
    157         GDK_o,             // 0x20: GDK_o
    158         GDK_p,             // 0x21: GDK_p
    159         GDK_bracketleft,   // 0x22: GDK_bracketleft
    160         GDK_bracketright,  // 0x23: GDK_bracketright
    161         0,                 // 0x24: GDK_Return
    162         0,                 // 0x25: GDK_Control_L
    163         GDK_a,             // 0x26: GDK_a
    164         GDK_s,             // 0x27: GDK_s
    165         GDK_d,             // 0x28: GDK_d
    166         GDK_f,             // 0x29: GDK_f
    167         GDK_g,             // 0x2A: GDK_g
    168         GDK_h,             // 0x2B: GDK_h
    169         GDK_j,             // 0x2C: GDK_j
    170         GDK_k,             // 0x2D: GDK_k
    171         GDK_l,             // 0x2E: GDK_l
    172         GDK_semicolon,     // 0x2F: GDK_semicolon
    173         GDK_apostrophe,    // 0x30: GDK_apostrophe
    174         GDK_grave,         // 0x31: GDK_grave
    175         0,                 // 0x32: GDK_Shift_L
    176         GDK_backslash,     // 0x33: GDK_backslash
    177         GDK_z,             // 0x34: GDK_z
    178         GDK_x,             // 0x35: GDK_x
    179         GDK_c,             // 0x36: GDK_c
    180         GDK_v,             // 0x37: GDK_v
    181         GDK_b,             // 0x38: GDK_b
    182         GDK_n,             // 0x39: GDK_n
    183         GDK_m,             // 0x3A: GDK_m
    184         GDK_comma,         // 0x3B: GDK_comma
    185         GDK_period,        // 0x3C: GDK_period
    186         GDK_slash,         // 0x3D: GDK_slash
    187         0,                 // 0x3E: GDK_Shift_R
    188         0,                 // 0x3F:
    189         0,                 // 0x40:
    190         0,                 // 0x41:
    191         0,                 // 0x42:
    192         0,                 // 0x43:
    193         0,                 // 0x44:
    194         0,                 // 0x45:
    195         0,                 // 0x46:
    196         0,                 // 0x47:
    197         0,                 // 0x48:
    198         0,                 // 0x49:
    199         0,                 // 0x4A:
    200         0,                 // 0x4B:
    201         0,                 // 0x4C:
    202         0,                 // 0x4D:
    203         0,                 // 0x4E:
    204         0,                 // 0x4F:
    205         0,                 // 0x50:
    206         0,                 // 0x51:
    207         0,                 // 0x52:
    208         0,                 // 0x53:
    209         0,                 // 0x54:
    210         0,                 // 0x55:
    211         0,                 // 0x56:
    212         0,                 // 0x57:
    213         0,                 // 0x58:
    214         0,                 // 0x59:
    215         0,                 // 0x5A:
    216         0,                 // 0x5B:
    217         0,                 // 0x5C:
    218         0,                 // 0x5D:
    219         0,                 // 0x5E:
    220         0,                 // 0x5F:
    221         0,                 // 0x60:
    222         0,                 // 0x61:
    223         0,                 // 0x62:
    224         0,                 // 0x63:
    225         0,                 // 0x64:
    226         0,                 // 0x65:
    227         0,                 // 0x66:
    228         0,                 // 0x67:
    229         0,                 // 0x68:
    230         0,                 // 0x69:
    231         0,                 // 0x6A:
    232         0,                 // 0x6B:
    233         0,                 // 0x6C:
    234         0,                 // 0x6D:
    235         0,                 // 0x6E:
    236         0,                 // 0x6F:
    237         0,                 // 0x70:
    238         0,                 // 0x71:
    239         0,                 // 0x72:
    240         GDK_Super_L,       // 0x73: GDK_Super_L
    241         GDK_Super_R,       // 0x74: GDK_Super_R
    242     };
    243 
    244     // |windowsKeyCode| has to include a valid virtual-key code even when we
    245     // use non-US layouts, e.g. even when we type an 'A' key of a US keyboard
    246     // on the Hebrew layout, |windowsKeyCode| should be VK_A.
    247     // On the other hand, |event->keyval| value depends on the current
    248     // GdkKeymap object, i.e. when we type an 'A' key of a US keyboard on
    249     // the Hebrew layout, |event->keyval| becomes GDK_hebrew_shin and this
    250     // WebCore::windowsKeyCodeForKeyEvent() call returns 0.
    251     // To improve compatibilty with Windows, we use |event->hardware_keycode|
    252     // for retrieving its Windows key-code for the keys when the
    253     // WebCore::windowsKeyCodeForEvent() call returns 0.
    254     // We shouldn't use |event->hardware_keycode| for keys that GdkKeymap
    255     // objects cannot change because |event->hardware_keycode| doesn't change
    256     // even when we change the layout options, e.g. when we swap a control
    257     // key and a caps-lock key, GTK doesn't swap their
    258     // |event->hardware_keycode| values but swap their |event->keyval| values.
    259     int windowsKeyCode = WebCore::windowsKeyCodeForKeyEvent(event->keyval);
    260     if (windowsKeyCode)
    261         return windowsKeyCode;
    262 
    263     const int tableSize = sizeof(hardwareCodeToGDKKeyval) / sizeof(hardwareCodeToGDKKeyval[0]);
    264     if (event->hardware_keycode < tableSize) {
    265         int keyval = hardwareCodeToGDKKeyval[event->hardware_keycode];
    266         if (keyval)
    267             return WebCore::windowsKeyCodeForKeyEvent(keyval);
    268     }
    269 
    270     // This key is one that keyboard-layout drivers cannot change.
    271     // Use |event->keyval| to retrieve its |windowsKeyCode| value.
    272     return WebCore::windowsKeyCodeForKeyEvent(event->keyval);
    273 }
    274 
    275 // Normalizes event->state to make it Windows/Mac compatible. Since the way
    276 // of setting modifier mask on X is very different than Windows/Mac as shown
    277 // in http://crbug.com/127142#c8, the normalization is necessary.
    278 static guint normalizeEventState(const GdkEventKey* event)
    279 {
    280     guint mask = 0;
    281     switch (gdkEventToWindowsKeyCode(event)) {
    282     case WebCore::VKEY_CONTROL:
    283     case WebCore::VKEY_LCONTROL:
    284     case WebCore::VKEY_RCONTROL:
    285         mask = GDK_CONTROL_MASK;
    286         break;
    287     case WebCore::VKEY_SHIFT:
    288     case WebCore::VKEY_LSHIFT:
    289     case WebCore::VKEY_RSHIFT:
    290         mask = GDK_SHIFT_MASK;
    291         break;
    292     case WebCore::VKEY_MENU:
    293     case WebCore::VKEY_LMENU:
    294     case WebCore::VKEY_RMENU:
    295         mask = GDK_MOD1_MASK;
    296         break;
    297     case WebCore::VKEY_CAPITAL:
    298         mask = GDK_LOCK_MASK;
    299         break;
    300     default:
    301         return event->state;
    302     }
    303     if (event->type == GDK_KEY_PRESS)
    304         return event->state | mask;
    305     return event->state & ~mask;
    306 }
    307 
    308 // Gets the corresponding control character of a specified key code. See:
    309 // http://en.wikipedia.org/wiki/Control_characters
    310 // We emulate Windows behavior here.
    311 static WebUChar getControlCharacter(int windowsKeyCode, bool shift)
    312 {
    313     if (windowsKeyCode >= WebCore::VKEY_A && windowsKeyCode <= WebCore::VKEY_Z) {
    314         // ctrl-A ~ ctrl-Z map to \x01 ~ \x1A
    315         return windowsKeyCode - WebCore::VKEY_A + 1;
    316     }
    317     if (shift) {
    318         // following graphics chars require shift key to input.
    319         switch (windowsKeyCode) {
    320         // ctrl-@ maps to \x00 (Null byte)
    321         case WebCore::VKEY_2:
    322             return 0;
    323         // ctrl-^ maps to \x1E (Record separator, Information separator two)
    324         case WebCore::VKEY_6:
    325             return 0x1E;
    326         // ctrl-_ maps to \x1F (Unit separator, Information separator one)
    327         case WebCore::VKEY_OEM_MINUS:
    328             return 0x1F;
    329         // Returns 0 for all other keys to avoid inputting unexpected chars.
    330         default:
    331             return 0;
    332         }
    333     } else {
    334         switch (windowsKeyCode) {
    335         // ctrl-[ maps to \x1B (Escape)
    336         case WebCore::VKEY_OEM_4:
    337             return 0x1B;
    338         // ctrl-\ maps to \x1C (File separator, Information separator four)
    339         case WebCore::VKEY_OEM_5:
    340             return 0x1C;
    341         // ctrl-] maps to \x1D (Group separator, Information separator three)
    342         case WebCore::VKEY_OEM_6:
    343             return 0x1D;
    344         // ctrl-Enter maps to \x0A (Line feed)
    345         case WebCore::VKEY_RETURN:
    346             return 0x0A;
    347         // Returns 0 for all other keys to avoid inputting unexpected chars.
    348         default:
    349             return 0;
    350         }
    351     }
    352 }
    353 
    354 // WebKeyboardEvent -----------------------------------------------------------
    355 
    356 WebKeyboardEvent WebInputEventFactory::keyboardEvent(const GdkEventKey* event)
    357 {
    358     WebKeyboardEvent result;
    359 
    360     result.timeStampSeconds = gdkEventTimeToWebEventTime(event->time);
    361     result.modifiers = gdkStateToWebEventModifiers(normalizeEventState(event));
    362 
    363     switch (event->type) {
    364     case GDK_KEY_RELEASE:
    365         result.type = WebInputEvent::KeyUp;
    366         break;
    367     case GDK_KEY_PRESS:
    368         result.type = WebInputEvent::RawKeyDown;
    369         break;
    370     default:
    371         ASSERT_NOT_REACHED();
    372     }
    373 
    374     // According to MSDN:
    375     // http://msdn.microsoft.com/en-us/library/ms646286(VS.85).aspx
    376     // Key events with Alt modifier and F10 are system key events.
    377     // We just emulate this behavior. It's necessary to prevent webkit from
    378     // processing keypress event generated by alt-d, etc.
    379     // F10 is not special on Linux, so don't treat it as system key.
    380     if (result.modifiers & WebInputEvent::AltKey)
    381         result.isSystemKey = true;
    382 
    383     // The key code tells us which physical key was pressed (for example, the
    384     // A key went down or up).  It does not determine whether A should be lower
    385     // or upper case.  This is what text does, which should be the keyval.
    386     int windowsKeyCode = gdkEventToWindowsKeyCode(event);
    387     result.windowsKeyCode = WebKeyboardEvent::windowsKeyCodeWithoutLocation(windowsKeyCode);
    388     result.modifiers |= WebKeyboardEvent::locationModifiersFromWindowsKeyCode(windowsKeyCode);
    389     result.nativeKeyCode = event->hardware_keycode;
    390 
    391     if (result.windowsKeyCode == WebCore::VKEY_RETURN)
    392         // We need to treat the enter key as a key press of character \r.  This
    393         // is apparently just how webkit handles it and what it expects.
    394         result.unmodifiedText[0] = '\r';
    395     else
    396         // FIXME: fix for non BMP chars
    397         result.unmodifiedText[0] =
    398             static_cast<WebUChar>(gdk_keyval_to_unicode(event->keyval));
    399 
    400     // If ctrl key is pressed down, then control character shall be input.
    401     if (result.modifiers & WebInputEvent::ControlKey)
    402         result.text[0] = getControlCharacter(
    403             result.windowsKeyCode, result.modifiers & WebInputEvent::ShiftKey);
    404     else
    405         result.text[0] = result.unmodifiedText[0];
    406 
    407     result.setKeyIdentifierFromWindowsKeyCode();
    408 
    409     // FIXME: Do we need to set IsAutoRepeat?
    410     if (isKeyPadKeyval(event->keyval))
    411         result.modifiers |= WebInputEvent::IsKeyPad;
    412 
    413     return result;
    414 }
    415 
    416 WebKeyboardEvent WebInputEventFactory::keyboardEvent(wchar_t character, int state, double timeStampSeconds)
    417 {
    418     // keyboardEvent(const GdkEventKey*) depends on the GdkEventKey object and
    419     // it is hard to use/ it from signal handlers which don't use GdkEventKey
    420     // objects (e.g. GtkIMContext signal handlers.) For such handlers, this
    421     // function creates a WebInputEvent::Char event without using a
    422     // GdkEventKey object.
    423     WebKeyboardEvent result;
    424     result.type = WebKit::WebInputEvent::Char;
    425     result.timeStampSeconds = timeStampSeconds;
    426     result.modifiers = gdkStateToWebEventModifiers(state);
    427     result.windowsKeyCode = character;
    428     result.nativeKeyCode = character;
    429     result.text[0] = character;
    430     result.unmodifiedText[0] = character;
    431 
    432     // According to MSDN:
    433     // http://msdn.microsoft.com/en-us/library/ms646286(VS.85).aspx
    434     // Key events with Alt modifier and F10 are system key events.
    435     // We just emulate this behavior. It's necessary to prevent webkit from
    436     // processing keypress event generated by alt-d, etc.
    437     // F10 is not special on Linux, so don't treat it as system key.
    438     if (result.modifiers & WebInputEvent::AltKey)
    439         result.isSystemKey = true;
    440 
    441     return result;
    442 }
    443 
    444 // WebMouseEvent --------------------------------------------------------------
    445 
    446 WebMouseEvent WebInputEventFactory::mouseEvent(const GdkEventButton* event)
    447 {
    448     WebMouseEvent result;
    449 
    450     result.timeStampSeconds = gdkEventTimeToWebEventTime(event->time);
    451 
    452     result.modifiers = gdkStateToWebEventModifiers(event->state);
    453     result.x = static_cast<int>(event->x);
    454     result.y = static_cast<int>(event->y);
    455     result.windowX = result.x;
    456     result.windowY = result.y;
    457     result.globalX = static_cast<int>(event->x_root);
    458     result.globalY = static_cast<int>(event->y_root);
    459     result.clickCount = 0;
    460 
    461     switch (event->type) {
    462     case GDK_BUTTON_PRESS:
    463         result.type = WebInputEvent::MouseDown;
    464         break;
    465     case GDK_BUTTON_RELEASE:
    466         result.type = WebInputEvent::MouseUp;
    467         break;
    468     case GDK_3BUTTON_PRESS:
    469     case GDK_2BUTTON_PRESS:
    470     default:
    471         ASSERT_NOT_REACHED();
    472     };
    473 
    474     result.button = WebMouseEvent::ButtonNone;
    475     if (event->button == 1)
    476         result.button = WebMouseEvent::ButtonLeft;
    477     else if (event->button == 2)
    478         result.button = WebMouseEvent::ButtonMiddle;
    479     else if (event->button == 3)
    480         result.button = WebMouseEvent::ButtonRight;
    481 
    482     if (result.type == WebInputEvent::MouseDown) {
    483         bool forgetPreviousClick = shouldForgetPreviousClick(event->window, event->time, event->x, event->y);
    484 
    485         if (!forgetPreviousClick && result.button == gLastClickButton)
    486             ++gNumClicks;
    487         else {
    488             gNumClicks = 1;
    489 
    490             gLastClickEventWindow = event->window;
    491             gLastClickX = event->x;
    492             gLastClickY = event->y;
    493             gLastClickButton = result.button;
    494         }
    495         gLastClickTime = event->time;
    496     }
    497     result.clickCount = gNumClicks;
    498 
    499     return result;
    500 }
    501 
    502 WebMouseEvent WebInputEventFactory::mouseEvent(const GdkEventMotion* event)
    503 {
    504     WebMouseEvent result;
    505 
    506     result.timeStampSeconds = gdkEventTimeToWebEventTime(event->time);
    507     result.modifiers = gdkStateToWebEventModifiers(event->state);
    508     result.x = static_cast<int>(event->x);
    509     result.y = static_cast<int>(event->y);
    510     result.windowX = result.x;
    511     result.windowY = result.y;
    512     result.globalX = static_cast<int>(event->x_root);
    513     result.globalY = static_cast<int>(event->y_root);
    514 
    515     switch (event->type) {
    516     case GDK_MOTION_NOTIFY:
    517         result.type = WebInputEvent::MouseMove;
    518         break;
    519     default:
    520         ASSERT_NOT_REACHED();
    521     }
    522 
    523     result.button = WebMouseEvent::ButtonNone;
    524     if (event->state & GDK_BUTTON1_MASK)
    525         result.button = WebMouseEvent::ButtonLeft;
    526     else if (event->state & GDK_BUTTON2_MASK)
    527         result.button = WebMouseEvent::ButtonMiddle;
    528     else if (event->state & GDK_BUTTON3_MASK)
    529         result.button = WebMouseEvent::ButtonRight;
    530 
    531     if (shouldForgetPreviousClick(event->window, event->time, event->x, event->y))
    532         resetClickCountState();
    533 
    534     return result;
    535 }
    536 
    537 WebMouseEvent WebInputEventFactory::mouseEvent(const GdkEventCrossing* event)
    538 {
    539     WebMouseEvent result;
    540 
    541     result.timeStampSeconds = gdkEventTimeToWebEventTime(event->time);
    542     result.modifiers = gdkStateToWebEventModifiers(event->state);
    543     result.x = static_cast<int>(event->x);
    544     result.y = static_cast<int>(event->y);
    545     result.windowX = result.x;
    546     result.windowY = result.y;
    547     result.globalX = static_cast<int>(event->x_root);
    548     result.globalY = static_cast<int>(event->y_root);
    549 
    550     switch (event->type) {
    551     case GDK_ENTER_NOTIFY:
    552     case GDK_LEAVE_NOTIFY:
    553         // Note that if we sent MouseEnter or MouseLeave to WebKit, it
    554         // wouldn't work - they don't result in the proper JavaScript events.
    555         // MouseMove does the right thing.
    556         result.type = WebInputEvent::MouseMove;
    557         break;
    558     default:
    559         ASSERT_NOT_REACHED();
    560     }
    561 
    562     result.button = WebMouseEvent::ButtonNone;
    563     if (event->state & GDK_BUTTON1_MASK)
    564         result.button = WebMouseEvent::ButtonLeft;
    565     else if (event->state & GDK_BUTTON2_MASK)
    566         result.button = WebMouseEvent::ButtonMiddle;
    567     else if (event->state & GDK_BUTTON3_MASK)
    568         result.button = WebMouseEvent::ButtonRight;
    569 
    570     if (shouldForgetPreviousClick(event->window, event->time, event->x, event->y))
    571         resetClickCountState();
    572 
    573     return result;
    574 }
    575 
    576 // WebMouseWheelEvent ---------------------------------------------------------
    577 
    578 WebMouseWheelEvent WebInputEventFactory::mouseWheelEvent(const GdkEventScroll* event)
    579 {
    580     WebMouseWheelEvent result;
    581 
    582     result.type = WebInputEvent::MouseWheel;
    583     result.button = WebMouseEvent::ButtonNone;
    584 
    585     result.timeStampSeconds = gdkEventTimeToWebEventTime(event->time);
    586     result.modifiers = gdkStateToWebEventModifiers(event->state);
    587     result.x = static_cast<int>(event->x);
    588     result.y = static_cast<int>(event->y);
    589     result.windowX = result.x;
    590     result.windowY = result.y;
    591     result.globalX = static_cast<int>(event->x_root);
    592     result.globalY = static_cast<int>(event->y_root);
    593 
    594     // How much should we scroll per mouse wheel event?
    595     // - Windows uses 3 lines by default and obeys a system setting.
    596     // - Mozilla has a pref that lets you either use the "system" number of lines
    597     //   to scroll, or lets the user override it.
    598     //   For the "system" number of lines, it appears they've hardcoded 3.
    599     //   See case NS_MOUSE_SCROLL in content/events/src/nsEventStateManager.cpp
    600     //   and InitMouseScrollEvent in widget/src/gtk2/nsCommonWidget.cpp .
    601     // - Gtk makes the scroll amount a function of the size of the scroll bar,
    602     //   which is not available to us here.
    603     // Instead, we pick a number that empirically matches Firefox's behavior.
    604     static const float scrollbarPixelsPerTick = 160.0f / 3.0f;
    605 
    606     switch (event->direction) {
    607     case GDK_SCROLL_UP:
    608         result.deltaY = scrollbarPixelsPerTick;
    609         result.wheelTicksY = 1;
    610         break;
    611     case GDK_SCROLL_DOWN:
    612         result.deltaY = -scrollbarPixelsPerTick;
    613         result.wheelTicksY = -1;
    614         break;
    615     case GDK_SCROLL_LEFT:
    616         result.deltaX = scrollbarPixelsPerTick;
    617         result.wheelTicksX = 1;
    618         break;
    619     case GDK_SCROLL_RIGHT:
    620         result.deltaX = -scrollbarPixelsPerTick;
    621         result.wheelTicksX = -1;
    622         break;
    623     }
    624 
    625     return result;
    626 }
    627 
    628 } // namespace WebKit
    629