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