1 // Copyright (c) 2011 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.
5 #include "ui/events/keycodes/keyboard_code_conversion.h"
9 #include "ui/events/event_constants.h"
10 #include "ui/events/keycodes/dom/dom_code.h"
11 #include "ui/events/keycodes/dom/dom_key.h"
12 #include "ui/events/keycodes/dom_us_layout_data.h"
18 // This table maps a subset of |KeyboardCode| (VKEYs) to DomKey and character.
19 // Only values not otherwise handled by GetMeaningFromKeyCode() are here.
20 const struct KeyboardCodeToMeaning
{
21 KeyboardCode key_code
;
23 base::char16 plain_character
;
24 base::char16 shift_character
;
25 } kKeyboardCodeToMeaning
[] = {
26 {VKEY_BACK
, DomKey::BACKSPACE
, '\b', 0},
27 {VKEY_TAB
, DomKey::TAB
, '\t', 0},
28 {VKEY_RETURN
, DomKey::ENTER
, '\r', 0},
29 {VKEY_ESCAPE
, DomKey::ESCAPE
, 0x1B, 0},
30 {VKEY_SPACE
, DomKey::CHARACTER
, ' ', 0},
31 {VKEY_MULTIPLY
, DomKey::CHARACTER
, '*', 0},
32 {VKEY_ADD
, DomKey::CHARACTER
, '+', 0},
33 {VKEY_SEPARATOR
, DomKey::CHARACTER
, ',', 0},
34 {VKEY_SUBTRACT
, DomKey::CHARACTER
, '-', 0},
35 {VKEY_DECIMAL
, DomKey::CHARACTER
, '.', 0},
36 {VKEY_DIVIDE
, DomKey::CHARACTER
, '/', 0},
37 {VKEY_OEM_1
, DomKey::CHARACTER
, ';', ':'},
38 {VKEY_OEM_PLUS
, DomKey::CHARACTER
, '=', '+'},
39 {VKEY_OEM_COMMA
, DomKey::CHARACTER
, ',', '<'},
40 {VKEY_OEM_MINUS
, DomKey::CHARACTER
, '-', '_'},
41 {VKEY_OEM_PERIOD
, DomKey::CHARACTER
, '.', '>'},
42 {VKEY_OEM_2
, DomKey::CHARACTER
, '/', '?'},
43 {VKEY_OEM_3
, DomKey::CHARACTER
, '`', '~'},
44 {VKEY_OEM_4
, DomKey::CHARACTER
, '[', '{'},
45 {VKEY_OEM_5
, DomKey::CHARACTER
, '\\', '|'},
46 {VKEY_OEM_6
, DomKey::CHARACTER
, ']', '}'},
47 {VKEY_OEM_7
, DomKey::CHARACTER
, '\'', '"'},
48 {VKEY_OEM_102
, DomKey::CHARACTER
, '<', '>'},
49 {VKEY_CLEAR
, DomKey::CLEAR
, 0, 0},
50 {VKEY_SHIFT
, DomKey::SHIFT
, 0, 0},
51 {VKEY_CONTROL
, DomKey::CONTROL
, 0, 0},
52 {VKEY_MENU
, DomKey::ALT
, 0, 0},
53 {VKEY_PAUSE
, DomKey::PAUSE
, 0, 0},
54 {VKEY_CAPITAL
, DomKey::CAPS_LOCK
, 0, 0},
55 // Windows conflates 'KanaMode' and 'HangulMode'.
56 {VKEY_KANA
, DomKey::KANA_MODE
, 0, 0},
57 {VKEY_JUNJA
, DomKey::JUNJA_MODE
, 0, 0},
58 {VKEY_FINAL
, DomKey::FINAL_MODE
, 0, 0},
59 // Windows conflates 'HanjaMode' and 'KanjiMode'.
60 {VKEY_HANJA
, DomKey::HANJA_MODE
, 0, 0},
61 {VKEY_CONVERT
, DomKey::CONVERT
, 0, 0},
62 {VKEY_NONCONVERT
, DomKey::NON_CONVERT
, 0, 0},
63 {VKEY_ACCEPT
, DomKey::ACCEPT
, 0, 0},
64 {VKEY_MODECHANGE
, DomKey::MODE_CHANGE
, 0, 0},
65 {VKEY_PRIOR
, DomKey::PAGE_UP
, 0, 0},
66 {VKEY_NEXT
, DomKey::PAGE_DOWN
, 0, 0},
67 {VKEY_END
, DomKey::END
, 0, 0},
68 {VKEY_HOME
, DomKey::HOME
, 0, 0},
69 {VKEY_LEFT
, DomKey::ARROW_LEFT
, 0, 0},
70 {VKEY_UP
, DomKey::ARROW_UP
, 0, 0},
71 {VKEY_RIGHT
, DomKey::ARROW_RIGHT
, 0, 0},
72 {VKEY_DOWN
, DomKey::ARROW_DOWN
, 0, 0},
73 {VKEY_SELECT
, DomKey::SELECT
, 0, 0},
74 {VKEY_PRINT
, DomKey::PRINT
, 0, 0},
75 {VKEY_EXECUTE
, DomKey::EXECUTE
, 0, 0},
76 {VKEY_SNAPSHOT
, DomKey::PRINT_SCREEN
, 0, 0},
77 {VKEY_INSERT
, DomKey::INSERT
, 0, 0},
78 {VKEY_DELETE
, DomKey::DEL
, 0, 0},
79 {VKEY_HELP
, DomKey::HELP
, 0, 0},
80 {VKEY_LWIN
, DomKey::OS
, 0, 0},
81 {VKEY_RWIN
, DomKey::OS
, 0, 0},
82 {VKEY_APPS
, DomKey::MEDIA_APPS
, 0, 0},
83 {VKEY_NUMLOCK
, DomKey::NUM_LOCK
, 0, 0},
84 {VKEY_SCROLL
, DomKey::SCROLL_LOCK
, 0, 0},
85 {VKEY_LSHIFT
, DomKey::SHIFT
, 0, 0},
86 {VKEY_RSHIFT
, DomKey::SHIFT
, 0, 0},
87 {VKEY_LCONTROL
, DomKey::CONTROL
, 0, 0},
88 {VKEY_RCONTROL
, DomKey::CONTROL
, 0, 0},
89 {VKEY_LMENU
, DomKey::ALT
, 0, 0},
90 {VKEY_RMENU
, DomKey::ALT
, 0, 0},
91 {VKEY_BROWSER_BACK
, DomKey::BROWSER_BACK
, 0, 0},
92 {VKEY_BROWSER_FORWARD
, DomKey::BROWSER_FORWARD
, 0, 0},
93 {VKEY_BROWSER_REFRESH
, DomKey::BROWSER_REFRESH
, 0, 0},
94 {VKEY_BROWSER_STOP
, DomKey::BROWSER_STOP
, 0, 0},
95 {VKEY_BROWSER_SEARCH
, DomKey::BROWSER_SEARCH
, 0, 0},
96 {VKEY_BROWSER_FAVORITES
, DomKey::BROWSER_FAVORITES
, 0, 0},
97 {VKEY_BROWSER_HOME
, DomKey::BROWSER_HOME
, 0, 0},
98 {VKEY_VOLUME_MUTE
, DomKey::VOLUME_MUTE
, 0, 0},
99 {VKEY_VOLUME_DOWN
, DomKey::VOLUME_DOWN
, 0, 0},
100 {VKEY_VOLUME_UP
, DomKey::VOLUME_UP
, 0, 0},
101 {VKEY_MEDIA_NEXT_TRACK
, DomKey::MEDIA_TRACK_NEXT
, 0, 0},
102 {VKEY_MEDIA_PREV_TRACK
, DomKey::MEDIA_TRACK_PREVIOUS
, 0, 0},
103 {VKEY_MEDIA_STOP
, DomKey::MEDIA_STOP
, 0, 0},
104 {VKEY_MEDIA_PLAY_PAUSE
, DomKey::MEDIA_PLAY_PAUSE
, 0, 0},
105 {VKEY_MEDIA_LAUNCH_MAIL
, DomKey::LAUNCH_MAIL
, 0, 0},
106 {VKEY_MEDIA_LAUNCH_MEDIA_SELECT
, DomKey::LAUNCH_MEDIA_PLAYER
, 0, 0},
107 {VKEY_MEDIA_LAUNCH_APP1
, DomKey::LAUNCH_MY_COMPUTER
, 0, 0},
108 {VKEY_MEDIA_LAUNCH_APP2
, DomKey::LAUNCH_CALCULATOR
, 0, 0},
109 {VKEY_OEM_8
, DomKey::SUPER
, 0, 0}, // ISO Level 5 Shift in ChromeOS
110 {VKEY_PROCESSKEY
, DomKey::PROCESS
, 0, 0},
111 {VKEY_DBE_SBCSCHAR
, DomKey::HANKAKU
, 0, 0},
112 {VKEY_DBE_DBCSCHAR
, DomKey::ZENKAKU
, 0, 0},
113 {VKEY_ATTN
, DomKey::ATTN
, 0, 0},
114 {VKEY_CRSEL
, DomKey::CR_SEL
, 0, 0},
115 {VKEY_EXSEL
, DomKey::EX_SEL
, 0, 0},
116 {VKEY_EREOF
, DomKey::ERASE_EOF
, 0, 0},
117 {VKEY_PLAY
, DomKey::MEDIA_PLAY
, 0, 0},
118 {VKEY_ZOOM
, DomKey::ZOOM_TOGGLE
, 0, 0},
119 {VKEY_OEM_CLEAR
, DomKey::CLEAR
, 0, 0},
120 {VKEY_ALTGR
, DomKey::ALT_GRAPH
, 0, 0},
121 #if defined(OS_POSIX)
122 {VKEY_POWER
, DomKey::POWER
, 0, 0},
123 {VKEY_BRIGHTNESS_DOWN
, DomKey::BRIGHTNESS_DOWN
, 0, 0},
124 {VKEY_BRIGHTNESS_UP
, DomKey::BRIGHTNESS_UP
, 0, 0},
125 {VKEY_COMPOSE
, DomKey::COMPOSE
, 0, 0},
126 {VKEY_OEM_103
, DomKey::MEDIA_REWIND
, 0, 0},
127 {VKEY_OEM_104
, DomKey::MEDIA_FAST_FORWARD
, 0, 0},
131 bool IsRightSideDomCode(DomCode code
) {
132 return (code
== DomCode::SHIFT_RIGHT
) || (code
== DomCode::CONTROL_RIGHT
) ||
133 (code
== DomCode::ALT_RIGHT
) || (code
== DomCode::OS_RIGHT
);
136 bool IsModifierDomCode(DomCode code
) {
137 return (code
== DomCode::CONTROL_LEFT
) || (code
== DomCode::CONTROL_RIGHT
) ||
138 (code
== DomCode::SHIFT_LEFT
) || (code
== DomCode::SHIFT_RIGHT
) ||
139 (code
== DomCode::ALT_LEFT
) || (code
== DomCode::ALT_RIGHT
) ||
140 (code
== DomCode::OS_LEFT
) || (code
== DomCode::OS_RIGHT
);
143 } // anonymous namespace
145 base::char16
GetCharacterFromKeyCode(KeyboardCode key_code
, int flags
) {
147 base::char16 character
;
148 if (GetMeaningFromKeyCode(key_code
, flags
, &dom_key
, &character
))
153 bool GetMeaningFromKeyCode(KeyboardCode key_code
,
156 base::char16
* character
) {
157 const bool ctrl
= (flags
& EF_CONTROL_DOWN
) != 0;
158 const bool shift
= (flags
& EF_SHIFT_DOWN
) != 0;
159 const bool upper
= shift
^ ((flags
& EF_CAPS_LOCK_DOWN
) != 0);
161 // Control characters.
163 // Following Windows behavior to map ctrl-a ~ ctrl-z to \x01 ~ \x1A.
164 if (key_code
>= VKEY_A
&& key_code
<= VKEY_Z
) {
165 *character
= static_cast<uint16
>(key_code
- VKEY_A
+ 1);
168 *dom_key
= DomKey::BACKSPACE
;
171 *dom_key
= DomKey::TAB
;
175 *dom_key
= DomKey::ENTER
;
178 *dom_key
= DomKey::CHARACTER
;
183 // Other control characters.
185 // The following graphics characters require the shift key to input.
187 // ctrl-@ maps to \x00 (Null byte)
189 *dom_key
= DomKey::CHARACTER
;
192 // ctrl-^ maps to \x1E (Record separator, Information separator two)
194 *dom_key
= DomKey::CHARACTER
;
197 // ctrl-_ maps to \x1F (Unit separator, Information separator one)
199 *dom_key
= DomKey::CHARACTER
;
202 // Returns 0 for all other keys to avoid inputting unexpected chars.
204 *dom_key
= DomKey::UNIDENTIFIED
;
210 // ctrl-[ maps to \x1B (Escape)
212 *dom_key
= DomKey::ESCAPE
;
215 // ctrl-\ maps to \x1C (File separator, Information separator four)
217 *dom_key
= DomKey::CHARACTER
;
220 // ctrl-] maps to \x1D (Group separator, Information separator three)
222 *dom_key
= DomKey::CHARACTER
;
225 // ctrl-Enter maps to \x0A (Line feed)
227 *dom_key
= DomKey::CHARACTER
;
230 // Returns 0 for all other keys to avoid inputting unexpected chars.
232 *dom_key
= DomKey::UNIDENTIFIED
;
239 // ASCII alphanumeric characters.
240 if (key_code
>= VKEY_A
&& key_code
<= VKEY_Z
) {
241 *dom_key
= DomKey::CHARACTER
;
242 *character
= static_cast<uint16
>(key_code
- VKEY_A
+ (upper
? 'A' : 'a'));
245 if (key_code
>= VKEY_0
&& key_code
<= VKEY_9
) {
246 *dom_key
= DomKey::CHARACTER
;
248 shift
? ")!@#$%^&*("[key_code
- VKEY_0
] : static_cast<uint16
>(key_code
);
251 if (key_code
>= VKEY_NUMPAD0
&& key_code
<= VKEY_NUMPAD9
) {
252 *dom_key
= DomKey::CHARACTER
;
253 *character
= static_cast<uint16
>(key_code
- VKEY_NUMPAD0
+ '0');
258 if (key_code
>= VKEY_F1
&& key_code
<= VKEY_F24
) {
260 static_cast<DomKey
>(key_code
- VKEY_F1
+ static_cast<int>(DomKey::F1
));
266 for (size_t i
= 0; i
< arraysize(kKeyboardCodeToMeaning
); ++i
) {
267 if (kKeyboardCodeToMeaning
[i
].key_code
== key_code
) {
268 const KeyboardCodeToMeaning
* p
= &kKeyboardCodeToMeaning
[i
];
270 *character
= (shift
&& p
->shift_character
) ? p
->shift_character
271 : p
->plain_character
;
275 *dom_key
= DomKey::UNIDENTIFIED
;
280 bool DomCodeToUsLayoutMeaning(DomCode dom_code
,
283 base::char16
* out_character
,
284 KeyboardCode
* out_key_code
) {
285 if ((flags
& EF_CONTROL_DOWN
) == EF_CONTROL_DOWN
) {
286 if (DomCodeToControlCharacter(dom_code
, flags
, out_dom_key
, out_character
,
290 if (!IsModifierDomCode(dom_code
)) {
291 *out_dom_key
= DomKey::UNIDENTIFIED
;
293 *out_key_code
= LocatedToNonLocatedKeyboardCode(
294 DomCodeToUsLayoutKeyboardCode(dom_code
));
298 for (const auto& it
: kPrintableCodeMap
) {
299 if (it
.dom_code
== dom_code
) {
300 int state
= ((flags
& EF_SHIFT_DOWN
) == EF_SHIFT_DOWN
);
301 base::char16 ch
= it
.character
[state
];
302 *out_dom_key
= DomKey::CHARACTER
;
304 if ((flags
& EF_CAPS_LOCK_DOWN
) == EF_CAPS_LOCK_DOWN
) {
306 if ((ch
>= 'a') && (ch
<= 'z'))
307 *out_character
= it
.character
[state
^ 1];
309 *out_key_code
= LocatedToNonLocatedKeyboardCode(
310 DomCodeToUsLayoutKeyboardCode(dom_code
));
315 for (const auto& it
: kNonPrintableCodeMap
) {
316 if (it
.dom_code
== dom_code
) {
317 *out_dom_key
= it
.dom_key
;
318 *out_character
= it
.character
;
319 *out_key_code
= NonPrintableDomKeyToKeyboardCode(it
.dom_key
);
323 if ((flags
& EF_CONTROL_DOWN
) == EF_CONTROL_DOWN
) {
324 *out_dom_key
= DomKey::UNIDENTIFIED
;
326 *out_key_code
= LocatedToNonLocatedKeyboardCode(
327 DomCodeToUsLayoutKeyboardCode(dom_code
));
333 bool DomCodeToControlCharacter(DomCode dom_code
,
336 base::char16
* character
,
337 KeyboardCode
* key_code
) {
338 if ((flags
& EF_CONTROL_DOWN
) == 0)
341 int code
= static_cast<int>(dom_code
);
342 const int kKeyA
= static_cast<int>(DomCode::KEY_A
);
343 // Control-A - Control-Z map to 0x01 - 0x1A.
344 if (code
>= kKeyA
&& code
<= static_cast<int>(DomCode::KEY_Z
)) {
345 *character
= static_cast<base::char16
>(code
- kKeyA
+ 1);
348 *dom_key
= DomKey::BACKSPACE
;
349 *key_code
= VKEY_BACK
;
352 *dom_key
= DomKey::TAB
;
353 *key_code
= VKEY_TAB
;
356 *dom_key
= DomKey::ENTER
;
357 *key_code
= VKEY_RETURN
;
360 *dom_key
= DomKey::CHARACTER
;
361 *key_code
= static_cast<KeyboardCode
>(code
- kKeyA
+ VKEY_A
);
367 if (flags
& EF_SHIFT_DOWN
) {
369 case DomCode::DIGIT2
:
372 *dom_key
= DomKey::CHARACTER
;
375 case DomCode::DIGIT6
:
378 *dom_key
= DomKey::CHARACTER
;
384 *dom_key
= DomKey::CHARACTER
;
385 *key_code
= VKEY_OEM_MINUS
;
396 *dom_key
= DomKey::CHARACTER
;
397 *key_code
= VKEY_RETURN
;
399 case DomCode::BRACKET_LEFT
:
402 *dom_key
= DomKey::ESCAPE
;
403 *key_code
= VKEY_OEM_4
;
405 case DomCode::BACKSLASH
:
408 *dom_key
= DomKey::CHARACTER
;
409 *key_code
= VKEY_OEM_5
;
411 case DomCode::BRACKET_RIGHT
:
414 *dom_key
= DomKey::CHARACTER
;
415 *key_code
= VKEY_OEM_6
;
422 DomKey
CharacterToDomKey(uint32 character
) {
425 return DomKey::BACKSPACE
;
429 return DomKey::ENTER
;
431 return DomKey::ESCAPE
;
435 return DomKey::CHARACTER
;
439 // Returns a Windows-based VKEY for a non-printable DOM Level 3 |key|.
440 // The returned VKEY is non-positional (e.g. VKEY_SHIFT).
441 KeyboardCode
NonPrintableDomKeyToKeyboardCode(DomKey dom_key
) {
442 for (const auto& it
: kDomKeyToKeyboardCodeMap
) {
443 if (it
.dom_key
== dom_key
)
449 // Determine the non-located VKEY corresponding to a located VKEY.
450 KeyboardCode
LocatedToNonLocatedKeyboardCode(KeyboardCode key_code
) {
488 // Determine the located VKEY corresponding to a non-located VKEY.
489 KeyboardCode
NonLocatedToLocatedKeyboardCode(KeyboardCode key_code
,
493 return IsRightSideDomCode(dom_code
) ? VKEY_RSHIFT
: VKEY_LSHIFT
;
495 return IsRightSideDomCode(dom_code
) ? VKEY_RCONTROL
: VKEY_LCONTROL
;
497 return IsRightSideDomCode(dom_code
) ? VKEY_RMENU
: VKEY_LMENU
;
499 return IsRightSideDomCode(dom_code
) ? VKEY_RWIN
: VKEY_LWIN
;
501 return (dom_code
== DomCode::NUMPAD0
) ? VKEY_NUMPAD0
: VKEY_0
;
503 return (dom_code
== DomCode::NUMPAD1
) ? VKEY_NUMPAD1
: VKEY_1
;
505 return (dom_code
== DomCode::NUMPAD2
) ? VKEY_NUMPAD2
: VKEY_2
;
507 return (dom_code
== DomCode::NUMPAD3
) ? VKEY_NUMPAD3
: VKEY_3
;
509 return (dom_code
== DomCode::NUMPAD4
) ? VKEY_NUMPAD4
: VKEY_4
;
511 return (dom_code
== DomCode::NUMPAD5
) ? VKEY_NUMPAD5
: VKEY_5
;
513 return (dom_code
== DomCode::NUMPAD6
) ? VKEY_NUMPAD6
: VKEY_6
;
515 return (dom_code
== DomCode::NUMPAD7
) ? VKEY_NUMPAD7
: VKEY_7
;
517 return (dom_code
== DomCode::NUMPAD8
) ? VKEY_NUMPAD8
: VKEY_8
;
519 return (dom_code
== DomCode::NUMPAD9
) ? VKEY_NUMPAD9
: VKEY_9
;
525 DomCode
UsLayoutKeyboardCodeToDomCode(KeyboardCode key_code
) {
526 key_code
= NonLocatedToLocatedKeyboardCode(key_code
, DomCode::NONE
);
527 for (const auto& it
: kDomCodeToKeyboardCodeMap
) {
528 if (it
.key_code
== key_code
)
531 for (const auto& it
: kFallbackKeyboardCodeToDomCodeMap
) {
532 if (it
.key_code
== key_code
)
535 return DomCode::NONE
;
538 KeyboardCode
DomCodeToUsLayoutKeyboardCode(DomCode dom_code
) {
539 const DomCodeToKeyboardCodeEntry
* end
=
540 kDomCodeToKeyboardCodeMap
+ arraysize(kDomCodeToKeyboardCodeMap
);
541 const DomCodeToKeyboardCodeEntry
* found
= std::lower_bound(
542 kDomCodeToKeyboardCodeMap
, end
, dom_code
,
543 [](const DomCodeToKeyboardCodeEntry
& a
, DomCode b
) {
544 return static_cast<int>(a
.dom_code
) < static_cast<int>(b
);
546 if ((found
!= end
) && (found
->dom_code
== dom_code
))
547 return found
->key_code
;