1643ca7872b450ea4efacab6188849e5aac2ba161Steve Block/*
2643ca7872b450ea4efacab6188849e5aac2ba161Steve Block * Copyright (C) 2004, 2006, 2007 Apple Inc. All rights reserved.
3643ca7872b450ea4efacab6188849e5aac2ba161Steve Block * Copyright (C) 2006-2009 Google Inc.
4643ca7872b450ea4efacab6188849e5aac2ba161Steve Block *
5643ca7872b450ea4efacab6188849e5aac2ba161Steve Block * Redistribution and use in source and binary forms, with or without
6643ca7872b450ea4efacab6188849e5aac2ba161Steve Block * modification, are permitted provided that the following conditions
7643ca7872b450ea4efacab6188849e5aac2ba161Steve Block * are met:
8643ca7872b450ea4efacab6188849e5aac2ba161Steve Block * 1. Redistributions of source code must retain the above copyright
9643ca7872b450ea4efacab6188849e5aac2ba161Steve Block *    notice, this list of conditions and the following disclaimer.
10643ca7872b450ea4efacab6188849e5aac2ba161Steve Block * 2. Redistributions in binary form must reproduce the above copyright
11643ca7872b450ea4efacab6188849e5aac2ba161Steve Block *    notice, this list of conditions and the following disclaimer in the
12643ca7872b450ea4efacab6188849e5aac2ba161Steve Block *    documentation and/or other materials provided with the distribution.
13643ca7872b450ea4efacab6188849e5aac2ba161Steve Block *
14643ca7872b450ea4efacab6188849e5aac2ba161Steve Block * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
15643ca7872b450ea4efacab6188849e5aac2ba161Steve Block * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16643ca7872b450ea4efacab6188849e5aac2ba161Steve Block * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
17643ca7872b450ea4efacab6188849e5aac2ba161Steve Block * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE COMPUTER, INC. OR
18643ca7872b450ea4efacab6188849e5aac2ba161Steve Block * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
19643ca7872b450ea4efacab6188849e5aac2ba161Steve Block * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
20643ca7872b450ea4efacab6188849e5aac2ba161Steve Block * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
21643ca7872b450ea4efacab6188849e5aac2ba161Steve Block * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
22643ca7872b450ea4efacab6188849e5aac2ba161Steve Block * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23643ca7872b450ea4efacab6188849e5aac2ba161Steve Block * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
24643ca7872b450ea4efacab6188849e5aac2ba161Steve Block * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25643ca7872b450ea4efacab6188849e5aac2ba161Steve Block */
26643ca7872b450ea4efacab6188849e5aac2ba161Steve Block
27643ca7872b450ea4efacab6188849e5aac2ba161Steve Block#include "config.h"
28643ca7872b450ea4efacab6188849e5aac2ba161Steve Block#include "WebInputEventFactory.h"
29643ca7872b450ea4efacab6188849e5aac2ba161Steve Block
30643ca7872b450ea4efacab6188849e5aac2ba161Steve Block#include <ApplicationServices/ApplicationServices.h>
31643ca7872b450ea4efacab6188849e5aac2ba161Steve Block#import <Cocoa/Cocoa.h>
32643ca7872b450ea4efacab6188849e5aac2ba161Steve Block
3381bc750723a18f21cd17d1b173cd2a4dda9cea6eBen Murdoch#import "KeyEventCocoa.h"
34643ca7872b450ea4efacab6188849e5aac2ba161Steve Block#include "WebInputEvent.h"
35643ca7872b450ea4efacab6188849e5aac2ba161Steve Block#include <wtf/ASCIICType.h>
36643ca7872b450ea4efacab6188849e5aac2ba161Steve Block
37643ca7872b450ea4efacab6188849e5aac2ba161Steve Blocknamespace WebKit {
38643ca7872b450ea4efacab6188849e5aac2ba161Steve Block
39643ca7872b450ea4efacab6188849e5aac2ba161Steve Block// WebKeyboardEvent -----------------------------------------------------------
40643ca7872b450ea4efacab6188849e5aac2ba161Steve Block
41643ca7872b450ea4efacab6188849e5aac2ba161Steve Block// ----------------------------------------------------------------------------
42643ca7872b450ea4efacab6188849e5aac2ba161Steve Block// Begin Apple code, copied from KeyEventMac.mm
43643ca7872b450ea4efacab6188849e5aac2ba161Steve Block//
44643ca7872b450ea4efacab6188849e5aac2ba161Steve Block// We can share some of this code if we factored it out of KeyEventMac, but
45643ca7872b450ea4efacab6188849e5aac2ba161Steve Block// the main problem is that it relies on the NSString ctor on String for
46643ca7872b450ea4efacab6188849e5aac2ba161Steve Block// conversions, and since we're building without PLATFORM(MAC), we don't have
47643ca7872b450ea4efacab6188849e5aac2ba161Steve Block// that. As a result we have to use NSString here exclusively and thus tweak
48643ca7872b450ea4efacab6188849e5aac2ba161Steve Block// the code so it's not re-usable as-is. One possiblity would be to make the
49643ca7872b450ea4efacab6188849e5aac2ba161Steve Block// upstream code only use NSString, but I'm not certain how far that change
50643ca7872b450ea4efacab6188849e5aac2ba161Steve Block// would propagate.
51643ca7872b450ea4efacab6188849e5aac2ba161Steve Block
52643ca7872b450ea4efacab6188849e5aac2ba161Steve Blockstatic inline bool isKeyUpEvent(NSEvent* event)
53643ca7872b450ea4efacab6188849e5aac2ba161Steve Block{
54643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    if ([event type] != NSFlagsChanged)
55643ca7872b450ea4efacab6188849e5aac2ba161Steve Block        return [event type] == NSKeyUp;
56643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    // FIXME: This logic fails if the user presses both Shift keys at once, for example:
57643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    // we treat releasing one of them as keyDown.
58643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    switch ([event keyCode]) {
59643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    case 54: // Right Command
60643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    case 55: // Left Command
61643ca7872b450ea4efacab6188849e5aac2ba161Steve Block        return ([event modifierFlags] & NSCommandKeyMask) == 0;
62643ca7872b450ea4efacab6188849e5aac2ba161Steve Block
63643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    case 57: // Capslock
64643ca7872b450ea4efacab6188849e5aac2ba161Steve Block        return ([event modifierFlags] & NSAlphaShiftKeyMask) == 0;
65643ca7872b450ea4efacab6188849e5aac2ba161Steve Block
66643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    case 56: // Left Shift
67643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    case 60: // Right Shift
68643ca7872b450ea4efacab6188849e5aac2ba161Steve Block        return ([event modifierFlags] & NSShiftKeyMask) == 0;
69643ca7872b450ea4efacab6188849e5aac2ba161Steve Block
70643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    case 58: // Left Alt
71643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    case 61: // Right Alt
72643ca7872b450ea4efacab6188849e5aac2ba161Steve Block        return ([event modifierFlags] & NSAlternateKeyMask) == 0;
73643ca7872b450ea4efacab6188849e5aac2ba161Steve Block
74643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    case 59: // Left Ctrl
75643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    case 62: // Right Ctrl
76643ca7872b450ea4efacab6188849e5aac2ba161Steve Block        return ([event modifierFlags] & NSControlKeyMask) == 0;
77643ca7872b450ea4efacab6188849e5aac2ba161Steve Block
78643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    case 63: // Function
79643ca7872b450ea4efacab6188849e5aac2ba161Steve Block        return ([event modifierFlags] & NSFunctionKeyMask) == 0;
80643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    }
81643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    return false;
82643ca7872b450ea4efacab6188849e5aac2ba161Steve Block}
83643ca7872b450ea4efacab6188849e5aac2ba161Steve Block
84643ca7872b450ea4efacab6188849e5aac2ba161Steve Blockstatic bool isKeypadEvent(NSEvent* event)
85643ca7872b450ea4efacab6188849e5aac2ba161Steve Block{
86643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    // Check that this is the type of event that has a keyCode.
87643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    switch ([event type]) {
88643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    case NSKeyDown:
89643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    case NSKeyUp:
90643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    case NSFlagsChanged:
91643ca7872b450ea4efacab6188849e5aac2ba161Steve Block        break;
92643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    default:
93643ca7872b450ea4efacab6188849e5aac2ba161Steve Block        return false;
94643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    }
95643ca7872b450ea4efacab6188849e5aac2ba161Steve Block
96d0825bca7fe65beaee391d30da42e937db621564Steve Block    if ([event modifierFlags] & NSNumericPadKeyMask)
97d0825bca7fe65beaee391d30da42e937db621564Steve Block        return true;
98d0825bca7fe65beaee391d30da42e937db621564Steve Block
99643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    switch ([event keyCode]) {
100643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    case 71: // Clear
101643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    case 81: // =
102643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    case 75: // /
103643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    case 67: // *
104643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    case 78: // -
105643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    case 69: // +
106643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    case 76: // Enter
107643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    case 65: // .
108643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    case 82: // 0
109643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    case 83: // 1
110643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    case 84: // 2
111643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    case 85: // 3
112643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    case 86: // 4
113643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    case 87: // 5
114643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    case 88: // 6
115643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    case 89: // 7
116643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    case 91: // 8
117643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    case 92: // 9
118643ca7872b450ea4efacab6188849e5aac2ba161Steve Block        return true;
119643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    }
120643ca7872b450ea4efacab6188849e5aac2ba161Steve Block
121643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    return false;
122643ca7872b450ea4efacab6188849e5aac2ba161Steve Block}
123643ca7872b450ea4efacab6188849e5aac2ba161Steve Block
124643ca7872b450ea4efacab6188849e5aac2ba161Steve Blockstatic int windowsKeyCodeForKeyEvent(NSEvent* event)
125643ca7872b450ea4efacab6188849e5aac2ba161Steve Block{
12681bc750723a18f21cd17d1b173cd2a4dda9cea6eBen Murdoch    int code = 0;
12781bc750723a18f21cd17d1b173cd2a4dda9cea6eBen Murdoch    // There are several kinds of characters for which we produce key code from char code:
12881bc750723a18f21cd17d1b173cd2a4dda9cea6eBen Murdoch    // 1. Roman letters. Windows keyboard layouts affect both virtual key codes and character codes for these,
12981bc750723a18f21cd17d1b173cd2a4dda9cea6eBen Murdoch    //    so e.g. 'A' gets the same keyCode on QWERTY, AZERTY or Dvorak layouts.
13081bc750723a18f21cd17d1b173cd2a4dda9cea6eBen Murdoch    // 2. Keys for which there is no known Mac virtual key codes, like PrintScreen.
13181bc750723a18f21cd17d1b173cd2a4dda9cea6eBen Murdoch    // 3. Certain punctuation keys. On Windows, these are also remapped depending on current keyboard layout,
13281bc750723a18f21cd17d1b173cd2a4dda9cea6eBen Murdoch    //    but see comment in windowsKeyCodeForCharCode().
13381bc750723a18f21cd17d1b173cd2a4dda9cea6eBen Murdoch    if ([event type] == NSKeyDown || [event type] == NSKeyUp) {
13481bc750723a18f21cd17d1b173cd2a4dda9cea6eBen Murdoch        // Cmd switches Roman letters for Dvorak-QWERTY layout, so try modified characters first.
13581bc750723a18f21cd17d1b173cd2a4dda9cea6eBen Murdoch        NSString* s = [event characters];
13681bc750723a18f21cd17d1b173cd2a4dda9cea6eBen Murdoch        code = [s length] > 0 ? WebCore::windowsKeyCodeForCharCode([s characterAtIndex:0]) : 0;
13781bc750723a18f21cd17d1b173cd2a4dda9cea6eBen Murdoch        if (code)
13881bc750723a18f21cd17d1b173cd2a4dda9cea6eBen Murdoch            return code;
13981bc750723a18f21cd17d1b173cd2a4dda9cea6eBen Murdoch
14081bc750723a18f21cd17d1b173cd2a4dda9cea6eBen Murdoch        // Ctrl+A on an AZERTY keyboard would get VK_Q keyCode if we relied on -[NSEvent keyCode] below.
14181bc750723a18f21cd17d1b173cd2a4dda9cea6eBen Murdoch        s = [event charactersIgnoringModifiers];
14281bc750723a18f21cd17d1b173cd2a4dda9cea6eBen Murdoch        code = [s length] > 0 ? WebCore::windowsKeyCodeForCharCode([s characterAtIndex:0]) : 0;
14381bc750723a18f21cd17d1b173cd2a4dda9cea6eBen Murdoch        if (code)
14481bc750723a18f21cd17d1b173cd2a4dda9cea6eBen Murdoch            return code;
145643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    }
146643ca7872b450ea4efacab6188849e5aac2ba161Steve Block
14781bc750723a18f21cd17d1b173cd2a4dda9cea6eBen Murdoch    // Map Mac virtual key code directly to Windows one for any keys not handled above.
14881bc750723a18f21cd17d1b173cd2a4dda9cea6eBen Murdoch    // 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).
14981bc750723a18f21cd17d1b173cd2a4dda9cea6eBen Murdoch    return WebCore::windowsKeyCodeForKeyCode([event keyCode]);
150643ca7872b450ea4efacab6188849e5aac2ba161Steve Block}
151643ca7872b450ea4efacab6188849e5aac2ba161Steve Block
152643ca7872b450ea4efacab6188849e5aac2ba161Steve Blockstatic inline NSString* textFromEvent(NSEvent* event)
153643ca7872b450ea4efacab6188849e5aac2ba161Steve Block{
154643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    if ([event type] == NSFlagsChanged)
155643ca7872b450ea4efacab6188849e5aac2ba161Steve Block        return @"";
156643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    return [event characters];
157643ca7872b450ea4efacab6188849e5aac2ba161Steve Block}
158643ca7872b450ea4efacab6188849e5aac2ba161Steve Block
159643ca7872b450ea4efacab6188849e5aac2ba161Steve Blockstatic inline NSString* unmodifiedTextFromEvent(NSEvent* event)
160643ca7872b450ea4efacab6188849e5aac2ba161Steve Block{
161643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    if ([event type] == NSFlagsChanged)
162643ca7872b450ea4efacab6188849e5aac2ba161Steve Block        return @"";
163643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    return [event charactersIgnoringModifiers];
164643ca7872b450ea4efacab6188849e5aac2ba161Steve Block}
165643ca7872b450ea4efacab6188849e5aac2ba161Steve Block
166643ca7872b450ea4efacab6188849e5aac2ba161Steve Blockstatic NSString* keyIdentifierForKeyEvent(NSEvent* event)
167643ca7872b450ea4efacab6188849e5aac2ba161Steve Block{
168643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    if ([event type] == NSFlagsChanged) {
169643ca7872b450ea4efacab6188849e5aac2ba161Steve Block        switch ([event keyCode]) {
170643ca7872b450ea4efacab6188849e5aac2ba161Steve Block        case 54: // Right Command
171643ca7872b450ea4efacab6188849e5aac2ba161Steve Block        case 55: // Left Command
172643ca7872b450ea4efacab6188849e5aac2ba161Steve Block            return @"Meta";
173643ca7872b450ea4efacab6188849e5aac2ba161Steve Block
174643ca7872b450ea4efacab6188849e5aac2ba161Steve Block        case 57: // Capslock
175643ca7872b450ea4efacab6188849e5aac2ba161Steve Block            return @"CapsLock";
176643ca7872b450ea4efacab6188849e5aac2ba161Steve Block
177643ca7872b450ea4efacab6188849e5aac2ba161Steve Block        case 56: // Left Shift
178643ca7872b450ea4efacab6188849e5aac2ba161Steve Block        case 60: // Right Shift
179643ca7872b450ea4efacab6188849e5aac2ba161Steve Block            return @"Shift";
180643ca7872b450ea4efacab6188849e5aac2ba161Steve Block
181643ca7872b450ea4efacab6188849e5aac2ba161Steve Block        case 58: // Left Alt
182643ca7872b450ea4efacab6188849e5aac2ba161Steve Block        case 61: // Right Alt
183643ca7872b450ea4efacab6188849e5aac2ba161Steve Block            return @"Alt";
184643ca7872b450ea4efacab6188849e5aac2ba161Steve Block
185643ca7872b450ea4efacab6188849e5aac2ba161Steve Block        case 59: // Left Ctrl
186643ca7872b450ea4efacab6188849e5aac2ba161Steve Block        case 62: // Right Ctrl
187643ca7872b450ea4efacab6188849e5aac2ba161Steve Block            return @"Control";
188643ca7872b450ea4efacab6188849e5aac2ba161Steve Block
189643ca7872b450ea4efacab6188849e5aac2ba161Steve Block// Begin non-Apple addition/modification --------------------------------------
190643ca7872b450ea4efacab6188849e5aac2ba161Steve Block        case 63: // Function
191643ca7872b450ea4efacab6188849e5aac2ba161Steve Block            return @"Function";
192643ca7872b450ea4efacab6188849e5aac2ba161Steve Block
193643ca7872b450ea4efacab6188849e5aac2ba161Steve Block        default: // Unknown, but this may be a strange/new keyboard.
194643ca7872b450ea4efacab6188849e5aac2ba161Steve Block            return @"Unidentified";
195643ca7872b450ea4efacab6188849e5aac2ba161Steve Block// End non-Apple addition/modification ----------------------------------------
196643ca7872b450ea4efacab6188849e5aac2ba161Steve Block        }
197643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    }
198643ca7872b450ea4efacab6188849e5aac2ba161Steve Block
199643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    NSString* s = [event charactersIgnoringModifiers];
200643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    if ([s length] != 1)
201643ca7872b450ea4efacab6188849e5aac2ba161Steve Block        return @"Unidentified";
202643ca7872b450ea4efacab6188849e5aac2ba161Steve Block
203643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    unichar c = [s characterAtIndex:0];
204643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    switch (c) {
205643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    // Each identifier listed in the DOM spec is listed here.
206643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    // Many are simply commented out since they do not appear on standard Macintosh keyboards
207643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    // or are on a key that doesn't have a corresponding character.
208643ca7872b450ea4efacab6188849e5aac2ba161Steve Block
209643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    // "Accept"
210643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    // "AllCandidates"
211643ca7872b450ea4efacab6188849e5aac2ba161Steve Block
212643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    // "Alt"
213643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    case NSMenuFunctionKey:
214643ca7872b450ea4efacab6188849e5aac2ba161Steve Block        return @"Alt";
215643ca7872b450ea4efacab6188849e5aac2ba161Steve Block
216643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    // "Apps"
217643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    // "BrowserBack"
218643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    // "BrowserForward"
219643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    // "BrowserHome"
220643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    // "BrowserRefresh"
221643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    // "BrowserSearch"
222643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    // "BrowserStop"
223643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    // "CapsLock"
224643ca7872b450ea4efacab6188849e5aac2ba161Steve Block
225643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    // "Clear"
226643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    case NSClearLineFunctionKey:
227643ca7872b450ea4efacab6188849e5aac2ba161Steve Block        return @"Clear";
228643ca7872b450ea4efacab6188849e5aac2ba161Steve Block
229643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    // "CodeInput"
230643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    // "Compose"
231643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    // "Control"
232643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    // "Crsel"
233643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    // "Convert"
234643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    // "Copy"
235643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    // "Cut"
236643ca7872b450ea4efacab6188849e5aac2ba161Steve Block
237643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    // "Down"
238643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    case NSDownArrowFunctionKey:
239643ca7872b450ea4efacab6188849e5aac2ba161Steve Block        return @"Down";
240643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    // "End"
241643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    case NSEndFunctionKey:
242643ca7872b450ea4efacab6188849e5aac2ba161Steve Block        return @"End";
243643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    // "Enter"
244643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    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
245643ca7872b450ea4efacab6188849e5aac2ba161Steve Block        return @"Enter";
246643ca7872b450ea4efacab6188849e5aac2ba161Steve Block
247643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    // "EraseEof"
248643ca7872b450ea4efacab6188849e5aac2ba161Steve Block
249643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    // "Execute"
250643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    case NSExecuteFunctionKey:
251643ca7872b450ea4efacab6188849e5aac2ba161Steve Block        return @"Execute";
252643ca7872b450ea4efacab6188849e5aac2ba161Steve Block
253643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    // "Exsel"
254643ca7872b450ea4efacab6188849e5aac2ba161Steve Block
255643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    // "F1"
256643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    case NSF1FunctionKey:
257643ca7872b450ea4efacab6188849e5aac2ba161Steve Block        return @"F1";
258643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    // "F2"
259643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    case NSF2FunctionKey:
260643ca7872b450ea4efacab6188849e5aac2ba161Steve Block        return @"F2";
261643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    // "F3"
262643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    case NSF3FunctionKey:
263643ca7872b450ea4efacab6188849e5aac2ba161Steve Block        return @"F3";
264643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    // "F4"
265643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    case NSF4FunctionKey:
266643ca7872b450ea4efacab6188849e5aac2ba161Steve Block        return @"F4";
267643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    // "F5"
268643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    case NSF5FunctionKey:
269643ca7872b450ea4efacab6188849e5aac2ba161Steve Block        return @"F5";
270643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    // "F6"
271643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    case NSF6FunctionKey:
272643ca7872b450ea4efacab6188849e5aac2ba161Steve Block        return @"F6";
273643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    // "F7"
274643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    case NSF7FunctionKey:
275643ca7872b450ea4efacab6188849e5aac2ba161Steve Block        return @"F7";
276643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    // "F8"
277643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    case NSF8FunctionKey:
278643ca7872b450ea4efacab6188849e5aac2ba161Steve Block        return @"F8";
279643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    // "F9"
280643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    case NSF9FunctionKey:
281643ca7872b450ea4efacab6188849e5aac2ba161Steve Block        return @"F9";
282643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    // "F10"
283643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    case NSF10FunctionKey:
284643ca7872b450ea4efacab6188849e5aac2ba161Steve Block        return @"F10";
285643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    // "F11"
286643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    case NSF11FunctionKey:
287643ca7872b450ea4efacab6188849e5aac2ba161Steve Block        return @"F11";
288643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    // "F12"
289643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    case NSF12FunctionKey:
290643ca7872b450ea4efacab6188849e5aac2ba161Steve Block        return @"F12";
291643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    // "F13"
292643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    case NSF13FunctionKey:
293643ca7872b450ea4efacab6188849e5aac2ba161Steve Block        return @"F13";
294643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    // "F14"
295643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    case NSF14FunctionKey:
296643ca7872b450ea4efacab6188849e5aac2ba161Steve Block        return @"F14";
297643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    // "F15"
298643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    case NSF15FunctionKey:
299643ca7872b450ea4efacab6188849e5aac2ba161Steve Block        return @"F15";
300643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    // "F16"
301643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    case NSF16FunctionKey:
302643ca7872b450ea4efacab6188849e5aac2ba161Steve Block        return @"F16";
303643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    // "F17"
304643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    case NSF17FunctionKey:
305643ca7872b450ea4efacab6188849e5aac2ba161Steve Block        return @"F17";
306643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    // "F18"
307643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    case NSF18FunctionKey:
308643ca7872b450ea4efacab6188849e5aac2ba161Steve Block        return @"F18";
309643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    // "F19"
310643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    case NSF19FunctionKey:
311643ca7872b450ea4efacab6188849e5aac2ba161Steve Block        return @"F19";
312643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    // "F20"
313643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    case NSF20FunctionKey:
314643ca7872b450ea4efacab6188849e5aac2ba161Steve Block        return @"F20";
315643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    // "F21"
316643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    case NSF21FunctionKey:
317643ca7872b450ea4efacab6188849e5aac2ba161Steve Block        return @"F21";
318643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    // "F22"
319643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    case NSF22FunctionKey:
320643ca7872b450ea4efacab6188849e5aac2ba161Steve Block        return @"F22";
321643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    // "F23"
322643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    case NSF23FunctionKey:
323643ca7872b450ea4efacab6188849e5aac2ba161Steve Block        return @"F23";
324643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    // "F24"
325643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    case NSF24FunctionKey:
326643ca7872b450ea4efacab6188849e5aac2ba161Steve Block        return @"F24";
327643ca7872b450ea4efacab6188849e5aac2ba161Steve Block
328643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    // "FinalMode"
329643ca7872b450ea4efacab6188849e5aac2ba161Steve Block
330643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    // "Find"
331643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    case NSFindFunctionKey:
332643ca7872b450ea4efacab6188849e5aac2ba161Steve Block        return @"Find";
333643ca7872b450ea4efacab6188849e5aac2ba161Steve Block
334643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    // "FullWidth"
335643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    // "HalfWidth"
336643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    // "HangulMode"
337643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    // "HanjaMode"
338643ca7872b450ea4efacab6188849e5aac2ba161Steve Block
339643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    // "Help"
340643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    case NSHelpFunctionKey:
341643ca7872b450ea4efacab6188849e5aac2ba161Steve Block        return @"Help";
342643ca7872b450ea4efacab6188849e5aac2ba161Steve Block
343643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    // "Hiragana"
344643ca7872b450ea4efacab6188849e5aac2ba161Steve Block
345643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    // "Home"
346643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    case NSHomeFunctionKey:
347643ca7872b450ea4efacab6188849e5aac2ba161Steve Block        return @"Home";
348643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    // "Insert"
349643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    case NSInsertFunctionKey:
350643ca7872b450ea4efacab6188849e5aac2ba161Steve Block        return @"Insert";
351643ca7872b450ea4efacab6188849e5aac2ba161Steve Block
352643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    // "JapaneseHiragana"
353643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    // "JapaneseKatakana"
354643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    // "JapaneseRomaji"
355643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    // "JunjaMode"
356643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    // "KanaMode"
357643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    // "KanjiMode"
358643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    // "Katakana"
359643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    // "LaunchApplication1"
360643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    // "LaunchApplication2"
361643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    // "LaunchMail"
362643ca7872b450ea4efacab6188849e5aac2ba161Steve Block
363643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    // "Left"
364643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    case NSLeftArrowFunctionKey:
365643ca7872b450ea4efacab6188849e5aac2ba161Steve Block        return @"Left";
366643ca7872b450ea4efacab6188849e5aac2ba161Steve Block
367643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    // "Meta"
368643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    // "MediaNextTrack"
369643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    // "MediaPlayPause"
370643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    // "MediaPreviousTrack"
371643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    // "MediaStop"
372643ca7872b450ea4efacab6188849e5aac2ba161Steve Block
373643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    // "ModeChange"
374643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    case NSModeSwitchFunctionKey:
375643ca7872b450ea4efacab6188849e5aac2ba161Steve Block        return @"ModeChange";
376643ca7872b450ea4efacab6188849e5aac2ba161Steve Block
377643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    // "Nonconvert"
378643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    // "NumLock"
379643ca7872b450ea4efacab6188849e5aac2ba161Steve Block
380643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    // "PageDown"
381643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    case NSPageDownFunctionKey:
382643ca7872b450ea4efacab6188849e5aac2ba161Steve Block        return @"PageDown";
383643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    // "PageUp"
384643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    case NSPageUpFunctionKey:
385643ca7872b450ea4efacab6188849e5aac2ba161Steve Block        return @"PageUp";
386643ca7872b450ea4efacab6188849e5aac2ba161Steve Block
387643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    // "Paste"
388643ca7872b450ea4efacab6188849e5aac2ba161Steve Block
389643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    // "Pause"
390643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    case NSPauseFunctionKey:
391643ca7872b450ea4efacab6188849e5aac2ba161Steve Block        return @"Pause";
392643ca7872b450ea4efacab6188849e5aac2ba161Steve Block
393643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    // "Play"
394643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    // "PreviousCandidate"
395643ca7872b450ea4efacab6188849e5aac2ba161Steve Block
396643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    // "PrintScreen"
397643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    case NSPrintScreenFunctionKey:
398643ca7872b450ea4efacab6188849e5aac2ba161Steve Block        return @"PrintScreen";
399643ca7872b450ea4efacab6188849e5aac2ba161Steve Block
400643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    // "Process"
401643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    // "Props"
402643ca7872b450ea4efacab6188849e5aac2ba161Steve Block
403643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    // "Right"
404643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    case NSRightArrowFunctionKey:
405643ca7872b450ea4efacab6188849e5aac2ba161Steve Block        return @"Right";
406643ca7872b450ea4efacab6188849e5aac2ba161Steve Block
407643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    // "RomanCharacters"
408643ca7872b450ea4efacab6188849e5aac2ba161Steve Block
409643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    // "Scroll"
410643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    case NSScrollLockFunctionKey:
411643ca7872b450ea4efacab6188849e5aac2ba161Steve Block        return @"Scroll";
412643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    // "Select"
413643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    case NSSelectFunctionKey:
414643ca7872b450ea4efacab6188849e5aac2ba161Steve Block        return @"Select";
415643ca7872b450ea4efacab6188849e5aac2ba161Steve Block
416643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    // "SelectMedia"
417643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    // "Shift"
418643ca7872b450ea4efacab6188849e5aac2ba161Steve Block
419643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    // "Stop"
420643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    case NSStopFunctionKey:
421643ca7872b450ea4efacab6188849e5aac2ba161Steve Block        return @"Stop";
422643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    // "Up"
423643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    case NSUpArrowFunctionKey:
424643ca7872b450ea4efacab6188849e5aac2ba161Steve Block        return @"Up";
425643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    // "Undo"
426643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    case NSUndoFunctionKey:
427643ca7872b450ea4efacab6188849e5aac2ba161Steve Block        return @"Undo";
428643ca7872b450ea4efacab6188849e5aac2ba161Steve Block
429643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    // "VolumeDown"
430643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    // "VolumeMute"
431643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    // "VolumeUp"
432643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    // "Win"
433643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    // "Zoom"
434643ca7872b450ea4efacab6188849e5aac2ba161Steve Block
435643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    // More function keys, not in the key identifier specification.
436643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    case NSF25FunctionKey:
437643ca7872b450ea4efacab6188849e5aac2ba161Steve Block        return @"F25";
438643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    case NSF26FunctionKey:
439643ca7872b450ea4efacab6188849e5aac2ba161Steve Block        return @"F26";
440643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    case NSF27FunctionKey:
441643ca7872b450ea4efacab6188849e5aac2ba161Steve Block        return @"F27";
442643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    case NSF28FunctionKey:
443643ca7872b450ea4efacab6188849e5aac2ba161Steve Block        return @"F28";
444643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    case NSF29FunctionKey:
445643ca7872b450ea4efacab6188849e5aac2ba161Steve Block        return @"F29";
446643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    case NSF30FunctionKey:
447643ca7872b450ea4efacab6188849e5aac2ba161Steve Block        return @"F30";
448643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    case NSF31FunctionKey:
449643ca7872b450ea4efacab6188849e5aac2ba161Steve Block        return @"F31";
450643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    case NSF32FunctionKey:
451643ca7872b450ea4efacab6188849e5aac2ba161Steve Block        return @"F32";
452643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    case NSF33FunctionKey:
453643ca7872b450ea4efacab6188849e5aac2ba161Steve Block        return @"F33";
454643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    case NSF34FunctionKey:
455643ca7872b450ea4efacab6188849e5aac2ba161Steve Block        return @"F34";
456643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    case NSF35FunctionKey:
457643ca7872b450ea4efacab6188849e5aac2ba161Steve Block        return @"F35";
458643ca7872b450ea4efacab6188849e5aac2ba161Steve Block
459643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    // Turn 0x7F into 0x08, because backspace needs to always be 0x08.
460643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    case 0x7F:
461643ca7872b450ea4efacab6188849e5aac2ba161Steve Block        return @"U+0008";
462643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    // Standard says that DEL becomes U+007F.
463643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    case NSDeleteFunctionKey:
464643ca7872b450ea4efacab6188849e5aac2ba161Steve Block        return @"U+007F";
465643ca7872b450ea4efacab6188849e5aac2ba161Steve Block
466643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    // Always use 0x09 for tab instead of AppKit's backtab character.
467643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    case NSBackTabCharacter:
468643ca7872b450ea4efacab6188849e5aac2ba161Steve Block        return @"U+0009";
469643ca7872b450ea4efacab6188849e5aac2ba161Steve Block
470643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    case NSBeginFunctionKey:
471643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    case NSBreakFunctionKey:
472643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    case NSClearDisplayFunctionKey:
473643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    case NSDeleteCharFunctionKey:
474643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    case NSDeleteLineFunctionKey:
475643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    case NSInsertCharFunctionKey:
476643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    case NSInsertLineFunctionKey:
477643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    case NSNextFunctionKey:
478643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    case NSPrevFunctionKey:
479643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    case NSPrintFunctionKey:
480643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    case NSRedoFunctionKey:
481643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    case NSResetFunctionKey:
482643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    case NSSysReqFunctionKey:
483643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    case NSSystemFunctionKey:
484643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    case NSUserFunctionKey:
485643ca7872b450ea4efacab6188849e5aac2ba161Steve Block        // FIXME: We should use something other than the vendor-area Unicode values for the above keys.
486643ca7872b450ea4efacab6188849e5aac2ba161Steve Block        // For now, just fall through to the default.
487643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    default:
488643ca7872b450ea4efacab6188849e5aac2ba161Steve Block        return [NSString stringWithFormat:@"U+%04X", WTF::toASCIIUpper(c)];
489643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    }
490643ca7872b450ea4efacab6188849e5aac2ba161Steve Block}
491643ca7872b450ea4efacab6188849e5aac2ba161Steve Block
492643ca7872b450ea4efacab6188849e5aac2ba161Steve Block// End Apple code.
493643ca7872b450ea4efacab6188849e5aac2ba161Steve Block// ----------------------------------------------------------------------------
494643ca7872b450ea4efacab6188849e5aac2ba161Steve Block
495643ca7872b450ea4efacab6188849e5aac2ba161Steve Blockstatic inline int modifiersFromEvent(NSEvent* event) {
496643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    int modifiers = 0;
497643ca7872b450ea4efacab6188849e5aac2ba161Steve Block
498643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    if ([event modifierFlags] & NSControlKeyMask)
499643ca7872b450ea4efacab6188849e5aac2ba161Steve Block        modifiers |= WebInputEvent::ControlKey;
500643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    if ([event modifierFlags] & NSShiftKeyMask)
501643ca7872b450ea4efacab6188849e5aac2ba161Steve Block        modifiers |= WebInputEvent::ShiftKey;
502643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    if ([event modifierFlags] & NSAlternateKeyMask)
503643ca7872b450ea4efacab6188849e5aac2ba161Steve Block        modifiers |= WebInputEvent::AltKey;
504643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    if ([event modifierFlags] & NSCommandKeyMask)
505643ca7872b450ea4efacab6188849e5aac2ba161Steve Block        modifiers |= WebInputEvent::MetaKey;
506e14391e94c850b8bd03680c23b38978db68687a8John Reck    if ([event modifierFlags] & NSAlphaShiftKeyMask)
507e14391e94c850b8bd03680c23b38978db68687a8John Reck        modifiers |= WebInputEvent::CapsLockOn;
508643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    // TODO(port): Set mouse button states
509643ca7872b450ea4efacab6188849e5aac2ba161Steve Block
510643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    return modifiers;
511643ca7872b450ea4efacab6188849e5aac2ba161Steve Block}
512643ca7872b450ea4efacab6188849e5aac2ba161Steve Block
513dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Blockstatic inline void setWebEventLocationFromEventInView(WebMouseEvent* result,
514dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block                                                      NSEvent* event,
515dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block                                                      NSView* view) {
516dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block    NSPoint windowLocal = [event locationInWindow];
517dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block
518dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block    NSPoint screenLocal = [[view window] convertBaseToScreen:windowLocal];
519dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block    result->globalX = screenLocal.x;
520dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block    // Flip y.
521dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block    NSScreen* primaryScreen = ([[NSScreen screens] count] > 0) ?
522dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block        [[NSScreen screens] objectAtIndex:0] : nil;
523dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block    if (primaryScreen)
524dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block        result->globalY = [primaryScreen frame].size.height - screenLocal.y;
525dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block    else
526dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block        result->globalY = screenLocal.y;
527dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block
528dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block    NSPoint contentLocal = [view convertPoint:windowLocal fromView:nil];
529dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block    result->x = contentLocal.x;
530dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block    result->y = [view frame].size.height - contentLocal.y;  // Flip y.
531dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block
532dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block    result->windowX = result->x;
533dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block    result->windowY = result->y;
534dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block}
535dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block
536643ca7872b450ea4efacab6188849e5aac2ba161Steve BlockWebKeyboardEvent WebInputEventFactory::keyboardEvent(NSEvent* event)
537643ca7872b450ea4efacab6188849e5aac2ba161Steve Block{
538643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    WebKeyboardEvent result;
539643ca7872b450ea4efacab6188849e5aac2ba161Steve Block
540643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    result.type =
541643ca7872b450ea4efacab6188849e5aac2ba161Steve Block        isKeyUpEvent(event) ? WebInputEvent::KeyUp : WebInputEvent::RawKeyDown;
542643ca7872b450ea4efacab6188849e5aac2ba161Steve Block
543643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    result.modifiers = modifiersFromEvent(event);
544643ca7872b450ea4efacab6188849e5aac2ba161Steve Block
545643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    if (isKeypadEvent(event))
546643ca7872b450ea4efacab6188849e5aac2ba161Steve Block        result.modifiers |= WebInputEvent::IsKeyPad;
547643ca7872b450ea4efacab6188849e5aac2ba161Steve Block
548643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    if (([event type] != NSFlagsChanged) && [event isARepeat])
549643ca7872b450ea4efacab6188849e5aac2ba161Steve Block        result.modifiers |= WebInputEvent::IsAutoRepeat;
550643ca7872b450ea4efacab6188849e5aac2ba161Steve Block
551643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    result.windowsKeyCode = windowsKeyCodeForKeyEvent(event);
552643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    result.nativeKeyCode = [event keyCode];
553643ca7872b450ea4efacab6188849e5aac2ba161Steve Block
554643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    NSString* textStr = textFromEvent(event);
555643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    NSString* unmodifiedStr = unmodifiedTextFromEvent(event);
556643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    NSString* identifierStr = keyIdentifierForKeyEvent(event);
557643ca7872b450ea4efacab6188849e5aac2ba161Steve Block
558643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    // Begin Apple code, copied from KeyEventMac.mm
559643ca7872b450ea4efacab6188849e5aac2ba161Steve Block
560643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    // Always use 13 for Enter/Return -- we don't want to use AppKit's
561643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    // different character for Enter.
562643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    if (result.windowsKeyCode == '\r') {
563643ca7872b450ea4efacab6188849e5aac2ba161Steve Block        textStr = @"\r";
564643ca7872b450ea4efacab6188849e5aac2ba161Steve Block        unmodifiedStr = @"\r";
565643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    }
566643ca7872b450ea4efacab6188849e5aac2ba161Steve Block
567643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    // The adjustments below are only needed in backward compatibility mode,
568643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    // but we cannot tell what mode we are in from here.
569643ca7872b450ea4efacab6188849e5aac2ba161Steve Block
570643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    // Turn 0x7F into 8, because backspace needs to always be 8.
571643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    if ([textStr isEqualToString:@"\x7F"])
572643ca7872b450ea4efacab6188849e5aac2ba161Steve Block        textStr = @"\x8";
573643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    if ([unmodifiedStr isEqualToString:@"\x7F"])
574643ca7872b450ea4efacab6188849e5aac2ba161Steve Block        unmodifiedStr = @"\x8";
575643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    // Always use 9 for tab -- we don't want to use AppKit's different character
576643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    // for shift-tab.
577643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    if (result.windowsKeyCode == 9) {
578643ca7872b450ea4efacab6188849e5aac2ba161Steve Block        textStr = @"\x9";
579643ca7872b450ea4efacab6188849e5aac2ba161Steve Block        unmodifiedStr = @"\x9";
580643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    }
581643ca7872b450ea4efacab6188849e5aac2ba161Steve Block
582643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    // End Apple code.
583643ca7872b450ea4efacab6188849e5aac2ba161Steve Block
584643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    if ([textStr length] < WebKeyboardEvent::textLengthCap &&
585643ca7872b450ea4efacab6188849e5aac2ba161Steve Block        [unmodifiedStr length] < WebKeyboardEvent::textLengthCap) {
586643ca7872b450ea4efacab6188849e5aac2ba161Steve Block        [textStr getCharacters:&result.text[0]];
587643ca7872b450ea4efacab6188849e5aac2ba161Steve Block        [unmodifiedStr getCharacters:&result.unmodifiedText[0]];
588643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    } else
589643ca7872b450ea4efacab6188849e5aac2ba161Steve Block        ASSERT_NOT_REACHED();
590643ca7872b450ea4efacab6188849e5aac2ba161Steve Block
591643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    [identifierStr getCString:&result.keyIdentifier[0]
592643ca7872b450ea4efacab6188849e5aac2ba161Steve Block                    maxLength:sizeof(result.keyIdentifier)
593643ca7872b450ea4efacab6188849e5aac2ba161Steve Block                     encoding:NSASCIIStringEncoding];
594643ca7872b450ea4efacab6188849e5aac2ba161Steve Block
595643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    result.timeStampSeconds = [event timestamp];
596643ca7872b450ea4efacab6188849e5aac2ba161Steve Block
597643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    // Windows and Linux set |isSystemKey| if alt is down. WebKit looks at this
598643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    // flag to decide if it should handle a key or not. E.g. alt-left/right
599643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    // shouldn't be used by WebKit to scroll the current page, because we want
600643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    // to get that key back for it to do history navigation. Hence, the
601643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    // corresponding situation on OS X is to set this for cmd key presses.
602643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    if (result.modifiers & WebInputEvent::MetaKey)
603643ca7872b450ea4efacab6188849e5aac2ba161Steve Block        result.isSystemKey = true;
604643ca7872b450ea4efacab6188849e5aac2ba161Steve Block
605643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    return result;
606643ca7872b450ea4efacab6188849e5aac2ba161Steve Block}
607643ca7872b450ea4efacab6188849e5aac2ba161Steve Block
608643ca7872b450ea4efacab6188849e5aac2ba161Steve BlockWebKeyboardEvent WebInputEventFactory::keyboardEvent(wchar_t character,
609643ca7872b450ea4efacab6188849e5aac2ba161Steve Block                                                     int modifiers,
610643ca7872b450ea4efacab6188849e5aac2ba161Steve Block                                                     double timeStampSeconds)
611643ca7872b450ea4efacab6188849e5aac2ba161Steve Block{
612643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    // keyboardEvent(NSEvent*) depends on the NSEvent object and
613643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    // it is hard to use it from methods of the NSTextInput protocol. For
614643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    // such methods, this function creates a WebInputEvent::Char event without
615643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    // using a NSEvent object.
616643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    WebKeyboardEvent result;
617643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    result.type = WebKit::WebInputEvent::Char;
618643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    result.timeStampSeconds = timeStampSeconds;
619643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    result.modifiers = modifiers;
620643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    result.windowsKeyCode = character;
621643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    result.nativeKeyCode = character;
622643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    result.text[0] = character;
623643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    result.unmodifiedText[0] = character;
624643ca7872b450ea4efacab6188849e5aac2ba161Steve Block
625643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    // Windows and Linux set |isSystemKey| if alt is down. WebKit looks at this
626643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    // flag to decide if it should handle a key or not. E.g. alt-left/right
627643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    // shouldn't be used by WebKit to scroll the current page, because we want
628643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    // to get that key back for it to do history navigation. Hence, the
629643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    // corresponding situation on OS X is to set this for cmd key presses.
630643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    if (result.modifiers & WebInputEvent::MetaKey)
631643ca7872b450ea4efacab6188849e5aac2ba161Steve Block        result.isSystemKey = true;
632643ca7872b450ea4efacab6188849e5aac2ba161Steve Block
633643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    return result;
634643ca7872b450ea4efacab6188849e5aac2ba161Steve Block}
635643ca7872b450ea4efacab6188849e5aac2ba161Steve Block
636643ca7872b450ea4efacab6188849e5aac2ba161Steve Block// WebMouseEvent --------------------------------------------------------------
637643ca7872b450ea4efacab6188849e5aac2ba161Steve Block
638643ca7872b450ea4efacab6188849e5aac2ba161Steve BlockWebMouseEvent WebInputEventFactory::mouseEvent(NSEvent* event, NSView* view)
639643ca7872b450ea4efacab6188849e5aac2ba161Steve Block{
640643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    WebMouseEvent result;
641643ca7872b450ea4efacab6188849e5aac2ba161Steve Block
642643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    result.clickCount = 0;
643643ca7872b450ea4efacab6188849e5aac2ba161Steve Block
644643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    switch ([event type]) {
645643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    case NSMouseExited:
646643ca7872b450ea4efacab6188849e5aac2ba161Steve Block        result.type = WebInputEvent::MouseLeave;
647643ca7872b450ea4efacab6188849e5aac2ba161Steve Block        result.button = WebMouseEvent::ButtonNone;
648643ca7872b450ea4efacab6188849e5aac2ba161Steve Block        break;
649643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    case NSLeftMouseDown:
650643ca7872b450ea4efacab6188849e5aac2ba161Steve Block        result.type = WebInputEvent::MouseDown;
651643ca7872b450ea4efacab6188849e5aac2ba161Steve Block        result.clickCount = [event clickCount];
652643ca7872b450ea4efacab6188849e5aac2ba161Steve Block        result.button = WebMouseEvent::ButtonLeft;
653643ca7872b450ea4efacab6188849e5aac2ba161Steve Block        break;
654643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    case NSOtherMouseDown:
655643ca7872b450ea4efacab6188849e5aac2ba161Steve Block        result.type = WebInputEvent::MouseDown;
656643ca7872b450ea4efacab6188849e5aac2ba161Steve Block        result.clickCount = [event clickCount];
657643ca7872b450ea4efacab6188849e5aac2ba161Steve Block        result.button = WebMouseEvent::ButtonMiddle;
658643ca7872b450ea4efacab6188849e5aac2ba161Steve Block        break;
659643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    case NSRightMouseDown:
660643ca7872b450ea4efacab6188849e5aac2ba161Steve Block        result.type = WebInputEvent::MouseDown;
661643ca7872b450ea4efacab6188849e5aac2ba161Steve Block        result.clickCount = [event clickCount];
662643ca7872b450ea4efacab6188849e5aac2ba161Steve Block        result.button = WebMouseEvent::ButtonRight;
663643ca7872b450ea4efacab6188849e5aac2ba161Steve Block        break;
664643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    case NSLeftMouseUp:
665643ca7872b450ea4efacab6188849e5aac2ba161Steve Block        result.type = WebInputEvent::MouseUp;
6666b70adc33054f8aee8c54d0f460458a9df11b8a5Russell Brenner        result.clickCount = [event clickCount];
667643ca7872b450ea4efacab6188849e5aac2ba161Steve Block        result.button = WebMouseEvent::ButtonLeft;
668643ca7872b450ea4efacab6188849e5aac2ba161Steve Block        break;
669643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    case NSOtherMouseUp:
670643ca7872b450ea4efacab6188849e5aac2ba161Steve Block        result.type = WebInputEvent::MouseUp;
6716b70adc33054f8aee8c54d0f460458a9df11b8a5Russell Brenner        result.clickCount = [event clickCount];
672643ca7872b450ea4efacab6188849e5aac2ba161Steve Block        result.button = WebMouseEvent::ButtonMiddle;
673643ca7872b450ea4efacab6188849e5aac2ba161Steve Block        break;
674643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    case NSRightMouseUp:
675643ca7872b450ea4efacab6188849e5aac2ba161Steve Block        result.type = WebInputEvent::MouseUp;
6766b70adc33054f8aee8c54d0f460458a9df11b8a5Russell Brenner        result.clickCount = [event clickCount];
677643ca7872b450ea4efacab6188849e5aac2ba161Steve Block        result.button = WebMouseEvent::ButtonRight;
678643ca7872b450ea4efacab6188849e5aac2ba161Steve Block        break;
679643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    case NSMouseMoved:
680643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    case NSMouseEntered:
681643ca7872b450ea4efacab6188849e5aac2ba161Steve Block        result.type = WebInputEvent::MouseMove;
682643ca7872b450ea4efacab6188849e5aac2ba161Steve Block        break;
683643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    case NSLeftMouseDragged:
684643ca7872b450ea4efacab6188849e5aac2ba161Steve Block        result.type = WebInputEvent::MouseMove;
685643ca7872b450ea4efacab6188849e5aac2ba161Steve Block        result.button = WebMouseEvent::ButtonLeft;
686643ca7872b450ea4efacab6188849e5aac2ba161Steve Block        break;
687643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    case NSOtherMouseDragged:
688643ca7872b450ea4efacab6188849e5aac2ba161Steve Block        result.type = WebInputEvent::MouseMove;
689643ca7872b450ea4efacab6188849e5aac2ba161Steve Block        result.button = WebMouseEvent::ButtonMiddle;
690643ca7872b450ea4efacab6188849e5aac2ba161Steve Block        break;
691643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    case NSRightMouseDragged:
692643ca7872b450ea4efacab6188849e5aac2ba161Steve Block        result.type = WebInputEvent::MouseMove;
693643ca7872b450ea4efacab6188849e5aac2ba161Steve Block        result.button = WebMouseEvent::ButtonRight;
694643ca7872b450ea4efacab6188849e5aac2ba161Steve Block        break;
695643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    default:
696643ca7872b450ea4efacab6188849e5aac2ba161Steve Block        ASSERT_NOT_REACHED();
697643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    }
698643ca7872b450ea4efacab6188849e5aac2ba161Steve Block
699dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block    setWebEventLocationFromEventInView(&result, event, view);
700643ca7872b450ea4efacab6188849e5aac2ba161Steve Block
701643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    result.modifiers = modifiersFromEvent(event);
702643ca7872b450ea4efacab6188849e5aac2ba161Steve Block
703643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    result.timeStampSeconds = [event timestamp];
704643ca7872b450ea4efacab6188849e5aac2ba161Steve Block
705643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    return result;
706643ca7872b450ea4efacab6188849e5aac2ba161Steve Block}
707643ca7872b450ea4efacab6188849e5aac2ba161Steve Block
708643ca7872b450ea4efacab6188849e5aac2ba161Steve Block// WebMouseWheelEvent ---------------------------------------------------------
709643ca7872b450ea4efacab6188849e5aac2ba161Steve Block
710643ca7872b450ea4efacab6188849e5aac2ba161Steve BlockWebMouseWheelEvent WebInputEventFactory::mouseWheelEvent(NSEvent* event, NSView* view)
711643ca7872b450ea4efacab6188849e5aac2ba161Steve Block{
712643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    WebMouseWheelEvent result;
713643ca7872b450ea4efacab6188849e5aac2ba161Steve Block
714643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    result.type = WebInputEvent::MouseWheel;
715643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    result.button = WebMouseEvent::ButtonNone;
716643ca7872b450ea4efacab6188849e5aac2ba161Steve Block
717643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    result.modifiers = modifiersFromEvent(event);
718643ca7872b450ea4efacab6188849e5aac2ba161Steve Block
719dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block    setWebEventLocationFromEventInView(&result, event, view);
720643ca7872b450ea4efacab6188849e5aac2ba161Steve Block
721643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    // Of Mice and Men
722643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    // ---------------
723643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    //
724643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    // There are three types of scroll data available on a scroll wheel CGEvent.
725643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    // Apple's documentation ([1]) is rather vague in their differences, and not
726643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    // terribly helpful in deciding which to use. This is what's really going on.
727643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    //
728643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    // First, these events behave very differently depending on whether a standard
729643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    // wheel mouse is used (one that scrolls in discrete units) or a
730643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    // trackpad/Mighty Mouse is used (which both provide continuous scrolling).
731643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    // You must check to see which was used for the event by testing the
732643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    // kCGScrollWheelEventIsContinuous field.
733643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    //
734643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    // Second, these events refer to "axes". Axis 1 is the y-axis, and axis 2 is
735643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    // the x-axis.
736643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    //
737643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    // Third, there is a concept of mouse acceleration. Scrolling the same amount
738643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    // of physical distance will give you different results logically depending on
739643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    // whether you scrolled a little at a time or in one continuous motion. Some
740643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    // fields account for this while others do not.
741643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    //
742643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    // Fourth, for trackpads there is a concept of chunkiness. When scrolling
743643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    // continuously, events can be delivered in chunks. That is to say, lots of
744643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    // scroll events with delta 0 will be delivered, and every so often an event
745643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    // with a non-zero delta will be delivered, containing the accumulated deltas
746643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    // from all the intermediate moves. [2]
747643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    //
748643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    // For notchy wheel mice (kCGScrollWheelEventIsContinuous == 0)
749643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    // ------------------------------------------------------------
750643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    //
751643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    // kCGScrollWheelEventDeltaAxis*
752643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    //   This is the rawest of raw events. For each mouse notch you get a value of
753643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    //   +1/-1. This does not take acceleration into account and thus is less
754643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    //   useful for building UIs.
755643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    //
756643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    // kCGScrollWheelEventPointDeltaAxis*
757643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    //   This is smarter. In general, for each mouse notch you get a value of
758643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    //   +1/-1, but this _does_ take acceleration into account, so you will get
759643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    //   larger values on longer scrolls. This field would be ideal for building
760643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    //   UIs except for one nasty bug: when the shift key is pressed, this set of
761643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    //   fields fails to move the value into the axis2 field (the other two types
762643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    //   of data do). This wouldn't be so bad except for the fact that while the
763643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    //   number of axes is used in the creation of a CGScrollWheelEvent, there is
764643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    //   no way to get that information out of the event once created.
765643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    //
766643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    // kCGScrollWheelEventFixedPtDeltaAxis*
767643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    //   This is a fixed value, and for each mouse notch you get a value of
768643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    //   +0.1/-0.1 (but, like above, scaled appropriately for acceleration). This
769643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    //   value takes acceleration into account, and in fact is identical to the
770643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    //   results you get from -[NSEvent delta*]. (That is, if you linked on Tiger
771643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    //   or greater; see [2] for details.)
772643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    //
773643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    // A note about continuous devices
774643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    // -------------------------------
775643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    //
776643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    // There are two devices that provide continuous scrolling events (trackpads
777643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    // and Mighty Mouses) and they behave rather differently. The Mighty Mouse
778643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    // behaves a lot like a regular mouse. There is no chunking, and the
779643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    // FixedPtDelta values are the PointDelta values multiplied by 0.1. With the
780643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    // trackpad, though, there is chunking. While the FixedPtDelta values are
781643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    // reasonable (they occur about every fifth event but have values five times
782643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    // larger than usual) the Delta values are unreasonable. They don't appear to
783643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    // accumulate properly.
784643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    //
785643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    // For continuous devices (kCGScrollWheelEventIsContinuous != 0)
786643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    // -------------------------------------------------------------
787643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    //
788643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    // kCGScrollWheelEventDeltaAxis*
789643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    //   This provides values with no acceleration. With a trackpad, these values
790643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    //   are chunked but each non-zero value does not appear to be cumulative.
791643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    //   This seems to be a bug.
792643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    //
793643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    // kCGScrollWheelEventPointDeltaAxis*
794643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    //   This provides values with acceleration. With a trackpad, these values are
795643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    //   not chunked and are highly accurate.
796643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    //
797643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    // kCGScrollWheelEventFixedPtDeltaAxis*
798643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    //   This provides values with acceleration. With a trackpad, these values are
799643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    //   chunked but unlike Delta events are properly cumulative.
800643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    //
801643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    // Summary
802643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    // -------
803643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    //
804643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    // In general the best approach to take is: determine if the event is
805643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    // continuous. If it is not, then use the FixedPtDelta events (or just stick
806643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    // with Cocoa events). They provide both acceleration and proper horizontal
807643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    // scrolling. If the event is continuous, then doing pixel scrolling with the
808643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    // PointDelta is the way to go. In general, avoid the Delta events. They're
809643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    // the oldest (dating back to 10.4, before CGEvents were public) but they lack
810643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    // acceleration and precision, making them useful only in specific edge cases.
811643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    //
812643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    // References
813643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    // ----------
814643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    //
815643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    // [1] <http://developer.apple.com/documentation/Carbon/Reference/QuartzEventServicesRef/Reference/reference.html>
816643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    // [2] <http://developer.apple.com/releasenotes/Cocoa/AppKitOlderNotes.html>
817643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    //     Scroll to the section headed "NSScrollWheel events".
818643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    //
819643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    // P.S. The "smooth scrolling" option in the system preferences is utterly
820643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    // unrelated to any of this.
821643ca7872b450ea4efacab6188849e5aac2ba161Steve Block
822643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    CGEventRef cgEvent = [event CGEvent];
823643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    ASSERT(cgEvent);
824643ca7872b450ea4efacab6188849e5aac2ba161Steve Block
825643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    // Wheel ticks are supposed to be raw, unaccelerated values, one per physical
826643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    // mouse wheel notch. The delta event is perfect for this (being a good
827643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    // "specific edge case" as mentioned above). Trackpads, unfortunately, do
828643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    // event chunking, and sending mousewheel events with 0 ticks causes some
829643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    // websites to malfunction. Therefore, for all continuous input devices we use
830643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    // the point delta data instead, since we cannot distinguish trackpad data
831643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    // from data from any other continuous device.
832643ca7872b450ea4efacab6188849e5aac2ba161Steve Block
8335abb8606fa57c3ebfc8b3c3dbc3fa4a25d2ae306Iain Merrick    // Conversion between wheel delta amounts and number of pixels to scroll.
8345abb8606fa57c3ebfc8b3c3dbc3fa4a25d2ae306Iain Merrick    static const double scrollbarPixelsPerCocoaTick = 40.0;
8355abb8606fa57c3ebfc8b3c3dbc3fa4a25d2ae306Iain Merrick
836643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    if (CGEventGetIntegerValueField(cgEvent, kCGScrollWheelEventIsContinuous)) {
8375abb8606fa57c3ebfc8b3c3dbc3fa4a25d2ae306Iain Merrick        result.deltaX = CGEventGetIntegerValueField(cgEvent, kCGScrollWheelEventPointDeltaAxis2);
8385abb8606fa57c3ebfc8b3c3dbc3fa4a25d2ae306Iain Merrick        result.deltaY = CGEventGetIntegerValueField(cgEvent, kCGScrollWheelEventPointDeltaAxis1);
8395abb8606fa57c3ebfc8b3c3dbc3fa4a25d2ae306Iain Merrick        result.wheelTicksX = result.deltaX / scrollbarPixelsPerCocoaTick;
8405abb8606fa57c3ebfc8b3c3dbc3fa4a25d2ae306Iain Merrick        result.wheelTicksY = result.deltaY / scrollbarPixelsPerCocoaTick;
841643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    } else {
842643ca7872b450ea4efacab6188849e5aac2ba161Steve Block        result.deltaX = [event deltaX] * scrollbarPixelsPerCocoaTick;
843643ca7872b450ea4efacab6188849e5aac2ba161Steve Block        result.deltaY = [event deltaY] * scrollbarPixelsPerCocoaTick;
8445abb8606fa57c3ebfc8b3c3dbc3fa4a25d2ae306Iain Merrick        result.wheelTicksY = CGEventGetIntegerValueField(cgEvent, kCGScrollWheelEventDeltaAxis1);
8455abb8606fa57c3ebfc8b3c3dbc3fa4a25d2ae306Iain Merrick        result.wheelTicksX = CGEventGetIntegerValueField(cgEvent, kCGScrollWheelEventDeltaAxis2);
846643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    }
847643ca7872b450ea4efacab6188849e5aac2ba161Steve Block
848643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    result.timeStampSeconds = [event timestamp];
849643ca7872b450ea4efacab6188849e5aac2ba161Steve Block
850643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    return result;
851643ca7872b450ea4efacab6188849e5aac2ba161Steve Block}
852643ca7872b450ea4efacab6188849e5aac2ba161Steve Block
853643ca7872b450ea4efacab6188849e5aac2ba161Steve Block} // namespace WebKit
854