diff --git a/dev/devicelab/pubspec.yaml b/dev/devicelab/pubspec.yaml index 276a75842a7..69e62ada815 100644 --- a/dev/devicelab/pubspec.yaml +++ b/dev/devicelab/pubspec.yaml @@ -10,7 +10,7 @@ environment: dependencies: args: 1.5.1 file: 5.0.7 - image: 2.0.6 + image: 2.0.7 meta: 1.1.6 path: 1.6.2 platform: 2.2.0 @@ -74,4 +74,4 @@ dev_dependencies: watcher: 0.9.7+10 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" yaml: 2.1.15 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" -# PUBSPEC CHECKSUM: bc81 +# PUBSPEC CHECKSUM: 5d82 diff --git a/dev/integration_tests/ui/pubspec.yaml b/dev/integration_tests/ui/pubspec.yaml index ea71ee6bc93..2b00664aeb9 100644 --- a/dev/integration_tests/ui/pubspec.yaml +++ b/dev/integration_tests/ui/pubspec.yaml @@ -6,7 +6,7 @@ environment: sdk: ">=2.0.0-dev.68.0 <3.0.0" dependencies: - image: 2.0.6 + image: 2.0.7 flutter: sdk: flutter flutter_driver: @@ -80,4 +80,4 @@ dev_dependencies: flutter: uses-material-design: true -# PUBSPEC CHECKSUM: e153 +# PUBSPEC CHECKSUM: 8654 diff --git a/dev/tools/gen_keycodes/README.md b/dev/tools/gen_keycodes/README.md new file mode 100644 index 00000000000..a1d0796b719 --- /dev/null +++ b/dev/tools/gen_keycodes/README.md @@ -0,0 +1,168 @@ +## Keycode Generator + +This directory contains a keycode generator that can generate Dart code for +the `LogicalKeyboardKey` and `PhysicalKeyboardKey` classes. It draws information +from both the Chromium and Android source bases, and incorporates the +information it finds in those sources into a single key database in JSON form. + +It then generates `keyboard_key.dart` (containing the `LogicalKeyboardKey` and +`PhysicalKeyboardKey` classes), and `keyboard_maps.dart`, containing +platform-specific immutable maps for translating platform keycodes and +information into the pre-defined key values in the `LogicalKeyboardKey` and +`PhysicalKeyboardKey` classes. + +The `data` subdirectory contains both some local data files, and the templates +used to generate the source files. + + - `data/key_data.json`: contains the merged data from all the other sources. + This file will be regenerated if "--collect" is specified for the + gen_keycodes script. + - `data/key_name_to_android_name.json`: contains a mapping from Flutter key + names to Android keycode names (with the "KEY_" prefix stripped off). + - `data/keyboard_key.tmpl`: contains the template for the `keyboard_key.dart` + file. Markers that begin and end with "@@@" denote the locations where + generated data will be inserted. + - `data/keyboard_maps.tmpl`: contains the template for the `keyboard_maps.dart` + file. Markers that begin and end with "@@@" denote the locations where + generated data will be inserted. + - `data/printable.json`: contains a mapping between Flutter key name and its + printable character. This character is used as the key label. + + ## Running the tool + +To run the `gen_keycodes` tool using the checked in `key_data.json` file, run +it like so: + +```bash +$FLUTTER_ROOT/bin/cache/dart-sdk/bin/dart bin/gen_keycodes.dart +``` + +This will rengerate the `keyboard_key.dart` and `keyboard_maps.dart` files in +place. + +If you wish to incorporate and parse changes from the Chromium and Android +source trees, add `--collect` to the command line. The script will download and +incorporate the changed data automatically. Note that the parsing is specific to +the format of the source code that it is reading, so if the format of those +files changes appreciably, you will need to update the parser. + +There are other options for manually specifying the file to read in place of the +downloaded files, use `--help` to see what is available. + +If the data in those files changes in the future to be unhelpful, then we can +switch to another data source, or abandon the parsing and maintain +`key_data.json` manually. All output files and local input files should be +checked in. + +## Key Code ID Scheme + +In order to provide keys with unique ID codes, Flutter uses a scheme to assign +codes which keeps us out of the business of minting new codes ourselves. + +The codes are meant to be opaque to the user, and should never be unpacked for +meaning, since the code scheme could change at any time, and the meaning is +likely to be retrievable in a more reliable and correct manner from the API. + +However, if you are porting Flutter to a new platform, you should follow the +following guidelines for specifying key codes. + +The key code is a 37-bit integer in a namespace that we control and define. It +has values in the following ranges. + + - **0x00 0000 0000 - 0x0 0010 FFFF**: For keys that generate Unicode + characters when pressed (this includes dead keys, but not e.g. function keys + or shift keys), the logical key code is the Unicode code point corresponding + to the representation of the key in the current keyboard mapping. The + Unicode code point might not actually match the string that is generated for + an unshifted key press of that key, for example we would use U+0034 for the + “4 $” key in the US layout, and also the “4 ;” key in the Russian layout, + and also, maybe less intuitively, for the “' 4 {“ in French layout (where in + the latter case, an unshifted press gets you a ', not a 4). Similarly, the Q + key in the US layout outputs a q in normal usage, but its code would be 0x0 + 0000 0051 (U+00051 being the code for the uppercase Q). + + - **0x01 0000 0000 - 0x01 FFFF FFFF**: For keys that are defined by the USB HID + standard, the key code consists of the 32 bit USB extended usage code. For + example, the Enter key would have code 0x0 0007 0028. Only keys that fall + into collections "Keyboard", "Keypad", and "Tablet PC System Controls" are + considered for this API; for example, a mixing desk with multiple + collections of volume controls would not be exposed via DOWN and UP events, + nor would a mouse, joystick, or golf simulator control. + + - **0x02 0000 0000 - 0xFF FFFF FFFF**: For keys that aren't defined in USB at the + time of implementation, but that we need to support. For example, if Flutter + were ever ported to the Symbolics LM-2, the "thumb up" key might be given + the code 0x14 0000 0001, where 0x14 is defined as the “Symbolics” platform + range. Where possible, we will use specific subranges of this space to reuse + keys from other platforms. When this is not possible, the prefix 0xFF is + reserved for “Custom” codes. Each platform from which we take codes will get + a unique prefix in the range 0x2-0xFE. If multiple systems define keys with + the same usage (not the same number), then the value with the lowest prefix + is used as the defining code. + + Prefixes will be: + + |Code|Platform| + |----|--------| + |0x02| Android| + |0x03|Fuchsia | + |0x04|iOS | + |0x05|macOS | + |0x06|Linux | + |0x07|Windows | + |0x08|Web | + |0xFF|Custom | + + Further ranges will be added as platforms are added. The platform prefix + does not define the platform it is used on, it is just the platform that + decides what the value is: the codes are mapped to the same value on all + platforms. + + - **0x100 0000 0000 - 0x1FF FFFF FFFF**: For keys that have no definition yet in + Flutter, but that are encountered in the field, this range is used to embed + the platform-specific keycode in an ID that must be tested for in a platform + specific way. For instance, if a platform generates a new USB HID code 0x07 + 00E8 that a Flutter app wasn’t compiled with, then it would appear in the + app as 0x100 0007 00E8, and the app could test against that code. Yes, this + also means that once they recompile with a version of Flutter that supports + this new HID code, apps looking for this code will break. This situation is + only meant to provide a fallback ability for apps to handle esoteric codes + that their version of Flutter doesn’t support yet. The prefix for this code + is the platform prefix from the previous sections, plus 0x100. + +**This is intended to get us out of the business of defining key codes where +possible.** We still have to have mapping tables, but at least the actual minting +of codes is deferred to other organizations to a large extent. Coming up with a +code is a mechanical process consisting of just picking the lowest number code +possible that matches the semantic meaning of the key according to the +definitions above. + +Here are some examples: + +For example, on a French keyboard layout, pressing CAPS LOCK then pressing +SHIFT + Y would generate the following sequence: + +DOWN, code 0x00070039. (CAPS LOCK DOWN)
+UP, code 0x00070039. (CAPS LOCK UP)
+DOWN, code 0x000700E1 (SHIFT DOWN)
+DOWN, code 0x0007001D, string U+00059 (Y DOWN, the code is for the "Z" key, but +string is the character, "Y")
+UP, code 0x0007001D (Y UP)
+UP, code 0x000700E1 (SHIFT UP)
+ +Here's another example. On a German keyboard layout, you press ^e (the ^ key is +at the top left of the keyboard and is a dead key) to produce a “ê”: + +DOWN, code 0x00070035 (GRAVE DOWN) Assuming that the keymap maps it to the same +logical key, it produces no string, because it's a dead key. The HID key is for +"Keyboard grave accent and tilde" in AT-101 keyboard typical position 1.
+UP, code 0x00070035 (GRAVE UP)
+DOWN, code 0x00070008, string U+000EA (Unicode for ê‬) (E DOWN).
+UP, code 0x00070008. (E UP).
+ +It is an important point that even though we’re representing many keys with USB +HID codes, these are not necessarily the same HID codes produced by the hardware +and presented to the driver, since on most platforms we have to map the platform +representation back to a HID code because we don’t have access to the original +HID code. USB HID is simply a conveniently well-defined standard that includes +many of the keys we would want. \ No newline at end of file diff --git a/dev/tools/gen_keycodes/bin/gen_keycodes.dart b/dev/tools/gen_keycodes/bin/gen_keycodes.dart new file mode 100644 index 00000000000..1b1ccb43d07 --- /dev/null +++ b/dev/tools/gen_keycodes/bin/gen_keycodes.dart @@ -0,0 +1,161 @@ +// Copyright 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'dart:async'; +import 'dart:convert'; +import 'dart:io' hide Platform; + +import 'package:args/args.dart'; +import 'package:http/http.dart' as http; +import 'package:path/path.dart' as path; + +import 'package:gen_keycodes/code_gen.dart'; +import 'package:gen_keycodes/key_data.dart'; +import 'package:gen_keycodes/utils.dart'; + +/// Get contents of the file that contains the key code mapping in Chromium +/// source. +Future getChromiumConversions() async { + final Uri keyCodeMapUri = Uri.parse('https://cs.chromium.org/codesearch/f/chromium/src/ui/events/keycodes/dom/keycode_converter_data.inc'); + return await http.read(keyCodeMapUri); +} + +/// Get contents of the file that contains the key codes in Android source. +Future getAndroidKeyCodes() async { + final Uri keyCodesUri = Uri.parse('https://android.googlesource.com/platform/frameworks/native/+/master/include/android/keycodes.h?format=TEXT'); + return utf8.decode(base64.decode(await http.read(keyCodesUri))); +} + +/// Get contents of the file that contains the scan codes in Android source. +/// Yes, this is just the generic keyboard layout file for base Android distro +/// This is because there isn't any facility in Android to get the keyboard +/// layout, so we're using this to match scan codes with symbol names for most +/// common keyboards. Other than some special keyboards and game pads, this +/// should be OK. +Future getAndroidScanCodes() async { + final Uri scanCodesUri = Uri.parse('https://android.googlesource.com/platform/frameworks/base/+/master/data/keyboards/Generic.kl?format=TEXT'); + return utf8.decode(base64.decode(await http.read(scanCodesUri))); +} + +Future main(List rawArguments) async { + final ArgParser argParser = ArgParser(); + argParser.addOption( + 'chromium-hid-codes', + defaultsTo: null, + help: 'The path to where the Chromium HID code mapping file should be ' + 'read. If --chromium-hid-codes is not specified, the input will be read ' + 'from the correct file in the Chromium repository.', + ); + argParser.addOption( + 'android-keycodes', + defaultsTo: null, + help: 'The path to where the Android keycodes header file should be read. ' + 'If --android-keycodes is not specified, the input will be read from the ' + 'correct file in the Android repository.', + ); + argParser.addOption( + 'android-scancodes', + defaultsTo: null, + help: 'The path to where the Android scancodes header file should be read. ' + 'If --android-scancodes is not specified, the input will be read from the ' + 'correct file in the Android repository.', + ); + argParser.addOption( + 'android-domkey', + defaultsTo: path.join(flutterRoot.path, 'dev', 'tools', 'gen_keycodes', 'data', 'key_name_to_android_name.json'), + help: 'The path to where the Android keycode to DomKey mapping is.', + ); + argParser.addOption( + 'data', + defaultsTo: path.join(flutterRoot.path, 'dev', 'tools', 'gen_keycodes', 'data', 'key_data.json'), + help: 'The path to where the key code data file should be written when ' + 'collected, and read from when generating output code. If --data is ' + 'not specified, the output will be written to/read from the current ' + "directory. If the output directory doesn't exist, it, and the path to " + 'it, will be created.', + ); + argParser.addOption( + 'code', + defaultsTo: path.join(flutterRoot.path, 'packages', 'flutter', 'lib', 'src', 'services', 'keyboard_key.dart'), + help: 'The path to where the output "keyboard_keys.dart" file should be' + 'written. If --code is not specified, the output will be written to the ' + 'correct directory in the flutter tree. If the output directory does not ' + 'exist, it, and the path to it, will be created.', + ); + argParser.addOption( + 'maps', + defaultsTo: path.join(flutterRoot.path, 'packages', 'flutter', 'lib', 'src', 'services', 'keyboard_maps.dart'), + help: 'The path to where the output "keyboard_maps.dart" file should be' + 'written. If --maps is not specified, the output will be written to the ' + 'correct directory in the flutter tree. If the output directory does not ' + 'exist, it, and the path to it, will be created.', + ); + argParser.addFlag( + 'collect', + defaultsTo: false, + negatable: false, + help: 'If this flag is set, then collect and parse header files from ' + 'Chromium and Android instead of reading pre-parsed data from ' + '"key_data.json", and then update "key_data.json" with the fresh data.', + ); + argParser.addFlag( + 'help', + defaultsTo: false, + negatable: false, + help: 'Print help for this command.', + ); + + final ArgResults parsedArguments = argParser.parse(rawArguments); + + if (parsedArguments['help']) { + print(argParser.usage); + exit(0); + } + + KeyData data; + if (parsedArguments['collect']) { + String hidCodes; + if (parsedArguments['chromium-hid-codes'] == null) { + hidCodes = await getChromiumConversions(); + } else { + hidCodes = File(parsedArguments['chromium-hid-codes']).readAsStringSync(); + } + + String androidKeyCodes; + if (parsedArguments['android-keycodes'] == null) { + androidKeyCodes = await getAndroidKeyCodes(); + } else { + androidKeyCodes = File(parsedArguments['android-keycodes']).readAsStringSync(); + } + + String androidScanCodes; + if (parsedArguments['android-scancodes'] == null) { + androidScanCodes = await getAndroidScanCodes(); + } else { + androidScanCodes = File(parsedArguments['android-scancodes']).readAsStringSync(); + } + + final String androidToDomKey = File(parsedArguments['android-domkey']).readAsStringSync(); + data = KeyData(hidCodes, androidScanCodes, androidKeyCodes, androidToDomKey); + + const JsonEncoder encoder = JsonEncoder.withIndent(' '); + File(parsedArguments['data']).writeAsStringSync(encoder.convert(data.toJson())); + } else { + data = KeyData.fromJson(json.decode(await File(parsedArguments['data']).readAsString())); + } + + final File codeFile = File(parsedArguments['code']); + if (!codeFile.existsSync()) { + codeFile.createSync(recursive: true); + } + + final File mapsFile = File(parsedArguments['maps']); + if (!mapsFile.existsSync()) { + mapsFile.createSync(recursive: true); + } + + final CodeGenerator generator = CodeGenerator(data); + await codeFile.writeAsString(generator.generateKeyboardKeys()); + await mapsFile.writeAsString(generator.generateKeyboardMaps()); +} diff --git a/dev/tools/gen_keycodes/data/key_data.json b/dev/tools/gen_keycodes/data/key_data.json new file mode 100644 index 00000000000..585295f0eba --- /dev/null +++ b/dev/tools/gen_keycodes/data/key_data.json @@ -0,0 +1,5384 @@ +{ + "none": { + "names": { + "domkey": "None", + "android": [ + "UNKNOWN" + ], + "english": "None", + "chromium": "none" + }, + "scanCodes": { + "android": null, + "usb": 0, + "linux": null, + "xkb": null, + "windows": null, + "macos": null + }, + "keyCodes": { + "android": [ + 0 + ] + } + }, + "hyper": { + "names": { + "domkey": "Hyper", + "android": null, + "english": "Hyper", + "chromium": "hyper" + }, + "scanCodes": { + "android": null, + "usb": 16, + "linux": null, + "xkb": null, + "windows": null, + "macos": null + }, + "keyCodes": { + "android": null + } + }, + "superKey": { + "names": { + "domkey": "Super", + "android": null, + "english": "Super Key", + "chromium": "super" + }, + "scanCodes": { + "android": null, + "usb": 17, + "linux": null, + "xkb": null, + "windows": null, + "macos": null + }, + "keyCodes": { + "android": null + } + }, + "fn": { + "names": { + "domkey": "Fn", + "android": [ + "FUNCTION" + ], + "english": "Fn", + "chromium": "fn" + }, + "scanCodes": { + "android": [ + 464 + ], + "usb": 18, + "linux": null, + "xkb": null, + "windows": null, + "macos": null + }, + "keyCodes": { + "android": [ + 119 + ] + } + }, + "fnLock": { + "names": { + "domkey": "FnLock", + "android": null, + "english": "Fn Lock", + "chromium": "fnLock" + }, + "scanCodes": { + "android": null, + "usb": 19, + "linux": null, + "xkb": null, + "windows": null, + "macos": null + }, + "keyCodes": { + "android": null + } + }, + "suspend": { + "names": { + "domkey": "Suspend", + "android": [ + "SUSPEND" + ], + "english": "Suspend", + "chromium": "suspend" + }, + "scanCodes": { + "android": [ + 205 + ], + "usb": 20, + "linux": null, + "xkb": null, + "windows": null, + "macos": null + }, + "keyCodes": { + "android": null + } + }, + "resume": { + "names": { + "domkey": "Resume", + "android": null, + "english": "Resume", + "chromium": "resume" + }, + "scanCodes": { + "android": null, + "usb": 21, + "linux": null, + "xkb": null, + "windows": null, + "macos": null + }, + "keyCodes": { + "android": null + } + }, + "turbo": { + "names": { + "domkey": "Turbo", + "android": null, + "english": "Turbo", + "chromium": "turbo" + }, + "scanCodes": { + "android": null, + "usb": 22, + "linux": null, + "xkb": null, + "windows": null, + "macos": null + }, + "keyCodes": { + "android": null + } + }, + "launchAssistant": { + "names": { + "domkey": "LaunchAssistant", + "android": [ + "ASSIST" + ], + "english": "Launch Assistant", + "chromium": "launchAssistant" + }, + "scanCodes": { + "android": null, + "usb": 23, + "linux": 583, + "xkb": 591, + "windows": null, + "macos": null + }, + "keyCodes": { + "android": [ + 219 + ] + } + }, + "sleep": { + "names": { + "domkey": "Sleep", + "android": [ + "SLEEP" + ], + "english": "Sleep", + "chromium": "sleep" + }, + "scanCodes": { + "android": [ + 142 + ], + "usb": 65666, + "linux": 142, + "xkb": 150, + "windows": 57439, + "macos": null + }, + "keyCodes": { + "android": [ + 223 + ] + } + }, + "wakeUp": { + "names": { + "domkey": "WakeUp", + "android": [ + "WAKEUP" + ], + "english": "Wake Up", + "chromium": "wakeUp" + }, + "scanCodes": { + "android": [ + 143 + ], + "usb": 65667, + "linux": 143, + "xkb": 151, + "windows": 57443, + "macos": null + }, + "keyCodes": { + "android": [ + 224 + ] + } + }, + "usbReserved": { + "names": { + "domkey": null, + "android": null, + "english": "Usb Reserved", + "chromium": "usbReserved" + }, + "scanCodes": { + "android": null, + "usb": 458752, + "linux": null, + "xkb": null, + "windows": null, + "macos": null + }, + "keyCodes": { + "android": null + } + }, + "usbErrorRollOver": { + "names": { + "domkey": null, + "android": null, + "english": "Usb Error Roll Over", + "chromium": "usbErrorRollOver" + }, + "scanCodes": { + "android": null, + "usb": 458753, + "linux": null, + "xkb": null, + "windows": 255, + "macos": null + }, + "keyCodes": { + "android": null + } + }, + "usbPostFail": { + "names": { + "domkey": null, + "android": null, + "english": "Usb Post Fail", + "chromium": "usbPostFail" + }, + "scanCodes": { + "android": null, + "usb": 458754, + "linux": null, + "xkb": null, + "windows": 252, + "macos": null + }, + "keyCodes": { + "android": null + } + }, + "usbErrorUndefined": { + "names": { + "domkey": null, + "android": null, + "english": "Usb Error Undefined", + "chromium": "usbErrorUndefined" + }, + "scanCodes": { + "android": null, + "usb": 458755, + "linux": null, + "xkb": null, + "windows": null, + "macos": null + }, + "keyCodes": { + "android": null + } + }, + "keyA": { + "names": { + "domkey": "KeyA", + "android": [ + "A" + ], + "english": "Key A", + "chromium": "usA" + }, + "scanCodes": { + "android": [ + 30 + ], + "usb": 458756, + "linux": 30, + "xkb": 38, + "windows": 30, + "macos": 0 + }, + "keyCodes": { + "android": [ + 29 + ] + } + }, + "keyB": { + "names": { + "domkey": "KeyB", + "android": [ + "B" + ], + "english": "Key B", + "chromium": "usB" + }, + "scanCodes": { + "android": [ + 48 + ], + "usb": 458757, + "linux": 48, + "xkb": 56, + "windows": 48, + "macos": 11 + }, + "keyCodes": { + "android": [ + 30 + ] + } + }, + "keyC": { + "names": { + "domkey": "KeyC", + "android": [ + "C" + ], + "english": "Key C", + "chromium": "usC" + }, + "scanCodes": { + "android": [ + 46 + ], + "usb": 458758, + "linux": 46, + "xkb": 54, + "windows": 46, + "macos": 8 + }, + "keyCodes": { + "android": [ + 31 + ] + } + }, + "keyD": { + "names": { + "domkey": "KeyD", + "android": [ + "D" + ], + "english": "Key D", + "chromium": "usD" + }, + "scanCodes": { + "android": [ + 32 + ], + "usb": 458759, + "linux": 32, + "xkb": 40, + "windows": 32, + "macos": 2 + }, + "keyCodes": { + "android": [ + 32 + ] + } + }, + "keyE": { + "names": { + "domkey": "KeyE", + "android": [ + "E" + ], + "english": "Key E", + "chromium": "usE" + }, + "scanCodes": { + "android": [ + 18 + ], + "usb": 458760, + "linux": 18, + "xkb": 26, + "windows": 18, + "macos": 14 + }, + "keyCodes": { + "android": [ + 33 + ] + } + }, + "keyF": { + "names": { + "domkey": "KeyF", + "android": [ + "F" + ], + "english": "Key F", + "chromium": "usF" + }, + "scanCodes": { + "android": [ + 33 + ], + "usb": 458761, + "linux": 33, + "xkb": 41, + "windows": 33, + "macos": 3 + }, + "keyCodes": { + "android": [ + 34 + ] + } + }, + "keyG": { + "names": { + "domkey": "KeyG", + "android": [ + "G" + ], + "english": "Key G", + "chromium": "usG" + }, + "scanCodes": { + "android": [ + 34 + ], + "usb": 458762, + "linux": 34, + "xkb": 42, + "windows": 34, + "macos": 5 + }, + "keyCodes": { + "android": [ + 35 + ] + } + }, + "keyH": { + "names": { + "domkey": "KeyH", + "android": [ + "H" + ], + "english": "Key H", + "chromium": "usH" + }, + "scanCodes": { + "android": [ + 35 + ], + "usb": 458763, + "linux": 35, + "xkb": 43, + "windows": 35, + "macos": 4 + }, + "keyCodes": { + "android": [ + 36 + ] + } + }, + "keyI": { + "names": { + "domkey": "KeyI", + "android": [ + "I" + ], + "english": "Key I", + "chromium": "usI" + }, + "scanCodes": { + "android": [ + 23 + ], + "usb": 458764, + "linux": 23, + "xkb": 31, + "windows": 23, + "macos": 34 + }, + "keyCodes": { + "android": [ + 37 + ] + } + }, + "keyJ": { + "names": { + "domkey": "KeyJ", + "android": [ + "J" + ], + "english": "Key J", + "chromium": "usJ" + }, + "scanCodes": { + "android": [ + 36 + ], + "usb": 458765, + "linux": 36, + "xkb": 44, + "windows": 36, + "macos": 38 + }, + "keyCodes": { + "android": [ + 38 + ] + } + }, + "keyK": { + "names": { + "domkey": "KeyK", + "android": [ + "K" + ], + "english": "Key K", + "chromium": "usK" + }, + "scanCodes": { + "android": [ + 37 + ], + "usb": 458766, + "linux": 37, + "xkb": 45, + "windows": 37, + "macos": 40 + }, + "keyCodes": { + "android": [ + 39 + ] + } + }, + "keyL": { + "names": { + "domkey": "KeyL", + "android": [ + "L" + ], + "english": "Key L", + "chromium": "usL" + }, + "scanCodes": { + "android": [ + 38 + ], + "usb": 458767, + "linux": 38, + "xkb": 46, + "windows": 38, + "macos": 37 + }, + "keyCodes": { + "android": [ + 40 + ] + } + }, + "keyM": { + "names": { + "domkey": "KeyM", + "android": [ + "M" + ], + "english": "Key M", + "chromium": "usM" + }, + "scanCodes": { + "android": [ + 50 + ], + "usb": 458768, + "linux": 50, + "xkb": 58, + "windows": 50, + "macos": 46 + }, + "keyCodes": { + "android": [ + 41 + ] + } + }, + "keyN": { + "names": { + "domkey": "KeyN", + "android": [ + "N" + ], + "english": "Key N", + "chromium": "usN" + }, + "scanCodes": { + "android": [ + 49 + ], + "usb": 458769, + "linux": 49, + "xkb": 57, + "windows": 49, + "macos": 45 + }, + "keyCodes": { + "android": [ + 42 + ] + } + }, + "keyO": { + "names": { + "domkey": "KeyO", + "android": [ + "O" + ], + "english": "Key O", + "chromium": "usO" + }, + "scanCodes": { + "android": [ + 24 + ], + "usb": 458770, + "linux": 24, + "xkb": 32, + "windows": 24, + "macos": 31 + }, + "keyCodes": { + "android": [ + 43 + ] + } + }, + "keyP": { + "names": { + "domkey": "KeyP", + "android": [ + "P" + ], + "english": "Key P", + "chromium": "usP" + }, + "scanCodes": { + "android": [ + 25 + ], + "usb": 458771, + "linux": 25, + "xkb": 33, + "windows": 25, + "macos": 35 + }, + "keyCodes": { + "android": [ + 44 + ] + } + }, + "keyQ": { + "names": { + "domkey": "KeyQ", + "android": [ + "Q" + ], + "english": "Key Q", + "chromium": "usQ" + }, + "scanCodes": { + "android": [ + 16 + ], + "usb": 458772, + "linux": 16, + "xkb": 24, + "windows": 16, + "macos": 12 + }, + "keyCodes": { + "android": [ + 45 + ] + } + }, + "keyR": { + "names": { + "domkey": "KeyR", + "android": [ + "R" + ], + "english": "Key R", + "chromium": "usR" + }, + "scanCodes": { + "android": [ + 19 + ], + "usb": 458773, + "linux": 19, + "xkb": 27, + "windows": 19, + "macos": 15 + }, + "keyCodes": { + "android": [ + 46 + ] + } + }, + "keyS": { + "names": { + "domkey": "KeyS", + "android": [ + "S" + ], + "english": "Key S", + "chromium": "usS" + }, + "scanCodes": { + "android": [ + 31 + ], + "usb": 458774, + "linux": 31, + "xkb": 39, + "windows": 31, + "macos": 1 + }, + "keyCodes": { + "android": [ + 47 + ] + } + }, + "keyT": { + "names": { + "domkey": "KeyT", + "android": [ + "T" + ], + "english": "Key T", + "chromium": "usT" + }, + "scanCodes": { + "android": [ + 20 + ], + "usb": 458775, + "linux": 20, + "xkb": 28, + "windows": 20, + "macos": 17 + }, + "keyCodes": { + "android": [ + 48 + ] + } + }, + "keyU": { + "names": { + "domkey": "KeyU", + "android": [ + "U" + ], + "english": "Key U", + "chromium": "usU" + }, + "scanCodes": { + "android": [ + 22 + ], + "usb": 458776, + "linux": 22, + "xkb": 30, + "windows": 22, + "macos": 32 + }, + "keyCodes": { + "android": [ + 49 + ] + } + }, + "keyV": { + "names": { + "domkey": "KeyV", + "android": [ + "V" + ], + "english": "Key V", + "chromium": "usV" + }, + "scanCodes": { + "android": [ + 47 + ], + "usb": 458777, + "linux": 47, + "xkb": 55, + "windows": 47, + "macos": 9 + }, + "keyCodes": { + "android": [ + 50 + ] + } + }, + "keyW": { + "names": { + "domkey": "KeyW", + "android": [ + "W" + ], + "english": "Key W", + "chromium": "usW" + }, + "scanCodes": { + "android": [ + 17 + ], + "usb": 458778, + "linux": 17, + "xkb": 25, + "windows": 17, + "macos": 13 + }, + "keyCodes": { + "android": [ + 51 + ] + } + }, + "keyX": { + "names": { + "domkey": "KeyX", + "android": [ + "X" + ], + "english": "Key X", + "chromium": "usX" + }, + "scanCodes": { + "android": [ + 45 + ], + "usb": 458779, + "linux": 45, + "xkb": 53, + "windows": 45, + "macos": 7 + }, + "keyCodes": { + "android": [ + 52 + ] + } + }, + "keyY": { + "names": { + "domkey": "KeyY", + "android": [ + "Y" + ], + "english": "Key Y", + "chromium": "usY" + }, + "scanCodes": { + "android": [ + 21 + ], + "usb": 458780, + "linux": 21, + "xkb": 29, + "windows": 21, + "macos": 16 + }, + "keyCodes": { + "android": [ + 53 + ] + } + }, + "keyZ": { + "names": { + "domkey": "KeyZ", + "android": [ + "Z" + ], + "english": "Key Z", + "chromium": "usZ" + }, + "scanCodes": { + "android": [ + 44 + ], + "usb": 458781, + "linux": 44, + "xkb": 52, + "windows": 44, + "macos": 6 + }, + "keyCodes": { + "android": [ + 54 + ] + } + }, + "digit1": { + "names": { + "domkey": "Digit1", + "android": [ + "1" + ], + "english": "Digit 1", + "chromium": "digit1" + }, + "scanCodes": { + "android": [ + 2 + ], + "usb": 458782, + "linux": 2, + "xkb": 10, + "windows": 2, + "macos": 18 + }, + "keyCodes": { + "android": [ + 8 + ] + } + }, + "digit2": { + "names": { + "domkey": "Digit2", + "android": [ + "2" + ], + "english": "Digit 2", + "chromium": "digit2" + }, + "scanCodes": { + "android": [ + 3 + ], + "usb": 458783, + "linux": 3, + "xkb": 11, + "windows": 3, + "macos": 19 + }, + "keyCodes": { + "android": [ + 9 + ] + } + }, + "digit3": { + "names": { + "domkey": "Digit3", + "android": [ + "3" + ], + "english": "Digit 3", + "chromium": "digit3" + }, + "scanCodes": { + "android": [ + 4 + ], + "usb": 458784, + "linux": 4, + "xkb": 12, + "windows": 4, + "macos": 20 + }, + "keyCodes": { + "android": [ + 10 + ] + } + }, + "digit4": { + "names": { + "domkey": "Digit4", + "android": [ + "4" + ], + "english": "Digit 4", + "chromium": "digit4" + }, + "scanCodes": { + "android": [ + 5 + ], + "usb": 458785, + "linux": 5, + "xkb": 13, + "windows": 5, + "macos": 21 + }, + "keyCodes": { + "android": [ + 11 + ] + } + }, + "digit5": { + "names": { + "domkey": "Digit5", + "android": [ + "5" + ], + "english": "Digit 5", + "chromium": "digit5" + }, + "scanCodes": { + "android": [ + 6 + ], + "usb": 458786, + "linux": 6, + "xkb": 14, + "windows": 6, + "macos": 23 + }, + "keyCodes": { + "android": [ + 12 + ] + } + }, + "digit6": { + "names": { + "domkey": "Digit6", + "android": [ + "6" + ], + "english": "Digit 6", + "chromium": "digit6" + }, + "scanCodes": { + "android": [ + 7 + ], + "usb": 458787, + "linux": 7, + "xkb": 15, + "windows": 7, + "macos": 22 + }, + "keyCodes": { + "android": [ + 13 + ] + } + }, + "digit7": { + "names": { + "domkey": "Digit7", + "android": [ + "7" + ], + "english": "Digit 7", + "chromium": "digit7" + }, + "scanCodes": { + "android": [ + 8 + ], + "usb": 458788, + "linux": 8, + "xkb": 16, + "windows": 8, + "macos": 26 + }, + "keyCodes": { + "android": [ + 14 + ] + } + }, + "digit8": { + "names": { + "domkey": "Digit8", + "android": [ + "8" + ], + "english": "Digit 8", + "chromium": "digit8" + }, + "scanCodes": { + "android": [ + 9 + ], + "usb": 458789, + "linux": 9, + "xkb": 17, + "windows": 9, + "macos": 28 + }, + "keyCodes": { + "android": [ + 15 + ] + } + }, + "digit9": { + "names": { + "domkey": "Digit9", + "android": [ + "9" + ], + "english": "Digit 9", + "chromium": "digit9" + }, + "scanCodes": { + "android": [ + 10 + ], + "usb": 458790, + "linux": 10, + "xkb": 18, + "windows": 10, + "macos": 25 + }, + "keyCodes": { + "android": [ + 16 + ] + } + }, + "digit0": { + "names": { + "domkey": "Digit0", + "android": [ + "0" + ], + "english": "Digit 0", + "chromium": "digit0" + }, + "scanCodes": { + "android": [ + 11 + ], + "usb": 458791, + "linux": 11, + "xkb": 19, + "windows": 11, + "macos": 29 + }, + "keyCodes": { + "android": [ + 7 + ] + } + }, + "enter": { + "names": { + "domkey": "Enter", + "android": [ + "ENTER" + ], + "english": "Enter", + "chromium": "enter" + }, + "scanCodes": { + "android": [ + 28 + ], + "usb": 458792, + "linux": 28, + "xkb": 36, + "windows": 28, + "macos": 36 + }, + "keyCodes": { + "android": [ + 66 + ] + } + }, + "escape": { + "names": { + "domkey": "Escape", + "android": [ + "ESCAPE" + ], + "english": "Escape", + "chromium": "escape" + }, + "scanCodes": { + "android": [ + 1 + ], + "usb": 458793, + "linux": 1, + "xkb": 9, + "windows": 1, + "macos": 53 + }, + "keyCodes": { + "android": [ + 111 + ] + } + }, + "backspace": { + "names": { + "domkey": "Backspace", + "android": [ + "DEL" + ], + "english": "Backspace", + "chromium": "backspace" + }, + "scanCodes": { + "android": [ + 14 + ], + "usb": 458794, + "linux": 14, + "xkb": 22, + "windows": 14, + "macos": 51 + }, + "keyCodes": { + "android": [ + 67 + ] + } + }, + "tab": { + "names": { + "domkey": "Tab", + "android": [ + "TAB" + ], + "english": "Tab", + "chromium": "tab" + }, + "scanCodes": { + "android": [ + 15 + ], + "usb": 458795, + "linux": 15, + "xkb": 23, + "windows": 15, + "macos": 48 + }, + "keyCodes": { + "android": [ + 61 + ] + } + }, + "space": { + "names": { + "domkey": "Space", + "android": [ + "SPACE" + ], + "english": "Space", + "chromium": "space" + }, + "scanCodes": { + "android": [ + 57 + ], + "usb": 458796, + "linux": 57, + "xkb": 65, + "windows": 57, + "macos": 49 + }, + "keyCodes": { + "android": [ + 62 + ] + } + }, + "minus": { + "names": { + "domkey": "Minus", + "android": [ + "MINUS" + ], + "english": "Minus", + "chromium": "minus" + }, + "scanCodes": { + "android": [ + 12 + ], + "usb": 458797, + "linux": 12, + "xkb": 20, + "windows": 12, + "macos": 27 + }, + "keyCodes": { + "android": [ + 69 + ] + } + }, + "equal": { + "names": { + "domkey": "Equal", + "android": [ + "EQUALS" + ], + "english": "Equal", + "chromium": "equal" + }, + "scanCodes": { + "android": [ + 13 + ], + "usb": 458798, + "linux": 13, + "xkb": 21, + "windows": 13, + "macos": 24 + }, + "keyCodes": { + "android": [ + 70 + ] + } + }, + "bracketLeft": { + "names": { + "domkey": "BracketLeft", + "android": [ + "LEFT_BRACKET" + ], + "english": "Bracket Left", + "chromium": "bracketLeft" + }, + "scanCodes": { + "android": [ + 26 + ], + "usb": 458799, + "linux": 26, + "xkb": 34, + "windows": 26, + "macos": 33 + }, + "keyCodes": { + "android": [ + 71 + ] + } + }, + "bracketRight": { + "names": { + "domkey": "BracketRight", + "android": [ + "RIGHT_BRACKET" + ], + "english": "Bracket Right", + "chromium": "bracketRight" + }, + "scanCodes": { + "android": [ + 27 + ], + "usb": 458800, + "linux": 27, + "xkb": 35, + "windows": 27, + "macos": 30 + }, + "keyCodes": { + "android": [ + 72 + ] + } + }, + "backslash": { + "names": { + "domkey": "Backslash", + "android": [ + "BACKSLASH" + ], + "english": "Backslash", + "chromium": "backslash" + }, + "scanCodes": { + "android": [ + 43, + 86 + ], + "usb": 458801, + "linux": 43, + "xkb": 51, + "windows": 43, + "macos": 42 + }, + "keyCodes": { + "android": [ + 73 + ] + } + }, + "semicolon": { + "names": { + "domkey": "Semicolon", + "android": [ + "SEMICOLON" + ], + "english": "Semicolon", + "chromium": "semicolon" + }, + "scanCodes": { + "android": [ + 39 + ], + "usb": 458803, + "linux": 39, + "xkb": 47, + "windows": 39, + "macos": 41 + }, + "keyCodes": { + "android": [ + 74 + ] + } + }, + "quote": { + "names": { + "domkey": "Quote", + "android": [ + "APOSTROPHE" + ], + "english": "Quote", + "chromium": "quote" + }, + "scanCodes": { + "android": [ + 40 + ], + "usb": 458804, + "linux": 40, + "xkb": 48, + "windows": 40, + "macos": 39 + }, + "keyCodes": { + "android": [ + 75 + ] + } + }, + "backquote": { + "names": { + "domkey": "Backquote", + "android": [ + "GRAVE" + ], + "english": "Backquote", + "chromium": "backquote" + }, + "scanCodes": { + "android": [ + 41 + ], + "usb": 458805, + "linux": 41, + "xkb": 49, + "windows": 41, + "macos": 50 + }, + "keyCodes": { + "android": [ + 68 + ] + } + }, + "comma": { + "names": { + "domkey": "Comma", + "android": [ + "COMMA" + ], + "english": "Comma", + "chromium": "comma" + }, + "scanCodes": { + "android": [ + 51 + ], + "usb": 458806, + "linux": 51, + "xkb": 59, + "windows": 51, + "macos": 43 + }, + "keyCodes": { + "android": [ + 55 + ] + } + }, + "period": { + "names": { + "domkey": "Period", + "android": [ + "PERIOD" + ], + "english": "Period", + "chromium": "period" + }, + "scanCodes": { + "android": [ + 52 + ], + "usb": 458807, + "linux": 52, + "xkb": 60, + "windows": 52, + "macos": 47 + }, + "keyCodes": { + "android": [ + 56 + ] + } + }, + "slash": { + "names": { + "domkey": "Slash", + "android": [ + "SLASH" + ], + "english": "Slash", + "chromium": "slash" + }, + "scanCodes": { + "android": [ + 53 + ], + "usb": 458808, + "linux": 53, + "xkb": 61, + "windows": 53, + "macos": 44 + }, + "keyCodes": { + "android": [ + 76 + ] + } + }, + "capsLock": { + "names": { + "domkey": "CapsLock", + "android": [ + "CAPS_LOCK" + ], + "english": "Caps Lock", + "chromium": "capsLock" + }, + "scanCodes": { + "android": [ + 58 + ], + "usb": 458809, + "linux": 58, + "xkb": 66, + "windows": 58, + "macos": 57 + }, + "keyCodes": { + "android": [ + 115 + ] + } + }, + "f1": { + "names": { + "domkey": "F1", + "android": [ + "F1" + ], + "english": "F1", + "chromium": "f1" + }, + "scanCodes": { + "android": [ + 59 + ], + "usb": 458810, + "linux": 59, + "xkb": 67, + "windows": 59, + "macos": 122 + }, + "keyCodes": { + "android": [ + 131 + ] + } + }, + "f2": { + "names": { + "domkey": "F2", + "android": [ + "F2" + ], + "english": "F2", + "chromium": "f2" + }, + "scanCodes": { + "android": [ + 60 + ], + "usb": 458811, + "linux": 60, + "xkb": 68, + "windows": 60, + "macos": 120 + }, + "keyCodes": { + "android": [ + 132 + ] + } + }, + "f3": { + "names": { + "domkey": "F3", + "android": [ + "F3" + ], + "english": "F3", + "chromium": "f3" + }, + "scanCodes": { + "android": [ + 61 + ], + "usb": 458812, + "linux": 61, + "xkb": 69, + "windows": 61, + "macos": 99 + }, + "keyCodes": { + "android": [ + 133 + ] + } + }, + "f4": { + "names": { + "domkey": "F4", + "android": [ + "F4" + ], + "english": "F4", + "chromium": "f4" + }, + "scanCodes": { + "android": [ + 62 + ], + "usb": 458813, + "linux": 62, + "xkb": 70, + "windows": 62, + "macos": 118 + }, + "keyCodes": { + "android": [ + 134 + ] + } + }, + "f5": { + "names": { + "domkey": "F5", + "android": [ + "F5" + ], + "english": "F5", + "chromium": "f5" + }, + "scanCodes": { + "android": [ + 63 + ], + "usb": 458814, + "linux": 63, + "xkb": 71, + "windows": 63, + "macos": 96 + }, + "keyCodes": { + "android": [ + 135 + ] + } + }, + "f6": { + "names": { + "domkey": "F6", + "android": [ + "F6" + ], + "english": "F6", + "chromium": "f6" + }, + "scanCodes": { + "android": [ + 64 + ], + "usb": 458815, + "linux": 64, + "xkb": 72, + "windows": 64, + "macos": 97 + }, + "keyCodes": { + "android": [ + 136 + ] + } + }, + "f7": { + "names": { + "domkey": "F7", + "android": [ + "F7" + ], + "english": "F7", + "chromium": "f7" + }, + "scanCodes": { + "android": [ + 65 + ], + "usb": 458816, + "linux": 65, + "xkb": 73, + "windows": 65, + "macos": 98 + }, + "keyCodes": { + "android": [ + 137 + ] + } + }, + "f8": { + "names": { + "domkey": "F8", + "android": [ + "F8" + ], + "english": "F8", + "chromium": "f8" + }, + "scanCodes": { + "android": [ + 66 + ], + "usb": 458817, + "linux": 66, + "xkb": 74, + "windows": 66, + "macos": 100 + }, + "keyCodes": { + "android": [ + 138 + ] + } + }, + "f9": { + "names": { + "domkey": "F9", + "android": [ + "F9" + ], + "english": "F9", + "chromium": "f9" + }, + "scanCodes": { + "android": [ + 67 + ], + "usb": 458818, + "linux": 67, + "xkb": 75, + "windows": 67, + "macos": 101 + }, + "keyCodes": { + "android": [ + 139 + ] + } + }, + "f10": { + "names": { + "domkey": "F10", + "android": [ + "F10" + ], + "english": "F10", + "chromium": "f10" + }, + "scanCodes": { + "android": [ + 68 + ], + "usb": 458819, + "linux": 68, + "xkb": 76, + "windows": 68, + "macos": 109 + }, + "keyCodes": { + "android": [ + 140 + ] + } + }, + "f11": { + "names": { + "domkey": "F11", + "android": [ + "F11" + ], + "english": "F11", + "chromium": "f11" + }, + "scanCodes": { + "android": [ + 87 + ], + "usb": 458820, + "linux": 87, + "xkb": 95, + "windows": 87, + "macos": 103 + }, + "keyCodes": { + "android": [ + 141 + ] + } + }, + "f12": { + "names": { + "domkey": "F12", + "android": [ + "F12" + ], + "english": "F12", + "chromium": "f12" + }, + "scanCodes": { + "android": [ + 88 + ], + "usb": 458821, + "linux": 88, + "xkb": 96, + "windows": 88, + "macos": 111 + }, + "keyCodes": { + "android": [ + 142 + ] + } + }, + "printScreen": { + "names": { + "domkey": "PrintScreen", + "android": [ + "SYSRQ" + ], + "english": "Print Screen", + "chromium": "printScreen" + }, + "scanCodes": { + "android": [ + 99 + ], + "usb": 458822, + "linux": 99, + "xkb": 107, + "windows": 57399, + "macos": null + }, + "keyCodes": { + "android": [ + 120 + ] + } + }, + "scrollLock": { + "names": { + "domkey": "ScrollLock", + "android": [ + "SCROLL_LOCK" + ], + "english": "Scroll Lock", + "chromium": "scrollLock" + }, + "scanCodes": { + "android": [ + 70 + ], + "usb": 458823, + "linux": 70, + "xkb": 78, + "windows": 70, + "macos": null + }, + "keyCodes": { + "android": [ + 116 + ] + } + }, + "pause": { + "names": { + "domkey": "Pause", + "android": [ + "BREAK" + ], + "english": "Pause", + "chromium": "pause" + }, + "scanCodes": { + "android": [ + 119, + 411 + ], + "usb": 458824, + "linux": 119, + "xkb": 127, + "windows": 69, + "macos": null + }, + "keyCodes": { + "android": [ + 121 + ] + } + }, + "insert": { + "names": { + "domkey": "Insert", + "android": [ + "INSERT" + ], + "english": "Insert", + "chromium": "insert" + }, + "scanCodes": { + "android": [ + 110 + ], + "usb": 458825, + "linux": 110, + "xkb": 118, + "windows": 57426, + "macos": 114 + }, + "keyCodes": { + "android": [ + 124 + ] + } + }, + "home": { + "names": { + "domkey": "Home", + "android": [ + "MOVE_HOME" + ], + "english": "Home", + "chromium": "home" + }, + "scanCodes": { + "android": [ + 102 + ], + "usb": 458826, + "linux": 102, + "xkb": 110, + "windows": 57415, + "macos": 115 + }, + "keyCodes": { + "android": [ + 122 + ] + } + }, + "pageUp": { + "names": { + "domkey": "PageUp", + "android": [ + "PAGE_UP" + ], + "english": "Page Up", + "chromium": "pageUp" + }, + "scanCodes": { + "android": [ + 104, + 177 + ], + "usb": 458827, + "linux": 104, + "xkb": 112, + "windows": 57417, + "macos": 116 + }, + "keyCodes": { + "android": [ + 92 + ] + } + }, + "delete": { + "names": { + "domkey": "Delete", + "android": [ + "FORWARD_DEL" + ], + "english": "Delete", + "chromium": "del" + }, + "scanCodes": { + "android": [ + 111 + ], + "usb": 458828, + "linux": 111, + "xkb": 119, + "windows": 57427, + "macos": 117 + }, + "keyCodes": { + "android": [ + 112 + ] + } + }, + "end": { + "names": { + "domkey": "End", + "android": [ + "MOVE_END" + ], + "english": "End", + "chromium": "end" + }, + "scanCodes": { + "android": [ + 107 + ], + "usb": 458829, + "linux": 107, + "xkb": 115, + "windows": 57423, + "macos": 119 + }, + "keyCodes": { + "android": [ + 123 + ] + } + }, + "pageDown": { + "names": { + "domkey": "PageDown", + "android": [ + "PAGE_DOWN" + ], + "english": "Page Down", + "chromium": "pageDown" + }, + "scanCodes": { + "android": [ + 109, + 178 + ], + "usb": 458830, + "linux": 109, + "xkb": 117, + "windows": 57425, + "macos": 121 + }, + "keyCodes": { + "android": [ + 93 + ] + } + }, + "arrowRight": { + "names": { + "domkey": "ArrowRight", + "android": [ + "DPAD_RIGHT" + ], + "english": "Arrow Right", + "chromium": "arrowRight" + }, + "scanCodes": { + "android": [ + 106 + ], + "usb": 458831, + "linux": 106, + "xkb": 114, + "windows": 57421, + "macos": 124 + }, + "keyCodes": { + "android": [ + 22 + ] + } + }, + "arrowLeft": { + "names": { + "domkey": "ArrowLeft", + "android": [ + "DPAD_LEFT" + ], + "english": "Arrow Left", + "chromium": "arrowLeft" + }, + "scanCodes": { + "android": [ + 105 + ], + "usb": 458832, + "linux": 105, + "xkb": 113, + "windows": 57419, + "macos": 123 + }, + "keyCodes": { + "android": [ + 21 + ] + } + }, + "arrowDown": { + "names": { + "domkey": "ArrowDown", + "android": [ + "DPAD_DOWN" + ], + "english": "Arrow Down", + "chromium": "arrowDown" + }, + "scanCodes": { + "android": [ + 108 + ], + "usb": 458833, + "linux": 108, + "xkb": 116, + "windows": 57424, + "macos": 125 + }, + "keyCodes": { + "android": [ + 20 + ] + } + }, + "arrowUp": { + "names": { + "domkey": "ArrowUp", + "android": [ + "DPAD_UP" + ], + "english": "Arrow Up", + "chromium": "arrowUp" + }, + "scanCodes": { + "android": [ + 103 + ], + "usb": 458834, + "linux": 103, + "xkb": 111, + "windows": 57416, + "macos": 126 + }, + "keyCodes": { + "android": [ + 19 + ] + } + }, + "numLock": { + "names": { + "domkey": "NumLock", + "android": [ + "NUM_LOCK" + ], + "english": "Num Lock", + "chromium": "numLock" + }, + "scanCodes": { + "android": [ + 69 + ], + "usb": 458835, + "linux": 69, + "xkb": 77, + "windows": 57413, + "macos": 71 + }, + "keyCodes": { + "android": [ + 143 + ] + } + }, + "numpadDivide": { + "names": { + "domkey": "NumpadDivide", + "android": [ + "NUMPAD_DIVIDE" + ], + "english": "Numpad Divide", + "chromium": "numpadDivide" + }, + "scanCodes": { + "android": [ + 98 + ], + "usb": 458836, + "linux": 98, + "xkb": 106, + "windows": 57397, + "macos": 75 + }, + "keyCodes": { + "android": [ + 154 + ] + } + }, + "numpadMultiply": { + "names": { + "domkey": "NumpadMultiply", + "android": [ + "NUMPAD_MULTIPLY" + ], + "english": "Numpad Multiply", + "chromium": "numpadMultiply" + }, + "scanCodes": { + "android": [ + 55 + ], + "usb": 458837, + "linux": 55, + "xkb": 63, + "windows": 55, + "macos": 67 + }, + "keyCodes": { + "android": [ + 155 + ] + } + }, + "numpadSubtract": { + "names": { + "domkey": "NumpadSubtract", + "android": [ + "NUMPAD_SUBTRACT" + ], + "english": "Numpad Subtract", + "chromium": "numpadSubtract" + }, + "scanCodes": { + "android": [ + 74 + ], + "usb": 458838, + "linux": 74, + "xkb": 82, + "windows": 74, + "macos": 78 + }, + "keyCodes": { + "android": [ + 156 + ] + } + }, + "numpadAdd": { + "names": { + "domkey": "NumpadAdd", + "android": [ + "NUMPAD_ADD" + ], + "english": "Numpad Add", + "chromium": "numpadAdd" + }, + "scanCodes": { + "android": [ + 78 + ], + "usb": 458839, + "linux": 78, + "xkb": 86, + "windows": 78, + "macos": 69 + }, + "keyCodes": { + "android": [ + 157 + ] + } + }, + "numpadEnter": { + "names": { + "domkey": "NumpadEnter", + "android": [ + "NUMPAD_ENTER" + ], + "english": "Numpad Enter", + "chromium": "numpadEnter" + }, + "scanCodes": { + "android": [ + 96 + ], + "usb": 458840, + "linux": 96, + "xkb": 104, + "windows": 57372, + "macos": 76 + }, + "keyCodes": { + "android": [ + 160 + ] + } + }, + "numpad1": { + "names": { + "domkey": "Numpad1", + "android": [ + "NUMPAD_1" + ], + "english": "Numpad 1", + "chromium": "numpad1" + }, + "scanCodes": { + "android": [ + 79 + ], + "usb": 458841, + "linux": 79, + "xkb": 87, + "windows": 79, + "macos": 83 + }, + "keyCodes": { + "android": [ + 145 + ] + } + }, + "numpad2": { + "names": { + "domkey": "Numpad2", + "android": [ + "NUMPAD_2" + ], + "english": "Numpad 2", + "chromium": "numpad2" + }, + "scanCodes": { + "android": [ + 80 + ], + "usb": 458842, + "linux": 80, + "xkb": 88, + "windows": 80, + "macos": 84 + }, + "keyCodes": { + "android": [ + 146 + ] + } + }, + "numpad3": { + "names": { + "domkey": "Numpad3", + "android": [ + "NUMPAD_3" + ], + "english": "Numpad 3", + "chromium": "numpad3" + }, + "scanCodes": { + "android": [ + 81 + ], + "usb": 458843, + "linux": 81, + "xkb": 89, + "windows": 81, + "macos": 85 + }, + "keyCodes": { + "android": [ + 147 + ] + } + }, + "numpad4": { + "names": { + "domkey": "Numpad4", + "android": [ + "NUMPAD_4" + ], + "english": "Numpad 4", + "chromium": "numpad4" + }, + "scanCodes": { + "android": [ + 75 + ], + "usb": 458844, + "linux": 75, + "xkb": 83, + "windows": 75, + "macos": 86 + }, + "keyCodes": { + "android": [ + 148 + ] + } + }, + "numpad5": { + "names": { + "domkey": "Numpad5", + "android": [ + "NUMPAD_5" + ], + "english": "Numpad 5", + "chromium": "numpad5" + }, + "scanCodes": { + "android": [ + 76 + ], + "usb": 458845, + "linux": 76, + "xkb": 84, + "windows": 76, + "macos": 87 + }, + "keyCodes": { + "android": [ + 149 + ] + } + }, + "numpad6": { + "names": { + "domkey": "Numpad6", + "android": [ + "NUMPAD_6" + ], + "english": "Numpad 6", + "chromium": "numpad6" + }, + "scanCodes": { + "android": [ + 77 + ], + "usb": 458846, + "linux": 77, + "xkb": 85, + "windows": 77, + "macos": 88 + }, + "keyCodes": { + "android": [ + 150 + ] + } + }, + "numpad7": { + "names": { + "domkey": "Numpad7", + "android": [ + "NUMPAD_7" + ], + "english": "Numpad 7", + "chromium": "numpad7" + }, + "scanCodes": { + "android": [ + 71 + ], + "usb": 458847, + "linux": 71, + "xkb": 79, + "windows": 71, + "macos": 89 + }, + "keyCodes": { + "android": [ + 151 + ] + } + }, + "numpad8": { + "names": { + "domkey": "Numpad8", + "android": [ + "NUMPAD_8" + ], + "english": "Numpad 8", + "chromium": "numpad8" + }, + "scanCodes": { + "android": [ + 72 + ], + "usb": 458848, + "linux": 72, + "xkb": 80, + "windows": 72, + "macos": 91 + }, + "keyCodes": { + "android": [ + 152 + ] + } + }, + "numpad9": { + "names": { + "domkey": "Numpad9", + "android": [ + "NUMPAD_9" + ], + "english": "Numpad 9", + "chromium": "numpad9" + }, + "scanCodes": { + "android": [ + 73 + ], + "usb": 458849, + "linux": 73, + "xkb": 81, + "windows": 73, + "macos": 92 + }, + "keyCodes": { + "android": [ + 153 + ] + } + }, + "numpad0": { + "names": { + "domkey": "Numpad0", + "android": [ + "NUMPAD_0" + ], + "english": "Numpad 0", + "chromium": "numpad0" + }, + "scanCodes": { + "android": [ + 82 + ], + "usb": 458850, + "linux": 82, + "xkb": 90, + "windows": 82, + "macos": 82 + }, + "keyCodes": { + "android": [ + 144 + ] + } + }, + "numpadDecimal": { + "names": { + "domkey": "NumpadDecimal", + "android": [ + "NUMPAD_DOT" + ], + "english": "Numpad Decimal", + "chromium": "numpadDecimal" + }, + "scanCodes": { + "android": [ + 83 + ], + "usb": 458851, + "linux": 83, + "xkb": 91, + "windows": 83, + "macos": 65 + }, + "keyCodes": { + "android": [ + 158 + ] + } + }, + "intlBackslash": { + "names": { + "domkey": "IntlBackslash", + "android": null, + "english": "Intl Backslash", + "chromium": "intlBackslash" + }, + "scanCodes": { + "android": null, + "usb": 458852, + "linux": 86, + "xkb": 94, + "windows": 86, + "macos": 10 + }, + "keyCodes": { + "android": null + } + }, + "contextMenu": { + "names": { + "domkey": "ContextMenu", + "android": [ + "MENU" + ], + "english": "Context Menu", + "chromium": "contextMenu" + }, + "scanCodes": { + "android": [ + 127, + 139 + ], + "usb": 458853, + "linux": 127, + "xkb": 135, + "windows": 57437, + "macos": 110 + }, + "keyCodes": { + "android": [ + 82 + ] + } + }, + "power": { + "names": { + "domkey": "Power", + "android": [ + "POWER" + ], + "english": "Power", + "chromium": "power" + }, + "scanCodes": { + "android": [ + 116, + 152 + ], + "usb": 458854, + "linux": 116, + "xkb": 124, + "windows": 57438, + "macos": null + }, + "keyCodes": { + "android": [ + 26 + ] + } + }, + "numpadEqual": { + "names": { + "domkey": "NumpadEqual", + "android": [ + "NUMPAD_EQUALS" + ], + "english": "Numpad Equal", + "chromium": "numpadEqual" + }, + "scanCodes": { + "android": [ + 117 + ], + "usb": 458855, + "linux": 117, + "xkb": 125, + "windows": 89, + "macos": 81 + }, + "keyCodes": { + "android": [ + 161 + ] + } + }, + "f13": { + "names": { + "domkey": "F13", + "android": [ + "F13" + ], + "english": "F13", + "chromium": "f13" + }, + "scanCodes": { + "android": [ + 183 + ], + "usb": 458856, + "linux": 183, + "xkb": 191, + "windows": 100, + "macos": 105 + }, + "keyCodes": { + "android": null + } + }, + "f14": { + "names": { + "domkey": "F14", + "android": [ + "F14" + ], + "english": "F14", + "chromium": "f14" + }, + "scanCodes": { + "android": [ + 184 + ], + "usb": 458857, + "linux": 184, + "xkb": 192, + "windows": 101, + "macos": 107 + }, + "keyCodes": { + "android": null + } + }, + "f15": { + "names": { + "domkey": "F15", + "android": [ + "F15" + ], + "english": "F15", + "chromium": "f15" + }, + "scanCodes": { + "android": [ + 185 + ], + "usb": 458858, + "linux": 185, + "xkb": 193, + "windows": 102, + "macos": 113 + }, + "keyCodes": { + "android": null + } + }, + "f16": { + "names": { + "domkey": "F16", + "android": [ + "F16" + ], + "english": "F16", + "chromium": "f16" + }, + "scanCodes": { + "android": [ + 186 + ], + "usb": 458859, + "linux": 186, + "xkb": 194, + "windows": 103, + "macos": 106 + }, + "keyCodes": { + "android": null + } + }, + "f17": { + "names": { + "domkey": "F17", + "android": [ + "F17" + ], + "english": "F17", + "chromium": "f17" + }, + "scanCodes": { + "android": [ + 187 + ], + "usb": 458860, + "linux": 187, + "xkb": 195, + "windows": 104, + "macos": 64 + }, + "keyCodes": { + "android": null + } + }, + "f18": { + "names": { + "domkey": "F18", + "android": [ + "F18" + ], + "english": "F18", + "chromium": "f18" + }, + "scanCodes": { + "android": [ + 188 + ], + "usb": 458861, + "linux": 188, + "xkb": 196, + "windows": 105, + "macos": 79 + }, + "keyCodes": { + "android": null + } + }, + "f19": { + "names": { + "domkey": "F19", + "android": [ + "F19" + ], + "english": "F19", + "chromium": "f19" + }, + "scanCodes": { + "android": [ + 189 + ], + "usb": 458862, + "linux": 189, + "xkb": 197, + "windows": 106, + "macos": 80 + }, + "keyCodes": { + "android": null + } + }, + "f20": { + "names": { + "domkey": "F20", + "android": [ + "F20" + ], + "english": "F20", + "chromium": "f20" + }, + "scanCodes": { + "android": [ + 190 + ], + "usb": 458863, + "linux": 190, + "xkb": 198, + "windows": 107, + "macos": 90 + }, + "keyCodes": { + "android": null + } + }, + "f21": { + "names": { + "domkey": "F21", + "android": [ + "F21" + ], + "english": "F21", + "chromium": "f21" + }, + "scanCodes": { + "android": [ + 191 + ], + "usb": 458864, + "linux": 191, + "xkb": 199, + "windows": 108, + "macos": null + }, + "keyCodes": { + "android": null + } + }, + "f22": { + "names": { + "domkey": "F22", + "android": [ + "F22" + ], + "english": "F22", + "chromium": "f22" + }, + "scanCodes": { + "android": [ + 192 + ], + "usb": 458865, + "linux": 192, + "xkb": 200, + "windows": 109, + "macos": null + }, + "keyCodes": { + "android": null + } + }, + "f23": { + "names": { + "domkey": "F23", + "android": [ + "F23" + ], + "english": "F23", + "chromium": "f23" + }, + "scanCodes": { + "android": [ + 193 + ], + "usb": 458866, + "linux": 193, + "xkb": 201, + "windows": 110, + "macos": null + }, + "keyCodes": { + "android": null + } + }, + "f24": { + "names": { + "domkey": "F24", + "android": [ + "F24" + ], + "english": "F24", + "chromium": "f24" + }, + "scanCodes": { + "android": [ + 194 + ], + "usb": 458867, + "linux": 194, + "xkb": 202, + "windows": 118, + "macos": null + }, + "keyCodes": { + "android": null + } + }, + "open": { + "names": { + "domkey": "Open", + "android": [ + "OPEN" + ], + "english": "Open", + "chromium": "open" + }, + "scanCodes": { + "android": [ + 134 + ], + "usb": 458868, + "linux": 134, + "xkb": 142, + "windows": null, + "macos": null + }, + "keyCodes": { + "android": null + } + }, + "help": { + "names": { + "domkey": "Help", + "android": [ + "HELP" + ], + "english": "Help", + "chromium": "help" + }, + "scanCodes": { + "android": [ + 138 + ], + "usb": 458869, + "linux": 138, + "xkb": 146, + "windows": 57403, + "macos": null + }, + "keyCodes": { + "android": [ + 259 + ] + } + }, + "select": { + "names": { + "domkey": "Select", + "android": null, + "english": "Select", + "chromium": "select" + }, + "scanCodes": { + "android": null, + "usb": 458871, + "linux": 132, + "xkb": 140, + "windows": null, + "macos": null + }, + "keyCodes": { + "android": null + } + }, + "again": { + "names": { + "domkey": "Again", + "android": [ + "AGAIN" + ], + "english": "Again", + "chromium": "again" + }, + "scanCodes": { + "android": [ + 129 + ], + "usb": 458873, + "linux": 129, + "xkb": 137, + "windows": null, + "macos": null + }, + "keyCodes": { + "android": null + } + }, + "undo": { + "names": { + "domkey": "Undo", + "android": [ + "UNDO" + ], + "english": "Undo", + "chromium": "undo" + }, + "scanCodes": { + "android": [ + 131 + ], + "usb": 458874, + "linux": 131, + "xkb": 139, + "windows": 57352, + "macos": null + }, + "keyCodes": { + "android": null + } + }, + "cut": { + "names": { + "domkey": "Cut", + "android": [ + "CUT" + ], + "english": "Cut", + "chromium": "cut" + }, + "scanCodes": { + "android": [ + 137 + ], + "usb": 458875, + "linux": 137, + "xkb": 145, + "windows": 57367, + "macos": null + }, + "keyCodes": { + "android": [ + 277 + ] + } + }, + "copy": { + "names": { + "domkey": "Copy", + "android": [ + "COPY" + ], + "english": "Copy", + "chromium": "copy" + }, + "scanCodes": { + "android": [ + 133 + ], + "usb": 458876, + "linux": 133, + "xkb": 141, + "windows": 57368, + "macos": null + }, + "keyCodes": { + "android": [ + 278 + ] + } + }, + "paste": { + "names": { + "domkey": "Paste", + "android": [ + "PASTE" + ], + "english": "Paste", + "chromium": "paste" + }, + "scanCodes": { + "android": [ + 135 + ], + "usb": 458877, + "linux": 135, + "xkb": 143, + "windows": 57354, + "macos": null + }, + "keyCodes": { + "android": [ + 279 + ] + } + }, + "find": { + "names": { + "domkey": "Find", + "android": [ + "FIND" + ], + "english": "Find", + "chromium": "find" + }, + "scanCodes": { + "android": [ + 136 + ], + "usb": 458878, + "linux": 136, + "xkb": 144, + "windows": null, + "macos": null + }, + "keyCodes": { + "android": null + } + }, + "audioVolumeMute": { + "names": { + "domkey": "AudioVolumeMute", + "android": [ + "VOLUME_MUTE" + ], + "english": "Audio Volume Mute", + "chromium": "volumeMute" + }, + "scanCodes": { + "android": [ + 113 + ], + "usb": 458879, + "linux": 113, + "xkb": 121, + "windows": 57376, + "macos": 74 + }, + "keyCodes": { + "android": [ + 164 + ] + } + }, + "audioVolumeUp": { + "names": { + "domkey": "AudioVolumeUp", + "android": [ + "VOLUME_UP" + ], + "english": "Audio Volume Up", + "chromium": "volumeUp" + }, + "scanCodes": { + "android": [ + 115 + ], + "usb": 458880, + "linux": 115, + "xkb": 123, + "windows": 57392, + "macos": 72 + }, + "keyCodes": { + "android": [ + 24 + ] + } + }, + "audioVolumeDown": { + "names": { + "domkey": "AudioVolumeDown", + "android": [ + "VOLUME_DOWN" + ], + "english": "Audio Volume Down", + "chromium": "volumeDown" + }, + "scanCodes": { + "android": [ + 114 + ], + "usb": 458881, + "linux": 114, + "xkb": 122, + "windows": 57390, + "macos": 73 + }, + "keyCodes": { + "android": [ + 25 + ] + } + }, + "numpadComma": { + "names": { + "domkey": "NumpadComma", + "android": [ + "NUMPAD_COMMA" + ], + "english": "Numpad Comma", + "chromium": "numpadComma" + }, + "scanCodes": { + "android": [ + 95, + 121 + ], + "usb": 458885, + "linux": 121, + "xkb": 129, + "windows": 126, + "macos": 95 + }, + "keyCodes": { + "android": [ + 159 + ] + } + }, + "intlRo": { + "names": { + "domkey": "IntlRo", + "android": null, + "english": "Intl Ro", + "chromium": "intlRo" + }, + "scanCodes": { + "android": null, + "usb": 458887, + "linux": 89, + "xkb": 97, + "windows": 115, + "macos": 94 + }, + "keyCodes": { + "android": null + } + }, + "kanaMode": { + "names": { + "domkey": "KanaMode", + "android": null, + "english": "Kana Mode", + "chromium": "kanaMode" + }, + "scanCodes": { + "android": null, + "usb": 458888, + "linux": 93, + "xkb": 101, + "windows": 112, + "macos": 104 + }, + "keyCodes": { + "android": null + } + }, + "intlYen": { + "names": { + "domkey": "IntlYen", + "android": null, + "english": "Intl Yen", + "chromium": "intlYen" + }, + "scanCodes": { + "android": null, + "usb": 458889, + "linux": 124, + "xkb": 132, + "windows": 125, + "macos": 93 + }, + "keyCodes": { + "android": null + } + }, + "convert": { + "names": { + "domkey": "Convert", + "android": [ + "HENKAN" + ], + "english": "Convert", + "chromium": "convert" + }, + "scanCodes": { + "android": [ + 92 + ], + "usb": 458890, + "linux": 92, + "xkb": 100, + "windows": 121, + "macos": null + }, + "keyCodes": { + "android": [ + 214 + ] + } + }, + "nonConvert": { + "names": { + "domkey": "NonConvert", + "android": [ + "MUHENKAN" + ], + "english": "Non Convert", + "chromium": "nonConvert" + }, + "scanCodes": { + "android": [ + 94 + ], + "usb": 458891, + "linux": 94, + "xkb": 102, + "windows": 123, + "macos": null + }, + "keyCodes": { + "android": [ + 213 + ] + } + }, + "lang1": { + "names": { + "domkey": "Lang1", + "android": null, + "english": "Lang 1", + "chromium": "lang1" + }, + "scanCodes": { + "android": null, + "usb": 458896, + "linux": 122, + "xkb": 130, + "windows": 114, + "macos": null + }, + "keyCodes": { + "android": null + } + }, + "lang2": { + "names": { + "domkey": "Lang2", + "android": null, + "english": "Lang 2", + "chromium": "lang2" + }, + "scanCodes": { + "android": null, + "usb": 458897, + "linux": 123, + "xkb": 131, + "windows": 113, + "macos": null + }, + "keyCodes": { + "android": null + } + }, + "lang3": { + "names": { + "domkey": "Lang3", + "android": [ + "KATAKANA" + ], + "english": "Lang 3", + "chromium": "lang3" + }, + "scanCodes": { + "android": [ + 90 + ], + "usb": 458898, + "linux": 90, + "xkb": 98, + "windows": 120, + "macos": null + }, + "keyCodes": { + "android": null + } + }, + "lang4": { + "names": { + "domkey": "Lang4", + "android": [ + "HIRAGANA" + ], + "english": "Lang 4", + "chromium": "lang4" + }, + "scanCodes": { + "android": [ + 91 + ], + "usb": 458899, + "linux": 91, + "xkb": 99, + "windows": 119, + "macos": null + }, + "keyCodes": { + "android": null + } + }, + "lang5": { + "names": { + "domkey": "Lang5", + "android": null, + "english": "Lang 5", + "chromium": "lang5" + }, + "scanCodes": { + "android": null, + "usb": 458900, + "linux": 85, + "xkb": 93, + "windows": null, + "macos": null + }, + "keyCodes": { + "android": null + } + }, + "abort": { + "names": { + "domkey": "Abort", + "android": null, + "english": "Abort", + "chromium": "abort" + }, + "scanCodes": { + "android": null, + "usb": 458907, + "linux": null, + "xkb": null, + "windows": null, + "macos": null + }, + "keyCodes": { + "android": null + } + }, + "props": { + "names": { + "domkey": "Props", + "android": [ + "PROPS" + ], + "english": "Props", + "chromium": "props" + }, + "scanCodes": { + "android": [ + 130 + ], + "usb": 458915, + "linux": null, + "xkb": null, + "windows": null, + "macos": null + }, + "keyCodes": { + "android": null + } + }, + "numpadParenLeft": { + "names": { + "domkey": "NumpadParenLeft", + "android": [ + "NUMPAD_LEFT_PAREN" + ], + "english": "Numpad Paren Left", + "chromium": "numpadParenLeft" + }, + "scanCodes": { + "android": [ + 179 + ], + "usb": 458934, + "linux": 179, + "xkb": 187, + "windows": null, + "macos": null + }, + "keyCodes": { + "android": [ + 162 + ] + } + }, + "numpadParenRight": { + "names": { + "domkey": "NumpadParenRight", + "android": [ + "NUMPAD_RIGHT_PAREN" + ], + "english": "Numpad Paren Right", + "chromium": "numpadParenRight" + }, + "scanCodes": { + "android": [ + 180 + ], + "usb": 458935, + "linux": 180, + "xkb": 188, + "windows": null, + "macos": null + }, + "keyCodes": { + "android": [ + 163 + ] + } + }, + "numpadBackspace": { + "names": { + "domkey": "NumpadBackspace", + "android": null, + "english": "Numpad Backspace", + "chromium": "numpadBackspace" + }, + "scanCodes": { + "android": null, + "usb": 458939, + "linux": null, + "xkb": null, + "windows": null, + "macos": null + }, + "keyCodes": { + "android": null + } + }, + "numpadMemoryStore": { + "names": { + "domkey": "NumpadMemoryStore", + "android": null, + "english": "Numpad Memory Store", + "chromium": "numpadMemoryStore" + }, + "scanCodes": { + "android": null, + "usb": 458960, + "linux": null, + "xkb": null, + "windows": null, + "macos": null + }, + "keyCodes": { + "android": null + } + }, + "numpadMemoryRecall": { + "names": { + "domkey": "NumpadMemoryRecall", + "android": null, + "english": "Numpad Memory Recall", + "chromium": "numpadMemoryRecall" + }, + "scanCodes": { + "android": null, + "usb": 458961, + "linux": null, + "xkb": null, + "windows": null, + "macos": null + }, + "keyCodes": { + "android": null + } + }, + "numpadMemoryClear": { + "names": { + "domkey": "NumpadMemoryClear", + "android": null, + "english": "Numpad Memory Clear", + "chromium": "numpadMemoryClear" + }, + "scanCodes": { + "android": null, + "usb": 458962, + "linux": null, + "xkb": null, + "windows": null, + "macos": null + }, + "keyCodes": { + "android": null + } + }, + "numpadMemoryAdd": { + "names": { + "domkey": "NumpadMemoryAdd", + "android": null, + "english": "Numpad Memory Add", + "chromium": "numpadMemoryAdd" + }, + "scanCodes": { + "android": null, + "usb": 458963, + "linux": null, + "xkb": null, + "windows": null, + "macos": null + }, + "keyCodes": { + "android": null + } + }, + "numpadMemorySubtract": { + "names": { + "domkey": "NumpadMemorySubtract", + "android": null, + "english": "Numpad Memory Subtract", + "chromium": "numpadMemorySubtract" + }, + "scanCodes": { + "android": null, + "usb": 458964, + "linux": null, + "xkb": null, + "windows": null, + "macos": null + }, + "keyCodes": { + "android": null + } + }, + "numpadSignChange": { + "names": { + "domkey": null, + "android": null, + "english": "Numpad Sign Change", + "chromium": "numpadSignChange" + }, + "scanCodes": { + "android": null, + "usb": 458967, + "linux": 118, + "xkb": 126, + "windows": null, + "macos": null + }, + "keyCodes": { + "android": null + } + }, + "numpadClear": { + "names": { + "domkey": "NumpadClear", + "android": null, + "english": "Numpad Clear", + "chromium": "numpadClear" + }, + "scanCodes": { + "android": null, + "usb": 458968, + "linux": null, + "xkb": null, + "windows": null, + "macos": null + }, + "keyCodes": { + "android": null + } + }, + "numpadClearEntry": { + "names": { + "domkey": "NumpadClearEntry", + "android": null, + "english": "Numpad Clear Entry", + "chromium": "numpadClearEntry" + }, + "scanCodes": { + "android": null, + "usb": 458969, + "linux": null, + "xkb": null, + "windows": null, + "macos": null + }, + "keyCodes": { + "android": null + } + }, + "controlLeft": { + "names": { + "domkey": "ControlLeft", + "android": [ + "CTRL_LEFT" + ], + "english": "Control Left", + "chromium": "controlLeft" + }, + "scanCodes": { + "android": [ + 29 + ], + "usb": 458976, + "linux": 29, + "xkb": 37, + "windows": 29, + "macos": 59 + }, + "keyCodes": { + "android": [ + 113 + ] + } + }, + "shiftLeft": { + "names": { + "domkey": "ShiftLeft", + "android": [ + "SHIFT_LEFT" + ], + "english": "Shift Left", + "chromium": "shiftLeft" + }, + "scanCodes": { + "android": [ + 42 + ], + "usb": 458977, + "linux": 42, + "xkb": 50, + "windows": 42, + "macos": 56 + }, + "keyCodes": { + "android": [ + 59 + ] + } + }, + "altLeft": { + "names": { + "domkey": "AltLeft", + "android": [ + "ALT_LEFT" + ], + "english": "Alt Left", + "chromium": "altLeft" + }, + "scanCodes": { + "android": [ + 56 + ], + "usb": 458978, + "linux": 56, + "xkb": 64, + "windows": 56, + "macos": 58 + }, + "keyCodes": { + "android": [ + 57 + ] + } + }, + "metaLeft": { + "names": { + "domkey": "MetaLeft", + "android": [ + "META_LEFT" + ], + "english": "Meta Left", + "chromium": "metaLeft" + }, + "scanCodes": { + "android": [ + 125 + ], + "usb": 458979, + "linux": 125, + "xkb": 133, + "windows": 57435, + "macos": 55 + }, + "keyCodes": { + "android": [ + 117 + ] + } + }, + "controlRight": { + "names": { + "domkey": "ControlRight", + "android": [ + "CTRL_RIGHT" + ], + "english": "Control Right", + "chromium": "controlRight" + }, + "scanCodes": { + "android": [ + 97 + ], + "usb": 458980, + "linux": 97, + "xkb": 105, + "windows": 57373, + "macos": 62 + }, + "keyCodes": { + "android": [ + 114 + ] + } + }, + "shiftRight": { + "names": { + "domkey": "ShiftRight", + "android": [ + "SHIFT_RIGHT" + ], + "english": "Shift Right", + "chromium": "shiftRight" + }, + "scanCodes": { + "android": [ + 54 + ], + "usb": 458981, + "linux": 54, + "xkb": 62, + "windows": 54, + "macos": 60 + }, + "keyCodes": { + "android": [ + 60 + ] + } + }, + "altRight": { + "names": { + "domkey": "AltRight", + "android": [ + "ALT_RIGHT" + ], + "english": "Alt Right", + "chromium": "altRight" + }, + "scanCodes": { + "android": [ + 100 + ], + "usb": 458982, + "linux": 100, + "xkb": 108, + "windows": 57400, + "macos": 61 + }, + "keyCodes": { + "android": [ + 58 + ] + } + }, + "metaRight": { + "names": { + "domkey": "MetaRight", + "android": [ + "META_RIGHT" + ], + "english": "Meta Right", + "chromium": "metaRight" + }, + "scanCodes": { + "android": [ + 126 + ], + "usb": 458983, + "linux": 126, + "xkb": 134, + "windows": 57436, + "macos": 54 + }, + "keyCodes": { + "android": [ + 118 + ] + } + }, + "info": { + "names": { + "domkey": null, + "android": [ + "INFO" + ], + "english": "Info", + "chromium": "info" + }, + "scanCodes": { + "android": [ + 358 + ], + "usb": 786528, + "linux": 358, + "xkb": 366, + "windows": null, + "macos": null + }, + "keyCodes": { + "android": [ + 165 + ] + } + }, + "closedCaptionToggle": { + "names": { + "domkey": null, + "android": [ + "CAPTIONS" + ], + "english": "Closed Caption Toggle", + "chromium": "closedCaptionToggle" + }, + "scanCodes": { + "android": null, + "usb": 786529, + "linux": 370, + "xkb": 378, + "windows": null, + "macos": null + }, + "keyCodes": { + "android": [ + 175 + ] + } + }, + "brightnessUp": { + "names": { + "domkey": "BrightnessUp", + "android": [ + "BRIGHTNESS_UP" + ], + "english": "Brightness Up", + "chromium": "brightnessUp" + }, + "scanCodes": { + "android": [ + 225 + ], + "usb": 786543, + "linux": 225, + "xkb": 233, + "windows": null, + "macos": null + }, + "keyCodes": { + "android": [ + 221 + ] + } + }, + "brightnessDown": { + "names": { + "domkey": "BrightnessDown", + "android": [ + "BRIGHTNESS_DOWN" + ], + "english": "Brightness Down", + "chromium": "brightnessDown" + }, + "scanCodes": { + "android": [ + 224 + ], + "usb": 786544, + "linux": 224, + "xkb": 232, + "windows": null, + "macos": null + }, + "keyCodes": { + "android": [ + 220 + ] + } + }, + "brightnessToggle": { + "names": { + "domkey": null, + "android": null, + "english": "Brightness Toggle", + "chromium": "brightnessToggle" + }, + "scanCodes": { + "android": null, + "usb": 786546, + "linux": 431, + "xkb": 439, + "windows": null, + "macos": null + }, + "keyCodes": { + "android": null + } + }, + "brightnessMinimum": { + "names": { + "domkey": null, + "android": null, + "english": "Brightness Minimum", + "chromium": "brightnessMinimum" + }, + "scanCodes": { + "android": null, + "usb": 786547, + "linux": 592, + "xkb": 600, + "windows": null, + "macos": null + }, + "keyCodes": { + "android": null + } + }, + "brightnessMaximum": { + "names": { + "domkey": null, + "android": null, + "english": "Brightness Maximum", + "chromium": "brightnessMaximum" + }, + "scanCodes": { + "android": null, + "usb": 786548, + "linux": 593, + "xkb": 601, + "windows": null, + "macos": null + }, + "keyCodes": { + "android": null + } + }, + "brightnessAuto": { + "names": { + "domkey": null, + "android": null, + "english": "Brightness Auto", + "chromium": "brightnessAuto" + }, + "scanCodes": { + "android": null, + "usb": 786549, + "linux": 244, + "xkb": 252, + "windows": null, + "macos": null + }, + "keyCodes": { + "android": null + } + }, + "mediaLast": { + "names": { + "domkey": null, + "android": [ + "LAST_CHANNEL" + ], + "english": "Media Last", + "chromium": "mediaLast" + }, + "scanCodes": { + "android": null, + "usb": 786563, + "linux": 405, + "xkb": 413, + "windows": null, + "macos": null + }, + "keyCodes": { + "android": [ + 229 + ] + } + }, + "launchPhone": { + "names": { + "domkey": null, + "android": null, + "english": "Launch Phone", + "chromium": "launchPhone" + }, + "scanCodes": { + "android": null, + "usb": 786572, + "linux": 169, + "xkb": 177, + "windows": null, + "macos": null + }, + "keyCodes": { + "android": null + } + }, + "programGuide": { + "names": { + "domkey": null, + "android": null, + "english": "Program Guide", + "chromium": "programGuide" + }, + "scanCodes": { + "android": null, + "usb": 786573, + "linux": 362, + "xkb": 370, + "windows": null, + "macos": null + }, + "keyCodes": { + "android": null + } + }, + "exit": { + "names": { + "domkey": null, + "android": [ + "EXIT" + ], + "english": "Exit", + "chromium": "exit" + }, + "scanCodes": { + "android": [ + 174 + ], + "usb": 786580, + "linux": 174, + "xkb": 182, + "windows": null, + "macos": null + }, + "keyCodes": { + "android": null + } + }, + "channelUp": { + "names": { + "domkey": null, + "android": [ + "CHANNEL_UP" + ], + "english": "Channel Up", + "chromium": "channelUp" + }, + "scanCodes": { + "android": [ + 402 + ], + "usb": 786588, + "linux": 410, + "xkb": 418, + "windows": null, + "macos": null + }, + "keyCodes": { + "android": [ + 166 + ] + } + }, + "channelDown": { + "names": { + "domkey": null, + "android": [ + "CHANNEL_DOWN" + ], + "english": "Channel Down", + "chromium": "channelDown" + }, + "scanCodes": { + "android": [ + 403 + ], + "usb": 786589, + "linux": 411, + "xkb": 419, + "windows": null, + "macos": null + }, + "keyCodes": { + "android": [ + 167 + ] + } + }, + "mediaPlay": { + "names": { + "domkey": "MediaPlay", + "android": [ + "MEDIA_PLAY" + ], + "english": "Media Play", + "chromium": "mediaPlay" + }, + "scanCodes": { + "android": [ + 200, + 207 + ], + "usb": 786608, + "linux": 207, + "xkb": 215, + "windows": null, + "macos": null + }, + "keyCodes": { + "android": [ + 126 + ] + } + }, + "mediaRecord": { + "names": { + "domkey": "MediaRecord", + "android": [ + "MEDIA_RECORD" + ], + "english": "Media Record", + "chromium": "mediaRecord" + }, + "scanCodes": { + "android": [ + 167 + ], + "usb": 786610, + "linux": 167, + "xkb": 175, + "windows": null, + "macos": null + }, + "keyCodes": { + "android": [ + 130 + ] + } + }, + "mediaFastForward": { + "names": { + "domkey": "MediaFastForward", + "android": [ + "MEDIA_FAST_FORWARD" + ], + "english": "Media Fast Forward", + "chromium": "mediaFastForward" + }, + "scanCodes": { + "android": [ + 208 + ], + "usb": 786611, + "linux": 208, + "xkb": 216, + "windows": null, + "macos": null + }, + "keyCodes": { + "android": [ + 90 + ] + } + }, + "mediaRewind": { + "names": { + "domkey": "MediaRewind", + "android": [ + "MEDIA_REWIND" + ], + "english": "Media Rewind", + "chromium": "mediaRewind" + }, + "scanCodes": { + "android": [ + 168 + ], + "usb": 786612, + "linux": 168, + "xkb": 176, + "windows": null, + "macos": null + }, + "keyCodes": { + "android": [ + 89 + ] + } + }, + "mediaTrackNext": { + "names": { + "domkey": "MediaTrackNext", + "android": [ + "MEDIA_NEXT" + ], + "english": "Media Track Next", + "chromium": "mediaTrackNext" + }, + "scanCodes": { + "android": [ + 163 + ], + "usb": 786613, + "linux": 163, + "xkb": 171, + "windows": 57369, + "macos": null + }, + "keyCodes": { + "android": [ + 87 + ] + } + }, + "mediaTrackPrevious": { + "names": { + "domkey": "MediaTrackPrevious", + "android": [ + "MEDIA_PREVIOUS" + ], + "english": "Media Track Previous", + "chromium": "mediaTrackPrevious" + }, + "scanCodes": { + "android": [ + 165 + ], + "usb": 786614, + "linux": 165, + "xkb": 173, + "windows": 57360, + "macos": null + }, + "keyCodes": { + "android": [ + 88 + ] + } + }, + "mediaStop": { + "names": { + "domkey": "MediaStop", + "android": [ + "MEDIA_STOP" + ], + "english": "Media Stop", + "chromium": "mediaStop" + }, + "scanCodes": { + "android": [ + 128, + 166 + ], + "usb": 786615, + "linux": 166, + "xkb": 174, + "windows": 57380, + "macos": null + }, + "keyCodes": { + "android": [ + 86 + ] + } + }, + "eject": { + "names": { + "domkey": "Eject", + "android": [ + "MEDIA_EJECT" + ], + "english": "Eject", + "chromium": "eject" + }, + "scanCodes": { + "android": [ + 161, + 162 + ], + "usb": 786616, + "linux": 161, + "xkb": 169, + "windows": 57388, + "macos": null + }, + "keyCodes": { + "android": [ + 129 + ] + } + }, + "mediaPlayPause": { + "names": { + "domkey": "MediaPlayPause", + "android": [ + "MEDIA_PLAY_PAUSE" + ], + "english": "Media Play Pause", + "chromium": "mediaPlayPause" + }, + "scanCodes": { + "android": [ + 164 + ], + "usb": 786637, + "linux": 164, + "xkb": 172, + "windows": 57378, + "macos": null + }, + "keyCodes": { + "android": [ + 85 + ] + } + }, + "speechInputToggle": { + "names": { + "domkey": null, + "android": null, + "english": "Speech Input Toggle", + "chromium": "speechInputToggle" + }, + "scanCodes": { + "android": null, + "usb": 786639, + "linux": 582, + "xkb": 590, + "windows": null, + "macos": null + }, + "keyCodes": { + "android": null + } + }, + "bassBoost": { + "names": { + "domkey": null, + "android": [ + "BASSBOOST" + ], + "english": "Bass Boost", + "chromium": "bassBoost" + }, + "scanCodes": { + "android": [ + 209 + ], + "usb": 786661, + "linux": 209, + "xkb": 217, + "windows": null, + "macos": null + }, + "keyCodes": { + "android": null + } + }, + "mediaSelect": { + "names": { + "domkey": "MediaSelect", + "android": null, + "english": "Media Select", + "chromium": "mediaSelect" + }, + "scanCodes": { + "android": null, + "usb": 786819, + "linux": 171, + "xkb": 179, + "windows": 57453, + "macos": null + }, + "keyCodes": { + "android": null + } + }, + "launchWordProcessor": { + "names": { + "domkey": null, + "android": null, + "english": "Launch Word Processor", + "chromium": "launchWordProcessor" + }, + "scanCodes": { + "android": null, + "usb": 786820, + "linux": 421, + "xkb": 429, + "windows": null, + "macos": null + }, + "keyCodes": { + "android": null + } + }, + "launchSpreadsheet": { + "names": { + "domkey": null, + "android": null, + "english": "Launch Spreadsheet", + "chromium": "launchSpreadsheet" + }, + "scanCodes": { + "android": null, + "usb": 786822, + "linux": 423, + "xkb": 431, + "windows": null, + "macos": null + }, + "keyCodes": { + "android": null + } + }, + "launchMail": { + "names": { + "domkey": "LaunchMail", + "android": [ + "ENVELOPE" + ], + "english": "Launch Mail", + "chromium": "launchMail" + }, + "scanCodes": { + "android": [ + 155, + 215 + ], + "usb": 786826, + "linux": 155, + "xkb": 163, + "windows": 57452, + "macos": null + }, + "keyCodes": { + "android": [ + 65 + ] + } + }, + "launchContacts": { + "names": { + "domkey": null, + "android": [ + "CONTACTS" + ], + "english": "Launch Contacts", + "chromium": "launchContacts" + }, + "scanCodes": { + "android": [ + 429 + ], + "usb": 786829, + "linux": 429, + "xkb": 437, + "windows": null, + "macos": null + }, + "keyCodes": { + "android": [ + 207 + ] + } + }, + "launchCalendar": { + "names": { + "domkey": null, + "android": [ + "CALENDAR" + ], + "english": "Launch Calendar", + "chromium": "launchCalendar" + }, + "scanCodes": { + "android": [ + 397 + ], + "usb": 786830, + "linux": 397, + "xkb": 405, + "windows": null, + "macos": null + }, + "keyCodes": { + "android": [ + 208 + ] + } + }, + "launchApp2": { + "names": { + "domkey": "LaunchApp2", + "android": null, + "english": "Launch App2", + "chromium": "launchApp2" + }, + "scanCodes": { + "android": null, + "usb": 786834, + "linux": 140, + "xkb": 148, + "windows": 57377, + "macos": null + }, + "keyCodes": { + "android": null + } + }, + "launchApp1": { + "names": { + "domkey": "LaunchApp1", + "android": null, + "english": "Launch App1", + "chromium": "launchApp1" + }, + "scanCodes": { + "android": null, + "usb": 786836, + "linux": 144, + "xkb": 152, + "windows": 57451, + "macos": null + }, + "keyCodes": { + "android": null + } + }, + "launchInternetBrowser": { + "names": { + "domkey": null, + "android": null, + "english": "Launch Internet Browser", + "chromium": "launchInternetBrowser" + }, + "scanCodes": { + "android": null, + "usb": 786838, + "linux": 150, + "xkb": 158, + "windows": null, + "macos": null + }, + "keyCodes": { + "android": null + } + }, + "logOff": { + "names": { + "domkey": null, + "android": null, + "english": "Log Off", + "chromium": "logOff" + }, + "scanCodes": { + "android": null, + "usb": 786844, + "linux": 433, + "xkb": 441, + "windows": null, + "macos": null + }, + "keyCodes": { + "android": null + } + }, + "lockScreen": { + "names": { + "domkey": null, + "android": null, + "english": "Lock Screen", + "chromium": "lockScreen" + }, + "scanCodes": { + "android": null, + "usb": 786846, + "linux": 152, + "xkb": 160, + "windows": null, + "macos": null + }, + "keyCodes": { + "android": null + } + }, + "launchControlPanel": { + "names": { + "domkey": "LaunchControlPanel", + "android": null, + "english": "Launch Control Panel", + "chromium": "launchControlPanel" + }, + "scanCodes": { + "android": null, + "usb": 786847, + "linux": 579, + "xkb": 587, + "windows": null, + "macos": null + }, + "keyCodes": { + "android": null + } + }, + "selectTask": { + "names": { + "domkey": "SelectTask", + "android": null, + "english": "Select Task", + "chromium": "selectTask" + }, + "scanCodes": { + "android": null, + "usb": 786850, + "linux": 580, + "xkb": 588, + "windows": null, + "macos": null + }, + "keyCodes": { + "android": null + } + }, + "launchDocuments": { + "names": { + "domkey": null, + "android": null, + "english": "Launch Documents", + "chromium": "launchDocuments" + }, + "scanCodes": { + "android": null, + "usb": 786855, + "linux": 235, + "xkb": 243, + "windows": null, + "macos": null + }, + "keyCodes": { + "android": null + } + }, + "spellCheck": { + "names": { + "domkey": null, + "android": null, + "english": "Spell Check", + "chromium": "spellCheck" + }, + "scanCodes": { + "android": null, + "usb": 786859, + "linux": 432, + "xkb": 440, + "windows": null, + "macos": null + }, + "keyCodes": { + "android": null + } + }, + "launchKeyboardLayout": { + "names": { + "domkey": null, + "android": null, + "english": "Launch Keyboard Layout", + "chromium": "launchKeyboardLayout" + }, + "scanCodes": { + "android": null, + "usb": 786862, + "linux": 374, + "xkb": 382, + "windows": null, + "macos": null + }, + "keyCodes": { + "android": null + } + }, + "launchScreenSaver": { + "names": { + "domkey": "LaunchScreenSaver", + "android": null, + "english": "Launch Screen Saver", + "chromium": "launchScreenSaver" + }, + "scanCodes": { + "android": null, + "usb": 786865, + "linux": 581, + "xkb": 589, + "windows": null, + "macos": null + }, + "keyCodes": { + "android": null + } + }, + "launchAudioBrowser": { + "names": { + "domkey": null, + "android": null, + "english": "Launch Audio Browser", + "chromium": "launchAudioBrowser" + }, + "scanCodes": { + "android": null, + "usb": 786871, + "linux": 392, + "xkb": 400, + "windows": null, + "macos": null + }, + "keyCodes": { + "android": null + } + }, + "newKey": { + "names": { + "domkey": null, + "android": [ + "NEW" + ], + "english": "New Key", + "chromium": "new" + }, + "scanCodes": { + "android": [ + 181 + ], + "usb": 786945, + "linux": 181, + "xkb": 189, + "windows": null, + "macos": null + }, + "keyCodes": { + "android": null + } + }, + "close": { + "names": { + "domkey": null, + "android": [ + "MEDIA_CLOSE", + "CLOSE" + ], + "english": "Close", + "chromium": "close" + }, + "scanCodes": { + "android": [ + 160, + 206 + ], + "usb": 786947, + "linux": 206, + "xkb": 214, + "windows": null, + "macos": null + }, + "keyCodes": { + "android": [ + 128 + ] + } + }, + "save": { + "names": { + "domkey": null, + "android": null, + "english": "Save", + "chromium": "save" + }, + "scanCodes": { + "android": null, + "usb": 786951, + "linux": 234, + "xkb": 242, + "windows": null, + "macos": null + }, + "keyCodes": { + "android": null + } + }, + "print": { + "names": { + "domkey": null, + "android": [ + "PRINT" + ], + "english": "Print", + "chromium": "print" + }, + "scanCodes": { + "android": [ + 210 + ], + "usb": 786952, + "linux": 210, + "xkb": 218, + "windows": null, + "macos": null + }, + "keyCodes": { + "android": null + } + }, + "browserSearch": { + "names": { + "domkey": "BrowserSearch", + "android": [ + "SEARCH" + ], + "english": "Browser Search", + "chromium": "browserSearch" + }, + "scanCodes": { + "android": [ + 217 + ], + "usb": 786977, + "linux": 217, + "xkb": 225, + "windows": 57445, + "macos": null + }, + "keyCodes": { + "android": [ + 84 + ] + } + }, + "browserHome": { + "names": { + "domkey": "BrowserHome", + "android": null, + "english": "Browser Home", + "chromium": "browserHome" + }, + "scanCodes": { + "android": null, + "usb": 786979, + "linux": 172, + "xkb": 180, + "windows": 57394, + "macos": null + }, + "keyCodes": { + "android": null + } + }, + "browserBack": { + "names": { + "domkey": "BrowserBack", + "android": null, + "english": "Browser Back", + "chromium": "browserBack" + }, + "scanCodes": { + "android": null, + "usb": 786980, + "linux": 158, + "xkb": 166, + "windows": 57450, + "macos": null + }, + "keyCodes": { + "android": null + } + }, + "browserForward": { + "names": { + "domkey": "BrowserForward", + "android": [ + "FORWARD" + ], + "english": "Browser Forward", + "chromium": "browserForward" + }, + "scanCodes": { + "android": [ + 159 + ], + "usb": 786981, + "linux": 159, + "xkb": 167, + "windows": 57449, + "macos": null + }, + "keyCodes": { + "android": [ + 125 + ] + } + }, + "browserStop": { + "names": { + "domkey": "BrowserStop", + "android": null, + "english": "Browser Stop", + "chromium": "browserStop" + }, + "scanCodes": { + "android": null, + "usb": 786982, + "linux": 128, + "xkb": 136, + "windows": 57448, + "macos": null + }, + "keyCodes": { + "android": null + } + }, + "browserRefresh": { + "names": { + "domkey": "BrowserRefresh", + "android": null, + "english": "Browser Refresh", + "chromium": "browserRefresh" + }, + "scanCodes": { + "android": null, + "usb": 786983, + "linux": 173, + "xkb": 181, + "windows": 57447, + "macos": null + }, + "keyCodes": { + "android": null + } + }, + "browserFavorites": { + "names": { + "domkey": "BrowserFavorites", + "android": [ + "BOOKMARK" + ], + "english": "Browser Favorites", + "chromium": "browserFavorites" + }, + "scanCodes": { + "android": [ + 156 + ], + "usb": 786986, + "linux": 156, + "xkb": 164, + "windows": 57446, + "macos": null + }, + "keyCodes": { + "android": [ + 174 + ] + } + }, + "zoomIn": { + "names": { + "domkey": null, + "android": [ + "ZOOM_IN" + ], + "english": "Zoom In", + "chromium": "zoomIn" + }, + "scanCodes": { + "android": null, + "usb": 786989, + "linux": 418, + "xkb": 426, + "windows": null, + "macos": null + }, + "keyCodes": { + "android": [ + 168 + ] + } + }, + "zoomOut": { + "names": { + "domkey": null, + "android": [ + "ZOOM_OUT" + ], + "english": "Zoom Out", + "chromium": "zoomOut" + }, + "scanCodes": { + "android": null, + "usb": 786990, + "linux": 419, + "xkb": 427, + "windows": null, + "macos": null + }, + "keyCodes": { + "android": [ + 169 + ] + } + }, + "zoomToggle": { + "names": { + "domkey": "ZoomToggle", + "android": [ + "TV_ZOOM_MODE" + ], + "english": "Zoom Toggle", + "chromium": "zoomToggle" + }, + "scanCodes": { + "android": null, + "usb": 786994, + "linux": null, + "xkb": null, + "windows": null, + "macos": null + }, + "keyCodes": { + "android": [ + 255 + ] + } + }, + "redo": { + "names": { + "domkey": null, + "android": [ + "REDO" + ], + "english": "Redo", + "chromium": "redo" + }, + "scanCodes": { + "android": [ + 182 + ], + "usb": 787065, + "linux": 182, + "xkb": 190, + "windows": null, + "macos": null + }, + "keyCodes": { + "android": null + } + }, + "mailReply": { + "names": { + "domkey": "MailReply", + "android": null, + "english": "Mail Reply", + "chromium": "mailReply" + }, + "scanCodes": { + "android": null, + "usb": 787081, + "linux": 232, + "xkb": 240, + "windows": null, + "macos": null + }, + "keyCodes": { + "android": null + } + }, + "mailForward": { + "names": { + "domkey": "MailForward", + "android": null, + "english": "Mail Forward", + "chromium": "mailForward" + }, + "scanCodes": { + "android": null, + "usb": 787083, + "linux": 233, + "xkb": 241, + "windows": null, + "macos": null + }, + "keyCodes": { + "android": null + } + }, + "mailSend": { + "names": { + "domkey": "MailSend", + "android": null, + "english": "Mail Send", + "chromium": "mailSend" + }, + "scanCodes": { + "android": null, + "usb": 787084, + "linux": 231, + "xkb": 239, + "windows": null, + "macos": null + }, + "keyCodes": { + "android": null + } + } +} \ No newline at end of file diff --git a/dev/tools/gen_keycodes/data/key_name_to_android_name.json b/dev/tools/gen_keycodes/data/key_name_to_android_name.json new file mode 100644 index 00000000000..f51ec020058 --- /dev/null +++ b/dev/tools/gen_keycodes/data/key_name_to_android_name.json @@ -0,0 +1,252 @@ +{ + "again": ["AGAIN"], + "altLeft": ["ALT_LEFT"], + "altRight": ["ALT_RIGHT"], + "appSwitch": ["APP_SWITCH"], + "arrowDown": ["DPAD_DOWN"], + "arrowLeft": ["DPAD_LEFT"], + "arrowRight": ["DPAD_RIGHT"], + "arrowUp": ["DPAD_UP"], + "audioVolumeDown": ["VOLUME_DOWN"], + "audioVolumeMute": ["VOLUME_MUTE"], + "audioVolumeUp": ["VOLUME_UP"], + "avrInput": ["AVR_INPUT"], + "avrPower": ["AVR_POWER"], + "bassBoost": ["BASSBOOST"], + "print": ["PRINT"], + "backquote": ["GRAVE"], + "backslash": ["BACKSLASH"], + "backspace": ["DEL"], + "bracketLeft": ["LEFT_BRACKET"], + "bracketRight": ["RIGHT_BRACKET"], + "brightnessDown": ["BRIGHTNESS_DOWN"], + "brightnessUp": ["BRIGHTNESS_UP"], + "browserFavorites": ["BOOKMARK"], + "browserForward": ["FORWARD"], + "browserSearch": ["SEARCH"], + "call": ["CALL"], + "camera": ["CAMERA"], + "cameraFocus": ["FOCUS"], + "capsLock": ["CAPS_LOCK"], + "channelDown": ["CHANNEL_DOWN"], + "channelUp": ["CHANNEL_UP"], + "clear": ["CLEAR"], + "close": ["MEDIA_CLOSE", "CLOSE"], + "closedCaptionToggle": ["CAPTIONS"], + "colorF0Red": ["PROG_RED"], + "colorF1Green": ["PROG_GREEN"], + "colorF2Yellow": ["PROG_YELLOW"], + "colorF3Blue": ["PROG_BLUE"], + "comma": ["COMMA"], + "contextMenu": ["MENU"], + "controlLeft": ["CTRL_LEFT"], + "controlRight": ["CTRL_RIGHT"], + "convert": ["HENKAN"], + "copy": ["COPY"], + "cut": ["CUT"], + "delete": ["FORWARD_DEL"], + "digit0": ["0"], + "digit1": ["1"], + "digit2": ["2"], + "digit3": ["3"], + "digit4": ["4"], + "digit5": ["5"], + "digit6": ["6"], + "digit7": ["7"], + "digit8": ["8"], + "digit9": ["9"], + "dvr": ["DVR"], + "eisu": ["EISU"], + "eject": ["MEDIA_EJECT"], + "end": ["MOVE_END"], + "endCall": ["ENDCALL"], + "enter": ["ENTER"], + "equal": ["EQUALS"], + "escape": ["ESCAPE"], + "exit": ["EXIT"], + "f1": ["F1"], + "f2": ["F2"], + "f3": ["F3"], + "f4": ["F4"], + "f5": ["F5"], + "f6": ["F6"], + "f7": ["F7"], + "f8": ["F8"], + "f9": ["F9"], + "f10": ["F10"], + "f11": ["F11"], + "f12": ["F12"], + "f13": ["F13"], + "f14": ["F14"], + "f15": ["F15"], + "f16": ["F16"], + "f17": ["F17"], + "f18": ["F18"], + "f19": ["F19"], + "f20": ["F20"], + "f21": ["F21"], + "f22": ["F22"], + "f23": ["F23"], + "f24": ["F24"], + "find": ["FIND"], + "fn": ["FUNCTION"], + "goBack": ["BACK"], + "goHome": ["HOME"], + "groupNext": ["LANGUAGE_SWITCH"], + "guide": ["GUIDE"], + "headsetHook": ["HEADSETHOOK"], + "help": ["HELP"], + "hiraganaKatakana": ["KATAKANA_HIRAGANA"], + "home": ["MOVE_HOME"], + "info": ["INFO"], + "insert": ["INSERT"], + "kanjiMode": ["KANA"], + "keyA": ["A"], + "keyB": ["B"], + "keyC": ["C"], + "keyD": ["D"], + "keyE": ["E"], + "keyF": ["F"], + "keyG": ["G"], + "keyH": ["H"], + "keyI": ["I"], + "keyJ": ["J"], + "keyK": ["K"], + "keyL": ["L"], + "keyM": ["M"], + "keyN": ["N"], + "keyO": ["O"], + "keyP": ["P"], + "keyQ": ["Q"], + "keyR": ["R"], + "keyS": ["S"], + "keyT": ["T"], + "keyU": ["U"], + "keyV": ["V"], + "keyW": ["W"], + "keyX": ["X"], + "keyY": ["Y"], + "keyZ": ["Z"], + "lang3": ["KATAKANA"], + "lang4": ["HIRAGANA"], + "launchAssistant": ["ASSIST"], + "launchCalculator": ["CALCULATOR"], + "launchCalendar": ["CALENDAR"], + "launchContacts": ["CONTACTS"], + "launchMail": ["ENVELOPE"], + "launchMusicPlayer": ["MUSIC"], + "launchWebBrowser": ["EXPLORER"], + "mannerMode": ["MANNER_MODE"], + "mediaAudioTrack": ["MEDIA_AUDIO_TRACK"], + "mediaFastForward": ["MEDIA_FAST_FORWARD"], + "mediaLast": ["LAST_CHANNEL"], + "mediaPause": ["MEDIA_PAUSE"], + "mediaPlay": ["MEDIA_PLAY"], + "mediaPlayPause": ["MEDIA_PLAY_PAUSE"], + "mediaRecord": ["MEDIA_RECORD"], + "mediaRewind": ["MEDIA_REWIND"], + "mediaSkipBackward": ["MEDIA_SKIP_BACKWARD"], + "mediaSkipForward": ["MEDIA_SKIP_FORWARD"], + "mediaStepBackward": ["MEDIA_STEP_BACKWARD"], + "mediaStepForward": ["MEDIA_STEP_FORWARD"], + "mediaStop": ["MEDIA_STOP"], + "mediaTopMenu": ["MEDIA_TOP_MENU"], + "mediaTrackNext": ["MEDIA_NEXT"], + "mediaTrackPrevious": ["MEDIA_PREVIOUS"], + "metaLeft": ["META_LEFT"], + "metaRight": ["META_RIGHT"], + "microphoneVolumeMute": ["MUTE"], + "minus": ["MINUS"], + "modeChange": ["SWITCH_CHARSET"], + "navigateIn": ["NAVIGATE_IN"], + "navigateNext": ["NAVIGATE_NEXT"], + "navigateOut": ["NAVIGATE_OUT"], + "navigatePrevious": ["NAVIGATE_PREVIOUS"], + "newKey": ["NEW"], + "nonConvert": ["MUHENKAN"], + "none": ["UNKNOWN"], + "notification": ["NOTIFICATION"], + "numLock": ["NUM_LOCK"], + "numpad0": ["NUMPAD_0"], + "numpad1": ["NUMPAD_1"], + "numpad2": ["NUMPAD_2"], + "numpad3": ["NUMPAD_3"], + "numpad4": ["NUMPAD_4"], + "numpad5": ["NUMPAD_5"], + "numpad6": ["NUMPAD_6"], + "numpad7": ["NUMPAD_7"], + "numpad8": ["NUMPAD_8"], + "numpad9": ["NUMPAD_9"], + "numpadAdd": ["NUMPAD_ADD"], + "numpadComma": ["NUMPAD_COMMA"], + "numpadDecimal": ["NUMPAD_DOT"], + "numpadDivide": ["NUMPAD_DIVIDE"], + "numpadEnter": ["NUMPAD_ENTER"], + "numpadEqual": ["NUMPAD_EQUALS"], + "numpadMultiply": ["NUMPAD_MULTIPLY"], + "numpadParenLeft": ["NUMPAD_LEFT_PAREN"], + "numpadParenRight": ["NUMPAD_RIGHT_PAREN"], + "numpadSubtract": ["NUMPAD_SUBTRACT"], + "open": ["OPEN"], + "pageDown": ["PAGE_DOWN"], + "pageUp": ["PAGE_UP"], + "pairing": ["PAIRING"], + "paste": ["PASTE"], + "pause": ["BREAK"], + "period": ["PERIOD"], + "power": ["POWER"], + "printScreen": ["SYSRQ"], + "props": ["PROPS"], + "quote": ["APOSTROPHE"], + "redo": ["REDO"], + "scrollLock": ["SCROLL_LOCK"], + "semicolon": ["SEMICOLON"], + "settings": ["SETTINGS"], + "shiftLeft": ["SHIFT_LEFT"], + "shiftRight": ["SHIFT_RIGHT"], + "slash": ["SLASH"], + "sleep": ["SLEEP"], + "space": ["SPACE"], + "standby": ["SLEEP"], + "stbInput": ["STB_INPUT"], + "stbPower": ["STB_POWER"], + "suspend": ["SUSPEND"], + "symbol": ["SYM"], + "tab": ["TAB"], + "teletext": ["TV_TELETEXT"], + "tv": ["TV"], + "tv3dMode": ["3D_MODE"], + "tvAntennaCable": ["TV_ANTENNA_CABLE"], + "tvAudioDescription": ["TV_AUDIO_DESCRIPTION"], + "tvAudioDescriptionMixDown": ["TV_AUDIO_DESCRIPTION_MIX_DOWN"], + "tvAudioDescriptionMixUp": ["TV_AUDIO_DESCRIPTION_MIX_UP"], + "tvContentsMenu": ["TV_CONTENTS_MENU"], + "tvDataService": ["TV_DATA_SERVICE"], + "tvInput": ["TV_INPUT"], + "tvInputComponent1": ["TV_INPUT_COMPONENT_1"], + "tvInputComponent2": ["TV_INPUT_COMPONENT_2"], + "tvInputComposite1": ["TV_INPUT_COMPOSITE_1"], + "tvInputComposite2": ["TV_INPUT_COMPOSITE_2"], + "tvInputHdmi1": ["TV_INPUT_HDMI_1"], + "tvInputHdmi2": ["TV_INPUT_HDMI_2"], + "tvInputHdmi3": ["TV_INPUT_HDMI_3"], + "tvInputHdmi4": ["TV_INPUT_HDMI_4"], + "tvInputVga1": ["TV_INPUT_VGA_1"], + "tvNetwork": ["TV_NETWORK"], + "tvNumberEntry": ["TV_NUMBER_ENTRY"], + "tvPower": ["TV_POWER"], + "tvRadioService": ["TV_RADIO_SERVICE"], + "tvSatellite": ["TV_SATELLITE"], + "tvSatelliteBs": ["TV_SATELLITE_BS"], + "tvSatelliteCs": ["TV_SATELLITE_CS"], + "tvSatelliteToggle": ["TV_SATELLITE_SERVICE"], + "tvTerrestrialAnalog": ["TV_TERRESTRIAL_ANALOG"], + "tvTerrestrialDigital": ["TV_TERRESTRIAL_DIGITAL"], + "tvTimer": ["TV_TIMER_PROGRAMMING"], + "undo": ["UNDO"], + "wakeUp": ["WAKEUP"], + "zenkakuHankaku": ["ZENKAKU_HANKAKU"], + "zoomIn": ["ZOOM_IN"], + "zoomOut": ["ZOOM_OUT"], + "zoomToggle": ["TV_ZOOM_MODE"] +} diff --git a/dev/tools/gen_keycodes/data/keyboard_key.tmpl b/dev/tools/gen_keycodes/data/keyboard_key.tmpl new file mode 100644 index 00000000000..69590955bd6 --- /dev/null +++ b/dev/tools/gen_keycodes/data/keyboard_key.tmpl @@ -0,0 +1,238 @@ +// Copyright 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// DO NOT EDIT -- DO NOT EDIT -- DO NOT EDIT +// This file is generated by dev/tools/gen_keycodes/bin/gen_keycodes.dart and +// should not be edited directly. +// +// Edit the template dev/tools/gen_keycodes/data/keyboard_key.tmpl instead. +// See dev/tools/gen_keycodes/README.md for more information. + +import 'package:flutter/foundation.dart'; + +/// A class with static values that describe the keys that are returned from +/// [RawKeyEvent.logicalKey]. +/// +/// These represent *logical* keys, which are keys which are interpreted in the +/// context of any modifiers, modes, or keyboard layouts which may be in effect. +/// +/// This is contrast to [PhysicalKeyboardKey], which represents a physical key +/// in a particular location on the keyboard, without regard for the modifier +/// state, mode, or keyboard layout. +/// +/// See also: +/// +/// * [RawKeyEvent], the keyboard event object received by widgets that listen +/// to keyboard events. +/// * [RawKeyboardListener], a widget used to listen to and supply handlers for +/// keyboard events. +class LogicalKeyboardKey extends Diagnosticable { + /// Defines a KeyboardKey value and optional debug name. + /// + /// To save executable size, it is recommended that the [debugName] be null in + /// release mode. You can do this using the [kReleaseMode] constant: + /// + /// {@tool sample} + /// const LogicalKeyboardKey mySpecialKey = LogicalKeyboardKey( + /// 0x0010000000a, + /// debugName: kReleaseMode ? null : 'Special Key', + /// ); + /// {@end-tool} + const LogicalKeyboardKey(this.keyId, {this.debugName, this.keyLabel}); + + /// A unique code representing this key. + /// + /// This is an opaque identifier, and should not be unpacked to derive + /// information from it, as the representation of the code could change at any + /// time. + final int keyId; + + /// The debug string to print for this keyboard key, which will be null in + /// release mode. + final String debugName; + + /// The Unicode string representing the character produced by a [RawKeyEvent]. + /// + /// This value is useful for describing or matching mnemonic keyboard + /// shortcuts. + /// + /// On most platforms this is a single code point, but it could contain any + /// Unicode string. The `keyLabel` differs from [RawKeyEvent.character] + /// because `keyLabel` only takes into account the key being pressed, not any + /// combining keys pressed before it, so, for example, an “o” that follows a + /// combining dieresis (“¨”, COMBINING DIAERESIS (U+0308)) would just return + /// “o” for [keyLabel], but would return “ö” for [RawKeyEvent.character]. + /// + /// {@macro flutter.services.RawKeyEventData.keyLabel} + final String keyLabel; + + @override + int get hashCode => keyId.hashCode; + + @override + bool operator ==(dynamic other) { + if (other.runtimeType != runtimeType) { + return false; + } + final LogicalKeyboardKey typedOther = other; + return keyId == typedOther.keyId; + } + + /// Finds the [LogicalKeyboardKey] that matches the given ID. + static LogicalKeyboardKey findKeyByKeyId(int keyId) => _knownLogicalKeys[keyId]; + + @override + void debugFillProperties(DiagnosticPropertiesBuilder properties) { + super.debugFillProperties(properties); + properties.add(StringProperty('keyId', '0x${keyId.toRadixString(16).padLeft(8, '0')}', showName: true)); + properties.add(StringProperty('keyLabel', keyLabel, showName: true)); + properties.add(StringProperty('debugName', debugName, showName: true, defaultValue: null)); + } + + /// Returns true if the given label represents a Unicode control character. + /// + /// Examples of control characters are characters like "U+000A LINE FEED (LF)" + /// or "U+001B ESCAPE (ESC)". + /// + /// See for more + /// information. + /// + /// Used by [RawKeyEvent] subclasses to help construct IDs. + static bool isControlCharacter(String label) { + if (label.length > 1) { + return false; + } + final int codeUnit = label.codeUnitAt(0); + return (codeUnit <= 0x1f && codeUnit >= 0x00) || (codeUnit >= 0x7f && codeUnit <= 0x9f); + } + + /// Returns true if the [keyId] of this object is one that is autogenerated by + /// Flutter. + /// + /// Autogenerated key IDs are generated in response to platform key codes + /// which Flutter doesn't recognize, and their IDs shouldn't be used in a + /// persistent way. + /// + /// Autogenerated IDs should be a rare occurrence: Flutter supports most keys. + /// + /// Keys that generate Unicode characters (even if unknown to Flutter) will + /// not return true for `isAutogenerated`, since they will be assigned a + /// Unicode-based code that will remain stable. + /// + /// If Flutter adds support for a previously unsupported key code, the ID it + /// reports will change, but the ID will remain stable on the platform it is + /// produced on until Flutter adds support for recognizing it. + /// + /// So, hypothetically, if Android added a new key code of 0xffff, + /// representing a new "do what I mean" key, then the autogenerated code would + /// be 0x1020000ffff, but once Flutter added the "doWhatIMean" key to the + /// definitions below, the new code would be 0x0020000ffff for all platforms + /// that had a "do what I mean" key from then on. + bool get isAutogenerated => (keyId & autogeneratedMask) != 0; + + /// Mask for the 32-bit value portion of the key code. This is used by + /// platform-specific code to generate Flutter key codes. + static const int valueMask = 0x000FFFFFFFF; + + /// Mask for the platform prefix portion of the key code. This is used by + /// platform-specific code to generate Flutter key codes. + static const int platformMask = 0x0FF00000000; + + /// Mask for the autogenerated bit portion of the key code. This is used by + /// platform-specific code to generate new Flutter key codes for keys which + /// are not recognized. + static const int autogeneratedMask = 0x10000000000; + + /// The code prefix for keys which have a Unicode representation. This is used + /// by platform-specific code to generate Flutter key codes. + static const int unicodePlane = 0x00000000000; + + /// The code prefix for keys which do not have a Unicode representation. This + /// is used by platform-specific code to generate Flutter key codes using HID + /// Usage codes. + static const int hidPlane = 0x00100000000; +@@@LOGICAL_KEY_DEFINITIONS@@@ + // A list of all predefined constant LogicalKeyboardKeys so they can be + // searched.. + static const Map _knownLogicalKeys = { +@@@LOGICAL_KEY_MAP@@@ + }; +} + +/// A class with static values that describe the keys that are returned from +/// [RawKeyEvent.physicalKey]. +/// +/// These represent *physical* keys, which are keys which represent a +/// particular key location on the keyboard. It ignores any modifiers, modes, +/// or keyboard layouts which may be in effect. This is contrast to +/// [LogicalKeyboardKey], which represents a logical key interpreted in the +/// context of modifiers, modes, and/or keyboard layouts. +/// +/// See also: +/// +/// * [RawKeyEvent], the keyboard event object received by widgets that listen +/// to keyboard events. +/// * [RawKeyboardListener], a widget used to listen to and supply handlers for +/// keyboard events. +class PhysicalKeyboardKey extends Diagnosticable { + /// Defines a KeyboardKey value and optional debug name. The debug name must + /// be null in release mode. + /// + /// To save executable size, it is recommended that the [debugName] be null in + /// release mode. You can do this using the [kReleaseMode] constant: + /// + /// {@tool sample} + /// const PhysicalKeyboardKey mySpecialPhysicalKey = PhysicalKeyboardKey( + /// 0x0010000000a, + /// debugName: kReleaseMode ? null : 'Special Key', + /// ); + /// {@end-tool} + const PhysicalKeyboardKey(this.usbHidUsage, {this.debugName}); + + /// The unique USB HID usage ID of this physical key on the keyboard. + /// + /// Due to the variations in platform APIs, this may not be the actual HID + /// usage code from the hardware, but a value derived from available + /// information on the platform. + /// + /// See + /// for the HID usage values and their meanings. + final int usbHidUsage; + + /// The debug string to print for this keyboard key, which will be null in + /// release mode. + final String debugName; + + /// Finds a known [PhysicalKeyboardKey] that matches the given USB HID usage + /// code. + static PhysicalKeyboardKey findKeyByCode(int usageCode) => _knownPhysicalKeys[usageCode]; + + @override + int get hashCode => usbHidUsage.hashCode; + + @override + bool operator ==(dynamic other) { + if (other.runtimeType != runtimeType) { + return false; + } + final PhysicalKeyboardKey typedOther = other; + return usbHidUsage == typedOther.usbHidUsage; + } + + @override + void debugFillProperties(DiagnosticPropertiesBuilder properties) { + super.debugFillProperties(properties); + properties.add(StringProperty('usbHidUsage', '0x${usbHidUsage.toRadixString(16).padLeft(8, '0')}', showName: true)); + properties.add(StringProperty('debugName', debugName, showName: true, defaultValue: null)); + } + + // Key constants for all keyboard keys in the USB HID specification at the + // time Flutter was built. +@@@PHYSICAL_KEY_DEFINITIONS@@@ + // A list of all the predefined constant PhysicalKeyboardKeys so that they + // can be searched. + static const Map _knownPhysicalKeys = { +@@@PHYSICAL_KEY_MAP@@@ + }; +} diff --git a/dev/tools/gen_keycodes/data/keyboard_maps.tmpl b/dev/tools/gen_keycodes/data/keyboard_maps.tmpl new file mode 100644 index 00000000000..a52371b4280 --- /dev/null +++ b/dev/tools/gen_keycodes/data/keyboard_maps.tmpl @@ -0,0 +1,40 @@ +// Copyright 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// DO NOT EDIT -- DO NOT EDIT -- DO NOT EDIT +// This file is generated by dev/tools/gen_keycodes/bin/gen_keycodes.dart and +// should not be edited directly. +// +// Edit dev/tools/gen_keycodes/data/keyboard_maps.tmpl instead. +// See dev/tools/gen_keycodes/README.md for more information. + +import 'keyboard_key.dart'; + +/// Maps Android-specific key codes to the matching [LogicalKeyboardKey]. +const Map kAndroidToLogicalKey = { +@@@ANDROID_KEY_CODE_MAP@@@ +}; + +/// Maps Android-specific scan codes to the matching [PhysicalKeyboardKey]. +const Map kAndroidToPhysicalKey = { +@@@ANDROID_SCAN_CODE_MAP@@@ +}; + +/// A map of Android key codes which have printable representations, but appear +/// on the number pad. Used to provide different key objects for keys like +/// KEY_EQUALS and NUMPAD_EQUALS. +const Map kAndroidNumPadMap = { +@@@ANDROID_NUMPAD_MAP@@@ +}; + +/// Maps Fuchsia-specific IDs to the matching [LogicalKeyboardKey]. +const Map kFuchsiaToLogicalKey = { +@@@FUCHSIA_KEY_CODE_MAP@@@ +}; + +/// Maps Fuchsia-specific USB HID Usage IDs to the matching +/// [PhysicalKeyboardKey]. +const Map kFuchsiaToPhysicalKey = { +@@@FUCHSIA_SCAN_CODE_MAP@@@ +}; diff --git a/dev/tools/gen_keycodes/data/printable.json b/dev/tools/gen_keycodes/data/printable.json new file mode 100644 index 00000000000..ae2ef3a8a0f --- /dev/null +++ b/dev/tools/gen_keycodes/data/printable.json @@ -0,0 +1,69 @@ +{ + "backquote": "`", + "backslash": "\\", + "bracketLeft": "[", + "bracketRight": "]", + "comma": ",", + "digit0": "0", + "digit1": "1", + "digit2": "2", + "digit3": "3", + "digit4": "4", + "digit5": "5", + "digit6": "6", + "digit7": "7", + "digit8": "8", + "digit9": "9", + "equal": "=", + "keyA": "A", + "keyB": "B", + "keyC": "C", + "keyD": "D", + "keyE": "E", + "keyF": "F", + "keyG": "G", + "keyH": "H", + "keyI": "I", + "keyJ": "J", + "keyK": "K", + "keyL": "L", + "keyM": "M", + "keyN": "N", + "keyO": "O", + "keyP": "P", + "keyQ": "Q", + "keyR": "R", + "keyS": "S", + "keyT": "T", + "keyU": "U", + "keyV": "V", + "keyW": "W", + "keyX": "X", + "keyY": "Y", + "keyZ": "Z", + "minus": "-", + "numpad0": "0", + "numpad1": "1", + "numpad2": "2", + "numpad3": "3", + "numpad4": "4", + "numpad5": "5", + "numpad6": "6", + "numpad7": "7", + "numpad8": "8", + "numpad9": "9", + "numpadAdd": "+", + "numpadComma": ",", + "numpadDecimal": ".", + "numpadDivide": "/", + "numpadEqual": "=", + "numpadMultiply": "*", + "numpadParenLeft": "(", + "numpadParenRight": ")", + "numpadSubtract": "-", + "period": ".", + "quote": "'", + "semicolon": ";", + "slash": "/", + "space": " " +} diff --git a/dev/tools/gen_keycodes/lib/code_gen.dart b/dev/tools/gen_keycodes/lib/code_gen.dart new file mode 100644 index 00000000000..f2a86ceaeb8 --- /dev/null +++ b/dev/tools/gen_keycodes/lib/code_gen.dart @@ -0,0 +1,195 @@ +// Copyright 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'dart:io'; +import 'package:path/path.dart' as path; + +import 'package:gen_keycodes/key_data.dart'; +import 'package:gen_keycodes/utils.dart'; + +/// Generates the keyboard_keys.dart and keyboard_maps.dart files, based on the +/// information in the key data structure given to it. +class CodeGenerator { + CodeGenerator(this.keyData); + + /// Given an [input] string, wraps the text at 80 characters and prepends each + /// line with the [prefix] string. Use for generated comments. + String wrapString(String input, String prefix) { + final int wrapWidth = 80 - prefix.length; + final StringBuffer result = StringBuffer(); + final List words = input.split(RegExp(r'\s+')); + String currentLine = words.removeAt(0); + for (String word in words) { + if ((currentLine.length + word.length) < wrapWidth) { + currentLine += ' $word'; + } else { + result.writeln('$prefix$currentLine'); + currentLine = '$word'; + } + } + if (currentLine.isNotEmpty) { + result.writeln('$prefix$currentLine'); + } + return result.toString(); + } + + /// Gets the generated definitions of PhysicalKeyboardKeys. + String get physicalDefinitions { + final StringBuffer definitions = StringBuffer(); + for (Key entry in keyData.data) { + final String comment = wrapString('Represents the location of a ' + '"${entry.commentName}" key on a generalized keyboard. See the function ' + '[RawKeyEvent.physicalKey] for more information.', ' /// '); + definitions.write(''' + +$comment static const PhysicalKeyboardKey ${entry.constantName} = PhysicalKeyboardKey(${toHex(entry.usbHidCode, digits: 8)}, debugName: kReleaseMode ? null : '${entry.commentName}'); +'''); + } + return definitions.toString(); + } + + /// Gets the generated definitions of LogicalKeyboardKeys. + String get logicalDefinitions { + String escapeLabel(String label) => label.contains("'") ? 'r"$label"' : "r'$label'"; + final StringBuffer definitions = StringBuffer(); + for (Key entry in keyData.data) { + final String comment = wrapString('Represents a logical "${entry.commentName}" key on the ' + 'keyboard. See the function [RawKeyEvent.logicalKey] for more information.', ' /// '); + if (entry.keyLabel == null) { + definitions.write(''' + +$comment static const LogicalKeyboardKey ${entry.constantName} = LogicalKeyboardKey(${toHex(entry.flutterId, digits: 11)}, debugName: kReleaseMode ? null : '${entry.commentName}'); +'''); + } else { + definitions.write(''' + +$comment static const LogicalKeyboardKey ${entry.constantName} = LogicalKeyboardKey(${toHex(entry.flutterId, digits: 11)}, keyLabel: ${escapeLabel(entry.keyLabel)}, debugName: kReleaseMode ? null : '${entry.commentName}'); +'''); + } + } + return definitions.toString(); + } + + /// This generates the map of USB HID codes to physical keys. + String get predefinedHidCodeMap { + final StringBuffer scanCodeMap = StringBuffer(); + for (Key entry in keyData.data) { + scanCodeMap.writeln(' ${toHex(entry.usbHidCode)}: ${entry.constantName},'); + } + return scanCodeMap.toString().trimRight(); + } + + /// THis generates the map of Flutter key codes to logical keys. + String get predefinedKeyCodeMap { + final StringBuffer keyCodeMap = StringBuffer(); + for (Key entry in keyData.data) { + keyCodeMap.writeln(' ${toHex(entry.flutterId, digits: 10)}: ${entry.constantName},'); + } + return keyCodeMap.toString().trimRight(); + } + + /// This generates the map of Android key codes to logical keys. + String get androidKeyCodeMap { + final StringBuffer androidKeyCodeMap = StringBuffer(); + for (Key entry in keyData.data) { + if (entry.androidKeyCodes != null) { + for (int code in entry.androidKeyCodes.cast()) { + androidKeyCodeMap.writeln(' $code: LogicalKeyboardKey.${entry.constantName},'); + } + } + } + return androidKeyCodeMap.toString().trimRight(); + } + + /// This generates the map of Android number pad key codes to logical keys. + String get androidNumpadMap { + final StringBuffer androidKeyCodeMap = StringBuffer(); + final List onlyNumpads = keyData.data.where((Key entry) { + return entry.constantName.startsWith('numpad') && entry.keyLabel != null; + }).toList(); + for (Key entry in onlyNumpads) { + if (entry.androidKeyCodes != null) { + for (int code in entry.androidKeyCodes.cast()) { + androidKeyCodeMap.writeln(' $code: LogicalKeyboardKey.${entry.constantName},'); + } + } + } + return androidKeyCodeMap.toString().trimRight(); + } + + /// This generates the map of Android scan codes to physical keys. + String get androidScanCodeMap { + final StringBuffer androidScanCodeMap = StringBuffer(); + for (Key entry in keyData.data) { + if (entry.androidScanCodes != null) { + for (int code in entry.androidScanCodes.cast()) { + androidScanCodeMap.writeln(' $code: PhysicalKeyboardKey.${entry.constantName},'); + } + } + } + return androidScanCodeMap.toString().trimRight(); + } + + /// This generates the map of Fuchsia key codes to logical keys. + String get fuchsiaKeyCodeMap { + final StringBuffer fuchsiaKeyCodeMap = StringBuffer(); + for (Key entry in keyData.data) { + if (entry.usbHidCode != null) { + fuchsiaKeyCodeMap.writeln(' ${toHex(entry.flutterId)}: LogicalKeyboardKey.${entry.constantName},'); + } + } + return fuchsiaKeyCodeMap.toString().trimRight(); + } + + /// This generates the map of Fuchsia USB HID codes to physical keys. + String get fuchsiaHidCodeMap { + final StringBuffer fuchsiaScanCodeMap = StringBuffer(); + for (Key entry in keyData.data) { + if (entry.usbHidCode != null) { + fuchsiaScanCodeMap.writeln(' ${toHex(entry.usbHidCode)}: PhysicalKeyboardKey.${entry.constantName},'); + } + } + return fuchsiaScanCodeMap.toString().trimRight(); + } + + /// Substitutes the various maps and definitions into the template file for + /// keyboard_key.dart. + String generateKeyboardKeys() { + final Map mappings = { + 'PHYSICAL_KEY_MAP': predefinedHidCodeMap, + 'LOGICAL_KEY_MAP': predefinedKeyCodeMap, + 'LOGICAL_KEY_DEFINITIONS': logicalDefinitions, + 'PHYSICAL_KEY_DEFINITIONS': physicalDefinitions, + }; + + final String template = File(path.join(flutterRoot.path, 'dev', 'tools', 'gen_keycodes', 'data', 'keyboard_key.tmpl')).readAsStringSync(); + return _injectDictionary(template, mappings); + } + + /// Substitutes the various platform specific maps into the template file for + /// keyboard_maps.dart. + String generateKeyboardMaps() { + final Map mappings = { + 'ANDROID_SCAN_CODE_MAP': androidScanCodeMap, + 'ANDROID_KEY_CODE_MAP': androidKeyCodeMap, + 'ANDROID_NUMPAD_MAP': androidNumpadMap, + 'FUCHSIA_SCAN_CODE_MAP': fuchsiaHidCodeMap, + 'FUCHSIA_KEY_CODE_MAP': fuchsiaKeyCodeMap, + }; + + final String template = File(path.join(flutterRoot.path, 'dev', 'tools', 'gen_keycodes', 'data', 'keyboard_maps.tmpl')).readAsStringSync(); + return _injectDictionary(template, mappings); + } + + /// The database of keys loaded from disk. + final KeyData keyData; + + static String _injectDictionary(String template, Map dictionary) { + String result = template; + for (String key in dictionary.keys) { + result = result.replaceAll('@@@$key@@@', dictionary[key]); + } + return result; + } +} diff --git a/dev/tools/gen_keycodes/lib/key_data.dart b/dev/tools/gen_keycodes/lib/key_data.dart new file mode 100644 index 00000000000..d2e66fe0f54 --- /dev/null +++ b/dev/tools/gen_keycodes/lib/key_data.dart @@ -0,0 +1,365 @@ +// Copyright 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'dart:convert'; +import 'dart:io'; + +import 'package:path/path.dart' as path; +import 'package:meta/meta.dart'; + +import 'package:gen_keycodes/utils.dart'; + +/// The data structure used to manage keyboard key entries. +/// +/// The main constructor parses the given input data into the data structure. +/// +/// The data structure can be also loaded and saved to JSON, with the +/// [KeyData.fromJson] constructor and [toJson] method, respectively. +class KeyData { + /// Parses the input data given in from the various data source files, + /// populating the data structure. + /// + /// None of the parameters may be null. + KeyData( + String chromiumHidCodes, + String androidKeyboardLayout, + String androidKeyCodeHeader, + String androidNameMap, + ) : assert(chromiumHidCodes != null), + assert(androidKeyboardLayout != null), + assert(androidKeyCodeHeader != null), + assert(androidNameMap != null) { + _nameToAndroidScanCodes = _readAndroidScanCodes(androidKeyboardLayout); + _nameToAndroidKeyCode = _readAndroidKeyCodes(androidKeyCodeHeader); + final Map> dynamicNames = json.decode(androidNameMap).cast>(); + _nameToAndroidName = dynamicNames.map>((String key, List value) { + return MapEntry>(key, value.cast()); + }); + data = _readHidEntries(chromiumHidCodes); + } + + /// Parses the given JSON data and populates the data structure from it. + KeyData.fromJson(Map contentMap) { + data = []; + for (String key in contentMap.keys) { + data.add(Key.fromJsonMapEntry(key, contentMap[key])); + } + } + + /// Converts the data structure into a JSON structure that can be parsed by + /// [KeyData.fromJson]. + Map toJson() { + for (Key entry in data) { + entry.androidKeyNames = _nameToAndroidName[entry.constantName]?.cast(); + if (entry.androidKeyNames != null && entry.androidKeyNames.isNotEmpty) { + for (String androidKeyName in entry.androidKeyNames) { + if (_nameToAndroidKeyCode[androidKeyName] != null) { + entry.androidKeyCodes ??= []; + entry.androidKeyCodes.add(_nameToAndroidKeyCode[androidKeyName]); + } + if (_nameToAndroidScanCodes[androidKeyName] != null && _nameToAndroidScanCodes[androidKeyName].isNotEmpty) { + entry.androidScanCodes ??= []; + entry.androidScanCodes.addAll(_nameToAndroidScanCodes[androidKeyName]); + } + } + } + } + + final Map outputMap = {}; + for (Key entry in data) { + outputMap[entry.constantName] = entry.toJson(); + } + return outputMap; + } + + /// The list of keys. + List data; + + /// The mapping from the Flutter name (e.g. "eject") to the Android name (e.g. + /// "MEDIA_EJECT"). + /// + /// Only populated if data is parsed from the source files, not if parsed from + /// JSON. + Map> _nameToAndroidName; + + /// The mapping from the Android name (e.g. "MEDIA_EJECT") to the integer scan + /// code (physical location) of the key. + /// + /// Only populated if data is parsed from the source files, not if parsed from + /// JSON. + Map> _nameToAndroidScanCodes; + + /// The mapping from Android name (e.g. "MEDIA_EJECT") to the integer key code + /// (logical meaning) of the key. + /// + /// Only populated if data is parsed from the source files, not if parsed from + /// JSON. + Map _nameToAndroidKeyCode; + + /// Parses entries from Androids Generic.kl scan code data file. + /// + /// Lines in this file look like this (without the ///): + /// key 100 ALT_RIGHT + /// # key 101 "KEY_LINEFEED" + /// + /// We parse the commented out lines as well as the non-commented lines, so so + /// that we can get names for all of the available scan codes, not just ones + /// defined for the generic profile. + /// + /// Also, note that some keys (notably MEDIA_EJECT) can be mapped to more than + /// one scan code, so the mapping can't just be 1:1, it has to be 1:many. + Map> _readAndroidScanCodes(String keyboardLayout) { + final RegExp keyEntry = RegExp(r'''#?\s*key\s+([0-9]+)\s*"?(?:KEY_)?([0-9A-Z_]+|\(undefined\))"?\s*(FUNCTION)?'''); + final Map> result = >{}; + keyboardLayout.replaceAllMapped(keyEntry, (Match match) { + if (match.group(3) == 'FUNCTION') { + // Skip odd duplicate Android FUNCTION keys (F1-F12 are already defined). + return ''; + } + final String name = match.group(2); + if (name == '(undefined)') { + // Skip undefined scan codes. + return ''; + } + final String androidName = match.group(2); + result[androidName] ??= []; + result[androidName].add(int.parse(match.group(1))); + }); + + return result; + } + + /// Parses entries from Android's keycodes.h key code data file. + /// + /// Lines in this file look like this (without the ///): + /// /** Left Control modifier key. */ + /// AKEYCODE_CTRL_LEFT = 113, + Map _readAndroidKeyCodes(String headerFile) { + final RegExp enumBlock = RegExp(r'enum\s*\{(.*)\};', multiLine: true); + // Eliminate everything outside of the enum block. + headerFile = headerFile.replaceAllMapped(enumBlock, (Match match) => match.group(1)); + final RegExp enumEntry = RegExp(r'''AKEYCODE_([A-Z0-9_]+)\s*=\s*([0-9]+),?'''); + final Map result = {}; + headerFile.replaceAllMapped(enumEntry, (Match match) { + result[match.group(1)] = int.parse(match.group(2)); + }); + return result; + } + + /// Parses entries from Chromium's HID code mapping header file. + /// + /// Lines in this file look like this (without the ///): + /// USB evdev XKB Win Mac Code Enum + /// USB_KEYMAP(0x000010, 0x0000, 0x0000, 0x0000, 0xffff, "Hyper", HYPER), + List _readHidEntries(String input) { + final List entries = []; + final RegExp usbMapRegExp = RegExp( + r'''USB_KEYMAP\s*\(\s*0x([a-fA-F0-9]+),\s*0x([a-fA-F0-9]+),''' + r'''\s*0x([a-fA-F0-9]+),\s*0x([a-fA-F0-9]+),\s*0x([a-fA-F0-9]+),\s*"?([^\s]+?)"?,\s*([^\s]+?)\s*\)''', + multiLine: true); + final RegExp commentRegExp = RegExp(r'//.*$', multiLine: true); + input = input.replaceAll(commentRegExp, ''); + input.replaceAllMapped(usbMapRegExp, (Match match) { + if (match != null) { + final int macScanCode = getHex(match.group(5)); + final int linuxScanCode = getHex(match.group(2)); + final int xKbScanCode = getHex(match.group(3)); + final int windowsScanCode = getHex(match.group(4)); + final Key newEntry = Key( + usbHidCode: getHex(match.group(1)), + linuxScanCode: linuxScanCode == 0 ? null : linuxScanCode, + xKbScanCode: xKbScanCode == 0 ? null : xKbScanCode, + windowsScanCode: windowsScanCode == 0 ? null : windowsScanCode, + macOsScanCode: macScanCode == 0xffff ? null : macScanCode, + name: match.group(6) == 'NULL' ? null : match.group(6), + // The input data has a typo... + chromiumName: shoutingToLowerCamel(match.group(7)).replaceAll('Minimium', 'Minimum'), + ); + if (newEntry.chromiumName == 'none') { + newEntry.name = 'None'; + } + if (newEntry.name == 'IntlHash') { + // Skip key that is not actually generated by any keyboard. + return ''; + } + entries.add(newEntry); + } + return match.group(0); + }); + return entries; + } +} + +/// A single entry in the key data structure. +/// +/// Can be read from JSON with the [Key..fromJsonMapEntry] constructor, or +/// written with the [toJson] method. +class Key { + /// Creates a single key entry from available data. + /// + /// The [usbHidCode] and [chromiumName] parameters must not be null. + Key({ + String enumName, + this.name, + @required this.usbHidCode, + this.linuxScanCode, + this.xKbScanCode, + this.windowsScanCode, + this.macOsScanCode, + @required this.chromiumName, + this.androidKeyNames, + this.androidScanCodes, + this.androidKeyCodes, + }) : assert(usbHidCode != null), + assert(chromiumName != null), + _constantName = enumName; + + /// Populates the key from a JSON map. + factory Key.fromJsonMapEntry(String name, Map map) { + return Key( + enumName: name, + name: map['names']['domkey'], + chromiumName: map['names']['chromium'], + usbHidCode: map['scanCodes']['usb'], + androidKeyNames: map['names']['android']?.cast(), + androidScanCodes: map['scanCodes']['android']?.cast(), + androidKeyCodes: map['keyCodes']['android']?.cast(), + linuxScanCode: map['scanCodes']['linux'], + xKbScanCode: map['scanCodes']['xkb'], + windowsScanCode: map['scanCodes']['windows'], + macOsScanCode: map['scanCodes']['macos'], + ); + } + + /// The USB HID code of the key + int usbHidCode; + + /// The Linux scan code of the key, from Chromium's header file. + int linuxScanCode; + /// The XKb scan code of the key from Chromium's header file. + int xKbScanCode; + /// The Windows scan code of the key from Chromium's header file. + int windowsScanCode; + /// The macOS scan code of the key from Chromium's header file. + int macOsScanCode; + /// The name of the key, mostly derived from the DomKey name in Chromium, + /// but where there was no DomKey representation, derived from the Chromium + /// symbol name. + String name; + /// The Chromium symbol name for the key. + String chromiumName; + /// The list of names that Android gives to this key (symbol names minus the + /// prefix). + List androidKeyNames; + /// The list of Android key codes matching this key, created by looking up the + /// Android name in the Chromium data, and substituting the Android key code + /// value. + List androidKeyCodes; + /// The list of Android scan codes matching this key, created by looking up + /// the Android name in the Chromium data, and substituting the Android scan + /// code value. + List androidScanCodes; + + /// Creates a JSON map from the key data. + Map toJson() { + return { + 'names': { + 'domkey': name, + 'android': androidKeyNames, + 'english': commentName, + 'chromium': chromiumName, + }, + 'scanCodes': { + 'android': androidScanCodes, + 'usb': usbHidCode, + 'linux': linuxScanCode, + 'xkb': xKbScanCode, + 'windows': windowsScanCode, + 'macos': macOsScanCode, + }, + 'keyCodes': >{ + 'android': androidKeyCodes, + }, + }; + } + + /// Returns the printable representation of this key, if any. + /// + /// If there is no printable representation, returns null. + String get keyLabel => printable[constantName]; + + int get flutterId { + if (printable.containsKey(constantName) && !constantName.startsWith('numpad')) { + return unicodePlane | (keyLabel.codeUnitAt(0) & valueMask); + } + return hidPlane | (usbHidCode & valueMask); + } + + /// Gets the name of the key suitable for placing in comments. + /// + /// Takes the [constantName] and converts it from lower camel case to capitalized + /// separate words (e.g. "wakeUp" converts to "Wake Up"). + String get commentName { + String upperCamel = lowerCamelToUpperCamel(constantName); + upperCamel = upperCamel.replaceAllMapped(RegExp(r'(Digit|Numpad|Lang)([0-9]+)'), (Match match) => '${match.group(1)} ${match.group(2)}'); + return upperCamel.replaceAllMapped(RegExp(r'([A-Z])'), (Match match) => ' ${match.group(1)}').trim(); + } + + /// Gets the named used for the key constant in the definitions in + /// keyboard_keys.dart. + /// + /// If set by the constructor, returns the name set, but otherwise constructs + /// the name from the various different names available, making sure that the + /// name isn't a Dart reserved word (if it is, then it adds the word "Key" to + /// the end of the name). + String get constantName { + if (_constantName == null) { + String result; + if (name == null || name.isEmpty) { + // If it doesn't have a DomKey name then use the Chromium symbol name. + result = chromiumName; + } else { + result = upperCamelToLowerCamel(name); + } + if (kDartReservedWords.contains(result)) { + return '${result}Key'; + } + // Don't set enumName: we want it to regen each time if never set, but + // to stay set if set by the JSON loading. + return result; + } + return _constantName; + } + set constantName(String value) => _constantName = value; + String _constantName; + + @override + String toString() { + return """'$constantName': (name: "$name", usbHidCode: ${toHex(usbHidCode)}, """ + '''linuxKeyCode: ${toHex(linuxScanCode)}, xKbKeyCode: ${toHex(xKbScanCode)}, ''' + '''windowsKeyCode: ${toHex(windowsScanCode)}, macOsKeyCode: ${toHex(macOsScanCode)}, ''' + '''chromiumSymbolName: $chromiumName'''; + } + + /// Returns the static map of printable representations. + static Map get printable { + if (_printable == null) { + final String printableKeys = File(path.join(flutterRoot.path, 'dev', 'tools', 'gen_keycodes', 'data', 'printable.json',)).readAsStringSync(); + final Map printable = json.decode(printableKeys); + _printable = printable.cast(); + } + return _printable; + } + static Map _printable; + + /// Mask for the 32-bit value portion of the code. + static const int valueMask = 0x000FFFFFFFF; + + /// The code prefix for keys which have a Unicode representation. + static const int unicodePlane = 0x00000000000; + + /// The code prefix for keys which do not have a Unicode representation, but + /// do have a USB HID ID. + static const int hidPlane = 0x00100000000; +} diff --git a/dev/tools/gen_keycodes/lib/utils.dart b/dev/tools/gen_keycodes/lib/utils.dart new file mode 100644 index 00000000000..0d556cf71ea --- /dev/null +++ b/dev/tools/gen_keycodes/lib/utils.dart @@ -0,0 +1,111 @@ +// Copyright 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'dart:io' hide Platform; + +import 'package:path/path.dart' as path; +import 'package:platform/platform.dart' show LocalPlatform; + +/// The location of the Flutter root directory, based on the known location of +/// this script. +final Directory flutterRoot = Directory(path.dirname(const LocalPlatform().script.toFilePath())).parent.parent.parent.parent; + +/// Converts `FOO_BAR` to `fooBar`. +String shoutingToLowerCamel(String shouting) { + final RegExp initialLetter = RegExp(r'_([^_])([^_]*)'); + final String snake = shouting.toLowerCase(); + final String result = snake.replaceAllMapped(initialLetter, (Match match) { + return match.group(1).toUpperCase() + match.group(2).toLowerCase(); + }); + return result; +} + +/// Converts 'FooBar' to 'fooBar'. +String upperCamelToLowerCamel(String upperCamel) { + return upperCamel.substring(0, 1).toLowerCase() + upperCamel.substring(1); +} + +/// Converts 'fooBar' to 'FooBar'. +String lowerCamelToUpperCamel(String lowerCamel) { + return lowerCamel.substring(0, 1).toUpperCase() + lowerCamel.substring(1); +} + +/// A list of Dart reserved words. +/// +/// Since these are Dart reserved words, we can't use them as-is for enum names. +const List kDartReservedWords = [ + 'abstract', + 'as', + 'assert', + 'async', + 'await', + 'break', + 'case', + 'catch', + 'class', + 'const', + 'continue', + 'covariant', + 'default', + 'deferred', + 'do', + 'dynamic', + 'else', + 'enum', + 'export', + 'extends', + 'external', + 'factory', + 'false', + 'final', + 'finally', + 'for', + 'Function', + 'get', + 'hide', + 'if', + 'implements', + 'import', + 'in', + 'interface', + 'is', + 'library', + 'mixin', + 'new', + 'null', + 'on', + 'operator', + 'part', + 'rethrow', + 'return', + 'set', + 'show', + 'static', + 'super', + 'switch', + 'sync', + 'this', + 'throw', + 'true', + 'try', + 'typedef', + 'var', + 'void', + 'while', + 'with', + 'yield', +]; + +/// Converts an integer into a hex string with the given number of digits. +String toHex(int value, {int digits = 8}) { + if (value == null) { + return 'null'; + } + return '0x${value.toRadixString(16).padLeft(digits, '0')}'; +} + +/// Parses an integer from a hex string. +int getHex(String input) { + return int.parse(input, radix: 16); +} diff --git a/dev/tools/gen_keycodes/pubspec.yaml b/dev/tools/gen_keycodes/pubspec.yaml new file mode 100644 index 00000000000..43f7a3ed196 --- /dev/null +++ b/dev/tools/gen_keycodes/pubspec.yaml @@ -0,0 +1,24 @@ +name: gen_keycodes +description: Generates keycode source files from various resources. + +environment: + # The pub client defaults to an <2.0.0 sdk constraint which we need to explicitly overwrite. + sdk: ">=2.0.0-dev.68.0 <3.0.0" + +dependencies: + args: 1.5.1 + http: 0.12.0+1 + path: 1.6.2 + platform: 2.2.0 + + async: 2.0.8 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + charcode: 1.1.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + collection: 1.14.11 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + http_parser: 3.1.3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + meta: 1.1.6 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + source_span: 1.5.4 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + string_scanner: 1.0.4 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + term_glyph: 1.1.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + typed_data: 1.1.6 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + +# PUBSPEC CHECKSUM: 433d diff --git a/packages/flutter_build/pubspec.yaml b/packages/flutter_build/pubspec.yaml index bd81dc578cb..e511e803cb2 100644 --- a/packages/flutter_build/pubspec.yaml +++ b/packages/flutter_build/pubspec.yaml @@ -33,7 +33,7 @@ dependencies: pedantic: 1.4.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" plugin: 0.2.0+3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" pool: 1.4.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - protobuf: 0.13.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + protobuf: 0.13.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" pub_semver: 1.4.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" pubspec_parse: 0.1.4 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" scratch_space: 0.0.3+2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" @@ -50,4 +50,4 @@ dartdoc: # Exclude this package from the hosted API docs. nodoc: true -# PUBSPEC CHECKSUM: 1f34 +# PUBSPEC CHECKSUM: 9f35 diff --git a/packages/flutter_tools/pubspec.yaml b/packages/flutter_tools/pubspec.yaml index 5e2f0dbbbdd..d141bb91f08 100644 --- a/packages/flutter_tools/pubspec.yaml +++ b/packages/flutter_tools/pubspec.yaml @@ -81,7 +81,7 @@ dependencies: pedantic: 1.4.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" petitparser: 2.1.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" pool: 1.4.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - protobuf: 0.13.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + protobuf: 0.13.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" pub_semver: 1.4.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" pubspec_parse: 0.1.4 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" scratch_space: 0.0.3+2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" @@ -100,7 +100,7 @@ dev_dependencies: collection: 1.14.11 mockito: 4.0.0 file_testing: 2.0.3 - vm_service_lib: 0.3.10+2 + vm_service_lib: 3.14.1 http_multi_server: 2.0.5 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" js: 0.6.1+1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" @@ -117,4 +117,4 @@ dartdoc: # Exclude this package from the hosted API docs. nodoc: true -# PUBSPEC CHECKSUM: 7930 +# PUBSPEC CHECKSUM: 32d8