1/*
2 * Copyright (C) 2004, 2006, 2007, 2010 Apple Inc. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 * 1. Redistributions of source code must retain the above copyright
8 *    notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 *    notice, this list of conditions and the following disclaimer in the
11 *    documentation and/or other materials provided with the distribution.
12 *
13 * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE COMPUTER, INC. OR
17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24 */
25
26#import "config.h"
27#import "PlatformKeyboardEvent.h"
28
29#if PLATFORM(MAC)
30
31#import "KeyEventCocoa.h"
32#import "Logging.h"
33#import "WindowsKeyboardCodes.h"
34#import <Carbon/Carbon.h>
35
36using namespace WTF;
37
38namespace WebCore {
39
40static bool isKeypadEvent(NSEvent* event)
41{
42    // Check that this is the type of event that has a keyCode.
43    switch ([event type]) {
44        case NSKeyDown:
45        case NSKeyUp:
46        case NSFlagsChanged:
47            break;
48        default:
49            return false;
50    }
51
52    if ([event modifierFlags] & NSNumericPadKeyMask)
53        return true;
54
55    switch ([event keyCode]) {
56        case 71: // Clear
57        case 81: // =
58        case 75: // /
59        case 67: // *
60        case 78: // -
61        case 69: // +
62        case 76: // Enter
63        case 65: // .
64        case 82: // 0
65        case 83: // 1
66        case 84: // 2
67        case 85: // 3
68        case 86: // 4
69        case 87: // 5
70        case 88: // 6
71        case 89: // 7
72        case 91: // 8
73        case 92: // 9
74            return true;
75     }
76
77     return false;
78}
79
80static inline bool isKeyUpEvent(NSEvent *event)
81{
82    if ([event type] != NSFlagsChanged)
83        return [event type] == NSKeyUp;
84    // FIXME: This logic fails if the user presses both Shift keys at once, for example:
85    // we treat releasing one of them as keyDown.
86    switch ([event keyCode]) {
87        case 54: // Right Command
88        case 55: // Left Command
89            return ([event modifierFlags] & NSCommandKeyMask) == 0;
90
91        case 57: // Capslock
92            return ([event modifierFlags] & NSAlphaShiftKeyMask) == 0;
93
94        case 56: // Left Shift
95        case 60: // Right Shift
96            return ([event modifierFlags] & NSShiftKeyMask) == 0;
97
98        case 58: // Left Alt
99        case 61: // Right Alt
100            return ([event modifierFlags] & NSAlternateKeyMask) == 0;
101
102        case 59: // Left Ctrl
103        case 62: // Right Ctrl
104            return ([event modifierFlags] & NSControlKeyMask) == 0;
105
106        case 63: // Function
107            return ([event modifierFlags] & NSFunctionKeyMask) == 0;
108    }
109    return false;
110}
111
112static inline String textFromEvent(NSEvent* event)
113{
114    if ([event type] == NSFlagsChanged)
115        return "";
116    return [event characters];
117}
118
119
120static inline String unmodifiedTextFromEvent(NSEvent* event)
121{
122    if ([event type] == NSFlagsChanged)
123        return "";
124    return [event charactersIgnoringModifiers];
125}
126
127static String keyIdentifierForKeyEvent(NSEvent* event)
128{
129    if ([event type] == NSFlagsChanged)
130        switch ([event keyCode]) {
131            case 54: // Right Command
132            case 55: // Left Command
133                return "Meta";
134
135            case 57: // Capslock
136                return "CapsLock";
137
138            case 56: // Left Shift
139            case 60: // Right Shift
140                return "Shift";
141
142            case 58: // Left Alt
143            case 61: // Right Alt
144                return "Alt";
145
146            case 59: // Left Ctrl
147            case 62: // Right Ctrl
148                return "Control";
149
150            default:
151                ASSERT_NOT_REACHED();
152                return "";
153        }
154
155    NSString *s = [event charactersIgnoringModifiers];
156    if ([s length] != 1) {
157        LOG(Events, "received an unexpected number of characters in key event: %u", [s length]);
158        return "Unidentified";
159    }
160    return keyIdentifierForCharCode([s characterAtIndex:0]);
161}
162
163static int windowsKeyCodeForKeyEvent(NSEvent *event)
164{
165    int code = 0;
166    // There are several kinds of characters for which we produce key code from char code:
167    // 1. Roman letters. Windows keyboard layouts affect both virtual key codes and character codes for these,
168    //    so e.g. 'A' gets the same keyCode on QWERTY, AZERTY or Dvorak layouts.
169    // 2. Keys for which there is no known Mac virtual key codes, like PrintScreen.
170    // 3. Certain punctuation keys. On Windows, these are also remapped depending on current keyboard layout,
171    //    but see comment in windowsKeyCodeForCharCode().
172    if ([event type] == NSKeyDown || [event type] == NSKeyUp) {
173        // Cmd switches Roman letters for Dvorak-QWERTY layout, so try modified characters first.
174        NSString* s = [event characters];
175        code = [s length] > 0 ? windowsKeyCodeForCharCode([s characterAtIndex:0]) : 0;
176        if (code)
177            return code;
178
179        // Ctrl+A on an AZERTY keyboard would get VK_Q keyCode if we relied on -[NSEvent keyCode] below.
180        s = [event charactersIgnoringModifiers];
181        code = [s length] > 0 ? windowsKeyCodeForCharCode([s characterAtIndex:0]) : 0;
182        if (code)
183            return code;
184    }
185
186    // Map Mac virtual key code directly to Windows one for any keys not handled above.
187    // 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).
188    return windowsKeyCodeForKeyCode([event keyCode]);
189}
190
191PlatformKeyboardEvent::PlatformKeyboardEvent(NSEvent *event)
192    : m_type(isKeyUpEvent(event) ? PlatformKeyboardEvent::KeyUp : PlatformKeyboardEvent::KeyDown)
193    , m_text(textFromEvent(event))
194    , m_unmodifiedText(unmodifiedTextFromEvent(event))
195    , m_keyIdentifier(keyIdentifierForKeyEvent(event))
196    , m_autoRepeat(([event type] != NSFlagsChanged) && [event isARepeat])
197    , m_windowsVirtualKeyCode(windowsKeyCodeForKeyEvent(event))
198    , m_nativeVirtualKeyCode([event keyCode])
199    , m_isKeypad(isKeypadEvent(event))
200    , m_shiftKey([event modifierFlags] & NSShiftKeyMask)
201    , m_ctrlKey([event modifierFlags] & NSControlKeyMask)
202    , m_altKey([event modifierFlags] & NSAlternateKeyMask)
203    , m_metaKey([event modifierFlags] & NSCommandKeyMask)
204    , m_macEvent(event)
205{
206    // Always use 13 for Enter/Return -- we don't want to use AppKit's different character for Enter.
207    if (m_windowsVirtualKeyCode == VK_RETURN) {
208        m_text = "\r";
209        m_unmodifiedText = "\r";
210    }
211
212    // AppKit sets text to "\x7F" for backspace, but the correct KeyboardEvent character code is 8.
213    if (m_windowsVirtualKeyCode == VK_BACK) {
214        m_text = "\x8";
215        m_unmodifiedText = "\x8";
216    }
217
218    // Always use 9 for Tab -- we don't want to use AppKit's different character for shift-tab.
219    if (m_windowsVirtualKeyCode == VK_TAB) {
220        m_text = "\x9";
221        m_unmodifiedText = "\x9";
222    }
223}
224
225void PlatformKeyboardEvent::disambiguateKeyDownEvent(Type type, bool backwardCompatibilityMode)
226{
227    // Can only change type from KeyDown to RawKeyDown or Char, as we lack information for other conversions.
228    ASSERT(m_type == KeyDown);
229    ASSERT(type == RawKeyDown || type == Char);
230    m_type = type;
231    if (backwardCompatibilityMode)
232        return;
233
234    if (type == RawKeyDown) {
235        m_text = String();
236        m_unmodifiedText = String();
237    } else {
238        m_keyIdentifier = String();
239        m_windowsVirtualKeyCode = 0;
240        if (m_text.length() == 1 && (m_text[0U] >= 0xF700 && m_text[0U] <= 0xF7FF)) {
241            // According to NSEvents.h, OpenStep reserves the range 0xF700-0xF8FF for function keys. However, some actual private use characters
242            // happen to be in this range, e.g. the Apple logo (Option+Shift+K).
243            // 0xF7FF is an arbitrary cut-off.
244            m_text = String();
245            m_unmodifiedText = String();
246        }
247    }
248}
249
250bool PlatformKeyboardEvent::currentCapsLockState()
251{
252    return GetCurrentKeyModifiers() & alphaLock;
253}
254
255void PlatformKeyboardEvent::getCurrentModifierState(bool& shiftKey, bool& ctrlKey, bool& altKey, bool& metaKey)
256{
257    UInt32 currentModifiers = GetCurrentKeyModifiers();
258    shiftKey = currentModifiers & ::shiftKey;
259    ctrlKey = currentModifiers & ::controlKey;
260    altKey = currentModifiers & ::optionKey;
261    metaKey = currentModifiers & ::cmdKey;
262}
263
264}
265
266#endif // PLATFORM(MAC)
267