1// Copyright (c) 2012 The Chromium Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5#import "ui/events/keycodes/keyboard_code_conversion_mac.h" 6 7#include <algorithm> 8 9#import <Carbon/Carbon.h> 10 11#include "base/check_op.h" 12#include "base/mac/mac_logging.h" 13#include "base/mac/scoped_cftyperef.h" 14#include "base/memory/scoped_policy.h" 15#include "base/stl_util.h" 16#include "ui/events/keycodes/dom/keycode_converter.h" 17 18namespace ui { 19 20namespace { 21 22// Per Apple docs, the buffer length can be up to 255 but is rarely more than 4. 23// https://developer.apple.com/documentation/coreservices/1390584-uckeytranslate 24constexpr int kUCKeyTranslateBufferLength = 4; 25 26bool IsUnicodeControl(unichar c) { 27 // C0 control characters: http://unicode.org/charts/PDF/U0000.pdf 28 // C1 control characters: http://unicode.org/charts/PDF/U0080.pdf 29 return c <= 0x1F || (c >= 0x7F && c <= 0x9F); 30} 31 32// This value is not defined but shows up as 0x36. 33const int kVK_RightCommand = 0x36; 34// Context menu is not defined but shows up as 0x6E. 35const int kVK_ContextMenu = 0x6E; 36 37// A struct to hold a Windows keycode to Mac virtual keycode mapping. 38struct KeyCodeMap { 39 KeyboardCode keycode; 40 int macKeycode; 41 unichar characterIgnoringAllModifiers; 42}; 43 44// Customized less operator for using std::lower_bound() on a KeyCodeMap array. 45bool operator<(const KeyCodeMap& a, const KeyCodeMap& b) { 46 return a.keycode < b.keycode; 47} 48 49// This array must keep sorted ascending according to the value of |keycode|, 50// so that we can binary search it. 51// TODO(suzhe): This map is not complete, missing entries have macKeycode == -1. 52const KeyCodeMap kKeyCodesMap[] = { 53 { VKEY_BACK /* 0x08 */, kVK_Delete, kBackspaceCharCode }, 54 { VKEY_TAB /* 0x09 */, kVK_Tab, kTabCharCode }, 55 { VKEY_BACKTAB /* 0x0A */, 0x21E4, '\031' }, 56 { VKEY_CLEAR /* 0x0C */, kVK_ANSI_KeypadClear, kClearCharCode }, 57 { VKEY_RETURN /* 0x0D */, kVK_Return, kReturnCharCode }, 58 { VKEY_SHIFT /* 0x10 */, kVK_Shift, 0 }, 59 { VKEY_CONTROL /* 0x11 */, kVK_Control, 0 }, 60 { VKEY_MENU /* 0x12 */, kVK_Option, 0 }, 61 { VKEY_PAUSE /* 0x13 */, -1, NSPauseFunctionKey }, 62 { VKEY_CAPITAL /* 0x14 */, kVK_CapsLock, 0 }, 63 { VKEY_KANA /* 0x15 */, kVK_JIS_Kana, 0 }, 64 { VKEY_HANGUL /* 0x15 */, -1, 0 }, 65 { VKEY_JUNJA /* 0x17 */, -1, 0 }, 66 { VKEY_FINAL /* 0x18 */, -1, 0 }, 67 { VKEY_HANJA /* 0x19 */, -1, 0 }, 68 { VKEY_KANJI /* 0x19 */, -1, 0 }, 69 { VKEY_ESCAPE /* 0x1B */, kVK_Escape, kEscapeCharCode }, 70 { VKEY_CONVERT /* 0x1C */, -1, 0 }, 71 { VKEY_NONCONVERT /* 0x1D */, -1, 0 }, 72 { VKEY_ACCEPT /* 0x1E */, -1, 0 }, 73 { VKEY_MODECHANGE /* 0x1F */, -1, 0 }, 74 { VKEY_SPACE /* 0x20 */, kVK_Space, kSpaceCharCode }, 75 { VKEY_PRIOR /* 0x21 */, kVK_PageUp, NSPageUpFunctionKey }, 76 { VKEY_NEXT /* 0x22 */, kVK_PageDown, NSPageDownFunctionKey }, 77 { VKEY_END /* 0x23 */, kVK_End, NSEndFunctionKey }, 78 { VKEY_HOME /* 0x24 */, kVK_Home, NSHomeFunctionKey }, 79 { VKEY_LEFT /* 0x25 */, kVK_LeftArrow, NSLeftArrowFunctionKey }, 80 { VKEY_UP /* 0x26 */, kVK_UpArrow, NSUpArrowFunctionKey }, 81 { VKEY_RIGHT /* 0x27 */, kVK_RightArrow, NSRightArrowFunctionKey }, 82 { VKEY_DOWN /* 0x28 */, kVK_DownArrow, NSDownArrowFunctionKey }, 83 { VKEY_SELECT /* 0x29 */, -1, 0 }, 84 { VKEY_PRINT /* 0x2A */, -1, NSPrintFunctionKey }, 85 { VKEY_EXECUTE /* 0x2B */, -1, NSExecuteFunctionKey }, 86 { VKEY_SNAPSHOT /* 0x2C */, -1, NSPrintScreenFunctionKey }, 87 { VKEY_INSERT /* 0x2D */, kVK_Help, NSInsertFunctionKey }, 88 { VKEY_DELETE /* 0x2E */, kVK_ForwardDelete, NSDeleteFunctionKey }, 89 { VKEY_HELP /* 0x2F */, kVK_Help, kHelpCharCode }, 90 { VKEY_0 /* 0x30 */, kVK_ANSI_0, '0' }, 91 { VKEY_1 /* 0x31 */, kVK_ANSI_1, '1' }, 92 { VKEY_2 /* 0x32 */, kVK_ANSI_2, '2' }, 93 { VKEY_3 /* 0x33 */, kVK_ANSI_3, '3' }, 94 { VKEY_4 /* 0x34 */, kVK_ANSI_4, '4' }, 95 { VKEY_5 /* 0x35 */, kVK_ANSI_5, '5' }, 96 { VKEY_6 /* 0x36 */, kVK_ANSI_6, '6' }, 97 { VKEY_7 /* 0x37 */, kVK_ANSI_7, '7' }, 98 { VKEY_8 /* 0x38 */, kVK_ANSI_8, '8' }, 99 { VKEY_9 /* 0x39 */, kVK_ANSI_9, '9' }, 100 { VKEY_A /* 0x41 */, kVK_ANSI_A, 'a' }, 101 { VKEY_B /* 0x42 */, kVK_ANSI_B, 'b' }, 102 { VKEY_C /* 0x43 */, kVK_ANSI_C, 'c' }, 103 { VKEY_D /* 0x44 */, kVK_ANSI_D, 'd' }, 104 { VKEY_E /* 0x45 */, kVK_ANSI_E, 'e' }, 105 { VKEY_F /* 0x46 */, kVK_ANSI_F, 'f' }, 106 { VKEY_G /* 0x47 */, kVK_ANSI_G, 'g' }, 107 { VKEY_H /* 0x48 */, kVK_ANSI_H, 'h' }, 108 { VKEY_I /* 0x49 */, kVK_ANSI_I, 'i' }, 109 { VKEY_J /* 0x4A */, kVK_ANSI_J, 'j' }, 110 { VKEY_K /* 0x4B */, kVK_ANSI_K, 'k' }, 111 { VKEY_L /* 0x4C */, kVK_ANSI_L, 'l' }, 112 { VKEY_M /* 0x4D */, kVK_ANSI_M, 'm' }, 113 { VKEY_N /* 0x4E */, kVK_ANSI_N, 'n' }, 114 { VKEY_O /* 0x4F */, kVK_ANSI_O, 'o' }, 115 { VKEY_P /* 0x50 */, kVK_ANSI_P, 'p' }, 116 { VKEY_Q /* 0x51 */, kVK_ANSI_Q, 'q' }, 117 { VKEY_R /* 0x52 */, kVK_ANSI_R, 'r' }, 118 { VKEY_S /* 0x53 */, kVK_ANSI_S, 's' }, 119 { VKEY_T /* 0x54 */, kVK_ANSI_T, 't' }, 120 { VKEY_U /* 0x55 */, kVK_ANSI_U, 'u' }, 121 { VKEY_V /* 0x56 */, kVK_ANSI_V, 'v' }, 122 { VKEY_W /* 0x57 */, kVK_ANSI_W, 'w' }, 123 { VKEY_X /* 0x58 */, kVK_ANSI_X, 'x' }, 124 { VKEY_Y /* 0x59 */, kVK_ANSI_Y, 'y' }, 125 { VKEY_Z /* 0x5A */, kVK_ANSI_Z, 'z' }, 126 { VKEY_LWIN /* 0x5B */, kVK_Command, 0 }, 127 { VKEY_RWIN /* 0x5C */, kVK_RightCommand, 0 }, 128 { VKEY_APPS /* 0x5D */, kVK_RightCommand, 0 }, 129 { VKEY_SLEEP /* 0x5F */, -1, 0 }, 130 { VKEY_NUMPAD0 /* 0x60 */, kVK_ANSI_Keypad0, '0' }, 131 { VKEY_NUMPAD1 /* 0x61 */, kVK_ANSI_Keypad1, '1' }, 132 { VKEY_NUMPAD2 /* 0x62 */, kVK_ANSI_Keypad2, '2' }, 133 { VKEY_NUMPAD3 /* 0x63 */, kVK_ANSI_Keypad3, '3' }, 134 { VKEY_NUMPAD4 /* 0x64 */, kVK_ANSI_Keypad4, '4' }, 135 { VKEY_NUMPAD5 /* 0x65 */, kVK_ANSI_Keypad5, '5' }, 136 { VKEY_NUMPAD6 /* 0x66 */, kVK_ANSI_Keypad6, '6' }, 137 { VKEY_NUMPAD7 /* 0x67 */, kVK_ANSI_Keypad7, '7' }, 138 { VKEY_NUMPAD8 /* 0x68 */, kVK_ANSI_Keypad8, '8' }, 139 { VKEY_NUMPAD9 /* 0x69 */, kVK_ANSI_Keypad9, '9' }, 140 { VKEY_MULTIPLY /* 0x6A */, kVK_ANSI_KeypadMultiply, '*' }, 141 { VKEY_ADD /* 0x6B */, kVK_ANSI_KeypadPlus, '+' }, 142 { VKEY_SEPARATOR /* 0x6C */, -1, 0 }, 143 { VKEY_SUBTRACT /* 0x6D */, kVK_ANSI_KeypadMinus, '-' }, 144 { VKEY_DECIMAL /* 0x6E */, kVK_ANSI_KeypadDecimal, '.' }, 145 { VKEY_DIVIDE /* 0x6F */, kVK_ANSI_KeypadDivide, '/' }, 146 { VKEY_F1 /* 0x70 */, kVK_F1, NSF1FunctionKey }, 147 { VKEY_F2 /* 0x71 */, kVK_F2, NSF2FunctionKey }, 148 { VKEY_F3 /* 0x72 */, kVK_F3, NSF3FunctionKey }, 149 { VKEY_F4 /* 0x73 */, kVK_F4, NSF4FunctionKey }, 150 { VKEY_F5 /* 0x74 */, kVK_F5, NSF5FunctionKey }, 151 { VKEY_F6 /* 0x75 */, kVK_F6, NSF6FunctionKey }, 152 { VKEY_F7 /* 0x76 */, kVK_F7, NSF7FunctionKey }, 153 { VKEY_F8 /* 0x77 */, kVK_F8, NSF8FunctionKey }, 154 { VKEY_F9 /* 0x78 */, kVK_F9, NSF9FunctionKey }, 155 { VKEY_F10 /* 0x79 */, kVK_F10, NSF10FunctionKey }, 156 { VKEY_F11 /* 0x7A */, kVK_F11, NSF11FunctionKey }, 157 { VKEY_F12 /* 0x7B */, kVK_F12, NSF12FunctionKey }, 158 { VKEY_F13 /* 0x7C */, kVK_F13, NSF13FunctionKey }, 159 { VKEY_F14 /* 0x7D */, kVK_F14, NSF14FunctionKey }, 160 { VKEY_F15 /* 0x7E */, kVK_F15, NSF15FunctionKey }, 161 { VKEY_F16 /* 0x7F */, kVK_F16, NSF16FunctionKey }, 162 { VKEY_F17 /* 0x80 */, kVK_F17, NSF17FunctionKey }, 163 { VKEY_F18 /* 0x81 */, kVK_F18, NSF18FunctionKey }, 164 { VKEY_F19 /* 0x82 */, kVK_F19, NSF19FunctionKey }, 165 { VKEY_F20 /* 0x83 */, kVK_F20, NSF20FunctionKey }, 166 { VKEY_F21 /* 0x84 */, -1, NSF21FunctionKey }, 167 { VKEY_F22 /* 0x85 */, -1, NSF22FunctionKey }, 168 { VKEY_F23 /* 0x86 */, -1, NSF23FunctionKey }, 169 { VKEY_F24 /* 0x87 */, -1, NSF24FunctionKey }, 170 { VKEY_NUMLOCK /* 0x90 */, -1, 0 }, 171 { VKEY_SCROLL /* 0x91 */, -1, NSScrollLockFunctionKey }, 172 { VKEY_LSHIFT /* 0xA0 */, kVK_Shift, 0 }, 173 { VKEY_RSHIFT /* 0xA1 */, kVK_Shift, 0 }, 174 { VKEY_LCONTROL /* 0xA2 */, kVK_Control, 0 }, 175 { VKEY_RCONTROL /* 0xA3 */, kVK_Control, 0 }, 176 { VKEY_LMENU /* 0xA4 */, -1, 0 }, 177 { VKEY_RMENU /* 0xA5 */, -1, 0 }, 178 { VKEY_BROWSER_BACK /* 0xA6 */, -1, 0 }, 179 { VKEY_BROWSER_FORWARD /* 0xA7 */, -1, 0 }, 180 { VKEY_BROWSER_REFRESH /* 0xA8 */, -1, 0 }, 181 { VKEY_BROWSER_STOP /* 0xA9 */, -1, 0 }, 182 { VKEY_BROWSER_SEARCH /* 0xAA */, -1, 0 }, 183 { VKEY_BROWSER_FAVORITES /* 0xAB */, -1, 0 }, 184 { VKEY_BROWSER_HOME /* 0xAC */, -1, 0 }, 185 { VKEY_VOLUME_MUTE /* 0xAD */, -1, 0 }, 186 { VKEY_VOLUME_DOWN /* 0xAE */, -1, 0 }, 187 { VKEY_VOLUME_UP /* 0xAF */, -1, 0 }, 188 { VKEY_MEDIA_NEXT_TRACK /* 0xB0 */, -1, 0 }, 189 { VKEY_MEDIA_PREV_TRACK /* 0xB1 */, -1, 0 }, 190 { VKEY_MEDIA_STOP /* 0xB2 */, -1, 0 }, 191 { VKEY_MEDIA_PLAY_PAUSE /* 0xB3 */, -1, 0 }, 192 { VKEY_MEDIA_LAUNCH_MAIL /* 0xB4 */, -1, 0 }, 193 { VKEY_MEDIA_LAUNCH_MEDIA_SELECT /* 0xB5 */, -1, 0 }, 194 { VKEY_MEDIA_LAUNCH_APP1 /* 0xB6 */, -1, 0 }, 195 { VKEY_MEDIA_LAUNCH_APP2 /* 0xB7 */, -1, 0 }, 196 { VKEY_OEM_1 /* 0xBA */, kVK_ANSI_Semicolon, ';' }, 197 { VKEY_OEM_PLUS /* 0xBB */, kVK_ANSI_Equal, '=' }, 198 { VKEY_OEM_COMMA /* 0xBC */, kVK_ANSI_Comma, ',' }, 199 { VKEY_OEM_MINUS /* 0xBD */, kVK_ANSI_Minus, '-' }, 200 { VKEY_OEM_PERIOD /* 0xBE */, kVK_ANSI_Period, '.' }, 201 { VKEY_OEM_2 /* 0xBF */, kVK_ANSI_Slash, '/' }, 202 { VKEY_OEM_3 /* 0xC0 */, kVK_ANSI_Grave, '`' }, 203 { VKEY_OEM_4 /* 0xDB */, kVK_ANSI_LeftBracket, '[' }, 204 { VKEY_OEM_5 /* 0xDC */, kVK_ANSI_Backslash, '\\' }, 205 { VKEY_OEM_6 /* 0xDD */, kVK_ANSI_RightBracket, ']' }, 206 { VKEY_OEM_7 /* 0xDE */, kVK_ANSI_Quote, '\'' }, 207 { VKEY_OEM_8 /* 0xDF */, -1, 0 }, 208 { VKEY_OEM_102 /* 0xE2 */, -1, 0 }, 209 { VKEY_PROCESSKEY /* 0xE5 */, -1, 0 }, 210 { VKEY_PACKET /* 0xE7 */, -1, 0 }, 211 { VKEY_ATTN /* 0xF6 */, -1, 0 }, 212 { VKEY_CRSEL /* 0xF7 */, -1, 0 }, 213 { VKEY_EXSEL /* 0xF8 */, -1, 0 }, 214 { VKEY_EREOF /* 0xF9 */, -1, 0 }, 215 { VKEY_PLAY /* 0xFA */, -1, 0 }, 216 { VKEY_ZOOM /* 0xFB */, -1, 0 }, 217 { VKEY_NONAME /* 0xFC */, -1, 0 }, 218 { VKEY_PA1 /* 0xFD */, -1, 0 }, 219 { VKEY_OEM_CLEAR /* 0xFE */, kVK_ANSI_KeypadClear, kClearCharCode } 220}; 221 222bool IsKeypadOrNumericKeyEvent(NSEvent* event) { 223 // Check that this is the type of event that has a keyCode. 224 switch ([event type]) { 225 case NSKeyDown: 226 case NSKeyUp: 227 case NSFlagsChanged: 228 break; 229 default: 230 return false; 231 } 232 233 switch ([event keyCode]) { 234 case kVK_ANSI_KeypadClear: 235 case kVK_ANSI_KeypadEquals: 236 case kVK_ANSI_KeypadMultiply: 237 case kVK_ANSI_KeypadDivide: 238 case kVK_ANSI_KeypadMinus: 239 case kVK_ANSI_KeypadPlus: 240 case kVK_ANSI_KeypadEnter: 241 case kVK_ANSI_KeypadDecimal: 242 case kVK_ANSI_Keypad0: 243 case kVK_ANSI_Keypad1: 244 case kVK_ANSI_Keypad2: 245 case kVK_ANSI_Keypad3: 246 case kVK_ANSI_Keypad4: 247 case kVK_ANSI_Keypad5: 248 case kVK_ANSI_Keypad6: 249 case kVK_ANSI_Keypad7: 250 case kVK_ANSI_Keypad8: 251 case kVK_ANSI_Keypad9: 252 case kVK_ANSI_0: 253 case kVK_ANSI_1: 254 case kVK_ANSI_2: 255 case kVK_ANSI_3: 256 case kVK_ANSI_4: 257 case kVK_ANSI_5: 258 case kVK_ANSI_6: 259 case kVK_ANSI_7: 260 case kVK_ANSI_8: 261 case kVK_ANSI_9: 262 return true; 263 } 264 265 return false; 266} 267 268// A convenient array for getting symbol characters on the number keys. 269const char kShiftCharsForNumberKeys[] = ")!@#$%^&*("; 270 271// Translates from character code to keyboard code. 272KeyboardCode KeyboardCodeFromCharCode(unichar charCode) { 273 switch (charCode) { 274 case 'a': case 'A': return VKEY_A; 275 case 'b': case 'B': return VKEY_B; 276 case 'c': case 'C': return VKEY_C; 277 case 'd': case 'D': return VKEY_D; 278 case 'e': case 'E': return VKEY_E; 279 case 'f': case 'F': return VKEY_F; 280 case 'g': case 'G': return VKEY_G; 281 case 'h': case 'H': return VKEY_H; 282 case 'i': case 'I': return VKEY_I; 283 case 'j': case 'J': return VKEY_J; 284 case 'k': case 'K': return VKEY_K; 285 case 'l': case 'L': return VKEY_L; 286 case 'm': case 'M': return VKEY_M; 287 case 'n': case 'N': return VKEY_N; 288 case 'o': case 'O': return VKEY_O; 289 case 'p': case 'P': return VKEY_P; 290 case 'q': case 'Q': return VKEY_Q; 291 case 'r': case 'R': return VKEY_R; 292 case 's': case 'S': return VKEY_S; 293 case 't': case 'T': return VKEY_T; 294 case 'u': case 'U': return VKEY_U; 295 case 'v': case 'V': return VKEY_V; 296 case 'w': case 'W': return VKEY_W; 297 case 'x': case 'X': return VKEY_X; 298 case 'y': case 'Y': return VKEY_Y; 299 case 'z': case 'Z': return VKEY_Z; 300 301 case NSPauseFunctionKey: return VKEY_PAUSE; 302 case NSSelectFunctionKey: return VKEY_SELECT; 303 case NSPrintFunctionKey: return VKEY_PRINT; 304 case NSExecuteFunctionKey: return VKEY_EXECUTE; 305 case NSPrintScreenFunctionKey: return VKEY_SNAPSHOT; 306 case NSInsertFunctionKey: return VKEY_INSERT; 307 case NSF21FunctionKey: return VKEY_F21; 308 case NSF22FunctionKey: return VKEY_F22; 309 case NSF23FunctionKey: return VKEY_F23; 310 case NSF24FunctionKey: return VKEY_F24; 311 case NSScrollLockFunctionKey: return VKEY_SCROLL; 312 313 // U.S. Specific mappings. Mileage may vary. 314 case ';': case ':': return VKEY_OEM_1; 315 case '=': case '+': return VKEY_OEM_PLUS; 316 case ',': case '<': return VKEY_OEM_COMMA; 317 case '-': case '_': return VKEY_OEM_MINUS; 318 case '.': case '>': return VKEY_OEM_PERIOD; 319 case '/': case '?': return VKEY_OEM_2; 320 case '`': case '~': return VKEY_OEM_3; 321 case '[': case '{': return VKEY_OEM_4; 322 case '\\': case '|': return VKEY_OEM_5; 323 case ']': case '}': return VKEY_OEM_6; 324 case '\'': case '"': return VKEY_OEM_7; 325 } 326 327 return VKEY_UNKNOWN; 328} 329 330DomKey DomKeyFromKeyCode(unsigned short keyCode) { 331 switch (keyCode) { 332 case kVK_ANSI_KeypadEnter: 333 case kVK_Return: 334 return DomKey::ENTER; 335 case kVK_Tab: 336 return DomKey::TAB; 337 case kVK_Delete: 338 return DomKey::BACKSPACE; 339 case kVK_Escape: 340 return DomKey::ESCAPE; 341 case kVK_Command: 342 case kVK_RightCommand: 343 return DomKey::META; 344 case kVK_Shift: 345 case kVK_RightShift: 346 return DomKey::SHIFT; 347 case kVK_CapsLock: 348 return DomKey::CAPS_LOCK; 349 case kVK_Option: 350 case kVK_RightOption: 351 return DomKey::ALT; 352 case kVK_Control: 353 case kVK_RightControl: 354 return DomKey::CONTROL; 355 case kVK_Function: 356 return DomKey::FN; 357 case kVK_VolumeUp: 358 return DomKey::AUDIO_VOLUME_UP; 359 case kVK_VolumeDown: 360 return DomKey::AUDIO_VOLUME_DOWN; 361 case kVK_Mute: 362 return DomKey::AUDIO_VOLUME_MUTE; 363 case kVK_F1: 364 return DomKey::F1; 365 case kVK_F2: 366 return DomKey::F2; 367 case kVK_F3: 368 return DomKey::F3; 369 case kVK_F4: 370 return DomKey::F4; 371 case kVK_F5: 372 return DomKey::F5; 373 case kVK_F6: 374 return DomKey::F6; 375 case kVK_F7: 376 return DomKey::F7; 377 case kVK_F8: 378 return DomKey::F8; 379 case kVK_F9: 380 return DomKey::F9; 381 case kVK_F10: 382 return DomKey::F10; 383 case kVK_F11: 384 return DomKey::F11; 385 case kVK_F12: 386 return DomKey::F12; 387 case kVK_F13: 388 return DomKey::F13; 389 case kVK_F14: 390 return DomKey::F14; 391 case kVK_F15: 392 return DomKey::F15; 393 case kVK_F16: 394 return DomKey::F16; 395 case kVK_F17: 396 return DomKey::F17; 397 case kVK_F18: 398 return DomKey::F18; 399 case kVK_F19: 400 return DomKey::F19; 401 case kVK_F20: 402 return DomKey::F20; 403 case kVK_Help: 404 return DomKey::HELP; 405 case kVK_Home: 406 return DomKey::HOME; 407 case kVK_PageUp: 408 return DomKey::PAGE_UP; 409 case kVK_ForwardDelete: 410 return DomKey::DEL; 411 case kVK_End: 412 return DomKey::END; 413 case kVK_PageDown: 414 return DomKey::PAGE_DOWN; 415 case kVK_LeftArrow: 416 return DomKey::ARROW_LEFT; 417 case kVK_RightArrow: 418 return DomKey::ARROW_RIGHT; 419 case kVK_DownArrow: 420 return DomKey::ARROW_DOWN; 421 case kVK_UpArrow: 422 return DomKey::ARROW_UP; 423 case kVK_ContextMenu: 424 return DomKey::CONTEXT_MENU; 425 default: 426 return DomKey::NONE; 427 } 428} 429 430DomKey DomKeyFromCharCode(unichar char_code) { 431 switch (char_code) { 432 case NSUpArrowFunctionKey: 433 return DomKey::ARROW_UP; 434 case NSDownArrowFunctionKey: 435 return DomKey::ARROW_DOWN; 436 case NSLeftArrowFunctionKey: 437 return DomKey::ARROW_LEFT; 438 case NSRightArrowFunctionKey: 439 return DomKey::ARROW_RIGHT; 440 case NSF1FunctionKey: 441 return DomKey::F1; 442 case NSF2FunctionKey: 443 return DomKey::F2; 444 case NSF3FunctionKey: 445 return DomKey::F3; 446 case NSF4FunctionKey: 447 return DomKey::F4; 448 case NSF5FunctionKey: 449 return DomKey::F5; 450 case NSF6FunctionKey: 451 return DomKey::F6; 452 case NSF7FunctionKey: 453 return DomKey::F7; 454 case NSF8FunctionKey: 455 return DomKey::F8; 456 case NSF9FunctionKey: 457 return DomKey::F9; 458 case NSF10FunctionKey: 459 return DomKey::F10; 460 case NSF11FunctionKey: 461 return DomKey::F11; 462 case NSF12FunctionKey: 463 return DomKey::F12; 464 case NSF13FunctionKey: 465 return DomKey::F13; 466 case NSF14FunctionKey: 467 return DomKey::F14; 468 case NSF15FunctionKey: 469 return DomKey::F15; 470 case NSF16FunctionKey: 471 return DomKey::F16; 472 case NSF17FunctionKey: 473 return DomKey::F17; 474 case NSF18FunctionKey: 475 return DomKey::F18; 476 case NSF19FunctionKey: 477 return DomKey::F19; 478 case NSF20FunctionKey: 479 return DomKey::F20; 480 case NSF21FunctionKey: 481 return DomKey::F21; 482 case NSF22FunctionKey: 483 return DomKey::F22; 484 case NSF23FunctionKey: 485 return DomKey::F23; 486 case NSF24FunctionKey: 487 return DomKey::F24; 488 case NSInsertFunctionKey: 489 return DomKey::INSERT; 490 case NSDeleteFunctionKey: 491 return DomKey::DEL; 492 case NSHomeFunctionKey: 493 return DomKey::HOME; 494 case NSEndFunctionKey: 495 return DomKey::END; 496 case NSPageUpFunctionKey: 497 return DomKey::PAGE_UP; 498 case NSPageDownFunctionKey: 499 return DomKey::PAGE_DOWN; 500 case NSPrintScreenFunctionKey: 501 return DomKey::PRINT_SCREEN; 502 case NSScrollLockFunctionKey: 503 return DomKey::SCROLL_LOCK; 504 case NSPauseFunctionKey: 505 return DomKey::PAUSE; 506 case NSPrintFunctionKey: 507 return DomKey::PRINT; 508 case NSClearLineFunctionKey: 509 return DomKey::CLEAR; 510 case NSSelectFunctionKey: 511 return DomKey::SELECT; 512 case NSExecuteFunctionKey: 513 return DomKey::EXECUTE; 514 case NSUndoFunctionKey: 515 return DomKey::UNDO; 516 case NSRedoFunctionKey: 517 return DomKey::REDO; 518 case NSFindFunctionKey: 519 return DomKey::FIND; 520 case NSHelpFunctionKey: 521 return DomKey::HELP; 522 default: 523 return DomKey::FromCharacter(char_code); 524 } 525} 526 527UniChar MacKeycodeAndModifiersToCharacter(unsigned short mac_keycode, 528 int modifiers, 529 bool* is_dead_key) { 530 // Convert NSEvent modifiers to format UCKeyTranslate accepts. See docs 531 // on UCKeyTranslate for more info. 532 int unicode_modifiers = 0; 533 if (modifiers & NSShiftKeyMask) 534 unicode_modifiers |= shiftKey; 535 if (modifiers & NSAlphaShiftKeyMask) 536 unicode_modifiers |= alphaLock; 537 // if (modifiers & NSControlKeyMask) 538 // unicode_modifiers |= controlKey; 539 if (modifiers & NSAlternateKeyMask) 540 unicode_modifiers |= optionKey; 541 // if (modifiers & NSCommandKeyMask) 542 // unicode_modifiers |= cmdKey; 543 UInt32 modifier_key_state = (unicode_modifiers >> 8) & 0xFF; 544 545 UInt32 dead_key_state = 0; 546 base::ScopedCFTypeRef<TISInputSourceRef> input_source( 547 TISCopyCurrentKeyboardLayoutInputSource()); 548 UniChar translated_char = TranslatedUnicodeCharFromKeyCode( 549 input_source.get(), static_cast<UInt16>(mac_keycode), kUCKeyActionDown, 550 modifier_key_state, LMGetKbdLast(), &dead_key_state); 551 552 *is_dead_key = dead_key_state != 0; 553 if (*is_dead_key) { 554 translated_char = TranslatedUnicodeCharFromKeyCode( 555 input_source.get(), static_cast<UInt16>(kVK_Space), kUCKeyActionDown, 0, 556 LMGetKbdLast(), &dead_key_state); 557 } 558 559 return translated_char; 560} 561 562} // namespace 563 564int MacKeyCodeForWindowsKeyCode(KeyboardCode keycode, 565 NSUInteger flags, 566 unichar* us_keyboard_shifted_character, 567 unichar* keyboard_character) { 568 // In release code, |flags| is used to lookup accelerators, so logic to handle 569 // caps lock properly isn't implemented. 570 DCHECK_EQ(0u, flags & NSAlphaShiftKeyMask); 571 572 KeyCodeMap from; 573 from.keycode = keycode; 574 575 const KeyCodeMap* ptr = std::lower_bound( 576 kKeyCodesMap, kKeyCodesMap + base::size(kKeyCodesMap), from); 577 578 if (ptr >= kKeyCodesMap + base::size(kKeyCodesMap) || 579 ptr->keycode != keycode || ptr->macKeycode == -1) 580 return -1; 581 582 int macKeycode = ptr->macKeycode; 583 if (keyboard_character) 584 *keyboard_character = ptr->characterIgnoringAllModifiers; 585 586 if (!us_keyboard_shifted_character) 587 return macKeycode; 588 589 *us_keyboard_shifted_character = ptr->characterIgnoringAllModifiers; 590 591 // Fill in |us_keyboard_shifted_character| according to flags. 592 if (flags & NSShiftKeyMask) { 593 if (keycode >= VKEY_0 && keycode <= VKEY_9) { 594 *us_keyboard_shifted_character = 595 kShiftCharsForNumberKeys[keycode - VKEY_0]; 596 } else if (keycode >= VKEY_A && keycode <= VKEY_Z) { 597 *us_keyboard_shifted_character = 'A' + (keycode - VKEY_A); 598 } else { 599 switch (macKeycode) { 600 case kVK_ANSI_Grave: 601 *us_keyboard_shifted_character = '~'; 602 break; 603 case kVK_ANSI_Minus: 604 *us_keyboard_shifted_character = '_'; 605 break; 606 case kVK_ANSI_Equal: 607 *us_keyboard_shifted_character = '+'; 608 break; 609 case kVK_ANSI_LeftBracket: 610 *us_keyboard_shifted_character = '{'; 611 break; 612 case kVK_ANSI_RightBracket: 613 *us_keyboard_shifted_character = '}'; 614 break; 615 case kVK_ANSI_Backslash: 616 *us_keyboard_shifted_character = '|'; 617 break; 618 case kVK_ANSI_Semicolon: 619 *us_keyboard_shifted_character = ':'; 620 break; 621 case kVK_ANSI_Quote: 622 *us_keyboard_shifted_character = '\"'; 623 break; 624 case kVK_ANSI_Comma: 625 *us_keyboard_shifted_character = '<'; 626 break; 627 case kVK_ANSI_Period: 628 *us_keyboard_shifted_character = '>'; 629 break; 630 case kVK_ANSI_Slash: 631 *us_keyboard_shifted_character = '?'; 632 break; 633 default: 634 break; 635 } 636 } 637 } 638 639 // TODO(suzhe): Support characters for Option key bindings. 640 return macKeycode; 641} 642 643KeyboardCode KeyboardCodeFromKeyCode(unsigned short keyCode) { 644 static const KeyboardCode kKeyboardCodes[] = { 645 /* 0 */ VKEY_A, 646 /* 1 */ VKEY_S, 647 /* 2 */ VKEY_D, 648 /* 3 */ VKEY_F, 649 /* 4 */ VKEY_H, 650 /* 5 */ VKEY_G, 651 /* 6 */ VKEY_Z, 652 /* 7 */ VKEY_X, 653 /* 8 */ VKEY_C, 654 /* 9 */ VKEY_V, 655 /* 0x0A */ VKEY_OEM_3, // Section key. 656 /* 0x0B */ VKEY_B, 657 /* 0x0C */ VKEY_Q, 658 /* 0x0D */ VKEY_W, 659 /* 0x0E */ VKEY_E, 660 /* 0x0F */ VKEY_R, 661 /* 0x10 */ VKEY_Y, 662 /* 0x11 */ VKEY_T, 663 /* 0x12 */ VKEY_1, 664 /* 0x13 */ VKEY_2, 665 /* 0x14 */ VKEY_3, 666 /* 0x15 */ VKEY_4, 667 /* 0x16 */ VKEY_6, 668 /* 0x17 */ VKEY_5, 669 /* 0x18 */ VKEY_OEM_PLUS, // =+ 670 /* 0x19 */ VKEY_9, 671 /* 0x1A */ VKEY_7, 672 /* 0x1B */ VKEY_OEM_MINUS, // -_ 673 /* 0x1C */ VKEY_8, 674 /* 0x1D */ VKEY_0, 675 /* 0x1E */ VKEY_OEM_6, // ]} 676 /* 0x1F */ VKEY_O, 677 /* 0x20 */ VKEY_U, 678 /* 0x21 */ VKEY_OEM_4, // {[ 679 /* 0x22 */ VKEY_I, 680 /* 0x23 */ VKEY_P, 681 /* 0x24 */ VKEY_RETURN, // Return 682 /* 0x25 */ VKEY_L, 683 /* 0x26 */ VKEY_J, 684 /* 0x27 */ VKEY_OEM_7, // '" 685 /* 0x28 */ VKEY_K, 686 /* 0x29 */ VKEY_OEM_1, // ;: 687 /* 0x2A */ VKEY_OEM_5, // \| 688 /* 0x2B */ VKEY_OEM_COMMA, // ,< 689 /* 0x2C */ VKEY_OEM_2, // /? 690 /* 0x2D */ VKEY_N, 691 /* 0x2E */ VKEY_M, 692 /* 0x2F */ VKEY_OEM_PERIOD, // .> 693 /* 0x30 */ VKEY_TAB, 694 /* 0x31 */ VKEY_SPACE, 695 /* 0x32 */ VKEY_OEM_3, // `~ 696 /* 0x33 */ VKEY_BACK, // Backspace 697 /* 0x34 */ VKEY_UNKNOWN, // n/a 698 /* 0x35 */ VKEY_ESCAPE, 699 /* 0x36 */ VKEY_APPS, // Right Command 700 /* 0x37 */ VKEY_LWIN, // Left Command 701 /* 0x38 */ VKEY_SHIFT, // Left Shift 702 /* 0x39 */ VKEY_CAPITAL, // Caps Lock 703 /* 0x3A */ VKEY_MENU, // Left Option 704 /* 0x3B */ VKEY_CONTROL, // Left Ctrl 705 /* 0x3C */ VKEY_SHIFT, // Right Shift 706 /* 0x3D */ VKEY_MENU, // Right Option 707 /* 0x3E */ VKEY_CONTROL, // Right Ctrl 708 /* 0x3F */ VKEY_UNKNOWN, // fn 709 /* 0x40 */ VKEY_F17, 710 /* 0x41 */ VKEY_DECIMAL, // Num Pad . 711 /* 0x42 */ VKEY_UNKNOWN, // n/a 712 /* 0x43 */ VKEY_MULTIPLY, // Num Pad * 713 /* 0x44 */ VKEY_UNKNOWN, // n/a 714 /* 0x45 */ VKEY_ADD, // Num Pad + 715 /* 0x46 */ VKEY_UNKNOWN, // n/a 716 /* 0x47 */ VKEY_CLEAR, // Num Pad Clear 717 /* 0x48 */ VKEY_VOLUME_UP, 718 /* 0x49 */ VKEY_VOLUME_DOWN, 719 /* 0x4A */ VKEY_VOLUME_MUTE, 720 /* 0x4B */ VKEY_DIVIDE, // Num Pad / 721 /* 0x4C */ VKEY_RETURN, // Num Pad Enter 722 /* 0x4D */ VKEY_UNKNOWN, // n/a 723 /* 0x4E */ VKEY_SUBTRACT, // Num Pad - 724 /* 0x4F */ VKEY_F18, 725 /* 0x50 */ VKEY_F19, 726 /* 0x51 */ VKEY_OEM_PLUS, // Num Pad =. 727 /* 0x52 */ VKEY_NUMPAD0, 728 /* 0x53 */ VKEY_NUMPAD1, 729 /* 0x54 */ VKEY_NUMPAD2, 730 /* 0x55 */ VKEY_NUMPAD3, 731 /* 0x56 */ VKEY_NUMPAD4, 732 /* 0x57 */ VKEY_NUMPAD5, 733 /* 0x58 */ VKEY_NUMPAD6, 734 /* 0x59 */ VKEY_NUMPAD7, 735 /* 0x5A */ VKEY_F20, 736 /* 0x5B */ VKEY_NUMPAD8, 737 /* 0x5C */ VKEY_NUMPAD9, 738 /* 0x5D */ VKEY_UNKNOWN, // Yen (JIS Keyboard Only) 739 /* 0x5E */ VKEY_UNKNOWN, // Underscore (JIS Keyboard Only) 740 /* 0x5F */ VKEY_UNKNOWN, // KeypadComma (JIS Keyboard Only) 741 /* 0x60 */ VKEY_F5, 742 /* 0x61 */ VKEY_F6, 743 /* 0x62 */ VKEY_F7, 744 /* 0x63 */ VKEY_F3, 745 /* 0x64 */ VKEY_F8, 746 /* 0x65 */ VKEY_F9, 747 /* 0x66 */ VKEY_UNKNOWN, // Eisu (JIS Keyboard Only) 748 /* 0x67 */ VKEY_F11, 749 /* 0x68 */ VKEY_UNKNOWN, // Kana (JIS Keyboard Only) 750 /* 0x69 */ VKEY_F13, 751 /* 0x6A */ VKEY_F16, 752 /* 0x6B */ VKEY_F14, 753 /* 0x6C */ VKEY_UNKNOWN, // n/a 754 /* 0x6D */ VKEY_F10, 755 /* 0x6E */ VKEY_APPS, // Context Menu key 756 /* 0x6F */ VKEY_F12, 757 /* 0x70 */ VKEY_UNKNOWN, // n/a 758 /* 0x71 */ VKEY_F15, 759 /* 0x72 */ VKEY_INSERT, // Help 760 /* 0x73 */ VKEY_HOME, // Home 761 /* 0x74 */ VKEY_PRIOR, // Page Up 762 /* 0x75 */ VKEY_DELETE, // Forward Delete 763 /* 0x76 */ VKEY_F4, 764 /* 0x77 */ VKEY_END, // End 765 /* 0x78 */ VKEY_F2, 766 /* 0x79 */ VKEY_NEXT, // Page Down 767 /* 0x7A */ VKEY_F1, 768 /* 0x7B */ VKEY_LEFT, // Left Arrow 769 /* 0x7C */ VKEY_RIGHT, // Right Arrow 770 /* 0x7D */ VKEY_DOWN, // Down Arrow 771 /* 0x7E */ VKEY_UP, // Up Arrow 772 /* 0x7F */ VKEY_UNKNOWN // n/a 773 }; 774 775 if (keyCode >= 0x80) 776 return VKEY_UNKNOWN; 777 778 return kKeyboardCodes[keyCode]; 779} 780 781KeyboardCode KeyboardCodeFromNSEvent(NSEvent* event) { 782 KeyboardCode code = VKEY_UNKNOWN; 783 784 // Numeric keys 0-9 should always return |keyCode| 0-9. 785 // https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/keyCode#Printable_keys_in_standard_position 786 if (!IsKeypadOrNumericKeyEvent(event) && 787 ([event type] == NSKeyDown || [event type] == NSKeyUp)) { 788 // Handles Dvorak-QWERTY Cmd case. 789 // https://github.com/WebKit/webkit/blob/4d41c98b1de467f5d2a8fcba84d7c5268f11b0cc/Source/WebCore/platform/mac/PlatformEventFactoryMac.mm#L329 790 NSString* characters = [event characters]; 791 if ([characters length] > 0) 792 code = KeyboardCodeFromCharCode([characters characterAtIndex:0]); 793 if (code) 794 return code; 795 796 characters = [event charactersIgnoringModifiers]; 797 if ([characters length] > 0) 798 code = KeyboardCodeFromCharCode([characters characterAtIndex:0]); 799 if (code) 800 return code; 801 } 802 return KeyboardCodeFromKeyCode([event keyCode]); 803} 804 805int ISOKeyboardKeyCodeMap(int nativeKeyCode) { 806 // OS X will swap 'Backquote' and 'IntlBackslash' if it's an ISO keyboard. 807 // https://crbug.com/600607 808 switch (nativeKeyCode) { 809 case kVK_ISO_Section: 810 return kVK_ANSI_Grave; 811 case kVK_ANSI_Grave: 812 return kVK_ISO_Section; 813 default: 814 return nativeKeyCode; 815 } 816} 817 818DomCode DomCodeFromNSEvent(NSEvent* event) { 819 if (KBGetLayoutType(LMGetKbdType()) == kKeyboardISO) { 820 return ui::KeycodeConverter::NativeKeycodeToDomCode( 821 ISOKeyboardKeyCodeMap([event keyCode])); 822 } 823 824 return ui::KeycodeConverter::NativeKeycodeToDomCode([event keyCode]); 825} 826 827DomKey DomKeyFromNSEvent(NSEvent* event) { 828 // Apply the lookup based on the character first since that has the 829 // Keyboard layout and modifiers already applied; whereas the keyCode 830 // doesn't. 831 if ([event type] == NSKeyDown || [event type] == NSKeyUp) { 832 // Cannot use [event characters] to check whether it's a dead key, because 833 // KeyUp event has the character form of the dead key in [event characters]. 834 bool is_dead_key = false; 835 // MacKeycodeAndModifiersToCharacter() is efficient (around 6E-4 ms). 836 unichar dead_dom_key_char = MacKeycodeAndModifiersToCharacter( 837 [event keyCode], [event modifierFlags], &is_dead_key); 838 if (is_dead_key) 839 return DomKey::DeadKeyFromCombiningCharacter(dead_dom_key_char); 840 841 // [event characters] will have dead key state applied. 842 NSString* characters = [event characters]; 843 if ([characters length] > 0) { 844 // An invalid dead key combination will produce two characters, according 845 // to spec DomKey should be the last character. 846 // e.g. On French keyboard [+a will produce "^q", DomKey should be 'q'. 847 unichar dom_key_char = 848 [characters characterAtIndex:[characters length] - 1]; 849 if (IsUnicodeControl(dom_key_char)) { 850 // Filter non-glyph modifiers if the generated characters are part of 851 // Unicode 'Other, Control' General Category. 852 // https://w3c.github.io/uievents-key/#selecting-key-attribute-values 853 bool unused_is_dead_key; 854 const int kAllowedModifiersMask = 855 NSShiftKeyMask | NSAlphaShiftKeyMask | NSAlternateKeyMask; 856 // MacKeycodeAndModifiersToCharacter() is efficient (around 6E-4 ms). 857 dom_key_char = MacKeycodeAndModifiersToCharacter( 858 [event keyCode], [event modifierFlags] & kAllowedModifiersMask, 859 &unused_is_dead_key); 860 } 861 862 // We need to check again because keys like ESC will produce control 863 // characters even without any modifiers. 864 if (!IsUnicodeControl(dom_key_char)) 865 return DomKeyFromCharCode(dom_key_char); 866 } 867 } 868 return DomKeyFromKeyCode([event keyCode]); 869} 870 871UniChar TranslatedUnicodeCharFromKeyCode(TISInputSourceRef input_source, 872 UInt16 key_code, 873 UInt16 key_action, 874 UInt32 modifier_key_state, 875 UInt32 keyboard_type, 876 UInt32* dead_key_state) { 877 DCHECK(dead_key_state); 878 879 CFDataRef layout_data = static_cast<CFDataRef>(TISGetInputSourceProperty( 880 input_source, kTISPropertyUnicodeKeyLayoutData)); 881 882 const UCKeyboardLayout* keyboard_layout = 883 reinterpret_cast<const UCKeyboardLayout*>(CFDataGetBytePtr(layout_data)); 884 DCHECK(keyboard_layout); 885 886 UniChar char_buffer[kUCKeyTranslateBufferLength] = {0}; 887 UniCharCount buffer_length = kUCKeyTranslateBufferLength; 888 889 OSStatus ret = UCKeyTranslate( 890 keyboard_layout, key_code, key_action, modifier_key_state, keyboard_type, 891 kUCKeyTranslateNoDeadKeysBit, dead_key_state, kUCKeyTranslateBufferLength, 892 &buffer_length, char_buffer); 893 OSSTATUS_DCHECK(ret == noErr, ret); 894 895 // TODO(input-dev): Handle multiple character case. Should be rare. 896 return char_buffer[0]; 897} 898 899} // namespace ui 900