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