Home | History | Annotate | Download | only in mac
      1 /*
      2  * Copyright (C) 2004, 2006, 2007 Apple Inc. All rights reserved.
      3  * Copyright (C) 2006-2009 Google Inc.
      4  *
      5  * Redistribution and use in source and binary forms, with or without
      6  * modification, are permitted provided that the following conditions
      7  * are met:
      8  * 1. Redistributions of source code must retain the above copyright
      9  *    notice, this list of conditions and the following disclaimer.
     10  * 2. Redistributions in binary form must reproduce the above copyright
     11  *    notice, this list of conditions and the following disclaimer in the
     12  *    documentation and/or other materials provided with the distribution.
     13  *
     14  * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
     15  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
     17  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE COMPUTER, INC. OR
     18  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
     19  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
     20  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
     21  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
     22  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     23  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
     24  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     25  */
     26 
     27 #include "config.h"
     28 #include "WebInputEventFactory.h"
     29 
     30 #include <ApplicationServices/ApplicationServices.h>
     31 #import <AvailabilityMacros.h>
     32 #import <Cocoa/Cocoa.h>
     33 
     34 #include "WebInputEvent.h"
     35 #import "core/platform/cocoa/KeyEventCocoa.h"
     36 #include "wtf/ASCIICType.h"
     37 
     38 #if __MAC_OS_X_VERSION_MAX_ALLOWED == 1060
     39 
     40 // Additional Lion APIs.
     41 enum {
     42     NSEventPhaseNone        = 0,
     43     NSEventPhaseBegan       = 0x1 << 0,
     44     NSEventPhaseStationary  = 0x1 << 1,
     45     NSEventPhaseChanged     = 0x1 << 2,
     46     NSEventPhaseEnded       = 0x1 << 3,
     47     NSEventPhaseCancelled   = 0x1 << 4
     48 };
     49 typedef NSUInteger NSEventPhase;
     50 
     51 @interface NSEvent (LionSDKDeclarations)
     52 - (NSEventPhase)phase;
     53 - (NSEventPhase)momentumPhase;
     54 @end
     55 
     56 #endif  // __MAC_OS_X_VERSION_MAX_ALLOWED == 1060
     57 
     58 #if __MAC_OS_X_VERSION_MAX_ALLOWED < 1080
     59 
     60 // Additional Mountain Lion APIs.
     61 enum {
     62     NSEventPhaseMayBegin    = 0x1 << 5
     63 };
     64 
     65 #endif  // __MAC_OS_X_VERSION_MAX_ALLOWED < 1080
     66 
     67 namespace WebKit {
     68 
     69 // WebKeyboardEvent -----------------------------------------------------------
     70 
     71 // ----------------------------------------------------------------------------
     72 // Begin Apple code, copied from KeyEventMac.mm
     73 //
     74 // We can share some of this code if we factored it out of KeyEventMac, but
     75 // the main problem is that it relies on the NSString ctor on String for
     76 // conversions, and since we're building without PLATFORM(MAC), we don't have
     77 // that. As a result we have to use NSString here exclusively and thus tweak
     78 // the code so it's not re-usable as-is. One possiblity would be to make the
     79 // upstream code only use NSString, but I'm not certain how far that change
     80 // would propagate.
     81 
     82 static inline bool isKeyUpEvent(NSEvent* event)
     83 {
     84     if ([event type] != NSFlagsChanged)
     85         return [event type] == NSKeyUp;
     86     // FIXME: This logic fails if the user presses both Shift keys at once, for example:
     87     // we treat releasing one of them as keyDown.
     88     switch ([event keyCode]) {
     89     case 54: // Right Command
     90     case 55: // Left Command
     91         return ([event modifierFlags] & NSCommandKeyMask) == 0;
     92 
     93     case 57: // Capslock
     94         return ([event modifierFlags] & NSAlphaShiftKeyMask) == 0;
     95 
     96     case 56: // Left Shift
     97     case 60: // Right Shift
     98         return ([event modifierFlags] & NSShiftKeyMask) == 0;
     99 
    100     case 58: // Left Alt
    101     case 61: // Right Alt
    102         return ([event modifierFlags] & NSAlternateKeyMask) == 0;
    103 
    104     case 59: // Left Ctrl
    105     case 62: // Right Ctrl
    106         return ([event modifierFlags] & NSControlKeyMask) == 0;
    107 
    108     case 63: // Function
    109         return ([event modifierFlags] & NSFunctionKeyMask) == 0;
    110     }
    111     return false;
    112 }
    113 
    114 static bool isKeypadEvent(NSEvent* event)
    115 {
    116     // Check that this is the type of event that has a keyCode.
    117     switch ([event type]) {
    118     case NSKeyDown:
    119     case NSKeyUp:
    120     case NSFlagsChanged:
    121         break;
    122     default:
    123         return false;
    124     }
    125 
    126     switch ([event keyCode]) {
    127     case 71: // Clear
    128     case 81: // =
    129     case 75: // /
    130     case 67: // *
    131     case 78: // -
    132     case 69: // +
    133     case 76: // Enter
    134     case 65: // .
    135     case 82: // 0
    136     case 83: // 1
    137     case 84: // 2
    138     case 85: // 3
    139     case 86: // 4
    140     case 87: // 5
    141     case 88: // 6
    142     case 89: // 7
    143     case 91: // 8
    144     case 92: // 9
    145         return true;
    146     }
    147 
    148     return false;
    149 }
    150 
    151 static int windowsKeyCodeForKeyEvent(NSEvent* event)
    152 {
    153     int code = 0;
    154     // There are several kinds of characters for which we produce key code from char code:
    155     // 1. Roman letters. Windows keyboard layouts affect both virtual key codes and character codes for these,
    156     //    so e.g. 'A' gets the same keyCode on QWERTY, AZERTY or Dvorak layouts.
    157     // 2. Keys for which there is no known Mac virtual key codes, like PrintScreen.
    158     // 3. Certain punctuation keys. On Windows, these are also remapped depending on current keyboard layout,
    159     //    but see comment in windowsKeyCodeForCharCode().
    160     if (!isKeypadEvent(event) && ([event type] == NSKeyDown || [event type] == NSKeyUp)) {
    161         // Cmd switches Roman letters for Dvorak-QWERTY layout, so try modified characters first.
    162         NSString* s = [event characters];
    163         code = [s length] > 0 ? WebCore::windowsKeyCodeForCharCode([s characterAtIndex:0]) : 0;
    164         if (code)
    165             return code;
    166 
    167         // Ctrl+A on an AZERTY keyboard would get VK_Q keyCode if we relied on -[NSEvent keyCode] below.
    168         s = [event charactersIgnoringModifiers];
    169         code = [s length] > 0 ? WebCore::windowsKeyCodeForCharCode([s characterAtIndex:0]) : 0;
    170         if (code)
    171             return code;
    172     }
    173 
    174     // Map Mac virtual key code directly to Windows one for any keys not handled above.
    175     // E.g. the key next to Caps Lock has the same Event.keyCode on U.S. keyboard ('A') and on Russian keyboard (CYRILLIC LETTER EF).
    176     return WebCore::windowsKeyCodeForKeyCode([event keyCode]);
    177 }
    178 
    179 static WebInputEvent::Type gestureEventTypeForEvent(NSEvent *event)
    180 {
    181     switch ([event type]) {
    182     case NSEventTypeBeginGesture:
    183         return WebInputEvent::GestureScrollBegin;
    184     case NSEventTypeEndGesture:
    185         return WebInputEvent::GestureScrollEnd;
    186     default:
    187         ASSERT_NOT_REACHED();
    188         return WebInputEvent::GestureScrollEnd;
    189     }
    190 }
    191 
    192 static inline NSString* textFromEvent(NSEvent* event)
    193 {
    194     if ([event type] == NSFlagsChanged)
    195         return @"";
    196     return [event characters];
    197 }
    198 
    199 static inline NSString* unmodifiedTextFromEvent(NSEvent* event)
    200 {
    201     if ([event type] == NSFlagsChanged)
    202         return @"";
    203     return [event charactersIgnoringModifiers];
    204 }
    205 
    206 static NSString* keyIdentifierForKeyEvent(NSEvent* event)
    207 {
    208     if ([event type] == NSFlagsChanged) {
    209         switch ([event keyCode]) {
    210         case 54: // Right Command
    211         case 55: // Left Command
    212             return @"Meta";
    213 
    214         case 57: // Capslock
    215             return @"CapsLock";
    216 
    217         case 56: // Left Shift
    218         case 60: // Right Shift
    219             return @"Shift";
    220 
    221         case 58: // Left Alt
    222         case 61: // Right Alt
    223             return @"Alt";
    224 
    225         case 59: // Left Ctrl
    226         case 62: // Right Ctrl
    227             return @"Control";
    228 
    229 // Begin non-Apple addition/modification --------------------------------------
    230         case 63: // Function
    231             return @"Function";
    232 
    233         default: // Unknown, but this may be a strange/new keyboard.
    234             return @"Unidentified";
    235 // End non-Apple addition/modification ----------------------------------------
    236         }
    237     }
    238 
    239     NSString* s = [event charactersIgnoringModifiers];
    240     if ([s length] != 1)
    241         return @"Unidentified";
    242 
    243     unichar c = [s characterAtIndex:0];
    244     switch (c) {
    245     // Each identifier listed in the DOM spec is listed here.
    246     // Many are simply commented out since they do not appear on standard Macintosh keyboards
    247     // or are on a key that doesn't have a corresponding character.
    248 
    249     // "Accept"
    250     // "AllCandidates"
    251 
    252     // "Alt"
    253     case NSMenuFunctionKey:
    254         return @"Alt";
    255 
    256     // "Apps"
    257     // "BrowserBack"
    258     // "BrowserForward"
    259     // "BrowserHome"
    260     // "BrowserRefresh"
    261     // "BrowserSearch"
    262     // "BrowserStop"
    263     // "CapsLock"
    264 
    265     // "Clear"
    266     case NSClearLineFunctionKey:
    267         return @"Clear";
    268 
    269     // "CodeInput"
    270     // "Compose"
    271     // "Control"
    272     // "Crsel"
    273     // "Convert"
    274     // "Copy"
    275     // "Cut"
    276 
    277     // "Down"
    278     case NSDownArrowFunctionKey:
    279         return @"Down";
    280     // "End"
    281     case NSEndFunctionKey:
    282         return @"End";
    283     // "Enter"
    284     case 0x3: case 0xA: case 0xD: // Macintosh calls the one on the main keyboard Return, but Windows calls it Enter, so we'll do the same for the DOM
    285         return @"Enter";
    286 
    287     // "EraseEof"
    288 
    289     // "Execute"
    290     case NSExecuteFunctionKey:
    291         return @"Execute";
    292 
    293     // "Exsel"
    294 
    295     // "F1"
    296     case NSF1FunctionKey:
    297         return @"F1";
    298     // "F2"
    299     case NSF2FunctionKey:
    300         return @"F2";
    301     // "F3"
    302     case NSF3FunctionKey:
    303         return @"F3";
    304     // "F4"
    305     case NSF4FunctionKey:
    306         return @"F4";
    307     // "F5"
    308     case NSF5FunctionKey:
    309         return @"F5";
    310     // "F6"
    311     case NSF6FunctionKey:
    312         return @"F6";
    313     // "F7"
    314     case NSF7FunctionKey:
    315         return @"F7";
    316     // "F8"
    317     case NSF8FunctionKey:
    318         return @"F8";
    319     // "F9"
    320     case NSF9FunctionKey:
    321         return @"F9";
    322     // "F10"
    323     case NSF10FunctionKey:
    324         return @"F10";
    325     // "F11"
    326     case NSF11FunctionKey:
    327         return @"F11";
    328     // "F12"
    329     case NSF12FunctionKey:
    330         return @"F12";
    331     // "F13"
    332     case NSF13FunctionKey:
    333         return @"F13";
    334     // "F14"
    335     case NSF14FunctionKey:
    336         return @"F14";
    337     // "F15"
    338     case NSF15FunctionKey:
    339         return @"F15";
    340     // "F16"
    341     case NSF16FunctionKey:
    342         return @"F16";
    343     // "F17"
    344     case NSF17FunctionKey:
    345         return @"F17";
    346     // "F18"
    347     case NSF18FunctionKey:
    348         return @"F18";
    349     // "F19"
    350     case NSF19FunctionKey:
    351         return @"F19";
    352     // "F20"
    353     case NSF20FunctionKey:
    354         return @"F20";
    355     // "F21"
    356     case NSF21FunctionKey:
    357         return @"F21";
    358     // "F22"
    359     case NSF22FunctionKey:
    360         return @"F22";
    361     // "F23"
    362     case NSF23FunctionKey:
    363         return @"F23";
    364     // "F24"
    365     case NSF24FunctionKey:
    366         return @"F24";
    367 
    368     // "FinalMode"
    369 
    370     // "Find"
    371     case NSFindFunctionKey:
    372         return @"Find";
    373 
    374     // "FullWidth"
    375     // "HalfWidth"
    376     // "HangulMode"
    377     // "HanjaMode"
    378 
    379     // "Help"
    380     case NSHelpFunctionKey:
    381         return @"Help";
    382 
    383     // "Hiragana"
    384 
    385     // "Home"
    386     case NSHomeFunctionKey:
    387         return @"Home";
    388     // "Insert"
    389     case NSInsertFunctionKey:
    390         return @"Insert";
    391 
    392     // "JapaneseHiragana"
    393     // "JapaneseKatakana"
    394     // "JapaneseRomaji"
    395     // "JunjaMode"
    396     // "KanaMode"
    397     // "KanjiMode"
    398     // "Katakana"
    399     // "LaunchApplication1"
    400     // "LaunchApplication2"
    401     // "LaunchMail"
    402 
    403     // "Left"
    404     case NSLeftArrowFunctionKey:
    405         return @"Left";
    406 
    407     // "Meta"
    408     // "MediaNextTrack"
    409     // "MediaPlayPause"
    410     // "MediaPreviousTrack"
    411     // "MediaStop"
    412 
    413     // "ModeChange"
    414     case NSModeSwitchFunctionKey:
    415         return @"ModeChange";
    416 
    417     // "Nonconvert"
    418     // "NumLock"
    419 
    420     // "PageDown"
    421     case NSPageDownFunctionKey:
    422         return @"PageDown";
    423     // "PageUp"
    424     case NSPageUpFunctionKey:
    425         return @"PageUp";
    426 
    427     // "Paste"
    428 
    429     // "Pause"
    430     case NSPauseFunctionKey:
    431         return @"Pause";
    432 
    433     // "Play"
    434     // "PreviousCandidate"
    435 
    436     // "PrintScreen"
    437     case NSPrintScreenFunctionKey:
    438         return @"PrintScreen";
    439 
    440     // "Process"
    441     // "Props"
    442 
    443     // "Right"
    444     case NSRightArrowFunctionKey:
    445         return @"Right";
    446 
    447     // "RomanCharacters"
    448 
    449     // "Scroll"
    450     case NSScrollLockFunctionKey:
    451         return @"Scroll";
    452     // "Select"
    453     case NSSelectFunctionKey:
    454         return @"Select";
    455 
    456     // "SelectMedia"
    457     // "Shift"
    458 
    459     // "Stop"
    460     case NSStopFunctionKey:
    461         return @"Stop";
    462     // "Up"
    463     case NSUpArrowFunctionKey:
    464         return @"Up";
    465     // "Undo"
    466     case NSUndoFunctionKey:
    467         return @"Undo";
    468 
    469     // "VolumeDown"
    470     // "VolumeMute"
    471     // "VolumeUp"
    472     // "Win"
    473     // "Zoom"
    474 
    475     // More function keys, not in the key identifier specification.
    476     case NSF25FunctionKey:
    477         return @"F25";
    478     case NSF26FunctionKey:
    479         return @"F26";
    480     case NSF27FunctionKey:
    481         return @"F27";
    482     case NSF28FunctionKey:
    483         return @"F28";
    484     case NSF29FunctionKey:
    485         return @"F29";
    486     case NSF30FunctionKey:
    487         return @"F30";
    488     case NSF31FunctionKey:
    489         return @"F31";
    490     case NSF32FunctionKey:
    491         return @"F32";
    492     case NSF33FunctionKey:
    493         return @"F33";
    494     case NSF34FunctionKey:
    495         return @"F34";
    496     case NSF35FunctionKey:
    497         return @"F35";
    498 
    499     // Turn 0x7F into 0x08, because backspace needs to always be 0x08.
    500     case 0x7F:
    501         return @"U+0008";
    502     // Standard says that DEL becomes U+007F.
    503     case NSDeleteFunctionKey:
    504         return @"U+007F";
    505 
    506     // Always use 0x09 for tab instead of AppKit's backtab character.
    507     case NSBackTabCharacter:
    508         return @"U+0009";
    509 
    510     case NSBeginFunctionKey:
    511     case NSBreakFunctionKey:
    512     case NSClearDisplayFunctionKey:
    513     case NSDeleteCharFunctionKey:
    514     case NSDeleteLineFunctionKey:
    515     case NSInsertCharFunctionKey:
    516     case NSInsertLineFunctionKey:
    517     case NSNextFunctionKey:
    518     case NSPrevFunctionKey:
    519     case NSPrintFunctionKey:
    520     case NSRedoFunctionKey:
    521     case NSResetFunctionKey:
    522     case NSSysReqFunctionKey:
    523     case NSSystemFunctionKey:
    524     case NSUserFunctionKey:
    525         // FIXME: We should use something other than the vendor-area Unicode values for the above keys.
    526         // For now, just fall through to the default.
    527     default:
    528         return [NSString stringWithFormat:@"U+%04X", WTF::toASCIIUpper(c)];
    529     }
    530 }
    531 
    532 // End Apple code.
    533 // ----------------------------------------------------------------------------
    534 
    535 static inline int modifiersFromEvent(NSEvent* event) {
    536     int modifiers = 0;
    537 
    538     if ([event modifierFlags] & NSControlKeyMask)
    539         modifiers |= WebInputEvent::ControlKey;
    540     if ([event modifierFlags] & NSShiftKeyMask)
    541         modifiers |= WebInputEvent::ShiftKey;
    542     if ([event modifierFlags] & NSAlternateKeyMask)
    543         modifiers |= WebInputEvent::AltKey;
    544     if ([event modifierFlags] & NSCommandKeyMask)
    545         modifiers |= WebInputEvent::MetaKey;
    546     if ([event modifierFlags] & NSAlphaShiftKeyMask)
    547         modifiers |= WebInputEvent::CapsLockOn;
    548     // TODO(port): Set mouse button states
    549 
    550     return modifiers;
    551 }
    552 
    553 static inline void setWebEventLocationFromEventInView(WebMouseEvent* result,
    554                                                       NSEvent* event,
    555                                                       NSView* view) {
    556     NSPoint windowLocal = [event locationInWindow];
    557 
    558     NSPoint screenLocal = [[view window] convertBaseToScreen:windowLocal];
    559     result->globalX = screenLocal.x;
    560     // Flip y.
    561     NSScreen* primaryScreen = ([[NSScreen screens] count] > 0) ?
    562         [[NSScreen screens] objectAtIndex:0] : nil;
    563     if (primaryScreen)
    564         result->globalY = [primaryScreen frame].size.height - screenLocal.y;
    565     else
    566         result->globalY = screenLocal.y;
    567 
    568     NSPoint contentLocal = [view convertPoint:windowLocal fromView:nil];
    569     result->x = contentLocal.x;
    570     result->y = [view frame].size.height - contentLocal.y;  // Flip y.
    571 
    572     result->windowX = result->x;
    573     result->windowY = result->y;
    574 
    575     result->movementX = [event deltaX];
    576     result->movementY = [event deltaY];
    577 }
    578 
    579 WebKeyboardEvent WebInputEventFactory::keyboardEvent(NSEvent* event)
    580 {
    581     WebKeyboardEvent result;
    582 
    583     result.type =
    584         isKeyUpEvent(event) ? WebInputEvent::KeyUp : WebInputEvent::RawKeyDown;
    585 
    586     result.modifiers = modifiersFromEvent(event);
    587 
    588     if (isKeypadEvent(event))
    589         result.modifiers |= WebInputEvent::IsKeyPad;
    590 
    591     if (([event type] != NSFlagsChanged) && [event isARepeat])
    592         result.modifiers |= WebInputEvent::IsAutoRepeat;
    593 
    594     int windowsKeyCode = windowsKeyCodeForKeyEvent(event);
    595     result.windowsKeyCode = WebKeyboardEvent::windowsKeyCodeWithoutLocation(windowsKeyCode);
    596     result.modifiers |= WebKeyboardEvent::locationModifiersFromWindowsKeyCode(windowsKeyCode);
    597     result.nativeKeyCode = [event keyCode];
    598 
    599     NSString* textStr = textFromEvent(event);
    600     NSString* unmodifiedStr = unmodifiedTextFromEvent(event);
    601     NSString* identifierStr = keyIdentifierForKeyEvent(event);
    602 
    603     // Begin Apple code, copied from KeyEventMac.mm
    604 
    605     // Always use 13 for Enter/Return -- we don't want to use AppKit's
    606     // different character for Enter.
    607     if (result.windowsKeyCode == '\r') {
    608         textStr = @"\r";
    609         unmodifiedStr = @"\r";
    610     }
    611 
    612     // The adjustments below are only needed in backward compatibility mode,
    613     // but we cannot tell what mode we are in from here.
    614 
    615     // Turn 0x7F into 8, because backspace needs to always be 8.
    616     if ([textStr isEqualToString:@"\x7F"])
    617         textStr = @"\x8";
    618     if ([unmodifiedStr isEqualToString:@"\x7F"])
    619         unmodifiedStr = @"\x8";
    620     // Always use 9 for tab -- we don't want to use AppKit's different character
    621     // for shift-tab.
    622     if (result.windowsKeyCode == 9) {
    623         textStr = @"\x9";
    624         unmodifiedStr = @"\x9";
    625     }
    626 
    627     // End Apple code.
    628 
    629     if ([textStr length] < WebKeyboardEvent::textLengthCap &&
    630         [unmodifiedStr length] < WebKeyboardEvent::textLengthCap) {
    631         [textStr getCharacters:&result.text[0]];
    632         [unmodifiedStr getCharacters:&result.unmodifiedText[0]];
    633     } else
    634         ASSERT_NOT_REACHED();
    635 
    636     [identifierStr getCString:&result.keyIdentifier[0]
    637                     maxLength:sizeof(result.keyIdentifier)
    638                      encoding:NSASCIIStringEncoding];
    639 
    640     result.timeStampSeconds = [event timestamp];
    641 
    642     // Windows and Linux set |isSystemKey| if alt is down. WebKit looks at this
    643     // flag to decide if it should handle a key or not. E.g. alt-left/right
    644     // shouldn't be used by WebKit to scroll the current page, because we want
    645     // to get that key back for it to do history navigation. Hence, the
    646     // corresponding situation on OS X is to set this for cmd key presses.
    647     if (result.modifiers & WebInputEvent::MetaKey)
    648         result.isSystemKey = true;
    649 
    650     return result;
    651 }
    652 
    653 WebKeyboardEvent WebInputEventFactory::keyboardEvent(wchar_t character,
    654                                                      int modifiers,
    655                                                      double timeStampSeconds)
    656 {
    657     // keyboardEvent(NSEvent*) depends on the NSEvent object and
    658     // it is hard to use it from methods of the NSTextInput protocol. For
    659     // such methods, this function creates a WebInputEvent::Char event without
    660     // using a NSEvent object.
    661     WebKeyboardEvent result;
    662     result.type = WebKit::WebInputEvent::Char;
    663     result.timeStampSeconds = timeStampSeconds;
    664     result.modifiers = modifiers;
    665     result.windowsKeyCode = character;
    666     result.nativeKeyCode = character;
    667     result.text[0] = character;
    668     result.unmodifiedText[0] = character;
    669 
    670     // Windows and Linux set |isSystemKey| if alt is down. WebKit looks at this
    671     // flag to decide if it should handle a key or not. E.g. alt-left/right
    672     // shouldn't be used by WebKit to scroll the current page, because we want
    673     // to get that key back for it to do history navigation. Hence, the
    674     // corresponding situation on OS X is to set this for cmd key presses.
    675     if (result.modifiers & WebInputEvent::MetaKey)
    676         result.isSystemKey = true;
    677 
    678     return result;
    679 }
    680 
    681 // WebMouseEvent --------------------------------------------------------------
    682 
    683 WebMouseEvent WebInputEventFactory::mouseEvent(NSEvent* event, NSView* view)
    684 {
    685     WebMouseEvent result;
    686 
    687     result.clickCount = 0;
    688 
    689     switch ([event type]) {
    690     case NSMouseExited:
    691         result.type = WebInputEvent::MouseLeave;
    692         result.button = WebMouseEvent::ButtonNone;
    693         break;
    694     case NSLeftMouseDown:
    695         result.type = WebInputEvent::MouseDown;
    696         result.clickCount = [event clickCount];
    697         result.button = WebMouseEvent::ButtonLeft;
    698         break;
    699     case NSOtherMouseDown:
    700         result.type = WebInputEvent::MouseDown;
    701         result.clickCount = [event clickCount];
    702         result.button = WebMouseEvent::ButtonMiddle;
    703         break;
    704     case NSRightMouseDown:
    705         result.type = WebInputEvent::MouseDown;
    706         result.clickCount = [event clickCount];
    707         result.button = WebMouseEvent::ButtonRight;
    708         break;
    709     case NSLeftMouseUp:
    710         result.type = WebInputEvent::MouseUp;
    711         result.clickCount = [event clickCount];
    712         result.button = WebMouseEvent::ButtonLeft;
    713         break;
    714     case NSOtherMouseUp:
    715         result.type = WebInputEvent::MouseUp;
    716         result.clickCount = [event clickCount];
    717         result.button = WebMouseEvent::ButtonMiddle;
    718         break;
    719     case NSRightMouseUp:
    720         result.type = WebInputEvent::MouseUp;
    721         result.clickCount = [event clickCount];
    722         result.button = WebMouseEvent::ButtonRight;
    723         break;
    724     case NSMouseMoved:
    725     case NSMouseEntered:
    726         result.type = WebInputEvent::MouseMove;
    727         break;
    728     case NSLeftMouseDragged:
    729         result.type = WebInputEvent::MouseMove;
    730         result.button = WebMouseEvent::ButtonLeft;
    731         break;
    732     case NSOtherMouseDragged:
    733         result.type = WebInputEvent::MouseMove;
    734         result.button = WebMouseEvent::ButtonMiddle;
    735         break;
    736     case NSRightMouseDragged:
    737         result.type = WebInputEvent::MouseMove;
    738         result.button = WebMouseEvent::ButtonRight;
    739         break;
    740     default:
    741         ASSERT_NOT_REACHED();
    742     }
    743 
    744     setWebEventLocationFromEventInView(&result, event, view);
    745 
    746     result.modifiers = modifiersFromEvent(event);
    747 
    748     result.timeStampSeconds = [event timestamp];
    749 
    750     return result;
    751 }
    752 
    753 // WebMouseWheelEvent ---------------------------------------------------------
    754 
    755 static WebMouseWheelEvent::Phase phaseForNSEventPhase(NSEventPhase eventPhase)
    756 {
    757     uint32_t phase = WebMouseWheelEvent::PhaseNone;
    758     if (eventPhase & NSEventPhaseBegan)
    759         phase |= WebMouseWheelEvent::PhaseBegan;
    760     if (eventPhase & NSEventPhaseStationary)
    761         phase |= WebMouseWheelEvent::PhaseStationary;
    762     if (eventPhase & NSEventPhaseChanged)
    763         phase |= WebMouseWheelEvent::PhaseChanged;
    764     if (eventPhase & NSEventPhaseEnded)
    765         phase |= WebMouseWheelEvent::PhaseEnded;
    766     if (eventPhase & NSEventPhaseCancelled)
    767         phase |= WebMouseWheelEvent::PhaseCancelled;
    768     if (eventPhase & NSEventPhaseMayBegin)
    769         phase |= WebMouseWheelEvent::PhaseMayBegin;
    770     return static_cast<WebMouseWheelEvent::Phase>(phase);
    771 }
    772 
    773 static WebMouseWheelEvent::Phase phaseForEvent(NSEvent *event)
    774 {
    775     if (![event respondsToSelector:@selector(phase)])
    776         return WebMouseWheelEvent::PhaseNone;
    777 
    778     NSEventPhase eventPhase = [event phase];
    779     return phaseForNSEventPhase(eventPhase);
    780 }
    781 
    782 static WebMouseWheelEvent::Phase momentumPhaseForEvent(NSEvent *event)
    783 {
    784     if (![event respondsToSelector:@selector(momentumPhase)])
    785         return WebMouseWheelEvent::PhaseNone;
    786 
    787     NSEventPhase eventMomentumPhase = [event momentumPhase];
    788     return phaseForNSEventPhase(eventMomentumPhase);
    789 }
    790 
    791 WebMouseWheelEvent WebInputEventFactory::mouseWheelEvent(NSEvent* event, NSView* view)
    792 {
    793     WebMouseWheelEvent result;
    794 
    795     result.type = WebInputEvent::MouseWheel;
    796     result.button = WebMouseEvent::ButtonNone;
    797 
    798     result.modifiers = modifiersFromEvent(event);
    799 
    800     setWebEventLocationFromEventInView(&result, event, view);
    801 
    802     // Of Mice and Men
    803     // ---------------
    804     //
    805     // There are three types of scroll data available on a scroll wheel CGEvent.
    806     // Apple's documentation ([1]) is rather vague in their differences, and not
    807     // terribly helpful in deciding which to use. This is what's really going on.
    808     //
    809     // First, these events behave very differently depending on whether a standard
    810     // wheel mouse is used (one that scrolls in discrete units) or a
    811     // trackpad/Mighty Mouse is used (which both provide continuous scrolling).
    812     // You must check to see which was used for the event by testing the
    813     // kCGScrollWheelEventIsContinuous field.
    814     //
    815     // Second, these events refer to "axes". Axis 1 is the y-axis, and axis 2 is
    816     // the x-axis.
    817     //
    818     // Third, there is a concept of mouse acceleration. Scrolling the same amount
    819     // of physical distance will give you different results logically depending on
    820     // whether you scrolled a little at a time or in one continuous motion. Some
    821     // fields account for this while others do not.
    822     //
    823     // Fourth, for trackpads there is a concept of chunkiness. When scrolling
    824     // continuously, events can be delivered in chunks. That is to say, lots of
    825     // scroll events with delta 0 will be delivered, and every so often an event
    826     // with a non-zero delta will be delivered, containing the accumulated deltas
    827     // from all the intermediate moves. [2]
    828     //
    829     // For notchy wheel mice (kCGScrollWheelEventIsContinuous == 0)
    830     // ------------------------------------------------------------
    831     //
    832     // kCGScrollWheelEventDeltaAxis*
    833     //   This is the rawest of raw events. For each mouse notch you get a value of
    834     //   +1/-1. This does not take acceleration into account and thus is less
    835     //   useful for building UIs.
    836     //
    837     // kCGScrollWheelEventPointDeltaAxis*
    838     //   This is smarter. In general, for each mouse notch you get a value of
    839     //   +1/-1, but this _does_ take acceleration into account, so you will get
    840     //   larger values on longer scrolls. This field would be ideal for building
    841     //   UIs except for one nasty bug: when the shift key is pressed, this set of
    842     //   fields fails to move the value into the axis2 field (the other two types
    843     //   of data do). This wouldn't be so bad except for the fact that while the
    844     //   number of axes is used in the creation of a CGScrollWheelEvent, there is
    845     //   no way to get that information out of the event once created.
    846     //
    847     // kCGScrollWheelEventFixedPtDeltaAxis*
    848     //   This is a fixed value, and for each mouse notch you get a value of
    849     //   +0.1/-0.1 (but, like above, scaled appropriately for acceleration). This
    850     //   value takes acceleration into account, and in fact is identical to the
    851     //   results you get from -[NSEvent delta*]. (That is, if you linked on Tiger
    852     //   or greater; see [2] for details.)
    853     //
    854     // A note about continuous devices
    855     // -------------------------------
    856     //
    857     // There are two devices that provide continuous scrolling events (trackpads
    858     // and Mighty Mouses) and they behave rather differently. The Mighty Mouse
    859     // behaves a lot like a regular mouse. There is no chunking, and the
    860     // FixedPtDelta values are the PointDelta values multiplied by 0.1. With the
    861     // trackpad, though, there is chunking. While the FixedPtDelta values are
    862     // reasonable (they occur about every fifth event but have values five times
    863     // larger than usual) the Delta values are unreasonable. They don't appear to
    864     // accumulate properly.
    865     //
    866     // For continuous devices (kCGScrollWheelEventIsContinuous != 0)
    867     // -------------------------------------------------------------
    868     //
    869     // kCGScrollWheelEventDeltaAxis*
    870     //   This provides values with no acceleration. With a trackpad, these values
    871     //   are chunked but each non-zero value does not appear to be cumulative.
    872     //   This seems to be a bug.
    873     //
    874     // kCGScrollWheelEventPointDeltaAxis*
    875     //   This provides values with acceleration. With a trackpad, these values are
    876     //   not chunked and are highly accurate.
    877     //
    878     // kCGScrollWheelEventFixedPtDeltaAxis*
    879     //   This provides values with acceleration. With a trackpad, these values are
    880     //   chunked but unlike Delta events are properly cumulative.
    881     //
    882     // Summary
    883     // -------
    884     //
    885     // In general the best approach to take is: determine if the event is
    886     // continuous. If it is not, then use the FixedPtDelta events (or just stick
    887     // with Cocoa events). They provide both acceleration and proper horizontal
    888     // scrolling. If the event is continuous, then doing pixel scrolling with the
    889     // PointDelta is the way to go. In general, avoid the Delta events. They're
    890     // the oldest (dating back to 10.4, before CGEvents were public) but they lack
    891     // acceleration and precision, making them useful only in specific edge cases.
    892     //
    893     // References
    894     // ----------
    895     //
    896     // [1] <http://developer.apple.com/documentation/Carbon/Reference/QuartzEventServicesRef/Reference/reference.html>
    897     // [2] <http://developer.apple.com/releasenotes/Cocoa/AppKitOlderNotes.html>
    898     //     Scroll to the section headed "NSScrollWheel events".
    899     //
    900     // P.S. The "smooth scrolling" option in the system preferences is utterly
    901     // unrelated to any of this.
    902 
    903     CGEventRef cgEvent = [event CGEvent];
    904     ASSERT(cgEvent);
    905 
    906     // Wheel ticks are supposed to be raw, unaccelerated values, one per physical
    907     // mouse wheel notch. The delta event is perfect for this (being a good
    908     // "specific edge case" as mentioned above). Trackpads, unfortunately, do
    909     // event chunking, and sending mousewheel events with 0 ticks causes some
    910     // websites to malfunction. Therefore, for all continuous input devices we use
    911     // the point delta data instead, since we cannot distinguish trackpad data
    912     // from data from any other continuous device.
    913 
    914     // Conversion between wheel delta amounts and number of pixels to scroll.
    915     static const double scrollbarPixelsPerCocoaTick = 40.0;
    916 
    917     if (CGEventGetIntegerValueField(cgEvent, kCGScrollWheelEventIsContinuous)) {
    918         result.deltaX = CGEventGetIntegerValueField(cgEvent, kCGScrollWheelEventPointDeltaAxis2);
    919         result.deltaY = CGEventGetIntegerValueField(cgEvent, kCGScrollWheelEventPointDeltaAxis1);
    920         result.wheelTicksX = result.deltaX / scrollbarPixelsPerCocoaTick;
    921         result.wheelTicksY = result.deltaY / scrollbarPixelsPerCocoaTick;
    922         result.hasPreciseScrollingDeltas = true;
    923     } else {
    924         result.deltaX = [event deltaX] * scrollbarPixelsPerCocoaTick;
    925         result.deltaY = [event deltaY] * scrollbarPixelsPerCocoaTick;
    926         result.wheelTicksY = CGEventGetIntegerValueField(cgEvent, kCGScrollWheelEventDeltaAxis1);
    927         result.wheelTicksX = CGEventGetIntegerValueField(cgEvent, kCGScrollWheelEventDeltaAxis2);
    928     }
    929 
    930     result.timeStampSeconds = [event timestamp];
    931 
    932     result.phase              = phaseForEvent(event);
    933     result.momentumPhase      = momentumPhaseForEvent(event);
    934 
    935     return result;
    936 }
    937 
    938 WebGestureEvent WebInputEventFactory::gestureEvent(NSEvent *event, NSView *view)
    939 {
    940     WebGestureEvent result;
    941 
    942     // Use a temporary WebMouseEvent to get the location.
    943     WebMouseEvent temp;
    944 
    945     setWebEventLocationFromEventInView(&temp, event, view);
    946     result.x = temp.x;
    947     result.y = temp.y;
    948     result.globalX = temp.globalX;
    949     result.globalY = temp.globalY;
    950 
    951     result.type = gestureEventTypeForEvent(event);
    952     result.modifiers = modifiersFromEvent(event);
    953     result.timeStampSeconds = [event timestamp];
    954 
    955     return result;
    956 }
    957 
    958 } // namespace WebKit
    959