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