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 36 using namespace WTF; 37 38 namespace WebCore { 39 40 static 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 80 static 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 112 static inline String textFromEvent(NSEvent* event) 113 { 114 if ([event type] == NSFlagsChanged) 115 return ""; 116 return [event characters]; 117 } 118 119 120 static inline String unmodifiedTextFromEvent(NSEvent* event) 121 { 122 if ([event type] == NSFlagsChanged) 123 return ""; 124 return [event charactersIgnoringModifiers]; 125 } 126 127 static 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 163 static 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 191 PlatformKeyboardEvent::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 225 void 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 250 bool PlatformKeyboardEvent::currentCapsLockState() 251 { 252 return GetCurrentKeyModifiers() & alphaLock; 253 } 254 255 void 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