1 // Copyright 2013 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 #include "ui/events/keycodes/dom/keycode_converter.h"
6
7 #include "base/logging.h"
8 #include "base/stl_util.h"
9 #include "base/strings/utf_string_conversion_utils.h"
10 #include "build/build_config.h"
11 #include "ui/events/keycodes/dom/dom_code.h"
12 #include "ui/events/keycodes/dom/dom_key.h"
13
14 namespace ui {
15
16 namespace {
17
18 // Table of USB codes (equivalent to DomCode values), native scan codes,
19 // and DOM Level 3 |code| strings.
20 #if defined(OS_WIN)
21 #define DOM_CODE(usb, evdev, xkb, win, mac, code, id) \
22 { usb, win, code }
23 #elif defined(OS_LINUX) || defined(OS_BSD)
24 #define DOM_CODE(usb, evdev, xkb, win, mac, code, id) \
25 { usb, xkb, code }
26 #elif defined(OS_MACOSX)
27 #define DOM_CODE(usb, evdev, xkb, win, mac, code, id) \
28 { usb, mac, code }
29 #elif defined(OS_ANDROID)
30 #define DOM_CODE(usb, evdev, xkb, win, mac, code, id) \
31 { usb, evdev, code }
32 #elif defined(OS_FUCHSIA)
33 // TODO(https://bugs.fuchsia.com/23982): Fuchsia currently delivers events
34 // with a USB Code but no Page specified, so only map |native_keycode| for
35 // Keyboard Usage Page codes, for now.
36 inline constexpr uint32_t CodeIfOnKeyboardPage(uint32_t usage) {
37 constexpr uint32_t kUsbHidKeyboardPageBase = 0x070000;
38 if ((usage & 0xffff0000) == kUsbHidKeyboardPageBase)
39 return usage & 0xffff;
40 return 0;
41 }
42 #define DOM_CODE(usb, evdev, xkb, win, mac, code, id) \
43 { usb, CodeIfOnKeyboardPage(usb), code }
44 #else
45 #error Unsupported platform
46 #endif
47 #define DOM_CODE_DECLARATION const KeycodeMapEntry kDomCodeMappings[] =
48 #include "ui/events/keycodes/dom/dom_code_data.inc"
49 #undef DOM_CODE
50 #undef DOM_CODE_DECLARATION
51
52 // Table of DomKey enum values and DOM Level 3 |key| strings.
53 struct DomKeyMapEntry {
54 DomKey dom_key;
55 const char* string;
56 };
57
58 #define DOM_KEY_MAP_DECLARATION const DomKeyMapEntry kDomKeyMappings[] =
59 #define DOM_KEY_UNI(key, id, value) {DomKey::id, key}
60 #define DOM_KEY_MAP(key, id, value) {DomKey::id, key}
61 #include "ui/events/keycodes/dom/dom_key_data.inc"
62 #undef DOM_KEY_MAP_DECLARATION
63 #undef DOM_KEY_MAP
64 #undef DOM_KEY_UNI
65
66 } // namespace
67
68 // static
NumKeycodeMapEntriesForTest()69 size_t KeycodeConverter::NumKeycodeMapEntriesForTest() {
70 return base::size(kDomCodeMappings);
71 }
72
73 // static
GetKeycodeMapForTest()74 const KeycodeMapEntry* KeycodeConverter::GetKeycodeMapForTest() {
75 return &kDomCodeMappings[0];
76 }
77
78 // static
DomKeyStringForTest(size_t index)79 const char* KeycodeConverter::DomKeyStringForTest(size_t index) {
80 if (index >= base::size(kDomKeyMappings))
81 return nullptr;
82 return kDomKeyMappings[index].string;
83 }
84
85 // static
InvalidNativeKeycode()86 int KeycodeConverter::InvalidNativeKeycode() {
87 return kDomCodeMappings[0].native_keycode;
88 }
89
90 // TODO(zijiehe): Most of the following functions can be optimized by using
91 // either multiple arrays or unordered_map.
92
93 // static
NativeKeycodeToDomCode(int native_keycode)94 DomCode KeycodeConverter::NativeKeycodeToDomCode(int native_keycode) {
95 for (auto& mapping : kDomCodeMappings) {
96 if (mapping.native_keycode == native_keycode)
97 return static_cast<DomCode>(mapping.usb_keycode);
98 }
99 return DomCode::NONE;
100 }
101
102 // static
DomCodeToNativeKeycode(DomCode code)103 int KeycodeConverter::DomCodeToNativeKeycode(DomCode code) {
104 return UsbKeycodeToNativeKeycode(static_cast<uint32_t>(code));
105 }
106
107 // static
CodeStringToDomCode(const std::string & code)108 DomCode KeycodeConverter::CodeStringToDomCode(const std::string& code) {
109 if (code.empty())
110 return DomCode::NONE;
111 for (auto& mapping : kDomCodeMappings) {
112 if (mapping.code && code == mapping.code) {
113 return static_cast<DomCode>(mapping.usb_keycode);
114 }
115 }
116 LOG(WARNING) << "unrecognized code string '" << code << "'";
117 return DomCode::NONE;
118 }
119
120 // static
DomCodeToCodeString(DomCode dom_code)121 const char* KeycodeConverter::DomCodeToCodeString(DomCode dom_code) {
122 for (auto& mapping : kDomCodeMappings) {
123 if (mapping.usb_keycode == static_cast<uint32_t>(dom_code)) {
124 if (mapping.code)
125 return mapping.code;
126 break;
127 }
128 }
129 return "";
130 }
131
132 // static
DomCodeToLocation(DomCode dom_code)133 DomKeyLocation KeycodeConverter::DomCodeToLocation(DomCode dom_code) {
134 static const struct {
135 DomCode code;
136 DomKeyLocation location;
137 } kLocations[] = {{DomCode::CONTROL_LEFT, DomKeyLocation::LEFT},
138 {DomCode::SHIFT_LEFT, DomKeyLocation::LEFT},
139 {DomCode::ALT_LEFT, DomKeyLocation::LEFT},
140 {DomCode::META_LEFT, DomKeyLocation::LEFT},
141 {DomCode::CONTROL_RIGHT, DomKeyLocation::RIGHT},
142 {DomCode::SHIFT_RIGHT, DomKeyLocation::RIGHT},
143 {DomCode::ALT_RIGHT, DomKeyLocation::RIGHT},
144 {DomCode::META_RIGHT, DomKeyLocation::RIGHT},
145 {DomCode::NUMPAD_DIVIDE, DomKeyLocation::NUMPAD},
146 {DomCode::NUMPAD_MULTIPLY, DomKeyLocation::NUMPAD},
147 {DomCode::NUMPAD_SUBTRACT, DomKeyLocation::NUMPAD},
148 {DomCode::NUMPAD_ADD, DomKeyLocation::NUMPAD},
149 {DomCode::NUMPAD_ENTER, DomKeyLocation::NUMPAD},
150 {DomCode::NUMPAD1, DomKeyLocation::NUMPAD},
151 {DomCode::NUMPAD2, DomKeyLocation::NUMPAD},
152 {DomCode::NUMPAD3, DomKeyLocation::NUMPAD},
153 {DomCode::NUMPAD4, DomKeyLocation::NUMPAD},
154 {DomCode::NUMPAD5, DomKeyLocation::NUMPAD},
155 {DomCode::NUMPAD6, DomKeyLocation::NUMPAD},
156 {DomCode::NUMPAD7, DomKeyLocation::NUMPAD},
157 {DomCode::NUMPAD8, DomKeyLocation::NUMPAD},
158 {DomCode::NUMPAD9, DomKeyLocation::NUMPAD},
159 {DomCode::NUMPAD0, DomKeyLocation::NUMPAD},
160 {DomCode::NUMPAD_DECIMAL, DomKeyLocation::NUMPAD},
161 {DomCode::NUMPAD_EQUAL, DomKeyLocation::NUMPAD},
162 {DomCode::NUMPAD_COMMA, DomKeyLocation::NUMPAD},
163 {DomCode::NUMPAD_PAREN_LEFT, DomKeyLocation::NUMPAD},
164 {DomCode::NUMPAD_PAREN_RIGHT, DomKeyLocation::NUMPAD},
165 {DomCode::NUMPAD_BACKSPACE, DomKeyLocation::NUMPAD},
166 {DomCode::NUMPAD_MEMORY_STORE, DomKeyLocation::NUMPAD},
167 {DomCode::NUMPAD_MEMORY_RECALL, DomKeyLocation::NUMPAD},
168 {DomCode::NUMPAD_MEMORY_CLEAR, DomKeyLocation::NUMPAD},
169 {DomCode::NUMPAD_MEMORY_ADD, DomKeyLocation::NUMPAD},
170 {DomCode::NUMPAD_MEMORY_SUBTRACT, DomKeyLocation::NUMPAD},
171 {DomCode::NUMPAD_SIGN_CHANGE, DomKeyLocation::NUMPAD},
172 {DomCode::NUMPAD_CLEAR, DomKeyLocation::NUMPAD},
173 {DomCode::NUMPAD_CLEAR_ENTRY, DomKeyLocation::NUMPAD}};
174 for (const auto& key : kLocations) {
175 if (key.code == dom_code)
176 return key.location;
177 }
178 return DomKeyLocation::STANDARD;
179 }
180
181 // static
KeyStringToDomKey(const std::string & key)182 DomKey KeycodeConverter::KeyStringToDomKey(const std::string& key) {
183 if (key.empty())
184 return DomKey::NONE;
185 // Check for standard key names.
186 for (auto& mapping : kDomKeyMappings) {
187 if (mapping.string && key == mapping.string) {
188 return mapping.dom_key;
189 }
190 }
191 if (key == "Dead") {
192 // The web KeyboardEvent string does not encode the combining character,
193 // so we just set it to the Unicode designated non-character 0xFFFF.
194 // This will round-trip convert back to 'Dead' but take no part in
195 // character composition.
196 return DomKey::DeadKeyFromCombiningCharacter(0xFFFF);
197 }
198 // Otherwise, if the string contains a single Unicode character,
199 // the key value is that character.
200 int32_t char_index = 0;
201 uint32_t character;
202 if (base::ReadUnicodeCharacter(key.c_str(),
203 static_cast<int32_t>(key.length()),
204 &char_index, &character) &&
205 key[++char_index] == 0) {
206 return DomKey::FromCharacter(character);
207 }
208 return DomKey::NONE;
209 }
210
211 // static
DomKeyToKeyString(DomKey dom_key)212 std::string KeycodeConverter::DomKeyToKeyString(DomKey dom_key) {
213 if (dom_key.IsDeadKey()) {
214 // All dead-key combining codes collapse to 'Dead', as UI Events
215 // KeyboardEvent represents the combining character separately.
216 return "Dead";
217 }
218 for (auto& mapping : kDomKeyMappings) {
219 if (mapping.dom_key == dom_key) {
220 if (mapping.string)
221 return mapping.string;
222 break;
223 }
224 }
225 if (dom_key.IsCharacter()) {
226 std::string s;
227 base::WriteUnicodeCharacter(dom_key.ToCharacter(), &s);
228 return s;
229 }
230 return std::string();
231 }
232
233 // static
IsDomKeyForModifier(DomKey dom_key)234 bool KeycodeConverter::IsDomKeyForModifier(DomKey dom_key) {
235 switch (dom_key) {
236 case DomKey::ACCEL:
237 case DomKey::ALT:
238 case DomKey::ALT_GRAPH:
239 case DomKey::CAPS_LOCK:
240 case DomKey::CONTROL:
241 case DomKey::FN:
242 case DomKey::FN_LOCK:
243 case DomKey::HYPER:
244 case DomKey::META:
245 case DomKey::NUM_LOCK:
246 case DomKey::SCROLL_LOCK:
247 case DomKey::SHIFT:
248 case DomKey::SUPER:
249 case DomKey::SYMBOL:
250 case DomKey::SYMBOL_LOCK:
251 case DomKey::SHIFT_LEVEL5:
252 return true;
253 default:
254 return false;
255 }
256 }
257
258 // USB keycodes
259 // Note that USB keycodes are not part of any web standard.
260 // Please don't use USB keycodes in new code.
261
262 // static
InvalidUsbKeycode()263 uint32_t KeycodeConverter::InvalidUsbKeycode() {
264 return kDomCodeMappings[0].usb_keycode;
265 }
266
267 // static
UsbKeycodeToNativeKeycode(uint32_t usb_keycode)268 int KeycodeConverter::UsbKeycodeToNativeKeycode(uint32_t usb_keycode) {
269 // Deal with some special-cases that don't fit the 1:1 mapping.
270 if (usb_keycode == 0x070032) // non-US hash.
271 usb_keycode = 0x070031; // US backslash.
272 #if defined(OS_MACOSX)
273 if (usb_keycode == 0x070046) // PrintScreen.
274 usb_keycode = 0x070068; // F13.
275 #endif
276
277 for (auto& mapping : kDomCodeMappings) {
278 if (mapping.usb_keycode == usb_keycode)
279 return mapping.native_keycode;
280 }
281 return InvalidNativeKeycode();
282 }
283
284 // static
NativeKeycodeToUsbKeycode(int native_keycode)285 uint32_t KeycodeConverter::NativeKeycodeToUsbKeycode(int native_keycode) {
286 for (auto& mapping : kDomCodeMappings) {
287 if (mapping.native_keycode == native_keycode)
288 return mapping.usb_keycode;
289 }
290 return InvalidUsbKeycode();
291 }
292
293 // static
UsbKeycodeToDomCode(uint32_t usb_keycode)294 DomCode KeycodeConverter::UsbKeycodeToDomCode(uint32_t usb_keycode) {
295 for (auto& mapping : kDomCodeMappings) {
296 if (mapping.usb_keycode == usb_keycode)
297 return static_cast<DomCode>(usb_keycode);
298 }
299 return DomCode::NONE;
300 }
301
302 // static
DomCodeToUsbKeycode(DomCode dom_code)303 uint32_t KeycodeConverter::DomCodeToUsbKeycode(DomCode dom_code) {
304 for (auto& mapping : kDomCodeMappings) {
305 if (mapping.usb_keycode == static_cast<uint32_t>(dom_code))
306 return mapping.usb_keycode;
307 }
308 return InvalidUsbKeycode();
309 }
310
311 // static
CodeStringToUsbKeycode(const std::string & code)312 uint32_t KeycodeConverter::CodeStringToUsbKeycode(const std::string& code) {
313 if (code.empty())
314 return InvalidUsbKeycode();
315
316 for (auto& mapping : kDomCodeMappings) {
317 if (mapping.code && code == mapping.code) {
318 return mapping.usb_keycode;
319 }
320 }
321 return InvalidUsbKeycode();
322 }
323
324 // static
CodeStringToNativeKeycode(const std::string & code)325 int KeycodeConverter::CodeStringToNativeKeycode(const std::string& code) {
326 return UsbKeycodeToNativeKeycode(CodeStringToUsbKeycode(code));
327 }
328
329 } // namespace ui
330