Unverified Commit 9956a35a authored by Tong Mu's avatar Tong Mu Committed by GitHub

Hardware keyboard: codegen (#73440)

Rewrites tools/gen_keycodes, the script that generates key mappings across the framework and the engine.
parent 0cb0767b
## 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.
- `data/synonyms.json`: contains a mapping between pseudo-keys that represent
other keys and the sets of keys they represent. For example, this contains
the "shift" key that represents either a "shiftLeft" or "shiftRight" key.
the `LogicalKeyboardKey` and `PhysicalKeyboardKey` classes.
It generates multiple files across Flutter. For framework, it generates
* [`keyboard_key.dart`](../../../packages/flutter/lib/src/services/keyboard_key.dart), which contains the definition and list of logical keys and physical keys; and
* [`keyboard_maps.dart`](../../../packages/flutter/lib/src/services/keyboard_maps.dart), which contains platform-specific immutable maps used for the `RawKeyboard` API.
For engine, it generates one key mapping file for each platform.
It draws information from various source bases, including online
repositories, and manual mapping in the `data` subdirectory. It incorporates
this information into a giant list of physical keys
([`physical_key_data.json`](data/physical_key_data.json)),
and another for logical keys
([`logical_key_data.json`](data/logical_key_data.json)).
The two files are checked in, and can be used as the data source next time so that
output files can be generated without the Internet.
## Running the tool
To run the `gen_keycodes` tool using the checked in `key_data.json` file, run
it like so:
The tool can be run based on the existing database. To do this, run:
```bash
$FLUTTER_ROOT/bin/cache/dart-sdk/bin/dart bin/gen_keycodes.dart
/PATH/TO/ROOT/bin/gen_keycodes
```
This will regenerate the `keyboard_key.dart` and `keyboard_maps.dart` files in
place.
The tool can also be run by rebuilding the database by drawing online information
anew before generating the files. To do this, run:
```bash
/PATH/TO/ROOT/bin/gen_keycodes --collect
```
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.
This will generate `physical_key_data.json` and `logical_key_data.json`. These
files should be checked in.
There are other options for manually specifying the file to read in place of the
downloaded files, use `--help` to see what is available.
By default this tool assumes that the gclient directory for flutter/engine
and the root for the flutter/flutter are placed at the same folder. If not,
use `--engine-root=/ENGINE/GCLIENT/ROOT` to specify the engine root.
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.
Other options can be found using `--help`.
## Key Code ID Scheme
......
include: ../../../analysis_options.yaml
#!/usr/bin/env bash
set -euo pipefail
# Needed because if it is set, cd may print the path it changed to.
unset CDPATH
# On Mac OS, readlink -f doesn't work, so follow_links traverses the path one
# link at a time, and then cds into the link destination and find out where it
# ends up.
#
# The returned filesystem path must be a format usable by Dart's URI parser,
# since the Dart command line tool treats its argument as a file URI, not a
# filename. For instance, multiple consecutive slashes should be reduced to a
# single slash, since double-slashes indicate a URI "authority", and these are
# supposed to be filenames. There is an edge case where this will return
# multiple slashes: when the input resolves to the root directory. However, if
# that were the case, we wouldn't be running this shell, so we don't do anything
# about it.
#
# The function is enclosed in a subshell to avoid changing the working directory
# of the caller.
function follow_links() (
cd -P "$(dirname -- "$1")"
file="$PWD/$(basename -- "$1")"
while [[ -h "$file" ]]; do
cd -P "$(dirname -- "$file")"
file="$(readlink -- "$file")"
cd -P "$(dirname -- "$file")"
file="$PWD/$(basename -- "$file")"
done
echo "$file"
)
PROG_NAME="$(follow_links "${BASH_SOURCE[0]}")"
BIN_DIR="$(cd "${PROG_NAME%/*}" ; pwd -P)"
DART_BIN="$BIN_DIR/../../../../bin/dart"
"$DART_BIN" --enable-asserts "$BIN_DIR/gen_keycodes.dart" "$@"
......@@ -8,27 +8,32 @@ import 'dart:io' hide Platform;
import 'package:args/args.dart';
import 'package:gen_keycodes/android_code_gen.dart';
import 'package:gen_keycodes/base_code_gen.dart';
import 'package:gen_keycodes/fuchsia_code_gen.dart';
import 'package:gen_keycodes/glfw_code_gen.dart';
import 'package:gen_keycodes/gtk_code_gen.dart';
import 'package:gen_keycodes/ios_code_gen.dart';
import 'package:gen_keycodes/key_data.dart';
import 'package:gen_keycodes/keyboard_keys_code_gen.dart';
import 'package:gen_keycodes/keyboard_maps_code_gen.dart';
import 'package:gen_keycodes/macos_code_gen.dart';
import 'package:gen_keycodes/utils.dart';
import 'package:gen_keycodes/web_code_gen.dart';
import 'package:gen_keycodes/windows_code_gen.dart';
import 'package:gen_keycodes/keyboard_keys_code_gen.dart';
import 'package:gen_keycodes/keyboard_maps_code_gen.dart';
import 'package:gen_keycodes/physical_key_data.dart';
import 'package:gen_keycodes/logical_key_data.dart';
import 'package:gen_keycodes/utils.dart';
import 'package:http/http.dart' as http;
import 'package:path/path.dart' as path;
/// Get contents of the file that contains the key code mapping in Chromium
/// Get contents of the file that contains the physical key mapping in Chromium
/// source.
Future<String> getChromiumConversions() async {
Future<String> getChromiumCodes() async {
final Uri keyCodesUri = Uri.parse('https://chromium.googlesource.com/codesearch/chromium/src/+/refs/heads/master/ui/events/keycodes/dom/dom_code_data.inc?format=TEXT');
return utf8.decode(base64.decode(await http.read(keyCodesUri)));
}
/// Get contents of the file that contains the logical key mapping in Chromium
/// source.
Future<String> getChromiumKeys() async {
final Uri keyCodesUri = Uri.parse('https://chromium.googlesource.com/codesearch/chromium/src/+/refs/heads/master/ui/events/keycodes/dom/dom_key_data.inc?format=TEXT');
return utf8.decode(base64.decode(await http.read(keyCodesUri)));
}
/// Get contents of the file that contains the key codes in Android source.
Future<String> getAndroidKeyCodes() async {
final Uri keyCodesUri = Uri.parse('https://android.googlesource.com/platform/frameworks/native/+/master/include/android/keycodes.h?format=TEXT');
......@@ -61,81 +66,35 @@ Future<String> getGtkKeyCodes() async {
return http.read(keyCodesUri);
}
String readDataFile(String fileName) {
return File(path.join(dataRoot, fileName)).readAsStringSync();
}
Future<void> main(List<String> 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(
'supplemental-hid-codes',
defaultsTo: path.join(flutterRoot.path, 'dev', 'tools', 'gen_keycodes', 'data', 'supplemental_hid_codes.inc'),
help: "The path to where the supplemental HID codes that don't appear in the "
'Chromium map should be read.',
);
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(
'glfw-keycodes',
defaultsTo: null,
help: 'The path to where the GLFW keycodes header file should be read. '
'If --glfw-keycodes is not specified, the input will be read from the '
'correct file in the GLFW github repository.',
);
argParser.addOption(
'gtk-keycodes',
defaultsTo: null,
help: 'The path to where the GTK keycodes header file should be read. '
'If --gtk-keycodes is not specified, the input will be read from the '
'correct file in the GTK repository.',
);
argParser.addOption(
'windows-keycodes',
defaultsTo: null,
help: 'The path to where the Windows keycodes header file should be read. '
'If --windows-keycodes is not specified, the input will be read from the '
'correct file in the Windows github repository.',
);
argParser.addOption(
'windows-domkey',
defaultsTo: path.join(flutterRoot.path, 'dev', 'tools', 'gen_keycodes', 'data', 'key_name_to_windows_name.json'),
help: 'The path to where the Windows keycode to DomKey mapping is.',
);
argParser.addOption(
'glfw-domkey',
defaultsTo: path.join(flutterRoot.path, 'dev', 'tools', 'gen_keycodes', 'data', 'key_name_to_glfw_name.json'),
help: 'The path to where the GLFW keycode to DomKey mapping is.',
'engine-root',
defaultsTo: path.join(flutterRoot.path, '..', 'engine', 'src', 'flutter'),
help: 'The path to the root of the flutter/engine repository. This is used '
'to place the generated engine mapping files. If --engine-root is not '
r'specified, it will default to $flutterRoot/../engine/src/flutter, '
'assuming the engine gclient folder is placed at the same folder as '
'the flutter/flutter repository.',
);
argParser.addOption(
'gtk-domkey',
defaultsTo: path.join(flutterRoot.path, 'dev', 'tools', 'gen_keycodes', 'data', 'key_name_to_gtk_name.json'),
help: 'The path to where the GTK keycode to DomKey mapping is.',
'physical-data',
defaultsTo: path.join(dataRoot, 'physical_key_data.json'),
help: 'The path to where the physical key data file should be written when '
'collected, and read from when generating output code. If --physical-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(
'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 '
'logical-data',
defaultsTo: path.join(dataRoot, 'logical_key_data.json'),
help: 'The path to where the logical key data file should be written when '
'collected, and read from when generating output code. If --logical-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.',
......@@ -143,7 +102,7 @@ Future<void> main(List<String> rawArguments) async {
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 '
help: 'The path to where the output "keyboard_key.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.',
......@@ -162,7 +121,8 @@ Future<void> main(List<String> rawArguments) async {
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.',
'"physical_key_data.json" and "logical_key_data.json", and then '
'update these files with the fresh data.',
);
argParser.addFlag(
'help',
......@@ -178,64 +138,57 @@ Future<void> main(List<String> rawArguments) async {
exit(0);
}
KeyData data;
if (parsedArguments['collect'] as bool) {
String hidCodes;
if (parsedArguments['chromium-hid-codes'] == null) {
hidCodes = await getChromiumConversions();
} else {
hidCodes = File(parsedArguments['chromium-hid-codes'] as String).readAsStringSync();
}
final String supplementalHidCodes = File(parsedArguments['supplemental-hid-codes'] as String).readAsStringSync();
hidCodes = '$hidCodes\n$supplementalHidCodes';
String androidKeyCodes;
if (parsedArguments['android-keycodes'] == null) {
androidKeyCodes = await getAndroidKeyCodes();
} else {
androidKeyCodes = File(parsedArguments['android-keycodes'] as String).readAsStringSync();
}
String androidScanCodes;
if (parsedArguments['android-scancodes'] == null) {
androidScanCodes = await getAndroidScanCodes();
} else {
androidScanCodes = File(parsedArguments['android-scancodes'] as String).readAsStringSync();
}
String glfwKeyCodes;
if (parsedArguments['glfw-keycodes'] == null) {
glfwKeyCodes = await getGlfwKeyCodes();
} else {
glfwKeyCodes = File(parsedArguments['glfw-keycodes'] as String).readAsStringSync();
}
String gtkKeyCodes;
if (parsedArguments['gtk-keycodes'] == null) {
gtkKeyCodes = await getGtkKeyCodes();
} else {
gtkKeyCodes = File(parsedArguments['gtk-keycodes'] as String).readAsStringSync();
}
String windowsKeyCodes;
if (parsedArguments['windows-keycodes'] == null) {
windowsKeyCodes = await getWindowsKeyCodes();
} else {
windowsKeyCodes = File(parsedArguments['windows-keycodes'] as String).readAsStringSync();
}
final String windowsToDomKey = File(parsedArguments['windows-domkey'] as String).readAsStringSync();
final String glfwToDomKey = File(parsedArguments['glfw-domkey'] as String).readAsStringSync();
final String gtkToDomKey = File(parsedArguments['gtk-domkey'] as String).readAsStringSync();
final String androidToDomKey = File(parsedArguments['android-domkey'] as String).readAsStringSync();
data = KeyData(hidCodes, androidScanCodes, androidKeyCodes, androidToDomKey, glfwKeyCodes, glfwToDomKey, gtkKeyCodes, gtkToDomKey, windowsKeyCodes, windowsToDomKey);
PlatformCodeGenerator.engineRoot = parsedArguments['engine-root'] as String;
PhysicalKeyData physicalData;
LogicalKeyData logicalData;
if (parsedArguments['collect'] as bool) {
// Physical
final String baseHidCodes = await getChromiumCodes();
final String supplementalHidCodes = readDataFile('supplemental_hid_codes.inc');
final String androidScanCodes = await getAndroidScanCodes();
final String androidToDomKey = readDataFile('android_key_name_to_name.json');
final String glfwKeyCodes = await getGlfwKeyCodes();
final String glfwToDomKey = readDataFile('glfw_key_name_to_name.json');
physicalData = PhysicalKeyData(
<String>[baseHidCodes, supplementalHidCodes].join('\n'),
androidScanCodes,
androidToDomKey,
glfwKeyCodes,
glfwToDomKey,
);
// Logical
final String gtkKeyCodes = await getGtkKeyCodes();
final String webLogicalKeys = await getChromiumKeys();
final String supplementalKeyData = readDataFile('supplemental_key_data.inc');
final String gtkToDomKey = readDataFile('gtk_logical_name_mapping.json');
final String windowsKeyCodes = await getWindowsKeyCodes();
final String windowsToDomKey = readDataFile('windows_logical_to_window_vk.json');
final String macosLogicalToPhysical = readDataFile('macos_logical_to_physical.json');
final String iosLogicalToPhysical = readDataFile('ios_logical_to_physical.json');
final String androidKeyCodes = await getAndroidKeyCodes();
logicalData = LogicalKeyData(
<String>[webLogicalKeys, supplementalKeyData].join('\n'),
gtkKeyCodes,
gtkToDomKey,
windowsKeyCodes,
windowsToDomKey,
androidKeyCodes,
androidToDomKey,
macosLogicalToPhysical,
iosLogicalToPhysical,
physicalData,
);
// Write data files
const JsonEncoder encoder = JsonEncoder.withIndent(' ');
File(parsedArguments['data'] as String).writeAsStringSync(encoder.convert(data.toJson()));
File(parsedArguments['physical-data'] as String).writeAsStringSync(encoder.convert(physicalData.toJson()) + '\n');
File(parsedArguments['logical-data'] as String).writeAsStringSync(encoder.convert(logicalData.toJson()) + '\n');
} else {
data = KeyData.fromJson(json.decode(await File(parsedArguments['data'] as String).readAsString()) as Map<String, dynamic>);
physicalData = PhysicalKeyData.fromJson(json.decode(await File(parsedArguments['physical-data'] as String).readAsString()) as Map<String, dynamic>);
logicalData = LogicalKeyData.fromJson(json.decode(await File(parsedArguments['logical-data'] as String).readAsString()) as Map<String, dynamic>);
}
final File codeFile = File(parsedArguments['code'] as String);
......@@ -243,51 +196,49 @@ Future<void> main(List<String> rawArguments) async {
codeFile.createSync(recursive: true);
}
print('Writing ${'key codes'.padRight(15)}${codeFile.absolute}');
await codeFile.writeAsString(KeyboardKeysCodeGenerator(data).generate());
await codeFile.writeAsString(KeyboardKeysCodeGenerator(physicalData, logicalData).generate());
final File mapsFile = File(parsedArguments['maps'] as String);
if (!mapsFile.existsSync()) {
mapsFile.createSync(recursive: true);
}
print('Writing ${'key maps'.padRight(15)}${mapsFile.absolute}');
await mapsFile.writeAsString(KeyboardMapsCodeGenerator(data).generate());
for (final String platform in <String>['android', 'macos', 'ios', 'glfw', 'fuchsia', 'linux', 'windows', 'web']) {
PlatformCodeGenerator codeGenerator;
switch (platform) {
case 'glfw':
codeGenerator = GlfwCodeGenerator(data);
break;
case 'fuchsia':
codeGenerator = FuchsiaCodeGenerator(data);
break;
case 'android':
codeGenerator = AndroidCodeGenerator(data);
break;
case 'macos':
codeGenerator = MacOsCodeGenerator(data);
break;
case 'ios':
codeGenerator = IosCodeGenerator(data);
break;
case 'windows':
codeGenerator = WindowsCodeGenerator(data);
break;
case 'linux':
codeGenerator = GtkCodeGenerator(data);
break;
case 'web':
codeGenerator = WebCodeGenerator(data);
break;
default:
assert(false);
}
await mapsFile.writeAsString(KeyboardMapsCodeGenerator(physicalData, logicalData).generate());
final Map<String, PlatformCodeGenerator> platforms = <String, PlatformCodeGenerator>{
'android': AndroidCodeGenerator(
physicalData,
logicalData,
),
'macos': MacOsCodeGenerator(
physicalData,
logicalData,
),
'windows': WindowsCodeGenerator(
physicalData,
logicalData,
readDataFile('windows_scancode_logical_map.json')
),
'linux': GtkCodeGenerator(
physicalData,
logicalData,
readDataFile('gtk_modifier_bit_mapping.json'),
readDataFile('gtk_lock_bit_mapping.json'),
),
'web': WebCodeGenerator(
physicalData,
logicalData,
readDataFile('web_logical_location_mapping.json'),
),
};
await Future.wait(platforms.entries.map((MapEntry<String, PlatformCodeGenerator> entry) {
final String platform = entry.key;
final PlatformCodeGenerator codeGenerator = entry.value;
final File platformFile = File(codeGenerator.outputPath(platform));
if (!platformFile.existsSync()) {
platformFile.createSync(recursive: true);
}
print('Writing ${'$platform map'.padRight(15)}${platformFile.absolute}');
await platformFile.writeAsString(codeGenerator.generate());
}
return platformFile.writeAsString(codeGenerator.generate());
}));
}
## Files
### General
| File name | Explanation |
| ---- | ---- |
| [`physical_key_data.json`](physical_key_data.json) | Contains the merged physical key data from all the other sources. This file is regenerated if "--collect" is specified for the gen_keycodes script, or used as a source otherwise. |
| [`logical_key_data.json`](logical_key_data.json) | Contains the merged logical key data from all the other sources. This file is regenerated if "--collect" is specified for the gen_keycodes script, or used as a source otherwise. |
| [`supplemental_hid_codes.inc`](supplemental_hid_codes.inc) | A supplementary HID list on top of Chromium's list of HID codes for extra physical keys. Certain entries may also overwrite Chromium's corresponding entries. |
| [`supplemental_key_data.inc`](supplemental_key_data.inc) | A supplementary key list on top of Chromium's list of keys for extra logical keys.|
| [`chromium_modifiers.json`](chromium_modifiers.json) | Maps the web's `key` for modifier keys to the names of the logical keys for these keys' left and right variations.This is used when generating logical keys to provide independent values for sided logical keys. Web uses the same `key` for modifier keys of different sides, but Flutter's logical key model treats them as different keys.|
| [`printable_to_numpads.json`](printable_to_numpads.json) | Maps a character to the names of the logical keys for these keys' number pad variations. This is used when generating logical keys to provide independent values for number pad logical keys. The web uses the character as the `key` for number pad keys, but Flutter's logical key model treats them as independent keys.|
| [`printable.json`](printable.json) | Maps Flutter key name to its printable character. This character is used as the key label.|
| [`synonyms.json`](synonyms.json) | Maps pseudo-keys that represent other keys to the sets of keys they represent. For example, this contains the "shift" key that represents either a "shiftLeft" or "shiftRight" key.|
### Framework
| File name | Explanation |
| ---- | ---- |
| [`keyboard_key.tmpl`](keyboard_key.tmpl) | The template for `keyboard_key.dart`. |
| [`keyboard_maps.tmpl`](keyboard_maps.tmpl) | The template for `keyboard_maps.dart`. |
### Android
| File name | Explanation |
| ---- | ---- |
| [`android_keyboard_map_java.tmpl`](android_keyboard_map_java.tmpl) | The template for `KeyboardMap.java`. |
| [`android_key_name_to_name.json`](android_key_name_to_name.json) | Maps a logical key name to the names of its corresponding keycode constants. This is used to convert logical keys.|
### iOS
| File name | Explanation |
| ---- | ---- |
| [`ios_logical_to_physical.json`](ios_logical_to_physical.json) | Maps a logical key name to the names of its corresponding physical keys. This is used to derive logical keys (from `keyCode`) that can't or shouldn't be derived from `characterIgnoringModifiers`. |
| [`ios_keyboard_map_cc.tmpl`](ios_keyboard_map_cc.tmpl) | The template for `keyboard_map.cc`. (Unused for now.) |
### Web
| File name | Explanation |
| ---- | ---- |
| [`web_key_map_dart.tmpl`](web_key_map_dart.tmpl) | The template for `key_map.dart`. |
| [`web_logical_location_mapping.json`](web_logical_location_mapping.json) | Maps a pair of the web's `key` and `location` to the name for its corresponding logical key. This is used to distinguish between logical keys with the same `key` but different `locations`. |
### Windows
| File name | Explanation |
| ---- | ---- |
| [`windows_flutter_key_map_cc.tmpl`](windows_flutter_key_map_cc.tmpl) | The template for `flutter_key_map.cc`. |
| [`windows_logical_to_window_vk.json`](windows_logical_to_window_vk.json) | Maps a logical key name to the names of its corresponding virtual keys in Win32. |
| [`windows_scancode_logical_map.json`](windows_scancode_logical_map.json) | Maps a physical key name to a logical key name. This is used to when a `keycode` maps to multiple keys (including when the `keycode` is 0), therefore can only be told apart by the scan code. |
### Linux (GTK)
| File name | Explanation |
| ---- | ---- |
| [`gtk_key_mapping_cc.tmpl`](gtk_key_mapping_cc.tmpl) | The template for `key_mapping.cc`. |
| [`gtk_lock_bit_mapping.json`](gtk_lock_bit_mapping.json) | Maps a name for GTK's modifier bit macro to Flutter's logical name (element #0) and physical name (element #1). This is used to generate checked keys that GTK should keep lock state synchronous on.|
| [`gtk_logical_name_mapping.json`](gtk_logical_name_mapping.json) | Maps a logical key name to the macro names of its corresponding `keyval`s. This is used to convert logical keys.|
| [`gtk_modifier_bit_mapping.json`](gtk_modifier_bit_mapping.json) | Maps a name for GTK's modifier bit macro to Flutter's logical name (element #0), physical name (element #1), and the physical name for the paired key (element #2). This is used to generate checked keys that GTK should keep pressing state synchronous on.|
| [`gtk_numpad_shift.json`](gtk_numpad_shift.json) | Maps the name of a `keyval` macro of a numpad key to that of the corresponding key with NumLock on. GTK uses different `keyval` for numpad keys with and without NumLock on, but Flutter's logical key model treats them as the same key.|
### Linux (GLFW)
| File name | Explanation |
| ---- | ---- |
| [`glfw_key_name_to_name.json`](glfw_key_name_to_name.json) | Maps a logical key name to the names of its GLFW macro. (Unused for now.) |
| [`glfw_keyboard_map_cc.tmpl`](glfw_keyboard_map_cc.tmpl) | The template for `keyboard_map.cc`. (Unused for now.) |
### macOS
| File name | Explanation |
| ---- | ---- |
| [`macos_key_code_map_cc.tmpl`](macos_key_code_map_cc.tmpl) | The template for `KeyCodeMap.cc`. |
| [`macos_logical_to_physical.json`](macos_logical_to_physical.json) | Maps a logical key name to the names of its corresponding physical keys. This is used to derive logical keys (from `keyCode`) that can't or shouldn't be derived from `characterIgnoringModifiers`. |
### Fuchsia
| File name | Explanation |
| ---- | ---- |
| [`fuchsia_keyboard_map_cc.tmpl`](fuchsia_keyboard_map_cc.tmpl) | The template for `keyboard_map.cc`. (Unused for now.) |
{
"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"],
"GameButton1": ["BUTTON_1"],
"GameButton2": ["BUTTON_2"],
"GameButton3": ["BUTTON_3"],
"GameButton4": ["BUTTON_4"],
"GameButton5": ["BUTTON_5"],
"GameButton6": ["BUTTON_6"],
"GameButton7": ["BUTTON_7"],
"GameButton8": ["BUTTON_8"],
"GameButton9": ["BUTTON_9"],
"GameButton10": ["BUTTON_10"],
"GameButton11": ["BUTTON_11"],
"GameButton12": ["BUTTON_12"],
"GameButton13": ["BUTTON_13"],
"GameButton14": ["BUTTON_14"],
"GameButton15": ["BUTTON_15"],
"GameButton16": ["BUTTON_16"],
"GameButtonA": ["BUTTON_A"],
"GameButtonB": ["BUTTON_B"],
"GameButtonC": ["BUTTON_C"],
"GameButtonLeft1": ["BUTTON_L1"],
"GameButtonLeft2": ["BUTTON_L2"],
"GameButtonMode": ["BUTTON_MODE"],
"GameButtonRight1": ["BUTTON_R1"],
"GameButtonRight2": ["BUTTON_R2"],
"GameButtonSelect": ["BUTTON_SELECT"],
"GameButtonStart": ["BUTTON_START"],
"GameButtonThumbLeft": ["BUTTON_THUMBL"],
"GameButtonThumbRight": ["BUTTON_THUMBR"],
"GameButtonX": ["BUTTON_X"],
"GameButtonY": ["BUTTON_Y"],
"GameButtonZ": ["BUTTON_Z"],
"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"],
"IntlRo": ["RO"],
"IntlYen": ["YEN"],
"Lang1": [],
"Lang2": [],
"Lang3": ["KATAKANA"],
"Lang4": ["HIRAGANA"],
"LaunchAssistant": ["ASSIST"],
"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"],
"Select": ["DPAD_CENTER"],
"Semicolon": ["SEMICOLON"],
"Settings": ["SETTINGS"],
"ShiftLeft": ["SHIFT_LEFT"],
"ShiftRight": ["SHIFT_RIGHT"],
"Slash": ["SLASH"],
"Sleep": ["SLEEP"],
"Space": ["SPACE"],
"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"]
}
// Copyright 2014 The Flutter Authors. All rights reserved.
package io.flutter.embedding.android;
// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <map>
// DO NOT EDIT -- DO NOT EDIT -- DO NOT EDIT
// This file is generated by flutter/flutter@dev/tools/gen_keycodes/bin/gen_keycodes.dart and
// should not be edited directly.
//
// Edit the template dev/tools/gen_keycodes/data/keyboard_map_android_cc.tmpl instead.
// Edit the template dev/tools/gen_keycodes/data/android_keyboard_map_java.tmpl instead.
// See dev/tools/gen_keycodes/README.md for more information.
/// Maps XKB specific key code values representing [PhysicalKeyboardKey].
const std::map<int, int> g_xkb_to_physical_key = {
@@@XKB_SCAN_CODE_MAP@@@
};
import java.util.HashMap;
public class KeyboardMap {
public static final HashMap<Long, Long> scanCodeToPhysical =
new HashMap<Long, Long>() {
private static final long serialVersionUID = 1L;
{
@@@ANDROID_SCAN_CODE_MAP@@@
}
};
public static final HashMap<Long, Long> keyCodeToLogical =
new HashMap<Long, Long>() {
private static final long serialVersionUID = 1L;
{
@@@ANDROID_KEY_CODE_MAP@@@
}
};
}
{
"Shift": ["ShiftLeft", "ShiftRight"],
"Meta": ["MetaLeft", "MetaRight"],
"Alt": ["AltLeft", "AltRight"],
"Control": ["ControlLeft", "ControlRight"]
}
......@@ -8,7 +8,7 @@
// This file is generated by flutter/flutter@dev/tools/gen_keycodes/bin/gen_keycodes.dart and
// should not be edited directly.
//
// Edit the template dev/tools/gen_keycodes/data/keyboard_map_fuchsia_cc.tmpl instead.
// Edit the template dev/tools/gen_keycodes/data/fuchsia_keyboard_map_cc.tmpl instead.
// See dev/tools/gen_keycodes/README.md for more information.
/// Maps Fuchsia-specific IDs to the matching LogicalKeyboardKey.
......
{
"AltLeft": ["LEFT_ALT"],
"AltRight": ["RIGHT_ALT"],
"ArrowDown": ["DOWN"],
"ArrowLeft": ["LEFT"],
"ArrowRight": ["RIGHT"],
"ArrowUp": ["UP"],
"Backquote": ["GRAVE_ACCENT"],
"Backslash": ["BACKSLASH"],
"Backspace": ["BACKSPACE"],
"BracketLeft": ["LEFT_BRACKET"],
"BracketRight": ["RIGHT_BRACKET"],
"CapsLock": ["CAPS_LOCK"],
"Comma": ["COMMA"],
"ContextMenu": ["MENU"],
"ControlLeft": ["LEFT_CONTROL"],
"ControlRight": ["RIGHT_CONTROL"],
"Delete": ["DELETE"],
"Digit0": ["0"],
"Digit1": ["1"],
"Digit2": ["2"],
"Digit3": ["3"],
"Digit4": ["4"],
"Digit5": ["5"],
"Digit6": ["6"],
"Digit7": ["7"],
"Digit8": ["8"],
"Digit9": ["9"],
"End": ["END"],
"Enter": ["ENTER"],
"Equal": ["EQUAL"],
"Escape": ["ESCAPE"],
"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"],
"F25": ["F25"],
"Home": ["HOME"],
"Insert": ["INSERT"],
"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"],
"MetaLeft": ["LEFT_SUPER"],
"MetaRight": ["RIGHT_SUPER"],
"Minus": ["MINUS"],
"NumLock": ["NUM_LOCK"],
"Numpad0": ["KP_0"],
"Numpad1": ["KP_1"],
"Numpad2": ["KP_2"],
"Numpad3": ["KP_3"],
"Numpad4": ["KP_4"],
"Numpad5": ["KP_5"],
"Numpad6": ["KP_6"],
"Numpad7": ["KP_7"],
"Numpad8": ["KP_8"],
"Numpad9": ["KP_9"],
"NumpadAdd": ["KP_ADD"],
"NumpadDecimal": ["KP_DECIMAL"],
"NumpadDivide": ["KP_DIVIDE"],
"NumpadEnter": ["KP_ENTER"],
"NumpadEqual": ["KP_EQUAL"],
"NumpadMultiply": ["KP_MULTIPLY"],
"NumpadSubtract": ["NUMPAD_SUBTRACT"],
"PageDown": ["PAGE_DOWN"],
"PageUp": ["PAGE_UP"],
"Pause": ["PAUSE"],
"Period": ["PERIOD"],
"PrintScreen": ["PRINT_SCREEN"],
"Quote": ["APOSTROPHE"],
"Semicolon": ["SEMICOLON"],
"ShiftLeft": ["LEFT_SHIFT"],
"ShiftRight": ["RIGHT_SHIFT"],
"Slash": ["SLASH"],
"Space": ["SPACE"],
"Tab": ["TAB"]
}
......@@ -8,7 +8,7 @@
// This file is generated by flutter/flutter@dev/tools/gen_keycodes/bin/gen_keycodes.dart and
// should not be edited directly.
//
// Edit the template dev/tools/gen_keycodes/data/keyboard_map_glfw_cc.tmpl instead.
// Edit the template dev/tools/gen_keycodes/data/glfw_keyboard_map_cc.tmpl instead.
// See dev/tools/gen_keycodes/README.md for more information.
/// Maps GLFW-specific key codes to the matching [LogicalKeyboardKey].
......
// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "key_mapping.h"
#include <glib.h>
#include <map>
#include "flutter/shell/platform/linux/fl_key_embedder_responder_private.h"
// DO NOT EDIT -- DO NOT EDIT -- DO NOT EDIT
// This file is generated by
// flutter/flutter@dev/tools/gen_keycodes/bin/gen_keycodes.dart and should not
// be edited directly.
//
// Edit the template dev/tools/gen_keycodes/data/gtk_key_mapping_cc.tmpl
// instead. See dev/tools/gen_keycodes/README.md for more information.
// Insert a new entry into a hashtable from uint64 to uint64.
//
// Returns whether the newly added value was already in the hash table or not.
static bool insert_record(GHashTable* table, guint64 xkb, guint64 fl_key) {
return g_hash_table_insert(table, uint64_to_gpointer(xkb),
uint64_to_gpointer(fl_key));
}
void initialize_xkb_to_physical_key(GHashTable* table) {
@@@XKB_SCAN_CODE_MAP@@@
}
void initialize_gtk_keyval_to_logical_key(GHashTable* table) {
@@@GTK_KEYVAL_CODE_MAP@@@
}
void initialize_modifier_bit_to_checked_keys(GHashTable* table) {
FlKeyEmbedderCheckedKey* data;
@@@GTK_MODIFIER_BIT_MAP@@@
}
void initialize_lock_bit_to_checked_keys(GHashTable* table) {
FlKeyEmbedderCheckedKey* data;
@@@GTK_MODE_BIT_MAP@@@
}
{
"LOCK": ["CapsLock", "CapsLock"],
"MOD2": ["NumLock", "NumLock"]
}
{
"NumpadAdd": ["KP_Add"],
"NumpadMultiply": ["KP_Multiply"],
"NumpadSubtract": ["KP_Subtract"],
"NumpadDivide": ["KP_Divide"],
"Space": ["KP_Space"],
"Period": ["KP_Decimal"],
"NumpadEqual": ["KP_Equal"],
"Numpad0": ["KP_0", "KP_Insert"],
"Numpad1": ["KP_1", "KP_End"],
"Numpad2": ["KP_2", "KP_Down"],
"Numpad3": ["KP_3", "KP_Page_Down"],
"Numpad4": ["KP_4", "KP_Left"],
"Numpad5": ["KP_5"],
"Numpad6": ["KP_6", "KP_Right"],
"Numpad7": ["KP_7", "KP_Home"],
"Numpad8": ["KP_8", "KP_Up"],
"Numpad9": ["KP_9", "KP_Page_Up"],
"NumpadDecimal": ["KP_Period", "KP_Delete"],
"NumpadEnter": ["KP_Enter"],
"AltLeft": ["Alt_L"],
"AltRight": ["Alt_R", "ISO_Level3_Shift"],
"ArrowDown": ["Down"],
"ArrowLeft": ["Left"],
"ArrowRight": ["Right"],
"ArrowUp": ["Up"],
"Attn": ["3270_Attn"],
"AudioVolumeDown": ["AudioLowerVolume"],
"AudioVolumeMute": ["AudioMute"],
"AudioVolumeUp": ["AudioRaiseVolume"],
"Backspace": ["BackSpace"],
"BrightnessDown": ["MonBrightnessDown"],
"BrightnessUp": ["MonBrightnessUp"],
"BrowserBack": ["Back"],
"BrowserFavorites": ["Favorites"],
"BrowserFavourites": ["Favourites"],
"BrowserForward": ["Forward"],
"BrowserHome": ["HomePage"],
"BrowserRefresh": ["Refresh"],
"BrowserSearch": ["Search"],
"BrowserStop": ["Stop"],
"Cancel": ["Cancel"],
"CapsLock": ["Caps_Lock"],
"Clear": ["Clear"],
"Close": ["Close"],
"CodeInput": ["Codeinput"],
"ContextMenu": ["Menu"],
"ControlLeft": ["Control_L"],
"ControlRight": ["Control_R"],
"Copy": ["Copy", "3270_Copy"],
"Cut": ["Cut"],
"Delete": ["Delete"],
"Eisu": ["Eisu_Shift"],
"Eject": ["Eject"],
"End": ["End"],
"Enter": ["Return", "3270_Enter", "ISO_Enter"],
"EraseEof": ["3270_EraseEOF"],
"Escape": ["Escape"],
"ExSel": ["3270_ExSelect"],
"Execute": ["Execute"],
"F1": ["F1", "KP_F1"],
"F2": ["F2", "KP_F2"],
"F3": ["F3", "KP_F3"],
"F4": ["F4", "KP_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"],
"F25": ["F25"],
"Find": ["Find"],
"GroupFirst": ["ISO_First_Group"],
"GroupLast": ["ISO_Last_Group"],
"GroupNext": ["ISO_Next_Group"],
"GroupPrevious": ["ISO_Prev_Group"],
"HangulMode": ["Hangul"],
"HanjaMode": ["Hangul_Hanja"],
"Hankaku": ["Hankaku"],
"Help": ["Help"],
"Hiragana": ["Hiragana"],
"HiraganaKatakana": ["Hiragana_Katakana"],
"Home": ["Home"],
"Hybernate": ["Hybernate"],
"Hyper": ["Hyper_L", "Hyper_R"],
"Insert": ["Insert"],
"IntlYen": ["yen"],
"KanjiMode": ["Kanji"],
"Katakana": ["Katakana"],
"KbdIllumDown": ["KbdBrightnessDown"],
"KbdIllumUp": ["KbdBrightnessUp"],
"LaunchAudioBrowser": ["Music"],
"LaunchCalendar": ["Calendar"],
"LaunchDocuments": ["Document"],
"LaunchInternetBrowser": ["WWW"],
"LaunchMail": ["Mail"],
"LaunchPhone": ["Phone"],
"LaunchScreenSaver": ["ScreenSaver"],
"LogOff": ["LogOff"],
"MailForward": ["MailForward"],
"MailReply": ["Reply"],
"MailSend": ["Send"],
"MediaFastForward": ["AudioForward"],
"MediaPause": ["AudioPause"],
"MediaPlay": ["AudioPlay", "3270_Play"],
"MediaRecord": ["AudioRecord"],
"MediaRewind": ["AudioRewind"],
"MediaStop": ["AudioStop"],
"MediaTrackNext": ["AudioNext"],
"MediaTrackPrevious": ["AudioPrev"],
"MetaLeft": ["Meta_L"],
"MetaRight": ["Meta_R"],
"ModeChange": ["Mode_switch"],
"New": ["New"],
"NumLock": ["Num_Lock"],
"Open": ["Open"],
"PageDown": ["Page_Down"],
"PageUp": ["Page_Up"],
"Paste": ["Paste"],
"Pause": ["Pause"],
"PowerOff": ["PowerOff"],
"PreviousCandidate": ["PreviousCandidate"],
"Print": ["Print"],
"PrintScreen": ["3270_PrintScreen"],
"Redo": ["Redo"],
"Resume": ["Resume"],
"Romaji": ["Romaji"],
"Save": ["Save"],
"ScrollLock": ["Scroll_Lock"],
"Select": ["Select"],
"ShiftLeft": ["Shift_L"],
"ShiftRight": ["Shift_R"],
"SingleCandidate": ["SingleCandidate"],
"Sleep": ["Sleep"],
"SpellCheck": ["Spell"],
"Standby": ["Standby"],
"Super": ["Super_L", "Super_R"],
"Suspend": ["Suspend"],
"Tab": ["Tab", "KP_Tab", "ISO_Left_Tab"],
"Undo": ["Undo"],
"WakeUp": ["WakeUp"],
"Zenkaku": ["Zenkaku"],
"ZenkakuHankaku": ["Zenkaku_Hankaku"],
"ZoomIn": ["ZoomIn"],
"ZoomOut": ["ZoomOut"]
}
{
"SHIFT": ["ShiftLeft", "ShiftLeft", "ShiftRight"],
"CONTROL": ["ControlLeft", "ControlLeft", "ControlRight"],
"MOD1": ["AltLeft", "AltLeft", "AltRight"],
"META": ["MetaLeft", "MetaLeft", "MetaRight"]
}
{
"Numpad0": "NumpadInsert",
"Numpad1": "NumpadEnd",
"Numpad2": "NumpadDown",
"Numpad3": "NumpadPageDown",
"Numpad4": "NumpadLeft",
"Numpad6": "NumpadRight",
"Numpad7": "NumpadHome",
"Numpad8": "NumpadUp",
"Numpad9": "NumpadPageUp",
"NumpadPeriod": "NumpadDelete"
}
......@@ -8,7 +8,7 @@
// This file is generated by flutter/flutter@dev/tools/gen_keycodes/bin/gen_keycodes.dart and
// should not be edited directly.
//
// Edit the template dev/tools/gen_keycodes/data/keyboard_map_ios_cc.tmpl instead.
// Edit the template dev/tools/gen_keycodes/data/ios_keyboard_map_cc.tmpl instead.
// See dev/tools/gen_keycodes/README.md for more information.
// Maps macOS-specific key code values representing [PhysicalKeyboardKey].
......
{
"Backspace": ["Backspace"],
"Escape": ["Escape"],
"CapsLock": ["CapsLock"],
"NumLock": ["NumLock"],
"ArrowDown": ["ArrowDown"],
"ArrowLeft": ["ArrowLeft"],
"ArrowRight": ["ArrowRight"],
"ArrowUp": ["ArrowUp"],
"End": ["End"],
"Home": ["Home"],
"PageDown": ["PageDown"],
"PageUp": ["PageUp"],
"Insert": ["Insert"],
"Delete": ["Delete"],
"ContextMenu": ["ContextMenu"],
"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"],
"NumpadEnter": ["NumpadEnter"],
"NumpadMultiply": ["NumpadMultiply"],
"NumpadAdd": ["NumpadAdd"],
"NumpadComma": ["NumpadComma"],
"NumpadSubtract": ["NumpadSubtract"],
"NumpadDecimal": ["NumpadDecimal"],
"NumpadDivide": ["NumpadDivide"],
"Numpad0": ["Numpad0"],
"Numpad1": ["Numpad1"],
"Numpad2": ["Numpad2"],
"Numpad3": ["Numpad3"],
"Numpad4": ["Numpad4"],
"Numpad5": ["Numpad5"],
"Numpad6": ["Numpad6"],
"Numpad7": ["Numpad7"],
"Numpad8": ["Numpad8"],
"Numpad9": ["Numpad9"],
"NumpadEqual": ["NumpadEqual"],
"AltLeft": ["AltLeft"],
"ControlLeft": ["ControlLeft"],
"MetaLeft": ["MetaLeft"],
"ShiftLeft": ["ShiftLeft"],
"AltRight": ["AltRight"],
"ControlRight": ["ControlRight"],
"MetaRight": ["MetaRight"],
"ShiftRight": ["ShiftRight"],
"Lang1": ["Lang1"],
"Lang2": ["Lang2"],
"Lang3": ["Lang3"],
"Lang4": ["Lang4"],
"Lang5": ["Lang5"],
"IntlYen": ["IntlYen"],
"IntlRo": ["IntlRo"],
"AudioVolumeUp": ["AudioVolumeUp"],
"AudioVolumeDown": ["AudioVolumeDown"],
"AudioVolumeMute": ["AudioVolumeMute"]
}
{
"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"],
"gameButton1": ["BUTTON_1"],
"gameButton2": ["BUTTON_2"],
"gameButton3": ["BUTTON_3"],
"gameButton4": ["BUTTON_4"],
"gameButton5": ["BUTTON_5"],
"gameButton6": ["BUTTON_6"],
"gameButton7": ["BUTTON_7"],
"gameButton8": ["BUTTON_8"],
"gameButton9": ["BUTTON_9"],
"gameButton10": ["BUTTON_10"],
"gameButton11": ["BUTTON_11"],
"gameButton12": ["BUTTON_12"],
"gameButton13": ["BUTTON_13"],
"gameButton14": ["BUTTON_14"],
"gameButton15": ["BUTTON_15"],
"gameButton16": ["BUTTON_16"],
"gameButtonA": ["BUTTON_A"],
"gameButtonB": ["BUTTON_B"],
"gameButtonC": ["BUTTON_C"],
"gameButtonLeft1": ["BUTTON_L1"],
"gameButtonLeft2": ["BUTTON_L2"],
"gameButtonMode": ["BUTTON_MODE"],
"gameButtonRight1": ["BUTTON_R1"],
"gameButtonRight2": ["BUTTON_R2"],
"gameButtonSelect": ["BUTTON_SELECT"],
"gameButtonStart": ["BUTTON_START"],
"gameButtonThumbLeft": ["BUTTON_THUMBL"],
"gameButtonThumbRight": ["BUTTON_THUMBR"],
"gameButtonX": ["BUTTON_X"],
"gameButtonY": ["BUTTON_Y"],
"gameButtonZ": ["BUTTON_Z"],
"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"],
"select": ["DPAD_CENTER"],
"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"]
}
{
"altLeft": ["LEFT_ALT"],
"altRight": ["RIGHT_ALT"],
"arrowDown": ["DOWN"],
"arrowLeft": ["LEFT"],
"arrowRight": ["RIGHT"],
"arrowUp": ["UP"],
"backquote": ["GRAVE_ACCENT"],
"backslash": ["BACKSLASH"],
"backspace": ["BACKSPACE"],
"bracketLeft": ["LEFT_BRACKET"],
"bracketRight": ["RIGHT_BRACKET"],
"capsLock": ["CAPS_LOCK"],
"comma": ["COMMA"],
"contextMenu": ["MENU"],
"controlLeft": ["LEFT_CONTROL"],
"controlRight": ["RIGHT_CONTROL"],
"delete": ["DELETE"],
"digit0": ["0"],
"digit1": ["1"],
"digit2": ["2"],
"digit3": ["3"],
"digit4": ["4"],
"digit5": ["5"],
"digit6": ["6"],
"digit7": ["7"],
"digit8": ["8"],
"digit9": ["9"],
"end": ["END"],
"enter": ["ENTER"],
"equal": ["EQUAL"],
"escape": ["ESCAPE"],
"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"],
"f25": ["F25"],
"home": ["HOME"],
"insert": ["INSERT"],
"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"],
"metaLeft": ["LEFT_SUPER"],
"metaRight": ["RIGHT_SUPER"],
"minus": ["MINUS"],
"numLock": ["NUM_LOCK"],
"numpad0": ["KP_0"],
"numpad1": ["KP_1"],
"numpad2": ["KP_2"],
"numpad3": ["KP_3"],
"numpad4": ["KP_4"],
"numpad5": ["KP_5"],
"numpad6": ["KP_6"],
"numpad7": ["KP_7"],
"numpad8": ["KP_8"],
"numpad9": ["KP_9"],
"numpadAdd": ["KP_ADD"],
"numpadDecimal": ["KP_DECIMAL"],
"numpadDivide": ["KP_DIVIDE"],
"numpadEnter": ["KP_ENTER"],
"numpadEqual": ["KP_EQUAL"],
"numpadMultiply": ["KP_MULTIPLY"],
"numpadSubtract": ["NUMPAD_SUBTRACT"],
"pageDown": ["PAGE_DOWN"],
"pageUp": ["PAGE_UP"],
"pause": ["PAUSE"],
"period": ["PERIOD"],
"printScreen": ["PRINT_SCREEN"],
"quote": ["APOSTROPHE"],
"semicolon": ["SEMICOLON"],
"shiftLeft": ["LEFT_SHIFT"],
"shiftRight": ["RIGHT_SHIFT"],
"slash": ["SLASH"],
"space": ["SPACE"],
"tab": ["TAB"]
}
{
"altLeft": ["Alt_L"],
"altRight": ["Alt_R", "ISO_Level3_Shift"],
"arrowDown": ["Down", "KP_Down"],
"arrowLeft": ["Left", "KP_Left"],
"arrowRight": ["Right", "KP_Right"],
"arrowUp": ["Up", "KP_Up"],
"audioVolumeDown": ["AudioLowerVolume"],
"audioVolumeMute": ["AudioMute"],
"audioVolumeUp": ["AudioRaiseVolume"],
"backquote": ["quoteleft"],
"backslash": ["backslash"],
"backspace": ["BackSpace"],
"bracketLeft": ["bracketleft"],
"bracketRight": ["bracketright"],
"brightnessDown": ["MonBrightnessDown"],
"brightnessUp": ["MonBrightnessUp"],
"browserBack": ["Back"],
"browserFavorites": ["Favorites"],
"browserFavourites": ["Favourites"],
"browserForward": ["Forward"],
"browserHome": ["HomePage"],
"browserRefresh": ["Refresh"],
"browserSearch": ["Search"],
"browserStop": ["Stop"],
"capsLock": ["Caps_Lock"],
"close": ["Close"],
"comma": ["comma"],
"contextMenu": ["Menu"],
"controlLeft": ["Control_L"],
"controlRight": ["Control_R"],
"copy": ["Copy", "3270_Copy"],
"delete": ["Delete", "KP_Delete"],
"digit0": ["0"],
"digit1": ["1"],
"digit2": ["2"],
"digit3": ["3"],
"digit4": ["4"],
"digit5": ["5"],
"digit6": ["6"],
"digit7": ["7"],
"digit8": ["8"],
"digit9": ["9"],
"eject": ["Eject"],
"end": ["End", "KP_End"],
"enter": ["Return", "Enter", "ISO_Enter"],
"equal": ["equal"],
"escape": ["Escape"],
"f1": ["F1", "KP_F1"],
"f2": ["F2", "KP_F2"],
"f3": ["F3", "KP_F3"],
"f4": ["F4", "KP_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"],
"f25": ["F25"],
"find": ["Find"],
"help": ["Help"],
"home": ["Home", "KP_Home"],
"hyper": ["Hyper_L", "Hyper_R"],
"insert": ["Insert", "KP_Insert"],
"intlYen": ["yen"],
"kanaMode": ["kana_switch"],
"kbdIllumDown": ["KbdBrightnessDown"],
"kbdIllumUp": ["KbdBrightnessUp"],
"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"],
"launchAudioBrowser": ["Music"],
"launchCalendar": ["Calendar"],
"launchDocuments": ["Document"],
"launchInternetBrowser": ["WWW"],
"launchMail": ["Mail"],
"launchPhone": ["Phone"],
"launchScreenSaver": ["ScreenSaver"],
"logOff": ["LogOff"],
"mailForward": ["MailForward"],
"mailReply": ["Reply"],
"mailSend": ["Send"],
"mediaFastForward": ["AudioForward"],
"mediaPause": ["AudioPause"],
"mediaPlay": ["AudioPlay", "3270_Play"],
"mediaRecord": ["AudioRecord"],
"mediaRewind": ["AudioRewind"],
"mediaStop": ["AudioStop"],
"mediaTrackNext": ["AudioNext"],
"mediaTrackPrevious": ["AudioPrev"],
"metaLeft": ["Meta_L"],
"metaRight": ["Meta_R"],
"minus": ["minus"],
"newKey": ["New"],
"numLock": ["Num_Lock"],
"numpad0": ["KP_0"],
"numpad1": ["KP_1"],
"numpad2": ["KP_2"],
"numpad3": ["KP_3"],
"numpad4": ["KP_4"],
"numpad5": ["KP_5"],
"numpad6": ["KP_6"],
"numpad7": ["KP_7"],
"numpad8": ["KP_8"],
"numpad9": ["KP_9"],
"numpadAdd": ["KP_Add"],
"numpadDecimal": ["KP_Decimal"],
"numpadDivide": ["KP_Divide"],
"numpadEnter": ["KP_Enter"],
"numpadEqual": ["KP_Equal"],
"numpadMultiply": ["KP_Multiply"],
"numpadSubtract": ["KP_Subtract"],
"open": ["Open"],
"pageDown": ["Page_Down", "KP_Page_Down"],
"pageUp": ["Page_Up", "KP_Page_Up"],
"paste": ["Paste"],
"pause": ["Pause"],
"period": ["period"],
"power": ["PowerOff"],
"print": ["Print"],
"printScreen": ["3270_PrintScreen"],
"quote": ["apostrophe"],
"redo": ["Redo"],
"resume": ["Resume"],
"save": ["Save"],
"scrollLock": ["Scroll_Lock"],
"select": ["Select"],
"semicolon": ["semicolon"],
"shiftLeft": ["Shift_L"],
"shiftRight": ["Shift_R"],
"slash": ["slash"],
"sleep": ["Sleep"],
"space": ["space", "KP_Space"],
"spellCheck": ["Spell"],
"superKey": ["Super_L", "Super_R"],
"suspend": ["Suspend"],
"tab": ["Tab", "KP_Tab", "ISO_Left_Tab"],
"undo": ["Undo"],
"wakeUp": ["WakeUp"],
"zoomIn": ["ZoomIn"],
"zoomOut": ["ZoomOut"]
}
{
"leftMouseButton": ["LBUTTON"],
"rightMouseButton": ["RBUTTON"],
"cancel": ["CANCEL"],
"middleMouseButton": ["MBUTTON"],
"xMouseButton1": ["XBUTTON1"],
"xMouseButton2": ["XBUTTON2"],
"backspace": ["BACK"],
"tab": ["TAB"],
"clear": ["CLEAR"],
"enter": ["RETURN"],
"shift": ["SHIFT"],
"control": ["CONTROL"],
"menu": ["MENU"],
"pause": ["PAUSE"],
"capsLock": ["CAPITAL"],
"kana": ["KANA"],
"hangeul": ["HANGEUL"],
"hangul": ["HANGUL"],
"junja": ["JUNJA"],
"final": ["FINAL"],
"hanja": ["HANJA"],
"kanji": ["KANJI"],
"escape": ["ESCAPE"],
"convert": ["CONVERT"],
"nonconvert": ["NONCONVERT"],
"accept": ["ACCEPT"],
"modeChange": ["MODECHANGE"],
"space": ["SPACE"],
"prior": ["PRIOR"],
"next": ["NEXT"],
"end": ["END"],
"home": ["HOME"],
"arrowLeft": ["LEFT"],
"arrowUp": ["UP"],
"arrowRight": ["RIGHT"],
"arrowDown": ["DOWN"],
"select": ["SELECT"],
"print": ["PRINT"],
"execute": ["EXECUTE"],
"snapshot": ["SNAPSHOT"],
"insert": ["INSERT"],
"delete": ["DELETE"],
"help": ["HELP"],
"metaLeft": ["LWIN"],
"metaRight": ["RWIN"],
"apps": ["APPS"],
"sleep": ["SLEEP"],
"numpad0": ["NUMPAD0"],
"numpad1": ["NUMPAD1"],
"numpad2": ["NUMPAD2"],
"numpad3": ["NUMPAD3"],
"numpad4": ["NUMPAD4"],
"numpad5": ["NUMPAD5"],
"numpad6": ["NUMPAD6"],
"numpad7": ["NUMPAD7"],
"numpad8": ["NUMPAD8"],
"numpad9": ["NUMPAD9"],
"numpadMultiply": ["MULTIPLY"],
"numpadAdd": ["ADD"],
"numpadSeparator": ["SEPARATOR"],
"numpadSubtract": ["SUBTRACT"],
"numpadDecimal": ["DECIMAL"],
"numpadDivide": ["DIVIDE"],
"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"],
"navigationView": ["NAVIGATION_VIEW"],
"navigationMenu": ["NAVIGATION_MENU"],
"navigationUp": ["NAVIGATION_UP"],
"navigationDown": ["NAVIGATION_DOWN"],
"navigationLeft": ["NAVIGATION_LEFT"],
"navigationRight": ["NAVIGATION_RIGHT"],
"navigationAccept": ["NAVIGATION_ACCEPT"],
"navigationCancel": ["NAVIGATION_CANCEL"],
"numlock": ["NUMLOCK"],
"scroll": ["SCROLL"],
"numpadEqual": ["OEM_NEC_EQUAL"],
"oemFjJisho": ["OEM_FJ_JISHO"],
"oemFjMasshou": ["OEM_FJ_MASSHOU"],
"oemFjTouroku": ["OEM_FJ_TOUROKU"],
"oemFjLoya": ["OEM_FJ_LOYA"],
"oemFjRoya": ["OEM_FJ_ROYA"],
"shiftLeft": ["LSHIFT"],
"shiftRight": ["RSHIFT"],
"controlLeft": ["LCONTROL"],
"controlRight": ["RCONTROL"],
"altLeft": ["LMENU"],
"altRight": ["RMENU"],
"browserBack": ["BROWSER_BACK"],
"browserForward": ["BROWSER_FORWARD"],
"browserRefresh": ["BROWSER_REFRESH"],
"browserStop": ["BROWSER_STOP"],
"browserSearch": ["BROWSER_SEARCH"],
"browserFavorites": ["BROWSER_FAVORITES"],
"browserHome": ["BROWSER_HOME"],
"volumeMute": ["VOLUME_MUTE"],
"volumeDown": ["VOLUME_DOWN"],
"volumeUp": ["VOLUME_UP"],
"mediaNextTrack": ["MEDIA_NEXT_TRACK"],
"mediaPrevTrack": ["MEDIA_PREV_TRACK"],
"mediaStop": ["MEDIA_STOP"],
"mediaPlayPause": ["MEDIA_PLAY_PAUSE"],
"launchMail": ["LAUNCH_MAIL"],
"launchMediaSelect": ["LAUNCH_MEDIA_SELECT"],
"launchApp1": ["LAUNCH_APP1"],
"launchApp2": ["LAUNCH_APP2"],
"semicolon": ["OEM_1"],
"equal": ["OEM_PLUS"],
"comma": ["OEM_COMMA"],
"minus": ["OEM_MINUS"],
"period": ["OEM_PERIOD"],
"slash": ["OEM_2"],
"backquote": ["OEM_3"],
"gamepadA": ["GAMEPAD_A"],
"gamepadB": ["GAMEPAD_B"],
"gamepadX": ["GAMEPAD_X"],
"gamepadY": ["GAMEPAD_Y"],
"gamepadRightShoulder": ["GAMEPAD_RIGHT_SHOULDER"],
"gamepadLeftShoulder": ["GAMEPAD_LEFT_SHOULDER"],
"gamepadLeftTrigger": ["GAMEPAD_LEFT_TRIGGER"],
"gamepadRightTrigger": ["GAMEPAD_RIGHT_TRIGGER"],
"gamepadDpadUp": ["GAMEPAD_DPAD_UP"],
"gamepadDpadDown": ["GAMEPAD_DPAD_DOWN"],
"gamepadDpadLeft": ["GAMEPAD_DPAD_LEFT"],
"gamepadDpadRight": ["GAMEPAD_DPAD_RIGHT"],
"gamepadMenu": ["GAMEPAD_MENU"],
"gamepadView": ["GAMEPAD_VIEW"],
"gamepadLeftThumbstickButton": ["GAMEPAD_LEFT_THUMBSTICK_BUTTON"],
"gamepadRightThumbstickButton": ["GAMEPAD_RIGHT_THUMBSTICK_BUTTON"],
"gamepadLeftThumbstickUp": ["GAMEPAD_LEFT_THUMBSTICK_UP"],
"gamepadLeftThumbstickDown": ["GAMEPAD_LEFT_THUMBSTICK_DOWN"],
"gamepadLeftThumbstickRight": ["GAMEPAD_LEFT_THUMBSTICK_RIGHT"],
"gamepadLeftThumbstickLeft": ["GAMEPAD_LEFT_THUMBSTICK_LEFT"],
"gamepadRightThumbstickUp": ["GAMEPAD_RIGHT_THUMBSTICK_UP"],
"gamepadRightThumbstickDown": ["GAMEPAD_RIGHT_THUMBSTICK_DOWN"],
"gamepadRightThumbstickRight": ["GAMEPAD_RIGHT_THUMBSTICK_RIGHT"],
"gamepadRightThumbstickLeft": ["GAMEPAD_RIGHT_THUMBSTICK_LEFT"],
"bracketLeft": ["OEM_4"],
"backslash": ["OEM_5"],
"bracketRight": ["OEM_6"],
"quote": ["OEM_7"],
"oem8": ["OEM_8"],
"oemAx": ["OEM_AX"],
"oem102": ["OEM_102"],
"icoHelp": ["ICO_HELP"],
"ico00": ["ICO_00"],
"processkey": ["PROCESSKEY"],
"icoClear": ["ICO_CLEAR"],
"packet": ["PACKET"],
"oemReset": ["OEM_RESET"],
"oemJump": ["OEM_JUMP"],
"oemPa1": ["OEM_PA1"],
"oemPa2": ["OEM_PA2"],
"oemPa3": ["OEM_PA3"],
"oemWsctrl": ["OEM_WSCTRL"],
"oemCusel": ["OEM_CUSEL"],
"oemAttn": ["OEM_ATTN"],
"oemFinish": ["OEM_FINISH"],
"oemCopy": ["OEM_COPY"],
"oemAuto": ["OEM_AUTO"],
"oemEnlw": ["OEM_ENLW"],
"oemBacktab": ["OEM_BACKTAB"],
"attn": ["ATTN"],
"crsel": ["CRSEL"],
"exsel": ["EXSEL"],
"ereof": ["EREOF"],
"play": ["PLAY"],
"zoom": ["ZOOM"],
"noname": ["NONAME"],
"pa1": ["PA1"],
"oemClear": ["OEM_CLEAR"],
"0": ["0"],
"1": ["1"],
"2": ["2"],
"3": ["3"],
"4": ["4"],
"5": ["5"],
"6": ["6"],
"7": ["7"],
"8": ["8"],
"9": ["9"],
"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"]
}
// Copyright 2014 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <map>
// DO NOT EDIT -- DO NOT EDIT -- DO NOT EDIT
// This file is generated by flutter/flutter@dev/tools/gen_keycodes/bin/gen_keycodes.dart and
// should not be edited directly.
//
// Edit the template dev/tools/gen_keycodes/data/keyboard_map_android_cc.tmpl instead.
// See dev/tools/gen_keycodes/README.md for more information.
// Maps Android-specific key codes to the matching LogicalKeyboardKey id.
const std::map<int, int> g_android_to_logical_key = {
@@@ANDROID_KEY_CODE_MAP@@@
};
// Maps Android-specific scan codes to the matching PhysicalKeyboardKey id (a.k.a. HID USB code).
const std::map<int, int> g_android_to_physical_key = {
@@@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. Maps Android key codes to PhysicalKeyboardKey
// codes (USB HID codes).
const std::map<int, int> g_android_numpad_map = {
@@@ANDROID_NUMPAD_MAP@@@
};
// Copyright 2014 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <map>
// DO NOT EDIT -- DO NOT EDIT -- DO NOT EDIT
// This file is generated by flutter/flutter@dev/tools/gen_keycodes/bin/gen_keycodes.dart and
// should not be edited directly.
//
// Edit the template dev/tools/gen_keycodes/data/keyboard_map_windows_cc.tmpl instead.
// See dev/tools/gen_keycodes/README.md for more information.
/// Maps g_windows_to_physical_key specific key code values representing [PhysicalKeyboardKey].
const std::map<int, uint64_t> g_windows_to_physical_key = {
@@@WINDOWS_SCAN_CODE_MAP@@@
};
/// Maps Windows-specific key codes to the matching [LogicalKeyboardKey].
const std::map<int, uint64_t> g_windows_to_logical_key = {
@@@WINDOWS_KEY_CODE_MAP@@@
};
/// A map of GLFW 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 std::map<int, uint64_t> g_windows_numpad_map = {
@@@WINDOWS_NUMPAD_MAP@@@
};
......@@ -59,6 +59,16 @@ const Map<int, LogicalKeyboardKey> kMacOsFunctionKeyMap = <int, LogicalKeyboardK
@@@MACOS_FUNCTION_KEY_MAP@@@
};
/// A map of macOS key codes presenting [LogicalKeyboardKey].
///
/// Logical key codes are not available in macOS key events. Most of the logical keys
/// are derived from its `characterIgnoringModifiers`, but those keys that don't
/// have a character representation will be derived from their key codes using
/// this map.
const Map<int, LogicalKeyboardKey> kMacOsToLogicalKey = <int, LogicalKeyboardKey>{
@@@MACOS_KEY_CODE_MAP@@@
};
/// Maps iOS-specific key code values representing [PhysicalKeyboardKey].
///
/// iOS doesn't provide a scan code, but a virtual keycode to represent a physical key.
......@@ -73,6 +83,16 @@ const Map<int, LogicalKeyboardKey> kIosNumPadMap = <int, LogicalKeyboardKey>{
@@@IOS_NUMPAD_MAP@@@
};
/// A map of iOS key codes presenting [LogicalKeyboardKey].
///
/// Logical key codes are not available in iOS key events. Most of the logical keys
/// are derived from its `characterIgnoringModifiers`, but those keys that don't
/// have a character representation will be derived from their key codes using
/// this map.
const Map<int, LogicalKeyboardKey> kIosToLogicalKey = <int, LogicalKeyboardKey>{
@@@IOS_KEY_CODE_MAP@@@
};
/// Maps GLFW-specific key codes to the matching [LogicalKeyboardKey].
const Map<int, LogicalKeyboardKey> kGlfwToLogicalKey = <int, LogicalKeyboardKey>{
@@@GLFW_KEY_CODE_MAP@@@
......@@ -119,6 +139,13 @@ const Map<String, LogicalKeyboardKey> kWebNumPadMap = <String, LogicalKeyboardKe
@@@WEB_NUMPAD_MAP@@@
};
/// A map of Web KeyboardEvent keys which needs to be decided based on location,
/// typically for numpad kyes and modifier keys. Used to provide different key
/// objects for keys like KEY_EQUALS and NUMPAD_EQUALS.
const Map<String, List<LogicalKeyboardKey?>> kWebLocationMap = <String, List<LogicalKeyboardKey?>>{
@@@WEB_LOCATION_MAP@@@
};
/// Maps Windows KeyboardEvent codes to the matching [LogicalKeyboardKey].
const Map<int, LogicalKeyboardKey> kWindowsToLogicalKey = <int, LogicalKeyboardKey>{
@@@WINDOWS_LOGICAL_KEY_MAP@@@
......
// Copyright 2014 The Flutter Authors. All rights reserved.
// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <map>
#import <Cocoa/Cocoa.h>
#import <Foundation/Foundation.h>
#include "./KeyCodeMap_internal.h"
// DO NOT EDIT -- DO NOT EDIT -- DO NOT EDIT
// This file is generated by flutter/flutter@dev/tools/gen_keycodes/bin/gen_keycodes.dart and
......@@ -11,22 +13,22 @@
// Edit the template dev/tools/gen_keycodes/data/keyboard_map_macos_cc.tmpl instead.
// See dev/tools/gen_keycodes/README.md for more information.
// Maps macOS-specific key code values representing [PhysicalKeyboardKey].
//
// MacOS doesn't provide a scan code, but a virtual keycode to represent a physical key.
const std::map<int, int> g_macos_to_physical_key = {
@@@MASK_CONSTANTS@@@
const NSDictionary* keyCodeToPhysicalKey = @{
@@@MACOS_SCAN_CODE_MAP@@@
};
// A map of macOS 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 std::map<int, int> g_macos_numpad_map = {
@@@MACOS_NUMPAD_MAP@@@
const NSDictionary* keyCodeToLogicalKey = @{
@@@MACOS_KEYCODE_LOGICAL_MAP@@@
};
// A map of macOS key codes which are numbered function keys, so that they
// can be excluded when asking "is the Fn modifier down?".
const std::map<int, int> g_macos_function_key_map = {
@@@MACOS_FUNCTION_KEY_MAP@@@
const NSDictionary* keyCodeToModifierFlag = @{
@@@KEYCODE_TO_MODIFIER_FLAG_MAP@@@
};
const NSDictionary* modifierFlagToKeyCode = @{
@@@MODIFIER_FLAG_TO_KEYCODE_MAP@@@
};
@@@SPECIAL_KEY_CONSTANTS@@@
{
"Backspace": ["Backspace"],
"Escape": ["Escape"],
"CapsLock": ["CapsLock"],
"Fn": ["Fn"],
"NumLock": ["NumLock"],
"ArrowDown": ["ArrowDown"],
"ArrowLeft": ["ArrowLeft"],
"ArrowRight": ["ArrowRight"],
"ArrowUp": ["ArrowUp"],
"End": ["End"],
"Home": ["Home"],
"PageDown": ["PageDown"],
"PageUp": ["PageUp"],
"Insert": ["Insert"],
"Delete": ["Delete"],
"ContextMenu": ["ContextMenu"],
"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"],
"NumpadEnter": ["NumpadEnter"],
"NumpadMultiply": ["NumpadMultiply"],
"NumpadAdd": ["NumpadAdd"],
"NumpadComma": ["NumpadComma"],
"NumpadSubtract": ["NumpadSubtract"],
"NumpadDecimal": ["NumpadDecimal"],
"NumpadDivide": ["NumpadDivide"],
"Numpad0": ["Numpad0"],
"Numpad1": ["Numpad1"],
"Numpad2": ["Numpad2"],
"Numpad3": ["Numpad3"],
"Numpad4": ["Numpad4"],
"Numpad5": ["Numpad5"],
"Numpad6": ["Numpad6"],
"Numpad7": ["Numpad7"],
"Numpad8": ["Numpad8"],
"Numpad9": ["Numpad9"],
"NumpadEqual": ["NumpadEqual"],
"AltLeft": ["AltLeft"],
"ControlLeft": ["ControlLeft"],
"MetaLeft": ["MetaLeft"],
"ShiftLeft": ["ShiftLeft"],
"AltRight": ["AltRight"],
"ControlRight": ["ControlRight"],
"MetaRight": ["MetaRight"],
"ShiftRight": ["ShiftRight"],
"Lang1": ["Lang1"],
"Lang2": ["Lang2"],
"IntlYen": ["IntlYen"],
"IntlRo": ["IntlRo"],
"AudioVolumeUp": ["AudioVolumeUp"],
"AudioVolumeDown": ["AudioVolumeDown"],
"AudioVolumeMute": ["AudioVolumeMute"]
}
{
"valueMask": {
"value": "0x000FFFFFFFF",
"description": [
"Mask for the 32-bit value portion of the key code.",
"This is used by platform-specific code to generate Flutter key codes."
]
},
"platformMask": {
"value": "0x0FF00000000",
"description": [
"Mask for the platform prefix portion of the key code.",
"This is used by platform-specific code to generate Flutter key codes."
]
},
"unicodePlane": {
"value": "0x00000000000",
"description": [
"The code prefix for keys which have a Unicode representation.",
"This is used by platform-specific code to generate Flutter key codes."
]
},
"autogeneratedMask": {
"value": "0x10000000000",
"description": [
"Mask for the auto-generated 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."
]
},
"synonymMask": {
"value": "0x20000000000",
"description": [
"Mask for the synonym pseudo-keys generated for keys which appear in more than one place on the keyboard.",
"IDs in this range are used to represent keys which appear in multiple places on the keyboard, such as the SHIFT, ALT, CTRL, and numeric keypad keys. These key codes will never be generated by the key event system, but may be used in key maps to represent the union of all the keys of each type in order to match them.",
"To look up the synonyms that are defined, look in the [synonyms] map."
]
},
"hidPlane": {
"value": "0x00100000000",
"description": [
"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."
]
}
}
{
"None": {
"names": {
"name": "None"
},
"scanCodes": {
"usb": 0
}
},
"Hyper": {
"names": {
"name": "Hyper",
"chromium": "Hyper"
},
"scanCodes": {
"usb": 16
}
},
"Super": {
"names": {
"name": "Super",
"chromium": "Super"
},
"scanCodes": {
"usb": 17
}
},
"Fn": {
"names": {
"name": "Fn",
"chromium": "Fn"
},
"scanCodes": {
"android": [
464
],
"usb": 18,
"macos": 63
}
},
"FnLock": {
"names": {
"name": "FnLock",
"chromium": "FnLock"
},
"scanCodes": {
"usb": 19
}
},
"Suspend": {
"names": {
"name": "Suspend",
"chromium": "Suspend"
},
"scanCodes": {
"android": [
205
],
"usb": 20
}
},
"Resume": {
"names": {
"name": "Resume",
"chromium": "Resume"
},
"scanCodes": {
"usb": 21
}
},
"Turbo": {
"names": {
"name": "Turbo",
"chromium": "Turbo"
},
"scanCodes": {
"usb": 22
}
},
"PrivacyScreenToggle": {
"names": {
"name": "PrivacyScreenToggle",
"chromium": "PrivacyScreenToggle"
},
"scanCodes": {
"usb": 23,
"linux": 633,
"xkb": 641
}
},
"Sleep": {
"names": {
"name": "Sleep",
"chromium": "Sleep"
},
"scanCodes": {
"android": [
142
],
"usb": 65666,
"linux": 142,
"xkb": 150,
"windows": 57439
}
},
"WakeUp": {
"names": {
"name": "WakeUp",
"chromium": "WakeUp"
},
"scanCodes": {
"android": [
143
],
"usb": 65667,
"linux": 143,
"xkb": 151,
"windows": 57443
}
},
"DisplayToggleIntExt": {
"names": {
"name": "DisplayToggleIntExt",
"chromium": "DisplayToggleIntExt"
},
"scanCodes": {
"usb": 65717,
"linux": 227,
"xkb": 235
}
},
"GameButton1": {
"names": {
"name": "GameButton1",
"chromium": "GameButton1"
},
"scanCodes": {
"android": [
256,
288
],
"usb": 392961
}
},
"GameButton2": {
"names": {
"name": "GameButton2",
"chromium": "GameButton2"
},
"scanCodes": {
"android": [
257,
289
],
"usb": 392962
}
},
"GameButton3": {
"names": {
"name": "GameButton3",
"chromium": "GameButton3"
},
"scanCodes": {
"android": [
258,
290
],
"usb": 392963
}
},
"GameButton4": {
"names": {
"name": "GameButton4",
"chromium": "GameButton4"
},
"scanCodes": {
"android": [
259,
291
],
"usb": 392964
}
},
"GameButton5": {
"names": {
"name": "GameButton5",
"chromium": "GameButton5"
},
"scanCodes": {
"android": [
260,
292
],
"usb": 392965
}
},
"GameButton6": {
"names": {
"name": "GameButton6",
"chromium": "GameButton6"
},
"scanCodes": {
"android": [
261,
293
],
"usb": 392966
}
},
"GameButton7": {
"names": {
"name": "GameButton7",
"chromium": "GameButton7"
},
"scanCodes": {
"android": [
262,
294
],
"usb": 392967
}
},
"GameButton8": {
"names": {
"name": "GameButton8",
"chromium": "GameButton8"
},
"scanCodes": {
"android": [
263,
295
],
"usb": 392968
}
},
"GameButton9": {
"names": {
"name": "GameButton9",
"chromium": "GameButton9"
},
"scanCodes": {
"android": [
264,
296
],
"usb": 392969
}
},
"GameButton10": {
"names": {
"name": "GameButton10",
"chromium": "GameButton10"
},
"scanCodes": {
"android": [
265,
297
],
"usb": 392970
}
},
"GameButton11": {
"names": {
"name": "GameButton11",
"chromium": "GameButton11"
},
"scanCodes": {
"android": [
266,
298
],
"usb": 392971
}
},
"GameButton12": {
"names": {
"name": "GameButton12",
"chromium": "GameButton12"
},
"scanCodes": {
"android": [
267,
299
],
"usb": 392972
}
},
"GameButton13": {
"names": {
"name": "GameButton13",
"chromium": "GameButton13"
},
"scanCodes": {
"android": [
268,
300
],
"usb": 392973
}
},
"GameButton14": {
"names": {
"name": "GameButton14",
"chromium": "GameButton14"
},
"scanCodes": {
"android": [
269,
301
],
"usb": 392974
}
},
"GameButton15": {
"names": {
"name": "GameButton15",
"chromium": "GameButton15"
},
"scanCodes": {
"android": [
270,
302
],
"usb": 392975
}
},
"GameButton16": {
"names": {
"name": "GameButton16",
"chromium": "GameButton16"
},
"scanCodes": {
"android": [
271,
303
],
"usb": 392976
}
},
"GameButtonA": {
"names": {
"name": "GameButtonA",
"chromium": "GameButtonA"
},
"scanCodes": {
"android": [
304
],
"usb": 392977
}
},
"GameButtonB": {
"names": {
"name": "GameButtonB",
"chromium": "GameButtonB"
},
"scanCodes": {
"android": [
305
],
"usb": 392978
}
},
"GameButtonC": {
"names": {
"name": "GameButtonC",
"chromium": "GameButtonC"
},
"scanCodes": {
"android": [
306
],
"usb": 392979
}
},
"GameButtonLeft1": {
"names": {
"name": "GameButtonLeft1",
"chromium": "GameButtonLeft1"
},
"scanCodes": {
"android": [
310
],
"usb": 392980
}
},
"GameButtonLeft2": {
"names": {
"name": "GameButtonLeft2",
"chromium": "GameButtonLeft2"
},
"scanCodes": {
"android": [
312
],
"usb": 392981
}
},
"GameButtonMode": {
"names": {
"name": "GameButtonMode",
"chromium": "GameButtonMode"
},
"scanCodes": {
"android": [
316
],
"usb": 392982
}
},
"GameButtonRight1": {
"names": {
"name": "GameButtonRight1",
"chromium": "GameButtonRight1"
},
"scanCodes": {
"android": [
311
],
"usb": 392983
}
},
"GameButtonRight2": {
"names": {
"name": "GameButtonRight2",
"chromium": "GameButtonRight2"
},
"scanCodes": {
"android": [
313
],
"usb": 392984
}
},
"GameButtonSelect": {
"names": {
"name": "GameButtonSelect",
"chromium": "GameButtonSelect"
},
"scanCodes": {
"android": [
314
],
"usb": 392985
}
},
"GameButtonStart": {
"names": {
"name": "GameButtonStart",
"chromium": "GameButtonStart"
},
"scanCodes": {
"android": [
315
],
"usb": 392986
}
},
"GameButtonThumbLeft": {
"names": {
"name": "GameButtonThumbLeft",
"chromium": "GameButtonThumbLeft"
},
"scanCodes": {
"android": [
317
],
"usb": 392987
}
},
"GameButtonThumbRight": {
"names": {
"name": "GameButtonThumbRight",
"chromium": "GameButtonThumbRight"
},
"scanCodes": {
"android": [
318
],
"usb": 392988
}
},
"GameButtonX": {
"names": {
"name": "GameButtonX",
"chromium": "GameButtonX"
},
"scanCodes": {
"android": [
307
],
"usb": 392989
}
},
"GameButtonY": {
"names": {
"name": "GameButtonY",
"chromium": "GameButtonY"
},
"scanCodes": {
"android": [
308
],
"usb": 392990
}
},
"GameButtonZ": {
"names": {
"name": "GameButtonZ",
"chromium": "GameButtonZ"
},
"scanCodes": {
"android": [
309
],
"usb": 392991
}
},
"UsbReserved": {
"names": {
"name": "UsbReserved"
},
"scanCodes": {
"usb": 458752,
"ios": 0
}
},
"UsbErrorRollOver": {
"names": {
"name": "UsbErrorRollOver"
},
"scanCodes": {
"usb": 458753,
"windows": 255,
"ios": 1
}
},
"UsbPostFail": {
"names": {
"name": "UsbPostFail"
},
"scanCodes": {
"usb": 458754,
"windows": 252,
"ios": 2
}
},
"UsbErrorUndefined": {
"names": {
"name": "UsbErrorUndefined"
},
"scanCodes": {
"usb": 458755,
"ios": 3
}
},
"KeyA": {
"names": {
"name": "KeyA",
"chromium": "KeyA"
},
"scanCodes": {
"android": [
30
],
"usb": 458756,
"linux": 30,
"xkb": 38,
"windows": 30,
"macos": 0,
"ios": 4
},
"keyCodes": {
"glfw": [
65
]
}
},
"KeyB": {
"names": {
"name": "KeyB",
"chromium": "KeyB"
},
"scanCodes": {
"android": [
48
],
"usb": 458757,
"linux": 48,
"xkb": 56,
"windows": 48,
"macos": 11,
"ios": 5
},
"keyCodes": {
"glfw": [
66
]
}
},
"KeyC": {
"names": {
"name": "KeyC",
"chromium": "KeyC"
},
"scanCodes": {
"android": [
46
],
"usb": 458758,
"linux": 46,
"xkb": 54,
"windows": 46,
"macos": 8,
"ios": 6
},
"keyCodes": {
"glfw": [
67
]
}
},
"KeyD": {
"names": {
"name": "KeyD",
"chromium": "KeyD"
},
"scanCodes": {
"android": [
32
],
"usb": 458759,
"linux": 32,
"xkb": 40,
"windows": 32,
"macos": 2,
"ios": 7
},
"keyCodes": {
"glfw": [
68
]
}
},
"KeyE": {
"names": {
"name": "KeyE",
"chromium": "KeyE"
},
"scanCodes": {
"android": [
18
],
"usb": 458760,
"linux": 18,
"xkb": 26,
"windows": 18,
"macos": 14,
"ios": 8
},
"keyCodes": {
"glfw": [
69
]
}
},
"KeyF": {
"names": {
"name": "KeyF",
"chromium": "KeyF"
},
"scanCodes": {
"android": [
33
],
"usb": 458761,
"linux": 33,
"xkb": 41,
"windows": 33,
"macos": 3,
"ios": 9
},
"keyCodes": {
"glfw": [
70
]
}
},
"KeyG": {
"names": {
"name": "KeyG",
"chromium": "KeyG"
},
"scanCodes": {
"android": [
34
],
"usb": 458762,
"linux": 34,
"xkb": 42,
"windows": 34,
"macos": 5,
"ios": 10
},
"keyCodes": {
"glfw": [
71
]
}
},
"KeyH": {
"names": {
"name": "KeyH",
"chromium": "KeyH"
},
"scanCodes": {
"android": [
35
],
"usb": 458763,
"linux": 35,
"xkb": 43,
"windows": 35,
"macos": 4,
"ios": 11
},
"keyCodes": {
"glfw": [
72
]
}
},
"KeyI": {
"names": {
"name": "KeyI",
"chromium": "KeyI"
},
"scanCodes": {
"android": [
23
],
"usb": 458764,
"linux": 23,
"xkb": 31,
"windows": 23,
"macos": 34,
"ios": 12
},
"keyCodes": {
"glfw": [
73
]
}
},
"KeyJ": {
"names": {
"name": "KeyJ",
"chromium": "KeyJ"
},
"scanCodes": {
"android": [
36
],
"usb": 458765,
"linux": 36,
"xkb": 44,
"windows": 36,
"macos": 38,
"ios": 13
},
"keyCodes": {
"glfw": [
74
]
}
},
"KeyK": {
"names": {
"name": "KeyK",
"chromium": "KeyK"
},
"scanCodes": {
"android": [
37
],
"usb": 458766,
"linux": 37,
"xkb": 45,
"windows": 37,
"macos": 40,
"ios": 14
},
"keyCodes": {
"glfw": [
75
]
}
},
"KeyL": {
"names": {
"name": "KeyL",
"chromium": "KeyL"
},
"scanCodes": {
"android": [
38
],
"usb": 458767,
"linux": 38,
"xkb": 46,
"windows": 38,
"macos": 37,
"ios": 15
},
"keyCodes": {
"glfw": [
76
]
}
},
"KeyM": {
"names": {
"name": "KeyM",
"chromium": "KeyM"
},
"scanCodes": {
"android": [
50
],
"usb": 458768,
"linux": 50,
"xkb": 58,
"windows": 50,
"macos": 46,
"ios": 16
},
"keyCodes": {
"glfw": [
77
]
}
},
"KeyN": {
"names": {
"name": "KeyN",
"chromium": "KeyN"
},
"scanCodes": {
"android": [
49
],
"usb": 458769,
"linux": 49,
"xkb": 57,
"windows": 49,
"macos": 45,
"ios": 17
},
"keyCodes": {
"glfw": [
78
]
}
},
"KeyO": {
"names": {
"name": "KeyO",
"chromium": "KeyO"
},
"scanCodes": {
"android": [
24
],
"usb": 458770,
"linux": 24,
"xkb": 32,
"windows": 24,
"macos": 31,
"ios": 18
},
"keyCodes": {
"glfw": [
79
]
}
},
"KeyP": {
"names": {
"name": "KeyP",
"chromium": "KeyP"
},
"scanCodes": {
"android": [
25
],
"usb": 458771,
"linux": 25,
"xkb": 33,
"windows": 25,
"macos": 35,
"ios": 19
},
"keyCodes": {
"glfw": [
80
]
}
},
"KeyQ": {
"names": {
"name": "KeyQ",
"chromium": "KeyQ"
},
"scanCodes": {
"android": [
16
],
"usb": 458772,
"linux": 16,
"xkb": 24,
"windows": 16,
"macos": 12,
"ios": 20
},
"keyCodes": {
"glfw": [
81
]
}
},
"KeyR": {
"names": {
"name": "KeyR",
"chromium": "KeyR"
},
"scanCodes": {
"android": [
19
],
"usb": 458773,
"linux": 19,
"xkb": 27,
"windows": 19,
"macos": 15,
"ios": 21
},
"keyCodes": {
"glfw": [
82
]
}
},
"KeyS": {
"names": {
"name": "KeyS",
"chromium": "KeyS"
},
"scanCodes": {
"android": [
31
],
"usb": 458774,
"linux": 31,
"xkb": 39,
"windows": 31,
"macos": 1,
"ios": 22
},
"keyCodes": {
"glfw": [
83
]
}
},
"KeyT": {
"names": {
"name": "KeyT",
"chromium": "KeyT"
},
"scanCodes": {
"android": [
20
],
"usb": 458775,
"linux": 20,
"xkb": 28,
"windows": 20,
"macos": 17,
"ios": 23
},
"keyCodes": {
"glfw": [
84
]
}
},
"KeyU": {
"names": {
"name": "KeyU",
"chromium": "KeyU"
},
"scanCodes": {
"android": [
22
],
"usb": 458776,
"linux": 22,
"xkb": 30,
"windows": 22,
"macos": 32,
"ios": 24
},
"keyCodes": {
"glfw": [
85
]
}
},
"KeyV": {
"names": {
"name": "KeyV",
"chromium": "KeyV"
},
"scanCodes": {
"android": [
47
],
"usb": 458777,
"linux": 47,
"xkb": 55,
"windows": 47,
"macos": 9,
"ios": 25
},
"keyCodes": {
"glfw": [
86
]
}
},
"KeyW": {
"names": {
"name": "KeyW",
"chromium": "KeyW"
},
"scanCodes": {
"android": [
17
],
"usb": 458778,
"linux": 17,
"xkb": 25,
"windows": 17,
"macos": 13,
"ios": 26
},
"keyCodes": {
"glfw": [
87
]
}
},
"KeyX": {
"names": {
"name": "KeyX",
"chromium": "KeyX"
},
"scanCodes": {
"android": [
45
],
"usb": 458779,
"linux": 45,
"xkb": 53,
"windows": 45,
"macos": 7,
"ios": 27
},
"keyCodes": {
"glfw": [
88
]
}
},
"KeyY": {
"names": {
"name": "KeyY",
"chromium": "KeyY"
},
"scanCodes": {
"android": [
21
],
"usb": 458780,
"linux": 21,
"xkb": 29,
"windows": 21,
"macos": 16,
"ios": 28
},
"keyCodes": {
"glfw": [
89
]
}
},
"KeyZ": {
"names": {
"name": "KeyZ",
"chromium": "KeyZ"
},
"scanCodes": {
"android": [
44
],
"usb": 458781,
"linux": 44,
"xkb": 52,
"windows": 44,
"macos": 6,
"ios": 29
},
"keyCodes": {
"glfw": [
90
]
}
},
"Digit1": {
"names": {
"name": "Digit1",
"chromium": "Digit1"
},
"scanCodes": {
"android": [
2
],
"usb": 458782,
"linux": 2,
"xkb": 10,
"windows": 2,
"macos": 18,
"ios": 30
},
"keyCodes": {
"glfw": [
49
]
}
},
"Digit2": {
"names": {
"name": "Digit2",
"chromium": "Digit2"
},
"scanCodes": {
"android": [
3
],
"usb": 458783,
"linux": 3,
"xkb": 11,
"windows": 3,
"macos": 19,
"ios": 31
},
"keyCodes": {
"glfw": [
50
]
}
},
"Digit3": {
"names": {
"name": "Digit3",
"chromium": "Digit3"
},
"scanCodes": {
"android": [
4
],
"usb": 458784,
"linux": 4,
"xkb": 12,
"windows": 4,
"macos": 20,
"ios": 32
},
"keyCodes": {
"glfw": [
51
]
}
},
"Digit4": {
"names": {
"name": "Digit4",
"chromium": "Digit4"
},
"scanCodes": {
"android": [
5
],
"usb": 458785,
"linux": 5,
"xkb": 13,
"windows": 5,
"macos": 21,
"ios": 33
},
"keyCodes": {
"glfw": [
52
]
}
},
"Digit5": {
"names": {
"name": "Digit5",
"chromium": "Digit5"
},
"scanCodes": {
"android": [
6
],
"usb": 458786,
"linux": 6,
"xkb": 14,
"windows": 6,
"macos": 23,
"ios": 34
},
"keyCodes": {
"glfw": [
53
]
}
},
"Digit6": {
"names": {
"name": "Digit6",
"chromium": "Digit6"
},
"scanCodes": {
"android": [
7
],
"usb": 458787,
"linux": 7,
"xkb": 15,
"windows": 7,
"macos": 22,
"ios": 35
},
"keyCodes": {
"glfw": [
54
]
}
},
"Digit7": {
"names": {
"name": "Digit7",
"chromium": "Digit7"
},
"scanCodes": {
"android": [
8
],
"usb": 458788,
"linux": 8,
"xkb": 16,
"windows": 8,
"macos": 26,
"ios": 36
},
"keyCodes": {
"glfw": [
55
]
}
},
"Digit8": {
"names": {
"name": "Digit8",
"chromium": "Digit8"
},
"scanCodes": {
"android": [
9
],
"usb": 458789,
"linux": 9,
"xkb": 17,
"windows": 9,
"macos": 28,
"ios": 37
},
"keyCodes": {
"glfw": [
56
]
}
},
"Digit9": {
"names": {
"name": "Digit9",
"chromium": "Digit9"
},
"scanCodes": {
"android": [
10
],
"usb": 458790,
"linux": 10,
"xkb": 18,
"windows": 10,
"macos": 25,
"ios": 38
},
"keyCodes": {
"glfw": [
57
]
}
},
"Digit0": {
"names": {
"name": "Digit0",
"chromium": "Digit0"
},
"scanCodes": {
"android": [
11
],
"usb": 458791,
"linux": 11,
"xkb": 19,
"windows": 11,
"macos": 29,
"ios": 39
},
"keyCodes": {
"glfw": [
48
]
}
},
"Enter": {
"names": {
"name": "Enter",
"chromium": "Enter"
},
"scanCodes": {
"android": [
28
],
"usb": 458792,
"linux": 28,
"xkb": 36,
"windows": 28,
"macos": 36,
"ios": 40
},
"keyCodes": {
"glfw": [
257
]
}
},
"Escape": {
"names": {
"name": "Escape",
"chromium": "Escape"
},
"scanCodes": {
"android": [
1
],
"usb": 458793,
"linux": 1,
"xkb": 9,
"windows": 1,
"macos": 53,
"ios": 41
},
"keyCodes": {
"glfw": [
256
]
}
},
"Backspace": {
"names": {
"name": "Backspace",
"chromium": "Backspace"
},
"scanCodes": {
"android": [
14
],
"usb": 458794,
"linux": 14,
"xkb": 22,
"windows": 14,
"macos": 51,
"ios": 42
},
"keyCodes": {
"glfw": [
259
]
}
},
"Tab": {
"names": {
"name": "Tab",
"chromium": "Tab"
},
"scanCodes": {
"android": [
15
],
"usb": 458795,
"linux": 15,
"xkb": 23,
"windows": 15,
"macos": 48,
"ios": 43
},
"keyCodes": {
"glfw": [
258
]
}
},
"Space": {
"names": {
"name": "Space",
"chromium": "Space"
},
"scanCodes": {
"android": [
57
],
"usb": 458796,
"linux": 57,
"xkb": 65,
"windows": 57,
"macos": 49,
"ios": 44
},
"keyCodes": {
"glfw": [
32
]
}
},
"Minus": {
"names": {
"name": "Minus",
"chromium": "Minus"
},
"scanCodes": {
"android": [
12
],
"usb": 458797,
"linux": 12,
"xkb": 20,
"windows": 12,
"macos": 27,
"ios": 45
},
"keyCodes": {
"glfw": [
45
]
}
},
"Equal": {
"names": {
"name": "Equal",
"chromium": "Equal"
},
"scanCodes": {
"android": [
13
],
"usb": 458798,
"linux": 13,
"xkb": 21,
"windows": 13,
"macos": 24,
"ios": 46
},
"keyCodes": {
"glfw": [
61
]
}
},
"BracketLeft": {
"names": {
"name": "BracketLeft",
"chromium": "BracketLeft"
},
"scanCodes": {
"android": [
26
],
"usb": 458799,
"linux": 26,
"xkb": 34,
"windows": 26,
"macos": 33,
"ios": 47
},
"keyCodes": {
"glfw": [
91
]
}
},
"BracketRight": {
"names": {
"name": "BracketRight",
"chromium": "BracketRight"
},
"scanCodes": {
"android": [
27
],
"usb": 458800,
"linux": 27,
"xkb": 35,
"windows": 27,
"macos": 30,
"ios": 48
},
"keyCodes": {
"glfw": [
93
]
}
},
"Backslash": {
"names": {
"name": "Backslash",
"chromium": "Backslash"
},
"scanCodes": {
"android": [
43,
86
],
"usb": 458801,
"linux": 43,
"xkb": 51,
"windows": 43,
"macos": 42,
"ios": 49
},
"keyCodes": {
"glfw": [
92
]
}
},
"Semicolon": {
"names": {
"name": "Semicolon",
"chromium": "Semicolon"
},
"scanCodes": {
"android": [
39
],
"usb": 458803,
"linux": 39,
"xkb": 47,
"windows": 39,
"macos": 41,
"ios": 51
},
"keyCodes": {
"glfw": [
59
]
}
},
"Quote": {
"names": {
"name": "Quote",
"chromium": "Quote"
},
"scanCodes": {
"android": [
40
],
"usb": 458804,
"linux": 40,
"xkb": 48,
"windows": 40,
"macos": 39,
"ios": 52
},
"keyCodes": {
"glfw": [
39
]
}
},
"Backquote": {
"names": {
"name": "Backquote",
"chromium": "Backquote"
},
"scanCodes": {
"android": [
41
],
"usb": 458805,
"linux": 41,
"xkb": 49,
"windows": 41,
"macos": 50,
"ios": 53
},
"keyCodes": {
"glfw": [
96
]
}
},
"Comma": {
"names": {
"name": "Comma",
"chromium": "Comma"
},
"scanCodes": {
"android": [
51
],
"usb": 458806,
"linux": 51,
"xkb": 59,
"windows": 51,
"macos": 43,
"ios": 54
},
"keyCodes": {
"glfw": [
44
]
}
},
"Period": {
"names": {
"name": "Period",
"chromium": "Period"
},
"scanCodes": {
"android": [
52
],
"usb": 458807,
"linux": 52,
"xkb": 60,
"windows": 52,
"macos": 47,
"ios": 55
},
"keyCodes": {
"glfw": [
46
]
}
},
"Slash": {
"names": {
"name": "Slash",
"chromium": "Slash"
},
"scanCodes": {
"android": [
53
],
"usb": 458808,
"linux": 53,
"xkb": 61,
"windows": 53,
"macos": 44,
"ios": 56
},
"keyCodes": {
"glfw": [
47
]
}
},
"CapsLock": {
"names": {
"name": "CapsLock",
"chromium": "CapsLock"
},
"scanCodes": {
"android": [
58
],
"usb": 458809,
"linux": 58,
"xkb": 66,
"windows": 58,
"macos": 57,
"ios": 57
},
"keyCodes": {
"glfw": [
280
]
}
},
"F1": {
"names": {
"name": "F1",
"chromium": "F1"
},
"scanCodes": {
"android": [
59
],
"usb": 458810,
"linux": 59,
"xkb": 67,
"windows": 59,
"macos": 122,
"ios": 58
},
"keyCodes": {
"glfw": [
290
]
}
},
"F2": {
"names": {
"name": "F2",
"chromium": "F2"
},
"scanCodes": {
"android": [
60
],
"usb": 458811,
"linux": 60,
"xkb": 68,
"windows": 60,
"macos": 120,
"ios": 59
},
"keyCodes": {
"glfw": [
291
]
}
},
"F3": {
"names": {
"name": "F3",
"chromium": "F3"
},
"scanCodes": {
"android": [
61
],
"usb": 458812,
"linux": 61,
"xkb": 69,
"windows": 61,
"macos": 99,
"ios": 60
},
"keyCodes": {
"glfw": [
292
]
}
},
"F4": {
"names": {
"name": "F4",
"chromium": "F4"
},
"scanCodes": {
"android": [
62
],
"usb": 458813,
"linux": 62,
"xkb": 70,
"windows": 62,
"macos": 118,
"ios": 61
},
"keyCodes": {
"glfw": [
293
]
}
},
"F5": {
"names": {
"name": "F5",
"chromium": "F5"
},
"scanCodes": {
"android": [
63
],
"usb": 458814,
"linux": 63,
"xkb": 71,
"windows": 63,
"macos": 96,
"ios": 62
},
"keyCodes": {
"glfw": [
294
]
}
},
"F6": {
"names": {
"name": "F6",
"chromium": "F6"
},
"scanCodes": {
"android": [
64
],
"usb": 458815,
"linux": 64,
"xkb": 72,
"windows": 64,
"macos": 97,
"ios": 63
},
"keyCodes": {
"glfw": [
295
]
}
},
"F7": {
"names": {
"name": "F7",
"chromium": "F7"
},
"scanCodes": {
"android": [
65
],
"usb": 458816,
"linux": 65,
"xkb": 73,
"windows": 65,
"macos": 98,
"ios": 64
},
"keyCodes": {
"glfw": [
296
]
}
},
"F8": {
"names": {
"name": "F8",
"chromium": "F8"
},
"scanCodes": {
"android": [
66
],
"usb": 458817,
"linux": 66,
"xkb": 74,
"windows": 66,
"macos": 100,
"ios": 65
},
"keyCodes": {
"glfw": [
297
]
}
},
"F9": {
"names": {
"name": "F9",
"chromium": "F9"
},
"scanCodes": {
"android": [
67
],
"usb": 458818,
"linux": 67,
"xkb": 75,
"windows": 67,
"macos": 101,
"ios": 66
},
"keyCodes": {
"glfw": [
298
]
}
},
"F10": {
"names": {
"name": "F10",
"chromium": "F10"
},
"scanCodes": {
"android": [
68
],
"usb": 458819,
"linux": 68,
"xkb": 76,
"windows": 68,
"macos": 109,
"ios": 67
},
"keyCodes": {
"glfw": [
299
]
}
},
"F11": {
"names": {
"name": "F11",
"chromium": "F11"
},
"scanCodes": {
"android": [
87
],
"usb": 458820,
"linux": 87,
"xkb": 95,
"windows": 87,
"macos": 103,
"ios": 68
},
"keyCodes": {
"glfw": [
300
]
}
},
"F12": {
"names": {
"name": "F12",
"chromium": "F12"
},
"scanCodes": {
"android": [
88
],
"usb": 458821,
"linux": 88,
"xkb": 96,
"windows": 88,
"macos": 111,
"ios": 69
},
"keyCodes": {
"glfw": [
301
]
}
},
"PrintScreen": {
"names": {
"name": "PrintScreen",
"chromium": "PrintScreen"
},
"scanCodes": {
"android": [
99
],
"usb": 458822,
"linux": 99,
"xkb": 107,
"windows": 57399,
"ios": 70
},
"keyCodes": {
"glfw": [
283
]
}
},
"ScrollLock": {
"names": {
"name": "ScrollLock",
"chromium": "ScrollLock"
},
"scanCodes": {
"android": [
70
],
"usb": 458823,
"linux": 70,
"xkb": 78,
"windows": 70,
"ios": 71
}
},
"Pause": {
"names": {
"name": "Pause",
"chromium": "Pause"
},
"scanCodes": {
"android": [
119,
411
],
"usb": 458824,
"linux": 119,
"xkb": 127,
"windows": 69,
"ios": 72
},
"keyCodes": {
"glfw": [
284
]
}
},
"Insert": {
"names": {
"name": "Insert",
"chromium": "Insert"
},
"scanCodes": {
"android": [
110
],
"usb": 458825,
"linux": 110,
"xkb": 118,
"windows": 57426,
"macos": 114,
"ios": 73
},
"keyCodes": {
"glfw": [
260
]
}
},
"Home": {
"names": {
"name": "Home",
"chromium": "Home"
},
"scanCodes": {
"android": [
102
],
"usb": 458826,
"linux": 102,
"xkb": 110,
"windows": 57415,
"macos": 115,
"ios": 74
},
"keyCodes": {
"glfw": [
268
]
}
},
"PageUp": {
"names": {
"name": "PageUp",
"chromium": "PageUp"
},
"scanCodes": {
"android": [
104,
177
],
"usb": 458827,
"linux": 104,
"xkb": 112,
"windows": 57417,
"macos": 116,
"ios": 75
},
"keyCodes": {
"glfw": [
266
]
}
},
"Delete": {
"names": {
"name": "Delete",
"chromium": "Delete"
},
"scanCodes": {
"android": [
111
],
"usb": 458828,
"linux": 111,
"xkb": 119,
"windows": 57427,
"macos": 117,
"ios": 76
},
"keyCodes": {
"glfw": [
261
]
}
},
"End": {
"names": {
"name": "End",
"chromium": "End"
},
"scanCodes": {
"android": [
107
],
"usb": 458829,
"linux": 107,
"xkb": 115,
"windows": 57423,
"macos": 119,
"ios": 77
},
"keyCodes": {
"glfw": [
269
]
}
},
"PageDown": {
"names": {
"name": "PageDown",
"chromium": "PageDown"
},
"scanCodes": {
"android": [
109,
178
],
"usb": 458830,
"linux": 109,
"xkb": 117,
"windows": 57425,
"macos": 121,
"ios": 78
},
"keyCodes": {
"glfw": [
267
]
}
},
"ArrowRight": {
"names": {
"name": "ArrowRight",
"chromium": "ArrowRight"
},
"scanCodes": {
"android": [
106
],
"usb": 458831,
"linux": 106,
"xkb": 114,
"windows": 57421,
"macos": 124,
"ios": 79
},
"keyCodes": {
"glfw": [
262
]
}
},
"ArrowLeft": {
"names": {
"name": "ArrowLeft",
"chromium": "ArrowLeft"
},
"scanCodes": {
"android": [
105
],
"usb": 458832,
"linux": 105,
"xkb": 113,
"windows": 57419,
"macos": 123,
"ios": 80
},
"keyCodes": {
"glfw": [
263
]
}
},
"ArrowDown": {
"names": {
"name": "ArrowDown",
"chromium": "ArrowDown"
},
"scanCodes": {
"android": [
108
],
"usb": 458833,
"linux": 108,
"xkb": 116,
"windows": 57424,
"macos": 125,
"ios": 81
},
"keyCodes": {
"glfw": [
264
]
}
},
"ArrowUp": {
"names": {
"name": "ArrowUp",
"chromium": "ArrowUp"
},
"scanCodes": {
"android": [
103
],
"usb": 458834,
"linux": 103,
"xkb": 111,
"windows": 57416,
"macos": 126,
"ios": 82
},
"keyCodes": {
"glfw": [
265
]
}
},
"NumLock": {
"names": {
"name": "NumLock",
"chromium": "NumLock"
},
"scanCodes": {
"android": [
69
],
"usb": 458835,
"linux": 69,
"xkb": 77,
"windows": 57413,
"macos": 71,
"ios": 83
},
"keyCodes": {
"glfw": [
282
]
}
},
"NumpadDivide": {
"names": {
"name": "NumpadDivide",
"chromium": "NumpadDivide"
},
"scanCodes": {
"android": [
98
],
"usb": 458836,
"linux": 98,
"xkb": 106,
"windows": 57397,
"macos": 75,
"ios": 84
},
"keyCodes": {
"glfw": [
331
]
}
},
"NumpadMultiply": {
"names": {
"name": "NumpadMultiply",
"chromium": "NumpadMultiply"
},
"scanCodes": {
"android": [
55
],
"usb": 458837,
"linux": 55,
"xkb": 63,
"windows": 55,
"macos": 67,
"ios": 85
},
"keyCodes": {
"glfw": [
332
]
}
},
"NumpadSubtract": {
"names": {
"name": "NumpadSubtract",
"chromium": "NumpadSubtract"
},
"scanCodes": {
"android": [
74
],
"usb": 458838,
"linux": 74,
"xkb": 82,
"windows": 74,
"macos": 78,
"ios": 86
}
},
"NumpadAdd": {
"names": {
"name": "NumpadAdd",
"chromium": "NumpadAdd"
},
"scanCodes": {
"android": [
78
],
"usb": 458839,
"linux": 78,
"xkb": 86,
"windows": 78,
"macos": 69,
"ios": 87
},
"keyCodes": {
"glfw": [
334
]
}
},
"NumpadEnter": {
"names": {
"name": "NumpadEnter",
"chromium": "NumpadEnter"
},
"scanCodes": {
"android": [
96
],
"usb": 458840,
"linux": 96,
"xkb": 104,
"windows": 57372,
"macos": 76,
"ios": 88
},
"keyCodes": {
"glfw": [
335
]
}
},
"Numpad1": {
"names": {
"name": "Numpad1",
"chromium": "Numpad1"
},
"scanCodes": {
"android": [
79
],
"usb": 458841,
"linux": 79,
"xkb": 87,
"windows": 79,
"macos": 83,
"ios": 89
},
"keyCodes": {
"glfw": [
321
]
}
},
"Numpad2": {
"names": {
"name": "Numpad2",
"chromium": "Numpad2"
},
"scanCodes": {
"android": [
80
],
"usb": 458842,
"linux": 80,
"xkb": 88,
"windows": 80,
"macos": 84,
"ios": 90
},
"keyCodes": {
"glfw": [
322
]
}
},
"Numpad3": {
"names": {
"name": "Numpad3",
"chromium": "Numpad3"
},
"scanCodes": {
"android": [
81
],
"usb": 458843,
"linux": 81,
"xkb": 89,
"windows": 81,
"macos": 85,
"ios": 91
},
"keyCodes": {
"glfw": [
323
]
}
},
"Numpad4": {
"names": {
"name": "Numpad4",
"chromium": "Numpad4"
},
"scanCodes": {
"android": [
75
],
"usb": 458844,
"linux": 75,
"xkb": 83,
"windows": 75,
"macos": 86,
"ios": 92
},
"keyCodes": {
"glfw": [
324
]
}
},
"Numpad5": {
"names": {
"name": "Numpad5",
"chromium": "Numpad5"
},
"scanCodes": {
"android": [
76
],
"usb": 458845,
"linux": 76,
"xkb": 84,
"windows": 76,
"macos": 87,
"ios": 93
},
"keyCodes": {
"glfw": [
325
]
}
},
"Numpad6": {
"names": {
"name": "Numpad6",
"chromium": "Numpad6"
},
"scanCodes": {
"android": [
77
],
"usb": 458846,
"linux": 77,
"xkb": 85,
"windows": 77,
"macos": 88,
"ios": 94
},
"keyCodes": {
"glfw": [
326
]
}
},
"Numpad7": {
"names": {
"name": "Numpad7",
"chromium": "Numpad7"
},
"scanCodes": {
"android": [
71
],
"usb": 458847,
"linux": 71,
"xkb": 79,
"windows": 71,
"macos": 89,
"ios": 95
},
"keyCodes": {
"glfw": [
327
]
}
},
"Numpad8": {
"names": {
"name": "Numpad8",
"chromium": "Numpad8"
},
"scanCodes": {
"android": [
72
],
"usb": 458848,
"linux": 72,
"xkb": 80,
"windows": 72,
"macos": 91,
"ios": 96
},
"keyCodes": {
"glfw": [
328
]
}
},
"Numpad9": {
"names": {
"name": "Numpad9",
"chromium": "Numpad9"
},
"scanCodes": {
"android": [
73
],
"usb": 458849,
"linux": 73,
"xkb": 81,
"windows": 73,
"macos": 92,
"ios": 97
},
"keyCodes": {
"glfw": [
329
]
}
},
"Numpad0": {
"names": {
"name": "Numpad0",
"chromium": "Numpad0"
},
"scanCodes": {
"android": [
82
],
"usb": 458850,
"linux": 82,
"xkb": 90,
"windows": 82,
"macos": 82,
"ios": 98
},
"keyCodes": {
"glfw": [
320
]
}
},
"NumpadDecimal": {
"names": {
"name": "NumpadDecimal",
"chromium": "NumpadDecimal"
},
"scanCodes": {
"android": [
83
],
"usb": 458851,
"linux": 83,
"xkb": 91,
"windows": 83,
"macos": 65,
"ios": 99
},
"keyCodes": {
"glfw": [
330
]
}
},
"IntlBackslash": {
"names": {
"name": "IntlBackslash",
"chromium": "IntlBackslash"
},
"scanCodes": {
"usb": 458852,
"linux": 86,
"xkb": 94,
"windows": 86,
"macos": 10,
"ios": 100
}
},
"ContextMenu": {
"names": {
"name": "ContextMenu",
"chromium": "ContextMenu"
},
"scanCodes": {
"android": [
127,
139
],
"usb": 458853,
"linux": 127,
"xkb": 135,
"windows": 57437,
"macos": 110,
"ios": 101
},
"keyCodes": {
"glfw": [
348
]
}
},
"Power": {
"names": {
"name": "Power",
"chromium": "Power"
},
"scanCodes": {
"android": [
116,
152
],
"usb": 458854,
"linux": 116,
"xkb": 124,
"windows": 57438,
"ios": 102
}
},
"NumpadEqual": {
"names": {
"name": "NumpadEqual",
"chromium": "NumpadEqual"
},
"scanCodes": {
"android": [
117
],
"usb": 458855,
"linux": 117,
"xkb": 125,
"windows": 89,
"macos": 81,
"ios": 103
},
"keyCodes": {
"glfw": [
336
]
}
},
"F13": {
"names": {
"name": "F13",
"chromium": "F13"
},
"scanCodes": {
"android": [
183
],
"usb": 458856,
"linux": 183,
"xkb": 191,
"windows": 100,
"macos": 105,
"ios": 104
},
"keyCodes": {
"glfw": [
302
]
}
},
"F14": {
"names": {
"name": "F14",
"chromium": "F14"
},
"scanCodes": {
"android": [
184
],
"usb": 458857,
"linux": 184,
"xkb": 192,
"windows": 101,
"macos": 107,
"ios": 105
},
"keyCodes": {
"glfw": [
303
]
}
},
"F15": {
"names": {
"name": "F15",
"chromium": "F15"
},
"scanCodes": {
"android": [
185
],
"usb": 458858,
"linux": 185,
"xkb": 193,
"windows": 102,
"macos": 113,
"ios": 106
},
"keyCodes": {
"glfw": [
304
]
}
},
"F16": {
"names": {
"name": "F16",
"chromium": "F16"
},
"scanCodes": {
"android": [
186
],
"usb": 458859,
"linux": 186,
"xkb": 194,
"windows": 103,
"macos": 106,
"ios": 107
},
"keyCodes": {
"glfw": [
305
]
}
},
"F17": {
"names": {
"name": "F17",
"chromium": "F17"
},
"scanCodes": {
"android": [
187
],
"usb": 458860,
"linux": 187,
"xkb": 195,
"windows": 104,
"macos": 64,
"ios": 108
},
"keyCodes": {
"glfw": [
306
]
}
},
"F18": {
"names": {
"name": "F18",
"chromium": "F18"
},
"scanCodes": {
"android": [
188
],
"usb": 458861,
"linux": 188,
"xkb": 196,
"windows": 105,
"macos": 79,
"ios": 109
},
"keyCodes": {
"glfw": [
307
]
}
},
"F19": {
"names": {
"name": "F19",
"chromium": "F19"
},
"scanCodes": {
"android": [
189
],
"usb": 458862,
"linux": 189,
"xkb": 197,
"windows": 106,
"macos": 80,
"ios": 110
},
"keyCodes": {
"glfw": [
308
]
}
},
"F20": {
"names": {
"name": "F20",
"chromium": "F20"
},
"scanCodes": {
"android": [
190
],
"usb": 458863,
"linux": 190,
"xkb": 198,
"windows": 107,
"macos": 90,
"ios": 111
},
"keyCodes": {
"glfw": [
309
]
}
},
"F21": {
"names": {
"name": "F21",
"chromium": "F21"
},
"scanCodes": {
"android": [
191
],
"usb": 458864,
"linux": 191,
"xkb": 199,
"windows": 108,
"ios": 112
},
"keyCodes": {
"glfw": [
310
]
}
},
"F22": {
"names": {
"name": "F22",
"chromium": "F22"
},
"scanCodes": {
"android": [
192
],
"usb": 458865,
"linux": 192,
"xkb": 200,
"windows": 109,
"ios": 113
},
"keyCodes": {
"glfw": [
311
]
}
},
"F23": {
"names": {
"name": "F23",
"chromium": "F23"
},
"scanCodes": {
"android": [
193
],
"usb": 458866,
"linux": 193,
"xkb": 201,
"windows": 110,
"ios": 114
},
"keyCodes": {
"glfw": [
312
]
}
},
"F24": {
"names": {
"name": "F24",
"chromium": "F24"
},
"scanCodes": {
"android": [
194
],
"usb": 458867,
"linux": 194,
"xkb": 202,
"windows": 118,
"ios": 115
}
},
"Open": {
"names": {
"name": "Open",
"chromium": "Open"
},
"scanCodes": {
"android": [
134
],
"usb": 458868,
"linux": 134,
"xkb": 142,
"ios": 116
}
},
"Help": {
"names": {
"name": "Help",
"chromium": "Help"
},
"scanCodes": {
"android": [
138
],
"usb": 458869,
"linux": 138,
"xkb": 146,
"windows": 57403,
"ios": 117
}
},
"Select": {
"names": {
"name": "Select",
"chromium": "Select"
},
"scanCodes": {
"android": [
353
],
"usb": 458871,
"linux": 132,
"xkb": 140,
"ios": 119
}
},
"Again": {
"names": {
"name": "Again",
"chromium": "Again"
},
"scanCodes": {
"android": [
129
],
"usb": 458873,
"linux": 129,
"xkb": 137,
"ios": 121
}
},
"Undo": {
"names": {
"name": "Undo",
"chromium": "Undo"
},
"scanCodes": {
"android": [
131
],
"usb": 458874,
"linux": 131,
"xkb": 139,
"windows": 57352,
"ios": 122
}
},
"Cut": {
"names": {
"name": "Cut",
"chromium": "Cut"
},
"scanCodes": {
"android": [
137
],
"usb": 458875,
"linux": 137,
"xkb": 145,
"windows": 57367,
"ios": 123
}
},
"Copy": {
"names": {
"name": "Copy",
"chromium": "Copy"
},
"scanCodes": {
"android": [
133
],
"usb": 458876,
"linux": 133,
"xkb": 141,
"windows": 57368,
"ios": 124
}
},
"Paste": {
"names": {
"name": "Paste",
"chromium": "Paste"
},
"scanCodes": {
"android": [
135
],
"usb": 458877,
"linux": 135,
"xkb": 143,
"windows": 57354,
"ios": 125
}
},
"Find": {
"names": {
"name": "Find",
"chromium": "Find"
},
"scanCodes": {
"android": [
136
],
"usb": 458878,
"linux": 136,
"xkb": 144,
"ios": 126
}
},
"AudioVolumeMute": {
"names": {
"name": "AudioVolumeMute",
"chromium": "AudioVolumeMute"
},
"scanCodes": {
"android": [
113
],
"usb": 458879,
"linux": 113,
"xkb": 121,
"windows": 57376,
"macos": 74,
"ios": 127
}
},
"AudioVolumeUp": {
"names": {
"name": "AudioVolumeUp",
"chromium": "AudioVolumeUp"
},
"scanCodes": {
"android": [
115
],
"usb": 458880,
"linux": 115,
"xkb": 123,
"windows": 57392,
"macos": 72,
"ios": 128
}
},
"AudioVolumeDown": {
"names": {
"name": "AudioVolumeDown",
"chromium": "AudioVolumeDown"
},
"scanCodes": {
"android": [
114
],
"usb": 458881,
"linux": 114,
"xkb": 122,
"windows": 57390,
"macos": 73,
"ios": 129
}
},
"NumpadComma": {
"names": {
"name": "NumpadComma",
"chromium": "NumpadComma"
},
"scanCodes": {
"android": [
95,
121
],
"usb": 458885,
"linux": 121,
"xkb": 129,
"windows": 126,
"macos": 95,
"ios": 133
}
},
"IntlRo": {
"names": {
"name": "IntlRo",
"chromium": "IntlRo"
},
"scanCodes": {
"android": [
89
],
"usb": 458887,
"linux": 89,
"xkb": 97,
"windows": 115,
"macos": 94,
"ios": 135
}
},
"KanaMode": {
"names": {
"name": "KanaMode",
"chromium": "KanaMode"
},
"scanCodes": {
"usb": 458888,
"linux": 93,
"xkb": 101,
"windows": 112,
"ios": 136
}
},
"IntlYen": {
"names": {
"name": "IntlYen",
"chromium": "IntlYen"
},
"scanCodes": {
"android": [
124
],
"usb": 458889,
"linux": 124,
"xkb": 132,
"windows": 125,
"macos": 93,
"ios": 137
}
},
"Convert": {
"names": {
"name": "Convert",
"chromium": "Convert"
},
"scanCodes": {
"android": [
92
],
"usb": 458890,
"linux": 92,
"xkb": 100,
"windows": 121,
"ios": 138
}
},
"NonConvert": {
"names": {
"name": "NonConvert",
"chromium": "NonConvert"
},
"scanCodes": {
"android": [
94
],
"usb": 458891,
"linux": 94,
"xkb": 102,
"windows": 123,
"ios": 139
}
},
"Lang1": {
"names": {
"name": "Lang1",
"chromium": "Lang1"
},
"scanCodes": {
"usb": 458896,
"linux": 122,
"xkb": 130,
"windows": 114,
"macos": 104,
"ios": 144
}
},
"Lang2": {
"names": {
"name": "Lang2",
"chromium": "Lang2"
},
"scanCodes": {
"usb": 458897,
"linux": 123,
"xkb": 131,
"windows": 113,
"macos": 102,
"ios": 145
}
},
"Lang3": {
"names": {
"name": "Lang3",
"chromium": "Lang3"
},
"scanCodes": {
"android": [
90
],
"usb": 458898,
"linux": 90,
"xkb": 98,
"windows": 120,
"ios": 146
}
},
"Lang4": {
"names": {
"name": "Lang4",
"chromium": "Lang4"
},
"scanCodes": {
"android": [
91
],
"usb": 458899,
"linux": 91,
"xkb": 99,
"windows": 119,
"ios": 147
}
},
"Lang5": {
"names": {
"name": "Lang5",
"chromium": "Lang5"
},
"scanCodes": {
"usb": 458900,
"linux": 85,
"xkb": 93,
"ios": 148
}
},
"Abort": {
"names": {
"name": "Abort",
"chromium": "Abort"
},
"scanCodes": {
"usb": 458907,
"ios": 155
}
},
"Props": {
"names": {
"name": "Props",
"chromium": "Props"
},
"scanCodes": {
"android": [
130
],
"usb": 458915,
"ios": 163
}
},
"NumpadParenLeft": {
"names": {
"name": "NumpadParenLeft",
"chromium": "NumpadParenLeft"
},
"scanCodes": {
"android": [
179
],
"usb": 458934,
"linux": 179,
"xkb": 187,
"ios": 182
}
},
"NumpadParenRight": {
"names": {
"name": "NumpadParenRight",
"chromium": "NumpadParenRight"
},
"scanCodes": {
"android": [
180
],
"usb": 458935,
"linux": 180,
"xkb": 188,
"ios": 183
}
},
"NumpadBackspace": {
"names": {
"name": "NumpadBackspace",
"chromium": "NumpadBackspace"
},
"scanCodes": {
"usb": 458939,
"ios": 187
}
},
"NumpadMemoryStore": {
"names": {
"name": "NumpadMemoryStore",
"chromium": "NumpadMemoryStore"
},
"scanCodes": {
"usb": 458960,
"ios": 208
}
},
"NumpadMemoryRecall": {
"names": {
"name": "NumpadMemoryRecall",
"chromium": "NumpadMemoryRecall"
},
"scanCodes": {
"usb": 458961,
"ios": 209
}
},
"NumpadMemoryClear": {
"names": {
"name": "NumpadMemoryClear",
"chromium": "NumpadMemoryClear"
},
"scanCodes": {
"usb": 458962,
"ios": 210
}
},
"NumpadMemoryAdd": {
"names": {
"name": "NumpadMemoryAdd",
"chromium": "NumpadMemoryAdd"
},
"scanCodes": {
"usb": 458963,
"ios": 211
}
},
"NumpadMemorySubtract": {
"names": {
"name": "NumpadMemorySubtract",
"chromium": "NumpadMemorySubtract"
},
"scanCodes": {
"usb": 458964,
"ios": 212
}
},
"NumpadSignChange": {
"names": {
"name": "NumpadSignChange"
},
"scanCodes": {
"usb": 458967,
"linux": 118,
"xkb": 126,
"ios": 215
}
},
"NumpadClear": {
"names": {
"name": "NumpadClear",
"chromium": "NumpadClear"
},
"scanCodes": {
"usb": 458968,
"ios": 216
}
},
"NumpadClearEntry": {
"names": {
"name": "NumpadClearEntry",
"chromium": "NumpadClearEntry"
},
"scanCodes": {
"usb": 458969,
"ios": 217
}
},
"ControlLeft": {
"names": {
"name": "ControlLeft",
"chromium": "ControlLeft"
},
"scanCodes": {
"android": [
29
],
"usb": 458976,
"linux": 29,
"xkb": 37,
"windows": 29,
"macos": 59,
"ios": 224
},
"keyCodes": {
"glfw": [
341
]
}
},
"ShiftLeft": {
"names": {
"name": "ShiftLeft",
"chromium": "ShiftLeft"
},
"scanCodes": {
"android": [
42
],
"usb": 458977,
"linux": 42,
"xkb": 50,
"windows": 42,
"macos": 56,
"ios": 225
},
"keyCodes": {
"glfw": [
340
]
}
},
"AltLeft": {
"names": {
"name": "AltLeft",
"chromium": "AltLeft"
},
"scanCodes": {
"android": [
56
],
"usb": 458978,
"linux": 56,
"xkb": 64,
"windows": 56,
"macos": 58,
"ios": 226
},
"keyCodes": {
"glfw": [
342
]
}
},
"MetaLeft": {
"names": {
"name": "MetaLeft",
"chromium": "MetaLeft"
},
"scanCodes": {
"android": [
125
],
"usb": 458979,
"linux": 125,
"xkb": 133,
"windows": 57435,
"macos": 55,
"ios": 227
},
"keyCodes": {
"glfw": [
343
]
}
},
"ControlRight": {
"names": {
"name": "ControlRight",
"chromium": "ControlRight"
},
"scanCodes": {
"android": [
97
],
"usb": 458980,
"linux": 97,
"xkb": 105,
"windows": 57373,
"macos": 62,
"ios": 228
},
"keyCodes": {
"glfw": [
345
]
}
},
"ShiftRight": {
"names": {
"name": "ShiftRight",
"chromium": "ShiftRight"
},
"scanCodes": {
"android": [
54
],
"usb": 458981,
"linux": 54,
"xkb": 62,
"windows": 54,
"macos": 60,
"ios": 229
},
"keyCodes": {
"glfw": [
344
]
}
},
"AltRight": {
"names": {
"name": "AltRight",
"chromium": "AltRight"
},
"scanCodes": {
"android": [
100
],
"usb": 458982,
"linux": 100,
"xkb": 108,
"windows": 57400,
"macos": 61,
"ios": 230
},
"keyCodes": {
"glfw": [
346
]
}
},
"MetaRight": {
"names": {
"name": "MetaRight",
"chromium": "MetaRight"
},
"scanCodes": {
"android": [
126
],
"usb": 458983,
"linux": 126,
"xkb": 134,
"windows": 57436,
"macos": 54,
"ios": 231
},
"keyCodes": {
"glfw": [
347
]
}
},
"Info": {
"names": {
"name": "Info"
},
"scanCodes": {
"android": [
358
],
"usb": 786528,
"linux": 358,
"xkb": 366
}
},
"ClosedCaptionToggle": {
"names": {
"name": "ClosedCaptionToggle"
},
"scanCodes": {
"android": [
370
],
"usb": 786529,
"linux": 370,
"xkb": 378
}
},
"BrightnessUp": {
"names": {
"name": "BrightnessUp",
"chromium": "BrightnessUp"
},
"scanCodes": {
"android": [
225
],
"usb": 786543,
"linux": 225,
"xkb": 233
}
},
"BrightnessDown": {
"names": {
"name": "BrightnessDown",
"chromium": "BrightnessDown"
},
"scanCodes": {
"android": [
224
],
"usb": 786544,
"linux": 224,
"xkb": 232
}
},
"BrightnessToggle": {
"names": {
"name": "BrightnessToggle"
},
"scanCodes": {
"usb": 786546,
"linux": 431,
"xkb": 439
}
},
"BrightnessMinimum": {
"names": {
"name": "BrightnessMinimum"
},
"scanCodes": {
"usb": 786547,
"linux": 592,
"xkb": 600
}
},
"BrightnessMaximum": {
"names": {
"name": "BrightnessMaximum"
},
"scanCodes": {
"usb": 786548,
"linux": 593,
"xkb": 601
}
},
"BrightnessAuto": {
"names": {
"name": "BrightnessAuto"
},
"scanCodes": {
"usb": 786549,
"linux": 244,
"xkb": 252
}
},
"KbdIllumUp": {
"names": {
"name": "KbdIllumUp"
},
"scanCodes": {
"usb": 786553,
"linux": 230,
"xkb": 238
}
},
"KbdIllumDown": {
"names": {
"name": "KbdIllumDown"
},
"scanCodes": {
"usb": 786554,
"linux": 229,
"xkb": 237
}
},
"MediaLast": {
"names": {
"name": "MediaLast"
},
"scanCodes": {
"android": [
405
],
"usb": 786563,
"linux": 405,
"xkb": 413
}
},
"LaunchPhone": {
"names": {
"name": "LaunchPhone"
},
"scanCodes": {
"usb": 786572,
"linux": 169,
"xkb": 177
}
},
"ProgramGuide": {
"names": {
"name": "ProgramGuide"
},
"scanCodes": {
"usb": 786573,
"linux": 362,
"xkb": 370
}
},
"Exit": {
"names": {
"name": "Exit"
},
"scanCodes": {
"android": [
174
],
"usb": 786580,
"linux": 174,
"xkb": 182
}
},
"ChannelUp": {
"names": {
"name": "ChannelUp"
},
"scanCodes": {
"android": [
402
],
"usb": 786588,
"linux": 410,
"xkb": 418
}
},
"ChannelDown": {
"names": {
"name": "ChannelDown"
},
"scanCodes": {
"android": [
403
],
"usb": 786589,
"linux": 411,
"xkb": 419
}
},
"MediaPlay": {
"names": {
"name": "MediaPlay",
"chromium": "MediaPlay"
},
"scanCodes": {
"android": [
200,
207
],
"usb": 786608,
"linux": 207,
"xkb": 215
}
},
"MediaPause": {
"names": {
"name": "MediaPause",
"chromium": "MediaPause"
},
"scanCodes": {
"android": [
201
],
"usb": 786609,
"linux": 201,
"xkb": 209
}
},
"MediaRecord": {
"names": {
"name": "MediaRecord",
"chromium": "MediaRecord"
},
"scanCodes": {
"android": [
167
],
"usb": 786610,
"linux": 167,
"xkb": 175
}
},
"MediaFastForward": {
"names": {
"name": "MediaFastForward",
"chromium": "MediaFastForward"
},
"scanCodes": {
"android": [
208
],
"usb": 786611,
"linux": 208,
"xkb": 216
}
},
"MediaRewind": {
"names": {
"name": "MediaRewind",
"chromium": "MediaRewind"
},
"scanCodes": {
"android": [
168
],
"usb": 786612,
"linux": 168,
"xkb": 176
}
},
"MediaTrackNext": {
"names": {
"name": "MediaTrackNext",
"chromium": "MediaTrackNext"
},
"scanCodes": {
"android": [
163
],
"usb": 786613,
"linux": 163,
"xkb": 171,
"windows": 57369
}
},
"MediaTrackPrevious": {
"names": {
"name": "MediaTrackPrevious",
"chromium": "MediaTrackPrevious"
},
"scanCodes": {
"android": [
165
],
"usb": 786614,
"linux": 165,
"xkb": 173,
"windows": 57360
}
},
"MediaStop": {
"names": {
"name": "MediaStop",
"chromium": "MediaStop"
},
"scanCodes": {
"android": [
128,
166
],
"usb": 786615,
"linux": 166,
"xkb": 174,
"windows": 57380
}
},
"Eject": {
"names": {
"name": "Eject",
"chromium": "Eject"
},
"scanCodes": {
"android": [
161,
162
],
"usb": 786616,
"linux": 161,
"xkb": 169,
"windows": 57388
}
},
"MediaPlayPause": {
"names": {
"name": "MediaPlayPause",
"chromium": "MediaPlayPause"
},
"scanCodes": {
"android": [
164
],
"usb": 786637,
"linux": 164,
"xkb": 172,
"windows": 57378
}
},
"SpeechInputToggle": {
"names": {
"name": "SpeechInputToggle"
},
"scanCodes": {
"usb": 786639,
"linux": 582,
"xkb": 590
}
},
"BassBoost": {
"names": {
"name": "BassBoost"
},
"scanCodes": {
"android": [
209
],
"usb": 786661,
"linux": 209,
"xkb": 217
}
},
"MediaSelect": {
"names": {
"name": "MediaSelect",
"chromium": "MediaSelect"
},
"scanCodes": {
"usb": 786819,
"linux": 171,
"xkb": 179,
"windows": 57453
}
},
"LaunchWordProcessor": {
"names": {
"name": "LaunchWordProcessor"
},
"scanCodes": {
"usb": 786820,
"linux": 421,
"xkb": 429
}
},
"LaunchSpreadsheet": {
"names": {
"name": "LaunchSpreadsheet"
},
"scanCodes": {
"usb": 786822,
"linux": 423,
"xkb": 431
}
},
"LaunchMail": {
"names": {
"name": "LaunchMail",
"chromium": "LaunchMail"
},
"scanCodes": {
"android": [
155,
215
],
"usb": 786826,
"linux": 155,
"xkb": 163,
"windows": 57452
}
},
"LaunchContacts": {
"names": {
"name": "LaunchContacts"
},
"scanCodes": {
"android": [
429
],
"usb": 786829,
"linux": 429,
"xkb": 437
}
},
"LaunchCalendar": {
"names": {
"name": "LaunchCalendar"
},
"scanCodes": {
"android": [
397
],
"usb": 786830,
"linux": 397,
"xkb": 405
}
},
"LaunchApp2": {
"names": {
"name": "LaunchApp2",
"chromium": "LaunchApp2"
},
"scanCodes": {
"usb": 786834,
"linux": 140,
"xkb": 148,
"windows": 57377
}
},
"LaunchApp1": {
"names": {
"name": "LaunchApp1",
"chromium": "LaunchApp1"
},
"scanCodes": {
"usb": 786836,
"linux": 144,
"xkb": 152,
"windows": 57451
}
},
"LaunchInternetBrowser": {
"names": {
"name": "LaunchInternetBrowser"
},
"scanCodes": {
"usb": 786838,
"linux": 150,
"xkb": 158
}
},
"LogOff": {
"names": {
"name": "LogOff"
},
"scanCodes": {
"usb": 786844,
"linux": 433,
"xkb": 441
}
},
"LockScreen": {
"names": {
"name": "LockScreen"
},
"scanCodes": {
"usb": 786846,
"linux": 152,
"xkb": 160
}
},
"LaunchControlPanel": {
"names": {
"name": "LaunchControlPanel",
"chromium": "LaunchControlPanel"
},
"scanCodes": {
"usb": 786847,
"linux": 579,
"xkb": 587
}
},
"SelectTask": {
"names": {
"name": "SelectTask",
"chromium": "SelectTask"
},
"scanCodes": {
"usb": 786850,
"linux": 580,
"xkb": 588
}
},
"LaunchDocuments": {
"names": {
"name": "LaunchDocuments"
},
"scanCodes": {
"usb": 786855,
"linux": 235,
"xkb": 243
}
},
"SpellCheck": {
"names": {
"name": "SpellCheck"
},
"scanCodes": {
"usb": 786859,
"linux": 432,
"xkb": 440
}
},
"LaunchKeyboardLayout": {
"names": {
"name": "LaunchKeyboardLayout"
},
"scanCodes": {
"usb": 786862,
"linux": 374,
"xkb": 382
}
},
"LaunchScreenSaver": {
"names": {
"name": "LaunchScreenSaver",
"chromium": "LaunchScreenSaver"
},
"scanCodes": {
"usb": 786865,
"linux": 581,
"xkb": 589
}
},
"LaunchAudioBrowser": {
"names": {
"name": "LaunchAudioBrowser"
},
"scanCodes": {
"usb": 786871,
"linux": 392,
"xkb": 400
}
},
"LaunchAssistant": {
"names": {
"name": "LaunchAssistant",
"chromium": "LaunchAssistant"
},
"scanCodes": {
"android": [
583
],
"usb": 786891,
"linux": 583,
"xkb": 591
}
},
"New": {
"names": {
"name": "New"
},
"scanCodes": {
"usb": 786945,
"linux": 181,
"xkb": 189
}
},
"Close": {
"names": {
"name": "Close"
},
"scanCodes": {
"android": [
160,
206
],
"usb": 786947,
"linux": 206,
"xkb": 214
}
},
"Save": {
"names": {
"name": "Save"
},
"scanCodes": {
"usb": 786951,
"linux": 234,
"xkb": 242
}
},
"Print": {
"names": {
"name": "Print"
},
"scanCodes": {
"android": [
210
],
"usb": 786952,
"linux": 210,
"xkb": 218
}
},
"BrowserSearch": {
"names": {
"name": "BrowserSearch",
"chromium": "BrowserSearch"
},
"scanCodes": {
"android": [
217
],
"usb": 786977,
"linux": 217,
"xkb": 225,
"windows": 57445
}
},
"BrowserHome": {
"names": {
"name": "BrowserHome",
"chromium": "BrowserHome"
},
"scanCodes": {
"usb": 786979,
"linux": 172,
"xkb": 180,
"windows": 57394
}
},
"BrowserBack": {
"names": {
"name": "BrowserBack",
"chromium": "BrowserBack"
},
"scanCodes": {
"usb": 786980,
"linux": 158,
"xkb": 166,
"windows": 57450
}
},
"BrowserForward": {
"names": {
"name": "BrowserForward",
"chromium": "BrowserForward"
},
"scanCodes": {
"android": [
159
],
"usb": 786981,
"linux": 159,
"xkb": 167,
"windows": 57449
}
},
"BrowserStop": {
"names": {
"name": "BrowserStop",
"chromium": "BrowserStop"
},
"scanCodes": {
"usb": 786982,
"linux": 128,
"xkb": 136,
"windows": 57448
}
},
"BrowserRefresh": {
"names": {
"name": "BrowserRefresh",
"chromium": "BrowserRefresh"
},
"scanCodes": {
"usb": 786983,
"linux": 173,
"xkb": 181,
"windows": 57447
}
},
"BrowserFavorites": {
"names": {
"name": "BrowserFavorites",
"chromium": "BrowserFavorites"
},
"scanCodes": {
"android": [
156
],
"usb": 786986,
"linux": 156,
"xkb": 164,
"windows": 57446
}
},
"ZoomIn": {
"names": {
"name": "ZoomIn"
},
"scanCodes": {
"usb": 786989,
"linux": 418,
"xkb": 426
}
},
"ZoomOut": {
"names": {
"name": "ZoomOut"
},
"scanCodes": {
"usb": 786990,
"linux": 419,
"xkb": 427
}
},
"ZoomToggle": {
"names": {
"name": "ZoomToggle",
"chromium": "ZoomToggle"
},
"scanCodes": {
"usb": 786994,
"linux": 372,
"xkb": 380
}
},
"Redo": {
"names": {
"name": "Redo"
},
"scanCodes": {
"android": [
182
],
"usb": 787065,
"linux": 182,
"xkb": 190
}
},
"MailReply": {
"names": {
"name": "MailReply",
"chromium": "MailReply"
},
"scanCodes": {
"usb": 787081,
"linux": 232,
"xkb": 240
}
},
"MailForward": {
"names": {
"name": "MailForward",
"chromium": "MailForward"
},
"scanCodes": {
"usb": 787083,
"linux": 233,
"xkb": 241
}
},
"MailSend": {
"names": {
"name": "MailSend",
"chromium": "MailSend"
},
"scanCodes": {
"usb": 787084,
"linux": 231,
"xkb": 239
}
},
"KeyboardLayoutSelect": {
"names": {
"name": "KeyboardLayoutSelect",
"chromium": "KeyboardLayoutSelect"
},
"scanCodes": {
"usb": 787101,
"linux": 584,
"xkb": 592
}
},
"ShowAllWindows": {
"names": {
"name": "ShowAllWindows",
"chromium": "ShowAllWindows"
},
"scanCodes": {
"usb": 787103,
"linux": 120,
"xkb": 128
}
}
}
{
"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": " "
"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": " "
}
{
"0": "Numpad0",
"1": "Numpad1",
"2": "Numpad2",
"3": "Numpad3",
"4": "Numpad4",
"5": "Numpad5",
"6": "Numpad6",
"7": "Numpad7",
"8": "Numpad8",
"9": "Numpad9",
".": "NumpadDecimal",
"+": "NumpadAdd",
"-": "NumpadSubtract",
"*": "NumpadMultiply",
"/": "NumpadDivide",
"=": "NumpadEqual",
",": "NumpadComma",
"(": "NumpadParenLeft",
")": "NumpadParenRight",
"\u000D": "NumpadEnter"
}
// These are supplemental key codes to be added to those that Chromium
// defines. Since the web doesn't have game controller buttons defined in the
// These are supplemental code data to be added to those that Chromium
// defines.
// ============================================================
// Game controller buttons
// ============================================================
// Since the web doesn't have game controller buttons defined in the
// same way, these map USB HID codes for game controller buttons to
// Android/Linux button names.
//
......@@ -44,6 +50,10 @@
DOM_CODE(0x05ff1e, 0x0000, 0x0000, 0x0000, 0xffff, "GameButtonY", BUTTON_Y),
DOM_CODE(0x05ff1f, 0x0000, 0x0000, 0x0000, 0xffff, "GameButtonZ", BUTTON_Z),
// ============================================================
// Fn key for Mac
// ============================================================
// The Mac defines a key code for the Fn key on Mac keyboards, but it's not
// defined on other platforms. Chromium does define an "Fn" row, but doesn't
// give it a Mac keycode. This overrides their definition.
......
// These are supplemental key data to be added to those that Chromium
// defines.
// ============================================================
// Printable keys
// ============================================================
// Key Enum Unicode code point
DOM_KEY_UNI("Space", SPACE, ' '),
DOM_KEY_UNI("Exclamation", EXCLAMATION, '!'),
DOM_KEY_UNI("NumberSign", NUMBER_SIGN, '#'),
DOM_KEY_UNI("Dollar", DOLLAR, '$'),
DOM_KEY_UNI("Percent", PERCENT, '%'),
DOM_KEY_UNI("Ampersand", AMPERSAND, '&'),
DOM_KEY_UNI("QuoteSingle", QUOTE_SINGLE, 0x0027),
DOM_KEY_UNI("Quote", QUOTE, '"')",
DOM_KEY_UNI("ParenthesisLeft", PARENTHESIS_LEFT, '('),
DOM_KEY_UNI("ParenthesisRight", PARENTHESIS_RIGHT, ')'),
DOM_KEY_UNI("Asterisk", ASTERISK, '*'),
DOM_KEY_UNI("Add", ADD, '+'),
DOM_KEY_UNI("Comma", COMMA, ','),
DOM_KEY_UNI("Minus", MINUS, '-'),
DOM_KEY_UNI("Period", PERIOD, '.'),
DOM_KEY_UNI("Slash", SLASH, '/'),
DOM_KEY_UNI("Digit0", DIGIT0, '0'),
DOM_KEY_UNI("Digit1", DIGIT1, '1'),
DOM_KEY_UNI("Digit2", DIGIT2, '2'),
DOM_KEY_UNI("Digit3", DIGIT3, '3'),
DOM_KEY_UNI("Digit4", DIGIT4, '4'),
DOM_KEY_UNI("Digit5", DIGIT5, '5'),
DOM_KEY_UNI("Digit6", DIGIT6, '6'),
DOM_KEY_UNI("Digit7", DIGIT7, '7'),
DOM_KEY_UNI("Digit8", DIGIT8, '8'),
DOM_KEY_UNI("Digit9", DIGIT9, '9'),
DOM_KEY_UNI("Colon", COLON, ':'),
DOM_KEY_UNI("Semicolon", SEMICOLON, ';'),
DOM_KEY_UNI("Less", LESS, '<'),
DOM_KEY_UNI("Equal", EQUAL, '='),
DOM_KEY_UNI("Greater", GREATER, '>'),
DOM_KEY_UNI("Question", QUESTION, '?'),
DOM_KEY_UNI("At", AT, '@'),
DOM_KEY_UNI("BracketLeft", BRACKET_LEFT, '['),
DOM_KEY_UNI("Backslash", BACKSLASH, 0x005c),
DOM_KEY_UNI("BracketRight", BRACKET_RIGHT, ']'),
DOM_KEY_UNI("Caret", CARET, '^'),
DOM_KEY_UNI("Backquote", BACKQUOTE, '`'),
DOM_KEY_UNI("Underscore", UNDERSCORE, '_'),
DOM_KEY_UNI("KeyA", KEY_A, 'a'),
DOM_KEY_UNI("KeyB", KEY_B, 'b'),
DOM_KEY_UNI("KeyC", KEY_C, 'c'),
DOM_KEY_UNI("KeyD", KEY_D, 'd'),
DOM_KEY_UNI("KeyE", KEY_E, 'e'),
DOM_KEY_UNI("KeyF", KEY_F, 'f'),
DOM_KEY_UNI("KeyG", KEY_G, 'g'),
DOM_KEY_UNI("KeyH", KEY_H, 'h'),
DOM_KEY_UNI("KeyI", KEY_I, 'i'),
DOM_KEY_UNI("KeyJ", KEY_J, 'j'),
DOM_KEY_UNI("KeyK", KEY_K, 'k'),
DOM_KEY_UNI("KeyL", KEY_L, 'l'),
DOM_KEY_UNI("KeyM", KEY_M, 'm'),
DOM_KEY_UNI("KeyN", KEY_N, 'n'),
DOM_KEY_UNI("KeyO", KEY_O, 'o'),
DOM_KEY_UNI("KeyP", KEY_P, 'p'),
DOM_KEY_UNI("KeyQ", KEY_Q, 'q'),
DOM_KEY_UNI("KeyR", KEY_R, 'r'),
DOM_KEY_UNI("KeyS", KEY_S, 's'),
DOM_KEY_UNI("KeyT", KEY_T, 't'),
DOM_KEY_UNI("KeyU", KEY_U, 'u'),
DOM_KEY_UNI("KeyV", KEY_V, 'v'),
DOM_KEY_UNI("KeyW", KEY_W, 'w'),
DOM_KEY_UNI("KeyX", KEY_X, 'x'),
DOM_KEY_UNI("KeyY", KEY_Y, 'y'),
DOM_KEY_UNI("KeyZ", KEY_Z, 'z'),
DOM_KEY_UNI("BraceLeft", BRACE_LEFT, '{'),
DOM_KEY_UNI("BraceRight", BRACE_RIGHT, '}'),
DOM_KEY_UNI("Tilde", TILDE, '~'),
DOM_KEY_UNI("Bar", BAR, '|'),
// ============================================================
// Game controller buttons
// ============================================================
// Since the web doesn't have game controller buttons defined in the
// same way, these map USB HID codes for game controller buttons to
// Android/Linux button names.
// Key Enum Value
DOM_KEY_MAP("GameButton1", GAME_BUTTON_1, 0x05ff01),
DOM_KEY_MAP("GameButton2", GAME_BUTTON_2, 0x05ff02),
DOM_KEY_MAP("GameButton3", GAME_BUTTON_3, 0x05ff03),
DOM_KEY_MAP("GameButton4", GAME_BUTTON_4, 0x05ff04),
DOM_KEY_MAP("GameButton5", GAME_BUTTON_5, 0x05ff05),
DOM_KEY_MAP("GameButton6", GAME_BUTTON_6, 0x05ff06),
DOM_KEY_MAP("GameButton7", GAME_BUTTON_7, 0x05ff07),
DOM_KEY_MAP("GameButton8", GAME_BUTTON_8, 0x05ff08),
DOM_KEY_MAP("GameButton9", GAME_BUTTON_9, 0x05ff09),
DOM_KEY_MAP("GameButton10", GAME_BUTTON_10, 0x05ff0a),
DOM_KEY_MAP("GameButton11", GAME_BUTTON_11, 0x05ff0b),
DOM_KEY_MAP("GameButton12", GAME_BUTTON_12, 0x05ff0c),
DOM_KEY_MAP("GameButton13", GAME_BUTTON_13, 0x05ff0d),
DOM_KEY_MAP("GameButton14", GAME_BUTTON_14, 0x05ff0e),
DOM_KEY_MAP("GameButton15", GAME_BUTTON_15, 0x05ff0f),
DOM_KEY_MAP("GameButton16", GAME_BUTTON_16, 0x05ff10),
DOM_KEY_MAP("GameButtonA", GAME_BUTTON_A, 0x05ff11),
DOM_KEY_MAP("GameButtonB", GAME_BUTTON_B, 0x05ff12),
DOM_KEY_MAP("GameButtonC", GAME_BUTTON_C, 0x05ff13),
DOM_KEY_MAP("GameButtonLeft1", GAME_BUTTON_L1, 0x05ff14),
DOM_KEY_MAP("GameButtonLeft2", GAME_BUTTON_L2, 0x05ff15),
DOM_KEY_MAP("GameButtonMode", GAME_BUTTON_MODE, 0x05ff16),
DOM_KEY_MAP("GameButtonRight1", GAME_BUTTON_R1, 0x05ff17),
DOM_KEY_MAP("GameButtonRight2", GAME_BUTTON_R2, 0x05ff18),
DOM_KEY_MAP("GameButtonSelect", GAME_BUTTON_SELECT, 0x05ff19),
DOM_KEY_MAP("GameButtonStart", GAME_BUTTON_START, 0x05ff1a),
DOM_KEY_MAP("GameButtonThumbLeft", GAME_BUTTON_THUMBL, 0x05ff1b),
DOM_KEY_MAP("GameButtonThumbRight", GAME_BUTTON_THUMBR, 0x05ff1c),
DOM_KEY_MAP("GameButtonX", GAME_BUTTON_X, 0x05ff1d),
DOM_KEY_MAP("GameButtonY", GAME_BUTTON_Y, 0x05ff1e),
DOM_KEY_MAP("GameButtonZ", GAME_BUTTON_Z, 0x05ff1f),
// ============================================================
// Other buttons
// ============================================================
// Key Enum Value
DOM_KEY_MAP("None", NONE, 0x0),
DOM_KEY_MAP("Suspend", SUSPEND, 0x100000014),
DOM_KEY_MAP("Resume", RESUME, 0x100000015),
DOM_KEY_MAP("Sleep", SLEEP, 0x100010082),
DOM_KEY_MAP("IntlBackslash", INTL_BACKSLASH, 0x100070064),
DOM_KEY_MAP("IntlRo", INTL_RO, 0x100070087),
DOM_KEY_MAP("IntlYen", INTL_YEN, 0x100070089),
DOM_KEY_MAP("Lang1", LANG1, 0x100070090),
DOM_KEY_MAP("Lang2", LANG2, 0x100070091),
DOM_KEY_MAP("Lang3", LANG3, 0x100070092),
DOM_KEY_MAP("Lang4", LANG4, 0x100070093),
DOM_KEY_MAP("Lang5", LANG5, 0x100070094),
DOM_KEY_MAP("Abort", ABORT, 0x10007009b),
{
"shift": ["ShiftLeft", "ShiftRight"],
"meta": ["MetaLeft", "MetaRight"],
"alt": ["AltLeft", "AltRight"],
"control": ["ControlLeft", "ControlRight"]
"Shift": ["ShiftLeft", "ShiftRight"],
"Meta": ["MetaLeft", "MetaRight"],
"Alt": ["AltLeft", "AltRight"],
"Control": ["ControlLeft", "ControlRight"]
}
......@@ -6,9 +6,12 @@
// 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_map_web.tmpl instead.
// Edit the template dev/tools/gen_keycodes/data/web_key_map_dart.tmpl instead.
// See dev/tools/gen_keycodes/README.md for more information.
// @dart = 2.12
part of engine;
/// Maps Web KeyboardEvent codes to the matching LogicalKeyboardKey id.
const Map<String, int> kWebToLogicalKey = <String, int>{
@@@WEB_LOGICAL_KEY_CODE_MAP@@@
......@@ -19,9 +22,14 @@ const Map<String, int> kWebToPhysicalKey = <String, int>{
@@@WEB_PHYSICAL_KEY_CODE_MAP@@@
};
/// A map of Web KeyboardEvent 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<String, int> kWebNumPadMap = <String, int>{
@@@WEB_NUMPAD_CODE_MAP@@@
/// Maps Web KeyboardEvent keys to Flutter logical IDs that depend on locations.
///
/// `KeyboardEvent.location` is defined as:
///
/// * 0: Standard
/// * 1: Left
/// * 2: Right
/// * 3: Numpad
const Map<String, List<int?>> kWebLogicalLocationMap = <String, List<int?>>{
@@@WEB_LOGICAL_LOCATION_MAP@@@
};
{
"0": ["Digit0", null, null, "Numpad0"],
"1": ["Digit1", null, null, "Numpad1"],
"2": ["Digit2", null, null, "Numpad2"],
"3": ["Digit3", null, null, "Numpad3"],
"4": ["Digit4", null, null, "Numpad4"],
"5": ["Digit5", null, null, "Numpad5"],
"6": ["Digit6", null, null, "Numpad6"],
"7": ["Digit7", null, null, "Numpad7"],
"8": ["Digit8", null, null, "Numpad8"],
"9": ["Digit9", null, null, "Numpad9"],
".": ["Period", null, null, "NumpadDecimal"],
"Insert": ["Insert", null, null, "Numpad0"],
"End": ["End", null, null, "Numpad1"],
"ArrowDown": ["ArrowDown", null, null, "Numpad2"],
"PageDown": ["PageDown", null, null, "Numpad3"],
"ArrowLeft": ["ArrowLeft", null, null, "Numpad4"],
"Clear": ["Clear", null, null, "Numpad5"],
"ArrowRight": ["ArrowRight", null, null, "Numpad6"],
"Home": ["Home", null, null, "Numpad7"],
"ArrowUp": ["ArrowUp", null, null, "Numpad8"],
"PageUp": ["PageUp", null, null, "Numpad9"],
"Delete": ["Delete", null, null, "NumpadDecimal"],
"/": ["Slash", null, null, "NumpadDivide"],
"*": ["Asterisk", null, null, "NumpadMultiply"],
"-": ["Minus", null, null, "NumpadSubtract"],
"+": ["Add", null, null, "NumpadAdd"],
"Enter": ["Enter", null, null, "NumpadEnter"],
"Shift": [null, "ShiftLeft", "ShiftRight", null],
"Control": [null, "ControlLeft", "ControlRight", null],
"Alt": [null, "AltLeft", "AltRight", null],
"Meta": [null, "MetaLeft", "MetaRight", null]
}
// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef FLUTTER_SHELL_PLATFORM_WINDOWS_FLUTTER_KEY_MAP_H_
#define FLUTTER_SHELL_PLATFORM_WINDOWS_FLUTTER_KEY_MAP_H_
#include "flutter/shell/platform/windows/keyboard_key_embedder_handler.h"
#include <map>
// DO NOT EDIT -- DO NOT EDIT -- DO NOT EDIT
// This file is generated by
// flutter/flutter@dev/tools/gen_keycodes/bin/gen_keycodes.dart and should not
// be edited directly.
//
// Edit the template dev/tools/gen_keycodes/data/windows_flutter_key_map_cc.tmpl
// instead. See dev/tools/gen_keycodes/README.md for more information.
namespace flutter {
std::map<uint64_t, uint64_t> KeyboardKeyEmbedderHandler::windowsToPhysicalMap_ =
{
@@@WINDOWS_SCAN_CODE_MAP@@@
};
std::map<uint64_t, uint64_t> KeyboardKeyEmbedderHandler::windowsToLogicalMap_ =
{
@@@WINDOWS_KEY_CODE_MAP@@@
};
std::map<uint64_t, uint64_t> KeyboardKeyEmbedderHandler::scanCodeToLogicalMap_ =
{
@@@WINDOWS_SCAN_CODE_TO_LOGICAL_MAP@@@
};
} // namespace flutter
#endif
{
"Cancel": ["CANCEL"],
"Backspace": ["BACK"],
"Tab": ["TAB"],
"Clear": ["CLEAR"],
"Enter": ["RETURN"],
"Menu": ["MENU"],
"Pause": ["PAUSE"],
"CapsLock": ["CAPITAL"],
"Lang1": ["HANGUL", "HANGEUL", "KANA"],
"JunjaMode": ["JUNJA"],
"FinalMode": ["FINAL"],
"HanjaMode": ["HANJA"],
"KanjiMode": ["KANJI"],
"Escape": ["ESCAPE"],
"Convert": ["CONVERT"],
"Nonconvert": ["NONCONVERT"],
"Accept": ["ACCEPT"],
"ModeChange": ["MODECHANGE"],
"Space": ["SPACE"],
"Home": ["HOME"],
"End": ["END"],
"ArrowLeft": ["LEFT"],
"ArrowUp": ["UP"],
"ArrowRight": ["RIGHT"],
"ArrowDown": ["DOWN"],
"Sleep": ["SLEEP"],
"Select": ["SELECT"],
"Print": ["PRINT"],
"Execute": ["EXECUTE"],
"Snapshot": ["SNAPSHOT"],
"Insert": ["INSERT"],
"Delete": ["DELETE"],
"Help": ["HELP"],
"MetaLeft": ["LWIN"],
"MetaRight": ["RWIN"],
"ContextMenu": ["APPS"],
"PageDown": ["NEXT"],
"PageUp": ["PRIOR"],
"Numpad0": ["NUMPAD0"],
"Numpad1": ["NUMPAD1"],
"Numpad2": ["NUMPAD2"],
"Numpad3": ["NUMPAD3"],
"Numpad4": ["NUMPAD4"],
"Numpad5": ["NUMPAD5"],
"Numpad6": ["NUMPAD6"],
"Numpad7": ["NUMPAD7"],
"Numpad8": ["NUMPAD8"],
"Numpad9": ["NUMPAD9"],
"NumpadMultiply": ["MULTIPLY"],
"NumpadAdd": ["ADD"],
"NumpadSeparator": ["SEPARATOR"],
"NumpadSubtract": ["SUBTRACT"],
"NumpadDecimal": ["DECIMAL"],
"NumpadDivide": ["DIVIDE"],
"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"],
"NavigationView": ["NAVIGATION_VIEW"],
"NavigationMenu": ["NAVIGATION_MENU"],
"NavigationUp": ["NAVIGATION_UP"],
"NavigationDown": ["NAVIGATION_DOWN"],
"NavigationLeft": ["NAVIGATION_LEFT"],
"NavigationRight": ["NAVIGATION_RIGHT"],
"NavigationAccept": ["NAVIGATION_ACCEPT"],
"NavigationCancel": ["NAVIGATION_CANCEL"],
"NumLock": ["NUMLOCK"],
"ScrollLock": ["SCROLL"],
"NumpadEqual": ["OEM_NEC_EQUAL"],
"ShiftLeft": ["LSHIFT", "SHIFT"],
"ShiftRight": ["RSHIFT"],
"ShiftLock": ["OEM_SHIFT"],
"ControlLeft": ["LCONTROL", "CONTROL"],
"ControlRight": ["RCONTROL"],
"AltLeft": ["LMENU"],
"AltRight": ["RMENU"],
"BrowserBack": ["BROWSER_BACK"],
"BrowserForward": ["BROWSER_FORWARD"],
"BrowserRefresh": ["BROWSER_REFRESH"],
"BrowserStop": ["BROWSER_STOP"],
"BrowserSearch": ["BROWSER_SEARCH"],
"BrowserFavorites": ["BROWSER_FAVORITES"],
"BrowserHome": ["BROWSER_HOME"],
"AudioVolumeMute": ["VOLUME_MUTE"],
"AudioVolumeDown": ["VOLUME_DOWN"],
"AudioVolumeUp": ["VOLUME_UP"],
"MediaNextTrack": ["MEDIA_NEXT_TRACK"],
"MediaPrevTrack": ["MEDIA_PREV_TRACK"],
"MediaStop": ["MEDIA_STOP"],
"MediaPlayPause": ["MEDIA_PLAY_PAUSE"],
"LaunchMail": ["LAUNCH_MAIL"],
"LaunchMediaSelect": ["LAUNCH_MEDIA_SELECT"],
"LaunchApp1": ["LAUNCH_APP1"],
"LaunchApp2": ["LAUNCH_APP2"],
"Semicolon": ["OEM_1"],
"Equal": ["OEM_PLUS"],
"Comma": ["OEM_COMMA"],
"Minus": ["OEM_MINUS"],
"Period": ["OEM_PERIOD"],
"Slash": ["OEM_2"],
"Backquote": ["OEM_3"],
"GameButton8": ["GAMEPAD_A"],
"GameButton9": ["GAMEPAD_B"],
"GameButton10": ["GAMEPAD_X"],
"GameButton11": ["GAMEPAD_Y"],
"GameButton12": ["GAMEPAD_RIGHT_SHOULDER"],
"GameButton13": ["GAMEPAD_LEFT_SHOULDER"],
"GameButton14": ["GAMEPAD_LEFT_TRIGGER"],
"GameButton15": ["GAMEPAD_RIGHT_TRIGGER"],
"GameButton16": ["GAMEPAD_DPAD_UP"],
"GameButton17": ["GAMEPAD_DPAD_DOWN"],
"GameButton18": ["GAMEPAD_DPAD_LEFT"],
"GameButton19": ["GAMEPAD_DPAD_RIGHT"],
"GamepadMenu": ["GAMEPAD_MENU"],
"GamepadView": ["GAMEPAD_VIEW"],
"GamepadLeftThumbstickButton": ["GAMEPAD_LEFT_THUMBSTICK_BUTTON"],
"GamepadRightThumbstickButton": ["GAMEPAD_RIGHT_THUMBSTICK_BUTTON"],
"GamepadLeftThumbstickUp": ["GAMEPAD_LEFT_THUMBSTICK_UP"],
"GamepadLeftThumbstickDown": ["GAMEPAD_LEFT_THUMBSTICK_DOWN"],
"GamepadLeftThumbstickRight": ["GAMEPAD_LEFT_THUMBSTICK_RIGHT"],
"GamepadLeftThumbstickLeft": ["GAMEPAD_LEFT_THUMBSTICK_LEFT"],
"GamepadRightThumbstickUp": ["GAMEPAD_RIGHT_THUMBSTICK_UP"],
"GamepadRightThumbstickDown": ["GAMEPAD_RIGHT_THUMBSTICK_DOWN"],
"GamepadRightThumbstickRight": ["GAMEPAD_RIGHT_THUMBSTICK_RIGHT"],
"GamepadRightThumbstickLeft": ["GAMEPAD_RIGHT_THUMBSTICK_LEFT"],
"BracketLeft": ["OEM_4"],
"Backslash": ["OEM_5"],
"BracketRight": ["OEM_6"],
"Quote": ["OEM_7"],
"Oem8": ["OEM_8"],
"OemAx": ["OEM_AX"],
"Oem102": ["OEM_102"],
"IcoHelp": ["ICO_HELP"],
"Ico00": ["ICO_00"],
"Processkey": ["PROCESSKEY"],
"IcoClear": ["ICO_CLEAR"],
"Packet": ["PACKET"],
"OemReset": ["OEM_RESET"],
"OemJump": ["OEM_JUMP"],
"OemPa1": ["OEM_PA1"],
"OemPa2": ["OEM_PA2"],
"OemPa3": ["OEM_PA3"],
"OemWsctrl": ["OEM_WSCTRL"],
"OemCusel": ["OEM_CUSEL"],
"OemAttn": ["OEM_ATTN"],
"OemFinish": ["OEM_FINISH"],
"OemCopy": ["OEM_COPY"],
"OemAuto": ["OEM_AUTO"],
"OemEnlw": ["OEM_ENLW"],
"OemBacktab": ["OEM_BACKTAB"],
"Attn": ["ATTN"],
"Crsel": ["CRSEL"],
"Exsel": ["EXSEL"],
"Ereof": ["EREOF"],
"Play": ["PLAY"],
"Zoom": ["ZOOM"],
"Noname": ["NONAME"],
"Pa1": ["PA1"],
"OemClear": ["OEM_CLEAR"],
"0": ["0"],
"1": ["1"],
"2": ["2"],
"3": ["3"],
"4": ["4"],
"5": ["5"],
"6": ["6"],
"7": ["7"],
"8": ["8"],
"9": ["9"],
"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"]
}
{
"ControlRight": "ControlRight",
"AltRight": "AltRight",
"Numpad1": "Numpad1",
"Numpad2": "Numpad2",
"Numpad3": "Numpad3",
"Numpad4": "Numpad4",
"Numpad5": "Numpad5",
"Numpad6": "Numpad6",
"Numpad7": "Numpad7",
"Numpad8": "Numpad8",
"Numpad9": "Numpad9",
"Numpad0": "Numpad0",
"NumpadAdd": "NumpadAdd",
"NumpadDecimal": "NumpadDecimal",
"NumpadDivide": "NumpadDivide",
"NumpadEqual": "NumpadEqual",
"NumpadMultiply": "NumpadMultiply",
"NumpadSubtract": "NumpadSubtract"
}
......@@ -5,36 +5,23 @@
import 'package:path/path.dart' as path;
import 'base_code_gen.dart';
import 'key_data.dart';
import 'logical_key_data.dart';
import 'physical_key_data.dart';
import 'utils.dart';
/// Generates the key mapping of Android, based on the information in the key
/// Generates the key mapping for Android, based on the information in the key
/// data structure given to it.
class AndroidCodeGenerator extends PlatformCodeGenerator {
AndroidCodeGenerator(KeyData keyData) : super(keyData);
AndroidCodeGenerator(PhysicalKeyData physicalData, LogicalKeyData logicalData)
: super(physicalData, logicalData);
/// This generates the map of Android key codes to logical keys.
String get _androidKeyCodeMap {
final StringBuffer androidKeyCodeMap = StringBuffer();
for (final Key entry in keyData.data) {
if (entry.androidKeyCodes != null) {
for (final int code in entry.androidKeyCodes.cast<int>()) {
androidKeyCodeMap.writeln(' { $code, ${toHex(entry.flutterId, digits: 10)} }, // ${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();
for (final Key entry in numpadKeyData) {
if (entry.androidKeyCodes != null) {
for (final int code in entry.androidKeyCodes.cast<int>()) {
androidKeyCodeMap.writeln(' { $code, ${toHex(entry.flutterId, digits: 10)} }, // ${entry.constantName}');
}
for (final LogicalKeyEntry entry in logicalData.entries) {
for (final int code in entry.androidValues) {
androidKeyCodeMap.writeln(' put(${toHex(code, digits: 10)}L, ${toHex(entry.value, digits: 10)}L); // ${entry.constantName}');
}
}
return androidKeyCodeMap.toString().trimRight();
......@@ -43,10 +30,10 @@ class AndroidCodeGenerator extends PlatformCodeGenerator {
/// This generates the map of Android scan codes to physical keys.
String get _androidScanCodeMap {
final StringBuffer androidScanCodeMap = StringBuffer();
for (final Key entry in keyData.data) {
for (final PhysicalKeyEntry entry in keyData.entries) {
if (entry.androidScanCodes != null) {
for (final int code in entry.androidScanCodes.cast<int>()) {
androidScanCodeMap.writeln(' { $code, ${toHex(entry.usbHidCode)} }, // ${entry.constantName}');
androidScanCodeMap.writeln(' put(${toHex(code, digits: 10)}L, ${toHex(entry.usbHidCode, digits: 10)}L); // ${entry.constantName}');
}
}
}
......@@ -54,14 +41,17 @@ class AndroidCodeGenerator extends PlatformCodeGenerator {
}
@override
String get templatePath => path.join(flutterRoot.path, 'dev', 'tools', 'gen_keycodes', 'data', 'keyboard_map_android_cc.tmpl');
String get templatePath => path.join(dataRoot, 'android_keyboard_map_java.tmpl');
@override
String outputPath(String platform) => path.join(PlatformCodeGenerator.engineRoot, 'shell', 'platform',
path.join('android', 'io', 'flutter', 'embedding', 'android', 'KeyboardMap.java'));
@override
Map<String, String> mappings() {
return <String, String>{
'ANDROID_SCAN_CODE_MAP': _androidScanCodeMap,
'ANDROID_KEY_CODE_MAP': _androidKeyCodeMap,
'ANDROID_NUMPAD_MAP': _androidNumpadMap,
};
}
}
......@@ -3,15 +3,14 @@
// found in the LICENSE file.
import 'dart:io';
import 'package:path/path.dart' as path;
import 'package:gen_keycodes/logical_key_data.dart';
import 'key_data.dart';
import 'utils.dart';
import 'physical_key_data.dart';
String _injectDictionary(String template, Map<String, String> dictionary) {
String result = template;
for (final String key in dictionary.keys) {
result = result.replaceAll('@@@$key@@@', dictionary[key]);
result = result.replaceAll('@@@$key@@@', dictionary[key] ?? '@@@$key@@@');
}
return result;
}
......@@ -26,7 +25,7 @@ String _injectDictionary(String template, Map<String, String> dictionary) {
/// Subclasses must implement [templatePath] and [mappings].
abstract class BaseCodeGenerator {
/// Create a code generator while providing [keyData] to be used in [mappings].
BaseCodeGenerator(this.keyData);
BaseCodeGenerator(this.keyData, this.logicalData);
/// Absolute path to the template file that this file is generated on.
String get templatePath;
......@@ -42,30 +41,20 @@ abstract class BaseCodeGenerator {
}
/// The database of keys loaded from disk.
final KeyData keyData;
final PhysicalKeyData keyData;
final LogicalKeyData logicalData;
}
/// A code generator which also defines platform-based behavior.
abstract class PlatformCodeGenerator extends BaseCodeGenerator {
PlatformCodeGenerator(KeyData keyData) : super(keyData);
// Used by platform code generators.
List<Key> get numpadKeyData {
return keyData.data.where((Key entry) {
return entry.constantName.startsWith('numpad') && entry.keyLabel != null;
}).toList();
}
// Used by platform code generators.
List<Key> get functionKeyData {
final RegExp functionKeyRe = RegExp(r'^f[0-9]+$');
return keyData.data.where((Key entry) {
return functionKeyRe.hasMatch(entry.constantName);
}).toList();
}
PlatformCodeGenerator(PhysicalKeyData keyData, LogicalKeyData logicalData)
: super(keyData, logicalData);
/// Absolute path to the output file.
///
/// How this value will be used is based on the callee.
String outputPath(String platform) => path.join(flutterRoot.path, '..', path.join('engine', 'src', 'flutter', 'shell', 'platform', platform, 'keycodes', 'keyboard_map_$platform.h'));
String outputPath(String platform);
static String engineRoot = '';
}
// Copyright 2014 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
/* === LOGICAL KEY BITMASKS === */
// The following constants are used to composite the value for a Flutter logical
// key.
//
// A Flutter logical key supports up to 53-bits, as limited by JavaScript
// integers.
/* Bit 31-0: Value */
// The lower 32 bits represent different values within a plane.
/// Mask for the 32-bit value portion of the code.
const int kValueMask = 0x000FFFFFFFF;
/* Bit 39-32: Source of value */
// The next 8 bits represent where the values come from, consequently how to
// interpret them.
/// Mask for bit #39-32 portion of the code, indicating the source of the value.
const int kPlatformMask = 0x0FF00000000;
/// The code prefix for keys that have a Unicode representation, generated from
/// their code points.
///
/// See also:
///
/// * [kUnprintablePlane], for keys that are also generated from code points but
/// unprintable.
const int kUnicodePlane = 0x00000000000;
/// The code prefix for keys generated from their USB HID usages.
const int kHidPlane = 0x00100000000;
/// The code prefix for unrecognized keys that are unique to Android, generated
/// from platform-specific codes.
const int kAndroidPlane = 0x00200000000;
/// The code prefix for unrecognized keys that are unique to Fuchsia, generated
/// from platform-specific codes.
const int kFuchsiaPlane = 0x00300000000;
/// The code prefix for unrecognized keys that are unique to iOS, generated
/// from platform-specific codes.
const int kIosPlane = 0x00400000000;
/// The code prefix for unrecognized keys that are unique to macOS, generated
/// from platform-specific codes.
const int kMacOsPlane = 0x00500000000;
/// The code prefix for unrecognized keys that are unique to Gtk, generated from
/// platform-specific codes.
const int kGtkPlane = 0x00600000000;
/// The code prefix for unrecognized keys that are unique to Windows, generated
/// from platform-specific codes.
const int kWindowsPlane = 0x00700000000;
/// The code prefix for unrecognized keys that are unique to Web, generated from
/// platform-specific codes.
const int kWebPlane = 0x00800000000;
/// The code prefix for keys that do not have a Unicode representation, generated
/// from their code points.
///
/// See also:
///
/// * [kUnicodePlane], for keys that are also generated from code points but
/// printable.
const int kUnprintablePlane = 0x01000000000;
/* Bit 43-40: Key variation */
// The next 4 bits represent keys that have multiple variations, such as a left
// and right variation of a modifier key, or a Numpad variation of a digit key.
/// Mask for bit #43-40 portion of the code, indicating the variation of the key.
const int kVariationMask = 0xF0000000000;
/// The code prefix for pseudo-keys which represent collections of key synonyms,
/// such as Control.
const int kSynonymPlane = 0x20000000000;
/// The code prefix for the left modifier of a pair of modifiers, such as Control
/// Left.
const int kLeftModifierPlane = 0x30000000000;
/// The code prefix for the right modifier of a pair of modifiers, such as Control
/// Right.
const int kRightModifierPlane = 0x40000000000;
/// The code prefix for a Numpad key, such as Numpad 1.
const int kNumpadPlane = 0x50000000000;
class MaskConstant {
const MaskConstant({required this.name, required this.value, required this.description});
final String name;
final int value;
final List<String> description;
}
const List<MaskConstant> maskConstants = <MaskConstant>[
MaskConstant(
name: 'valueMask',
value: kValueMask,
description: <String>[
'Mask for the 32-bit value portion of the key code.',
'This is used by platform-specific code to generate Flutter key codes.',
],
),
MaskConstant(
name: 'platformMask',
value: kPlatformMask,
description: <String>[
'Mask for the platform prefix portion of the key code.',
'This is used by platform-specific code to generate Flutter key codes.'
],
),
MaskConstant(
name: 'unicodePlane',
value: kUnicodePlane,
description: <String>[
'The code prefix for keys which have a Unicode representation.',
'This is used by platform-specific code to generate Flutter key codes.'
],
),
MaskConstant(
name: 'synonymMask',
value: kSynonymPlane,
description: <String>[
'Mask for the synonym pseudo-keys generated for keys which appear in more than one place on the keyboard.',
'IDs in this range are used to represent keys which appear in multiple places on the keyboard, such as the SHIFT, ALT, CTRL, and numeric keypad keys. These key codes will never be generated by the key event system, but may be used in key maps to represent the union of all the keys of each type in order to match them.',
'To look up the synonyms that are defined, look in the [synonyms] map.'
],
),
MaskConstant(
name: 'hidPlane',
value: kHidPlane,
description: <String>[
'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.'
],
),
];
// Copyright 2014 The Flutter 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 'package:path/path.dart' as path;
import 'base_code_gen.dart';
import 'key_data.dart';
import 'utils.dart';
/// Generates the key mapping of Fuchsia, based on the information in the key
/// data structure given to it.
class FuchsiaCodeGenerator extends PlatformCodeGenerator {
FuchsiaCodeGenerator(KeyData keyData) : super(keyData);
/// This generates the map of Fuchsia key codes to logical keys.
String get _fuchsiaKeyCodeMap {
final StringBuffer fuchsiaKeyCodeMap = StringBuffer();
for (final Key entry in keyData.data) {
if (entry.usbHidCode != null) {
fuchsiaKeyCodeMap.writeln(' { ${toHex(entry.flutterId)}, ${toHex(entry.flutterId, digits: 10)} }, // ${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 (final Key entry in keyData.data) {
if (entry.usbHidCode != null) {
fuchsiaScanCodeMap.writeln(' { ${toHex(entry.usbHidCode)}, ${toHex(entry.usbHidCode)} }, // ${entry.constantName}');
}
}
return fuchsiaScanCodeMap.toString().trimRight();
}
@override
String get templatePath => path.join(flutterRoot.path, 'dev', 'tools', 'gen_keycodes', 'data', 'keyboard_map_fuchsia_cc.tmpl');
@override
Map<String, String> mappings() {
return <String, String>{
'FUCHSIA_SCAN_CODE_MAP': _fuchsiaHidCodeMap,
'FUCHSIA_KEY_CODE_MAP': _fuchsiaKeyCodeMap,
};
}
}
// Copyright 2014 The Flutter 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 'package:path/path.dart' as path;
import 'base_code_gen.dart';
import 'key_data.dart';
import 'utils.dart';
/// Generates the key mapping of GLFW, based on the information in the key
/// data structure given to it.
class GlfwCodeGenerator extends PlatformCodeGenerator {
GlfwCodeGenerator(KeyData keyData) : super(keyData);
/// This generates the map of GLFW number pad key codes to logical keys.
String get _glfwNumpadMap {
final StringBuffer glfwNumpadMap = StringBuffer();
for (final Key entry in numpadKeyData) {
if (entry.glfwKeyCodes != null) {
for (final int code in entry.glfwKeyCodes.cast<int>()) {
glfwNumpadMap.writeln(' { $code, ${toHex(entry.flutterId, digits: 10)} }, // ${entry.constantName}');
}
}
}
return glfwNumpadMap.toString().trimRight();
}
/// This generates the map of GLFW key codes to logical keys.
String get _glfwKeyCodeMap {
final StringBuffer glfwKeyCodeMap = StringBuffer();
for (final Key entry in keyData.data) {
if (entry.glfwKeyCodes != null) {
for (final int code in entry.glfwKeyCodes.cast<int>()) {
glfwKeyCodeMap.writeln(' { $code, ${toHex(entry.flutterId, digits: 10)} }, // ${entry.constantName}');
}
}
}
return glfwKeyCodeMap.toString().trimRight();
}
@override
String get templatePath => path.join(flutterRoot.path, 'dev', 'tools', 'gen_keycodes', 'data', 'keyboard_map_glfw_cc.tmpl');
@override
Map<String, String> mappings() {
return <String, String>{
'GLFW_KEY_CODE_MAP': _glfwKeyCodeMap,
'GLFW_NUMPAD_MAP': _glfwNumpadMap,
};
}
}
......@@ -5,33 +5,104 @@
import 'package:path/path.dart' as path;
import 'base_code_gen.dart';
import 'key_data.dart';
import 'logical_key_data.dart';
import 'physical_key_data.dart';
import 'utils.dart';
/// Generates the key mapping of GTK, based on the information in the key
/// Generates the key mapping for GTK, based on the information in the key
/// data structure given to it.
class GtkCodeGenerator extends PlatformCodeGenerator {
GtkCodeGenerator(KeyData keyData) : super(keyData);
GtkCodeGenerator(
PhysicalKeyData keyData,
LogicalKeyData logicalData,
String modifierBitMapping,
String lockBitMapping,
) : _modifierBitMapping = parseMapOfListOfString(modifierBitMapping),
_lockBitMapping = parseMapOfListOfString(lockBitMapping),
super(keyData, logicalData);
/// This generates the map of XKB scan codes to USB HID codes.
String get xkbScanCodeMap {
final StringBuffer xkbScanCodeMap = StringBuffer();
for (final Key entry in keyData.data) {
/// This generates the map of XKB scan codes to Flutter physical keys.
String get _xkbScanCodeMap {
final OutputLines<int> lines = OutputLines<int>('GTK scancode map');
for (final PhysicalKeyEntry entry in keyData.entries) {
if (entry.xKbScanCode != null) {
xkbScanCodeMap.writeln(' { ${toHex(entry.xKbScanCode)}, ${toHex(entry.usbHidCode)} }, // ${entry.constantName}');
lines.add(entry.xKbScanCode!, ' insert_record(table, ${toHex(entry.xKbScanCode)}, ${toHex(entry.usbHidCode)}); // ${entry.constantName}');
}
}
return xkbScanCodeMap.toString().trimRight();
return lines.sortedJoin().trimRight();
}
/// This generates the map of GTK keyval codes to Flutter logical keys.
String get _gtkKeyvalCodeMap {
final OutputLines<int> lines = OutputLines<int>('GTK keyval map');
for (final LogicalKeyEntry entry in logicalData.entries) {
zipStrict(entry.gtkValues, entry.gtkNames, (int value, String name) {
lines.add(value, ' insert_record(table, ${toHex(value)}, ${toHex(entry.value, digits: 11)}); // $name');
});
}
return lines.sortedJoin().trimRight();
}
static String constructMapFromModToKeys(
Map<String, List<String>> source,
PhysicalKeyData physicalData,
LogicalKeyData logicalData,
String debugFunctionName,
) {
final StringBuffer result = StringBuffer();
source.forEach((String modifierBitName, List<String> keyNames) {
assert(keyNames.length == 2 || keyNames.length == 3);
final String primaryLogicalName = keyNames[0];
final String primaryPhysicalName = keyNames[1];
final String? secondaryPhysicalName = keyNames.length == 3 ? keyNames[2] : null;
final LogicalKeyEntry primaryLogical = logicalData.entryByName(primaryLogicalName);
final PhysicalKeyEntry primaryPhysical = physicalData.entryByName(primaryPhysicalName);
final PhysicalKeyEntry? secondaryPhysical = secondaryPhysicalName == null ? null : physicalData.entryByName(secondaryPhysicalName);
if (secondaryPhysical == null && secondaryPhysicalName != null) {
print('Unrecognized secondary physical key $secondaryPhysicalName specified for $debugFunctionName.');
return;
}
final String pad = secondaryPhysical == null ? '' : ' ';
result.writeln('''
data = g_new(FlKeyEmbedderCheckedKey, 1);
g_hash_table_insert(table, GUINT_TO_POINTER(GDK_${modifierBitName}_MASK), data);
data->is_caps_lock = ${primaryPhysicalName == 'CapsLock' ? 'true' : 'false'};
data->primary_logical_key = ${toHex(primaryLogical.value, digits: 11)};$pad // ${primaryLogical.constantName}
data->primary_physical_key = ${toHex(primaryPhysical.usbHidCode, digits: 9)};$pad // ${primaryPhysical.constantName}''');
if (secondaryPhysical != null) {
result.writeln('''
data->secondary_physical_key = ${toHex(secondaryPhysical.usbHidCode, digits: 9)}; // ${secondaryPhysical.constantName}''');
}
});
return result.toString().trimRight();
}
String get _gtkModifierBitMap {
return constructMapFromModToKeys(_modifierBitMapping, keyData, logicalData, 'gtkModifierBitMap');
}
final Map<String, List<String>> _modifierBitMapping;
String get _gtkModeBitMap {
return constructMapFromModToKeys(_lockBitMapping, keyData, logicalData, 'gtkModeBitMap');
}
final Map<String, List<String>> _lockBitMapping;
@override
String get templatePath => path.join(dataRoot, 'gtk_key_mapping_cc.tmpl');
@override
String get templatePath => path.join(flutterRoot.path, 'dev', 'tools', 'gen_keycodes', 'data', 'keyboard_map_linux_cc.tmpl');
String outputPath(String platform) => path.join(PlatformCodeGenerator.engineRoot,
'shell', 'platform', 'linux', 'key_mapping.cc');
@override
Map<String, String> mappings() {
return <String, String>{
'XKB_SCAN_CODE_MAP': xkbScanCodeMap,
'XKB_SCAN_CODE_MAP': _xkbScanCodeMap,
'GTK_KEYVAL_CODE_MAP': _gtkKeyvalCodeMap,
'GTK_MODIFIER_BIT_MAP': _gtkModifierBitMap,
'GTK_MODE_BIT_MAP': _gtkModeBitMap,
};
}
}
// Copyright 2014 The Flutter 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 'package:path/path.dart' as path;
import 'base_code_gen.dart';
import 'key_data.dart';
import 'utils.dart';
/// Generates the key mapping of iOS, based on the information in the key
/// data structure given to it.
class IosCodeGenerator extends PlatformCodeGenerator {
IosCodeGenerator(KeyData keyData) : super(keyData);
/// This generates the map of iOS key codes to physical keys.
String get _iosScanCodeMap {
final StringBuffer iosScanCodeMap = StringBuffer();
for (final Key entry in keyData.data) {
if (entry.iosScanCode != null) {
iosScanCodeMap.writeln(' { ${toHex(entry.iosScanCode)}, ${toHex(entry.usbHidCode)} }, // ${entry.constantName}');
}
}
return iosScanCodeMap.toString().trimRight();
}
/// This generates the map of iOS number pad key codes to logical keys.
String get _iosNumpadMap {
final StringBuffer iosNumPadMap = StringBuffer();
for (final Key entry in numpadKeyData) {
if (entry.iosScanCode != null) {
iosNumPadMap.writeln(' { ${toHex(entry.iosScanCode)}, ${toHex(entry.flutterId, digits: 10)} }, // ${entry.constantName}');
}
}
return iosNumPadMap.toString().trimRight();
}
@override
String get templatePath => path.join(flutterRoot.path, 'dev', 'tools', 'gen_keycodes', 'data', 'keyboard_map_ios_cc.tmpl');
@override
String outputPath(String platform) => path.joinAll(<String>[flutterRoot.path, '..', 'engine', 'src', 'flutter', 'shell', 'platform', 'darwin', platform, 'keycodes', 'keyboard_map_$platform.h']);
@override
Map<String, String> mappings() {
// There is no iOS keycode map since iOS uses keycode to represent a physical key.
// The LogicalKeyboardKey is generated by raw_keyboard_macos.dart from the unmodified characters
// from NSEvent.
return <String, String>{
'IOS_SCAN_CODE_MAP': _iosScanCodeMap,
'IOS_NUMPAD_MAP': _iosNumpadMap
};
}
}
// Copyright 2014 The Flutter 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:gen_keycodes/utils.dart';
import 'package:meta/meta.dart';
import 'package:path/path.dart' as path;
/// 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,
String glfwKeyCodeHeader,
String glfwNameMap,
String gtkKeyCodeHeader,
String gtkNameMap,
String windowsKeyCodeHeader,
String windowsNameMap,
) : assert(chromiumHidCodes != null),
assert(androidKeyboardLayout != null),
assert(androidKeyCodeHeader != null),
assert(androidNameMap != null),
assert(glfwKeyCodeHeader != null),
assert(glfwNameMap != null),
assert(gtkKeyCodeHeader != null),
assert(gtkNameMap != null),
assert(windowsKeyCodeHeader != null),
assert(windowsNameMap != null) {
_nameToAndroidScanCodes = _readAndroidScanCodes(androidKeyboardLayout);
_nameToAndroidKeyCode = _readAndroidKeyCodes(androidKeyCodeHeader);
_nameToGlfwKeyCode = _readGlfwKeyCodes(glfwKeyCodeHeader);
_nameToGtkKeyCode = _readGtkKeyCodes(gtkKeyCodeHeader);
_nameToWindowsKeyCode = _readWindowsKeyCodes(windowsKeyCodeHeader);
// Cast Android dom map
final Map<String, List<dynamic>> dynamicAndroidNames = (json.decode(androidNameMap) as Map<String, dynamic>).cast<String, List<dynamic>>();
_nameToAndroidName = dynamicAndroidNames.map<String, List<String>>((String key, List<dynamic> value) {
return MapEntry<String, List<String>>(key, value.cast<String>());
});
// Cast GLFW dom map
final Map<String, List<dynamic>> dynamicGlfwNames = (json.decode(glfwNameMap) as Map<String, dynamic>).cast<String, List<dynamic>>();
_nameToGlfwName = dynamicGlfwNames.map<String, List<String>>((String key, List<dynamic> value) {
return MapEntry<String, List<String>>(key, value.cast<String>());
});
// Cast GTK dom map
final Map<String, List<dynamic>> dynamicGtkNames = (json.decode(gtkNameMap) as Map<String, dynamic>).cast<String, List<dynamic>>();
_nameToGtkName = dynamicGtkNames.map<String, List<String>>((String key, List<dynamic> value) {
return MapEntry<String, List<String>>(key, value.cast<String>());
});
// Cast Windows dom map
final Map<String, List<dynamic>> dynamicWindowsNames = (json.decode(windowsNameMap) as Map<String, dynamic>).cast<String, List<dynamic>>();
_nameToWindowsName = dynamicWindowsNames.map<String, List<String>>((String key, List<dynamic> value) {
return MapEntry<String, List<String>>(key, value.cast<String>());
});
data = _readHidEntries(chromiumHidCodes);
}
/// Parses the given JSON data and populates the data structure from it.
KeyData.fromJson(Map<String, dynamic> contentMap) {
data = <Key>[
for (final String key in contentMap.keys) Key.fromJsonMapEntry(key, contentMap[key] as Map<String, dynamic>),
];
}
/// Converts the data structure into a JSON structure that can be parsed by
/// [KeyData.fromJson].
Map<String, dynamic> toJson() {
for (final Key entry in data) {
// Android Key names
entry.androidKeyNames = _nameToAndroidName[entry.constantName]?.cast<String>();
if (entry.androidKeyNames != null && entry.androidKeyNames.isNotEmpty) {
for (final String androidKeyName in entry.androidKeyNames) {
if (_nameToAndroidKeyCode[androidKeyName] != null) {
entry.androidKeyCodes ??= <int>[];
entry.androidKeyCodes.add(_nameToAndroidKeyCode[androidKeyName]);
}
if (_nameToAndroidScanCodes[androidKeyName] != null && _nameToAndroidScanCodes[androidKeyName].isNotEmpty) {
entry.androidScanCodes ??= <int>[];
entry.androidScanCodes.addAll(_nameToAndroidScanCodes[androidKeyName]);
}
}
}
// GLFW key names
entry.glfwKeyNames = _nameToGlfwName[entry.constantName]?.cast<String>();
if (entry.glfwKeyNames != null && entry.glfwKeyNames.isNotEmpty) {
for (final String glfwKeyName in entry.glfwKeyNames) {
if (_nameToGlfwKeyCode[glfwKeyName] != null) {
entry.glfwKeyCodes ??= <int>[];
entry.glfwKeyCodes.add(_nameToGlfwKeyCode[glfwKeyName]);
}
}
}
// GTK key names
entry.gtkKeyNames = _nameToGtkName[entry.constantName]?.cast<String>();
if (entry.gtkKeyNames != null && entry.gtkKeyNames.isNotEmpty) {
for (final String gtkKeyName in entry.gtkKeyNames) {
if (_nameToGtkKeyCode[gtkKeyName] != null) {
entry.gtkKeyCodes ??= <int>[];
entry.gtkKeyCodes.add(_nameToGtkKeyCode[gtkKeyName]);
}
}
}
// Windows key names
entry.windowsKeyNames = _nameToWindowsName[entry.constantName]?.cast<String>();
if (entry.windowsKeyNames != null && entry.windowsKeyNames.isNotEmpty) {
for (final String windowsKeyName in entry.windowsKeyNames) {
if (_nameToWindowsKeyCode[windowsKeyName] != null) {
entry.windowsKeyCodes ??= <int>[];
entry.windowsKeyCodes.add(_nameToWindowsKeyCode[windowsKeyName]);
}
}
}
}
final Map<String, dynamic> outputMap = <String, dynamic>{};
for (final Key entry in data) {
outputMap[entry.constantName] = entry.toJson();
}
return outputMap;
}
/// The list of keys.
List<Key> 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<String, List<String>> _nameToAndroidName;
/// The mapping from the Flutter name (e.g. "eject") to the GLFW name (e.g.
/// "GLFW_MEDIA_EJECT").
///
/// Only populated if data is parsed from the source files, not if parsed from
/// JSON.
Map<String, List<String>> _nameToGlfwName;
/// The mapping from the Flutter name (e.g. "eject") to the GTK name (e.g.
/// "GDK_KEY_Eject").
///
/// Only populated if data is parsed from the source files, not if parsed from
/// JSON.
Map<String, List<String>> _nameToGtkName;
/// 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<String, List<int>> _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<String, int> _nameToAndroidKeyCode;
/// The mapping from GLFW name (e.g. "GLFW_KEY_COMMA") 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<String, int> _nameToGlfwKeyCode;
/// The mapping from GTK name (e.g. "GTK_KEY_comma") 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<String, int> _nameToGtkKeyCode;
/// The mapping from Widows name (e.g. "RETURN") 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<String, int> _nameToWindowsKeyCode;
/// The mapping from the Flutter name (e.g. "enter") to the Windows name (e.g.
/// "RETURN").
///
/// Only populated if data is parsed from the source files, not if parsed from
/// JSON.
Map<String, List<String>> _nameToWindowsName;
/// 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<String, List<int>> _readAndroidScanCodes(String keyboardLayout) {
final RegExp keyEntry = RegExp(r'#?\s*key\s+([0-9]+)\s*"?(?:KEY_)?([0-9A-Z_]+|\(undefined\))"?\s*(FUNCTION)?');
final Map<String, List<int>> result = <String, List<int>>{};
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] ??= <int>[];
result[androidName].add(int.parse(match.group(1)));
return null;
});
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<String, int> _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<String, int> result = <String, int>{};
for (final Match match in enumEntry.allMatches(headerFile)) {
result[match.group(1)] = int.parse(match.group(2));
}
return result;
}
/// Parses entries from GLFW's keycodes.h key code data file.
///
/// Lines in this file look like this (without the ///):
/// /** Space key. */
/// #define GLFW_KEY_SPACE 32,
Map<String, int> _readGlfwKeyCodes(String headerFile) {
// Only get the KEY definitions, ignore the rest (mouse, joystick, etc).
final RegExp definedCodes = RegExp(r'define GLFW_KEY_([A-Z0-9_]+)\s*([A-Z0-9_]+),?');
final Map<String, dynamic> replaced = <String, dynamic>{};
for (final Match match in definedCodes.allMatches(headerFile)) {
replaced[match.group(1)] = int.tryParse(match.group(2)) ?? match.group(2).replaceAll('GLFW_KEY_', '');
}
final Map<String, int> result = <String, int>{};
replaced.forEach((String key, dynamic value) {
// Some definition values point to other definitions (e.g #define GLFW_KEY_LAST GLFW_KEY_MENU).
if (value is String) {
result[key] = replaced[value] as int;
} else {
result[key] = value as int;
}
});
return result;
}
/// Parses entries from GTK's gdkkeysyms.h key code data file.
///
/// Lines in this file look like this (without the ///):
/// /** Space key. */
/// #define GDK_KEY_space 0x020
Map<String, int> _readGtkKeyCodes(String headerFile) {
final RegExp definedCodes = RegExp(r'#define GDK_KEY_([a-zA-Z0-9_]+)\s*0x([0-9a-f]+),?');
final Map<String, int> replaced = <String, int>{};
for (final Match match in definedCodes.allMatches(headerFile)) {
replaced[match.group(1)] = int.parse(match.group(2), radix: 16);
}
return replaced;
}
Map<String, int> _readWindowsKeyCodes(String headerFile) {
final RegExp definedCodes = RegExp(r'define VK_([A-Z0-9_]+)\s*([A-Z0-9_x]+),?');
final Map<String, int> replaced = <String, int>{};
for (final Match match in definedCodes.allMatches(headerFile)) {
replaced[match.group(1)] = int.tryParse(match.group(2));
}
// The header doesn't explicitly define the [0-9] and [A-Z], but they mention that the range
// is equivalent to the ASCII value.
for (int i = 0x30; i <= 0x39; i++) {
replaced[String.fromCharCode(i)] = i;
}
for (int i = 0x41; i <= 0x5A; i++) {
replaced[String.fromCharCode(i)] = i;
}
return replaced;
}
/// 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
/// DOM_CODE(0x000010, 0x0000, 0x0000, 0x0000, 0xffff, "Hyper", HYPER),
List<Key> _readHidEntries(String input) {
final List<Key> entries = <Key>[];
final RegExp usbMapRegExp = RegExp(
r'DOM_CODE\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 usbHidCode = getHex(match.group(1));
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: usbHidCode,
linuxScanCode: linuxScanCode == 0 ? null : linuxScanCode,
xKbScanCode: xKbScanCode == 0 ? null : xKbScanCode,
windowsScanCode: windowsScanCode == 0 ? null : windowsScanCode,
macOsScanCode: macScanCode == 0xffff ? null : macScanCode,
iosScanCode: (usbHidCode & 0x070000) == 0x070000 ? (usbHidCode ^ 0x070000) : null,
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 '';
}
// Remove duplicates: last one wins, so that supplemental codes
// override.
entries.removeWhere((Key entry) => entry.usbHidCode == newEntry.usbHidCode);
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.windowsKeyNames,
this.windowsKeyCodes,
this.macOsScanCode,
this.iosScanCode,
@required this.chromiumName,
this.androidKeyNames,
this.androidScanCodes,
this.androidKeyCodes,
this.glfwKeyNames,
this.glfwKeyCodes,
this.gtkKeyNames,
this.gtkKeyCodes,
}) : assert(usbHidCode != null),
assert(chromiumName != null),
_constantName = enumName;
/// Populates the key from a JSON map.
factory Key.fromJsonMapEntry(String name, Map<String, dynamic> map) {
return Key(
enumName: name,
name: map['names']['domkey'] as String,
chromiumName: map['names']['chromium'] as String,
usbHidCode: map['scanCodes']['usb'] as int,
androidKeyNames: (map['names']['android'] as List<dynamic>)?.cast<String>(),
androidScanCodes: (map['scanCodes']['android'] as List<dynamic>)?.cast<int>(),
androidKeyCodes: (map['keyCodes']['android'] as List<dynamic>)?.cast<int>(),
linuxScanCode: map['scanCodes']['linux'] as int,
xKbScanCode: map['scanCodes']['xkb'] as int,
windowsScanCode: map['scanCodes']['windows'] as int,
windowsKeyCodes: (map['keyCodes']['windows'] as List<dynamic>)?.cast<int>(),
windowsKeyNames: (map['names']['windows'] as List<dynamic>)?.cast<String>(),
macOsScanCode: map['scanCodes']['macos'] as int,
iosScanCode: map['scanCodes']['ios'] as int,
glfwKeyNames: (map['names']['glfw'] as List<dynamic>)?.cast<String>(),
glfwKeyCodes: (map['keyCodes']['glfw'] as List<dynamic>)?.cast<int>(),
gtkKeyNames: (map['names']['gtk'] as List<dynamic>)?.cast<String>(),
gtkKeyCodes: (map['keyCodes']['gtk'] as List<dynamic>)?.cast<int>(),
);
}
/// 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 list of Windows key codes matching this key, created by looking up the
/// Windows name in the Chromium data, and substituting the Windows key code
/// value.
List<int> windowsKeyCodes;
/// The list of names that Windows gives to this key (symbol names minus the
/// prefix).
List<String> windowsKeyNames;
/// The macOS scan code of the key from Chromium's header file.
int macOsScanCode;
/// The iOS scan code of the key from UIKey's documentation (USB Hid table)
int iosScanCode;
/// 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<String> 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<int> 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<int> androidScanCodes;
/// The list of names that GFLW gives to this key (symbol names minus the
/// prefix).
List<String> glfwKeyNames;
/// The list of GLFW key codes matching this key, created by looking up the
/// Linux name in the Chromium data, and substituting the GLFW key code
/// value.
List<int> glfwKeyCodes;
/// The list of names that GTK gives to this key (symbol names minus the
/// prefix).
List<String> gtkKeyNames;
/// The list of GTK key codes matching this key, created by looking up the
/// Linux name in the GTK data, and substituting the GTK key code
/// value.
List<int> gtkKeyCodes;
/// Creates a JSON map from the key data.
Map<String, dynamic> toJson() {
return <String, dynamic>{
'names': <String, dynamic>{
'domkey': name,
'android': androidKeyNames,
'english': commentName,
'chromium': chromiumName,
'glfw': glfwKeyNames,
'gtk': gtkKeyNames,
'windows': windowsKeyNames,
},
'scanCodes': <String, dynamic>{
'android': androidScanCodes,
'usb': usbHidCode,
'linux': linuxScanCode,
'xkb': xKbScanCode,
'windows': windowsScanCode,
'macos': macOsScanCode,
'ios': iosScanCode,
},
'keyCodes': <String, List<int>>{
'android': androidKeyCodes,
'glfw': glfwKeyCodes,
'gtk': gtkKeyCodes,
'windows': windowsKeyCodes,
},
};
}
/// 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);
}
static String getCommentName(String constantName) {
String upperCamel = lowerCamelToUpperCamel(constantName);
upperCamel = upperCamel.replaceAllMapped(RegExp(r'(Digit|Numpad|Lang|Button|Left|Right)([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 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 => getCommentName(constantName);
/// 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)}, """
'linuxScanCode: ${toHex(linuxScanCode)}, xKbScanCode: ${toHex(xKbScanCode)}, '
'windowsKeyCode: ${toHex(windowsScanCode)}, macOsScanCode: ${toHex(macOsScanCode)}, '
'windowsScanCode: ${toHex(windowsScanCode)}, chromiumSymbolName: $chromiumName '
'iOSScanCode: ${toHex(iosScanCode)})';
}
/// Returns the static map of printable representations.
static Map<String, String> get printable {
if (_printable == null) {
final String printableKeys = File(path.join(flutterRoot.path, 'dev', 'tools', 'gen_keycodes', 'data', 'printable.json',)).readAsStringSync();
final Map<String, dynamic> printable = json.decode(printableKeys) as Map<String, dynamic>;
_printable = printable.cast<String, String>();
}
return _printable;
}
static Map<String, String> _printable;
/// Returns the static map of synonym representations.
///
/// These include synonyms for keys which don't have printable
/// representations, and appear in more than one place on the keyboard (e.g.
/// SHIFT, ALT, etc.).
static Map<String, List<dynamic>> get synonyms {
if (_synonym == null) {
final String synonymKeys = File(path.join(flutterRoot.path, 'dev', 'tools', 'gen_keycodes', 'data', 'synonyms.json',)).readAsStringSync();
final Map<String, dynamic> synonym = json.decode(synonymKeys) as Map<String, dynamic>;
_synonym = synonym.cast<String, List<dynamic>>();
}
return _synonym;
}
static Map<String, List<dynamic>> _synonym;
/// 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;
/// The code prefix for pseudo-keys which represent collections of key synonyms.
static const int synonymPlane = 0x20000000000;
}
......@@ -5,39 +5,40 @@
import 'package:path/path.dart' as path;
import 'base_code_gen.dart';
import 'key_data.dart';
import 'constants.dart';
import 'logical_key_data.dart';
import 'physical_key_data.dart';
import 'utils.dart';
/// 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<String> words = input.split(RegExp(r'\s+'));
String currentLine = words.removeAt(0);
for (final 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();
String _wrapString(String input) {
return wrapString(input, prefix: ' /// ');
}
/// Generates the keyboard_keys.dart based on the information in the key data
class SynonymKeyInfo {
SynonymKeyInfo(this.keys, this.name);
final List<LogicalKeyEntry> keys;
final String name;
// Use the first item in the synonyms as a template for the ID to use.
// It won't end up being the same value because it'll be in the pseudo-key
// plane.
LogicalKeyEntry get primaryKey => keys[0];
int get value => (primaryKey.value & ~kVariationMask) + kSynonymPlane;
String get constantName => upperCamelToLowerCamel(name);
}
/// Generates the keyboard_key.dart based on the information in the key data
/// structure given to it.
class KeyboardKeysCodeGenerator extends BaseCodeGenerator {
KeyboardKeysCodeGenerator(KeyData keyData) : super(keyData);
KeyboardKeysCodeGenerator(PhysicalKeyData keyData, LogicalKeyData logicalData) : super(keyData, logicalData);
/// Gets the generated definitions of PhysicalKeyboardKeys.
String get _physicalDefinitions {
final StringBuffer definitions = StringBuffer();
for (final Key entry in keyData.data) {
for (final PhysicalKeyEntry entry in keyData.entries) {
final String firstComment = _wrapString('Represents the location of the '
'"${entry.commentName}" key on a generalized keyboard.');
final String otherComments = _wrapString('See the function '
......@@ -53,7 +54,7 @@ $otherComments static const PhysicalKeyboardKey ${entry.constantName} = Physica
String get _physicalDebugNames {
final StringBuffer result = StringBuffer();
for (final Key entry in keyData.data) {
for (final PhysicalKeyEntry entry in keyData.entries) {
result.write('''
${toHex(entry.usbHidCode, digits: 8)}: '${entry.commentName}',
''');
......@@ -64,7 +65,7 @@ $otherComments static const PhysicalKeyboardKey ${entry.constantName} = Physica
/// Gets the generated definitions of LogicalKeyboardKeys.
String get _logicalDefinitions {
final StringBuffer definitions = StringBuffer();
void printKey(int flutterId, String constantName, String commentName, {String otherComments}) {
void printKey(int flutterId, String constantName, String commentName, {String? otherComments}) {
final String firstComment = _wrapString('Represents the logical "$commentName" key on the keyboard.');
otherComments ??= _wrapString('See the function [RawKeyEvent.logicalKey] for more information.');
definitions.write('''
......@@ -74,22 +75,20 @@ $otherComments static const LogicalKeyboardKey $constantName = LogicalKeyboardK
''');
}
for (final Key entry in keyData.data) {
for (final LogicalKeyEntry entry in logicalData.entries) {
printKey(
entry.flutterId,
entry.value,
entry.constantName,
entry.commentName,
);
}
for (final String name in Key.synonyms.keys) {
for (final SynonymKeyInfo synonymInfo in synonyms) {
// Use the first item in the synonyms as a template for the ID to use.
// It won't end up being the same value because it'll be in the pseudo-key
// plane.
final Key entry = keyData.data.firstWhere((Key item) => item.name == Key.synonyms[name][0]);
final Set<String> unionNames = Key.synonyms[name].map<String>((dynamic name) {
return upperCamelToLowerCamel(name as String);
}).toSet();
printKey(Key.synonymPlane | entry.flutterId, name, Key.getCommentName(name),
final Set<String> unionNames = synonymInfo.keys.map(
(LogicalKeyEntry entry) => entry.constantName).toSet();
printKey(synonymInfo.value, synonymInfo.constantName, PhysicalKeyEntry.getCommentName(synonymInfo.name),
otherComments: _wrapString('This key represents the union of the keys '
'$unionNames when comparing keys. This key will never be generated '
'directly, its main use is in defining key maps.'));
......@@ -98,30 +97,26 @@ $otherComments static const LogicalKeyboardKey $constantName = LogicalKeyboardK
}
String get _logicalSynonyms {
final StringBuffer synonyms = StringBuffer();
for (final String name in Key.synonyms.keys) {
for (final String synonym in Key.synonyms[name].cast<String>()) {
final String keyName = upperCamelToLowerCamel(synonym);
synonyms.writeln(' $keyName: $name,');
final StringBuffer result = StringBuffer();
for (final SynonymKeyInfo synonymInfo in synonyms) {
for (final LogicalKeyEntry key in synonymInfo.keys) {
final String synonymName = upperCamelToLowerCamel(synonymInfo.name);
result.writeln(' ${key.constantName}: $synonymName,');
}
}
return synonyms.toString();
return result.toString();
}
String get _logicalKeyLabels {
final StringBuffer result = StringBuffer();
for (final Key entry in keyData.data) {
for (final LogicalKeyEntry entry in logicalData.entries) {
result.write('''
${toHex(entry.flutterId, digits: 11)}: '${entry.commentName}',
${toHex(entry.value, digits: 11)}: '${entry.commentName}',
''');
}
for (final String name in Key.synonyms.keys) {
// Use the first item in the synonyms as a template for the ID to use.
// It won't end up being the same value because it'll be in the pseudo-key
// plane.
final Key entry = keyData.data.firstWhere((Key item) => item.name == Key.synonyms[name][0]);
for (final SynonymKeyInfo synonymInfo in synonyms) {
result.write('''
${toHex(Key.synonymPlane | entry.flutterId, digits: 11)}: '${Key.getCommentName(name)}',
${toHex(synonymInfo.value)}: '${synonymInfo.name}',
''');
}
return result.toString();
......@@ -130,7 +125,7 @@ $otherComments static const LogicalKeyboardKey $constantName = LogicalKeyboardK
/// This generates the map of USB HID codes to physical keys.
String get _predefinedHidCodeMap {
final StringBuffer scanCodeMap = StringBuffer();
for (final Key entry in keyData.data) {
for (final PhysicalKeyEntry entry in keyData.entries) {
scanCodeMap.writeln(' ${toHex(entry.usbHidCode)}: ${entry.constantName},');
}
return scanCodeMap.toString().trimRight();
......@@ -139,35 +134,39 @@ $otherComments static const LogicalKeyboardKey $constantName = LogicalKeyboardK
/// This generates the map of Flutter key codes to logical keys.
String get _predefinedKeyCodeMap {
final StringBuffer keyCodeMap = StringBuffer();
for (final Key entry in keyData.data) {
keyCodeMap.writeln(' ${toHex(entry.flutterId, digits: 10)}: ${entry.constantName},');
for (final LogicalKeyEntry entry in logicalData.entries) {
keyCodeMap.writeln(' ${toHex(entry.value, digits: 11)}: ${entry.constantName},');
}
for (final String entry in Key.synonyms.keys) {
// Use the first item in the synonyms as a template for the ID to use.
// It won't end up being the same value because it'll be in the pseudo-key
// plane.
final Key primaryKey = keyData.data.firstWhere((Key item) {
return item.name == Key.synonyms[entry][0];
}, orElse: () => null);
assert(primaryKey != null);
keyCodeMap.writeln(' ${toHex(Key.synonymPlane | primaryKey.flutterId, digits: 10)}: $entry,');
for (final SynonymKeyInfo synonymInfo in synonyms) {
keyCodeMap.writeln(' ${toHex(synonymInfo.value, digits: 11)}: ${synonymInfo.constantName},');
}
return keyCodeMap.toString().trimRight();
}
@override
String get templatePath => path.join(flutterRoot.path, 'dev', 'tools', 'gen_keycodes', 'data', 'keyboard_key.tmpl');
String get templatePath => path.join(dataRoot, 'keyboard_key.tmpl');
@override
Map<String, String> mappings() {
return <String, String>{
'PHYSICAL_KEY_MAP': _predefinedHidCodeMap,
'LOGICAL_KEY_MAP': _predefinedKeyCodeMap,
'LOGICAL_KEY_DEFINITIONS': _logicalDefinitions,
'LOGICAL_KEY_SYNONYMS': _logicalSynonyms,
'LOGICAL_KEY_KEY_LABELS': _logicalKeyLabels,
'PHYSICAL_KEY_MAP': _predefinedHidCodeMap,
'PHYSICAL_KEY_DEFINITIONS': _physicalDefinitions,
'PHYSICAL_KEY_DEBUG_NAMES': _physicalDebugNames,
};
}
late final List<SynonymKeyInfo> synonyms = LogicalKeyData.synonyms.entries.map(
(MapEntry<String, List<String>> synonymDefinition) {
final List<LogicalKeyEntry> entries = synonymDefinition.value.map(
(String name) => logicalData.entryByName(name)).toList();
return SynonymKeyInfo(
entries,
synonymDefinition.key,
);
}
).toList();
}
......@@ -2,87 +2,101 @@
// 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 'base_code_gen.dart';
import 'key_data.dart';
import 'logical_key_data.dart';
import 'physical_key_data.dart';
import 'utils.dart';
bool _isAsciiLetter(String? char) {
if (char == null)
return false;
const int charUpperA = 0x41;
const int charUpperZ = 0x5A;
const int charLowerA = 0x61;
const int charLowerZ = 0x7A;
assert(char.length == 1);
final int charCode = char.codeUnitAt(0);
return (charCode >= charUpperA && charCode <= charUpperZ)
|| (charCode >= charLowerA && charCode <= charLowerZ);
}
/// Generates the keyboard_maps.dart files, based on the information in the key
/// data structure given to it.
class KeyboardMapsCodeGenerator extends BaseCodeGenerator {
KeyboardMapsCodeGenerator(KeyData keyData) : super(keyData);
KeyboardMapsCodeGenerator(PhysicalKeyData keyData, LogicalKeyData logicalData)
: super(keyData, logicalData);
List<Key> get numpadKeyData {
return keyData.data.where((Key entry) {
return entry.constantName.startsWith('numpad') && entry.keyLabel != null;
List<PhysicalKeyEntry> get _numpadKeyData {
return keyData.entries.where((PhysicalKeyEntry entry) {
return entry.constantName.startsWith('numpad') && LogicalKeyData.printable.containsKey(entry.name);
}).toList();
}
List<Key> get functionKeyData {
List<PhysicalKeyEntry> get _functionKeyData {
final RegExp functionKeyRe = RegExp(r'^f[0-9]+$');
return keyData.data.where((Key entry) {
return keyData.entries.where((PhysicalKeyEntry entry) {
return functionKeyRe.hasMatch(entry.constantName);
}).toList();
}
List<LogicalKeyEntry> get _numpadLogicalKeyData {
return logicalData.entries.where((LogicalKeyEntry entry) {
return entry.constantName.startsWith('numpad') && LogicalKeyData.printable.containsKey(entry.name);
}).toList();
}
/// This generates the map of GLFW number pad key codes to logical keys.
String get glfwNumpadMap {
String get _glfwNumpadMap {
final StringBuffer glfwNumpadMap = StringBuffer();
for (final Key entry in numpadKeyData) {
if (entry.glfwKeyCodes != null) {
for (final int code in entry.glfwKeyCodes.cast<int>()) {
for (final PhysicalKeyEntry entry in _numpadKeyData) {
for (final int code in entry.glfwKeyCodes) {
glfwNumpadMap.writeln(' $code: LogicalKeyboardKey.${entry.constantName},');
}
}
}
return glfwNumpadMap.toString().trimRight();
}
/// This generates the map of GLFW key codes to logical keys.
String get glfwKeyCodeMap {
String get _glfwKeyCodeMap {
final StringBuffer glfwKeyCodeMap = StringBuffer();
for (final Key entry in keyData.data) {
if (entry.glfwKeyCodes != null) {
for (final int code in entry.glfwKeyCodes.cast<int>()) {
for (final PhysicalKeyEntry entry in keyData.entries) {
for (final int code in entry.glfwKeyCodes) {
glfwKeyCodeMap.writeln(' $code: LogicalKeyboardKey.${entry.constantName},');
}
}
}
return glfwKeyCodeMap.toString().trimRight();
}
/// This generates the map of GTK number pad key codes to logical keys.
String get gtkNumpadMap {
final StringBuffer gtkNumpadMap = StringBuffer();
for (final Key entry in numpadKeyData) {
if (entry.gtkKeyCodes != null) {
for (final int code in entry.gtkKeyCodes.cast<int>()) {
gtkNumpadMap.writeln(' $code: LogicalKeyboardKey.${entry.constantName},');
String get _gtkNumpadMap {
final OutputLines<int> lines = OutputLines<int>('GTK numpad map');
for (final LogicalKeyEntry entry in _numpadLogicalKeyData) {
for (final int code in entry.gtkValues) {
lines.add(code, ' $code: LogicalKeyboardKey.${entry.constantName},');
}
}
}
return gtkNumpadMap.toString().trimRight();
return lines.sortedJoin().trimRight();
}
/// This generates the map of GTK key codes to logical keys.
String get gtkKeyCodeMap {
final StringBuffer gtkKeyCodeMap = StringBuffer();
for (final Key entry in keyData.data) {
if (entry.gtkKeyCodes != null) {
for (final int code in entry.gtkKeyCodes.cast<int>()) {
gtkKeyCodeMap.writeln(' $code: LogicalKeyboardKey.${entry.constantName},');
}
String get _gtkKeyCodeMap {
final OutputLines<int> lines = OutputLines<int>('GTK key code map');
for (final LogicalKeyEntry entry in logicalData.entries) {
for (final int code in entry.gtkValues) {
lines.add(code, ' $code: LogicalKeyboardKey.${entry.constantName},');
}
}
return gtkKeyCodeMap.toString().trimRight();
return lines.sortedJoin().trimRight();
}
/// This generates the map of XKB USB HID codes to physical keys.
String get xkbScanCodeMap {
String get _xkbScanCodeMap {
final StringBuffer xkbScanCodeMap = StringBuffer();
for (final Key entry in keyData.data) {
for (final PhysicalKeyEntry entry in keyData.entries) {
if (entry.xKbScanCode != null) {
xkbScanCodeMap.writeln(' ${toHex(entry.xKbScanCode)}: PhysicalKeyboardKey.${entry.constantName},');
}
......@@ -91,37 +105,33 @@ class KeyboardMapsCodeGenerator extends BaseCodeGenerator {
}
/// This generates the map of Android key codes to logical keys.
String get androidKeyCodeMap {
final StringBuffer androidKeyCodeMap = StringBuffer();
for (final Key entry in keyData.data) {
if (entry.androidKeyCodes != null) {
for (final int code in entry.androidKeyCodes.cast<int>()) {
androidKeyCodeMap.writeln(' $code: LogicalKeyboardKey.${entry.constantName},');
String get _androidKeyCodeMap {
final OutputLines<int> lines = OutputLines<int>('Android key code map');
for (final LogicalKeyEntry entry in logicalData.entries) {
for (final int code in entry.androidValues) {
lines.add(code, ' $code: LogicalKeyboardKey.${entry.constantName},');
}
}
}
return androidKeyCodeMap.toString().trimRight();
return lines.sortedJoin().trimRight();
}
/// This generates the map of Android number pad key codes to logical keys.
String get androidNumpadMap {
final StringBuffer androidKeyCodeMap = StringBuffer();
for (final Key entry in numpadKeyData) {
if (entry.androidKeyCodes != null) {
for (final int code in entry.androidKeyCodes.cast<int>()) {
androidKeyCodeMap.writeln(' $code: LogicalKeyboardKey.${entry.constantName},');
}
String get _androidNumpadMap {
final OutputLines<int> lines = OutputLines<int>('Android numpad map');
for (final LogicalKeyEntry entry in _numpadLogicalKeyData) {
for (final int code in entry.androidValues) {
lines.add(code, ' $code: LogicalKeyboardKey.${entry.constantName},');
}
}
return androidKeyCodeMap.toString().trimRight();
return lines.sortedJoin().trimRight();
}
/// This generates the map of Android scan codes to physical keys.
String get androidScanCodeMap {
String get _androidScanCodeMap {
final StringBuffer androidScanCodeMap = StringBuffer();
for (final Key entry in keyData.data) {
for (final PhysicalKeyEntry entry in keyData.entries) {
if (entry.androidScanCodes != null) {
for (final int code in entry.androidScanCodes.cast<int>()) {
for (final int code in entry.androidScanCodes) {
androidScanCodeMap.writeln(' $code: PhysicalKeyboardKey.${entry.constantName},');
}
}
......@@ -130,9 +140,9 @@ class KeyboardMapsCodeGenerator extends BaseCodeGenerator {
}
/// This generates the map of Windows scan codes to physical keys.
String get windowsScanCodeMap {
String get _windowsScanCodeMap {
final StringBuffer windowsScanCodeMap = StringBuffer();
for (final Key entry in keyData.data) {
for (final PhysicalKeyEntry entry in keyData.entries) {
if (entry.windowsScanCode != null) {
windowsScanCodeMap.writeln(' ${toHex(entry.windowsScanCode)}: PhysicalKeyboardKey.${entry.constantName},');
}
......@@ -141,35 +151,38 @@ class KeyboardMapsCodeGenerator extends BaseCodeGenerator {
}
/// This generates the map of Windows number pad key codes to logical keys.
String get windowsNumpadMap {
final StringBuffer windowsNumPadMap = StringBuffer();
for (final Key entry in numpadKeyData) {
if (entry.windowsKeyCodes != null){
for (final int code in entry.windowsKeyCodes) {
windowsNumPadMap.writeln(' $code: LogicalKeyboardKey.${entry.constantName},');
String get _windowsNumpadMap {
final OutputLines<int> lines = OutputLines<int>('Windows numpad map');
for (final LogicalKeyEntry entry in _numpadLogicalKeyData) {
for (final int code in entry.windowsValues) {
lines.add(code, ' $code: LogicalKeyboardKey.${entry.constantName},');
}
}
}
return windowsNumPadMap.toString().trimRight();
return lines.sortedJoin().trimRight();
}
/// This generates the map of Windows key codes to logical keys.
String get windowsKeyCodeMap {
final StringBuffer windowsKeyCodeMap = StringBuffer();
for (final Key entry in keyData.data) {
if (entry.windowsKeyCodes != null) {
for (final int code in entry.windowsKeyCodes) {
windowsKeyCodeMap.writeln(' $code: LogicalKeyboardKey.${entry.constantName},');
String get _windowsKeyCodeMap {
final OutputLines<int> lines = OutputLines<int>('Windows key code map');
for (final LogicalKeyEntry entry in logicalData.entries) {
// Letter keys on Windows are not recorded in logical_key_data.json,
// because they are not used by the embedding. Add them manually.
final List<int>? keyCodes = entry.windowsValues.isNotEmpty
? entry.windowsValues
: (_isAsciiLetter(entry.keyLabel) ? <int>[entry.keyLabel!.toUpperCase().codeUnitAt(0)] : null);
if (keyCodes != null) {
for (final int code in keyCodes) {
lines.add(code, ' $code: LogicalKeyboardKey.${entry.constantName},');
}
}
}
return windowsKeyCodeMap.toString().trimRight();
return lines.sortedJoin().trimRight();
}
/// This generates the map of macOS key codes to physical keys.
String get macOsScanCodeMap {
String get _macOsScanCodeMap {
final StringBuffer macOsScanCodeMap = StringBuffer();
for (final Key entry in keyData.data) {
for (final PhysicalKeyEntry entry in keyData.entries) {
if (entry.macOsScanCode != null) {
macOsScanCodeMap.writeln(' ${toHex(entry.macOsScanCode)}: PhysicalKeyboardKey.${entry.constantName},');
}
......@@ -178,9 +191,9 @@ class KeyboardMapsCodeGenerator extends BaseCodeGenerator {
}
/// This generates the map of macOS number pad key codes to logical keys.
String get macOsNumpadMap {
String get _macOsNumpadMap {
final StringBuffer macOsNumPadMap = StringBuffer();
for (final Key entry in numpadKeyData) {
for (final PhysicalKeyEntry entry in _numpadKeyData) {
if (entry.macOsScanCode != null) {
macOsNumPadMap.writeln(' ${toHex(entry.macOsScanCode)}: LogicalKeyboardKey.${entry.constantName},');
}
......@@ -188,9 +201,9 @@ class KeyboardMapsCodeGenerator extends BaseCodeGenerator {
return macOsNumPadMap.toString().trimRight();
}
String get macOsFunctionKeyMap {
String get _macOsFunctionKeyMap {
final StringBuffer macOsFunctionKeyMap = StringBuffer();
for (final Key entry in functionKeyData) {
for (final PhysicalKeyEntry entry in _functionKeyData) {
if (entry.macOsScanCode != null) {
macOsFunctionKeyMap.writeln(' ${toHex(entry.macOsScanCode)}: LogicalKeyboardKey.${entry.constantName},');
}
......@@ -198,43 +211,65 @@ class KeyboardMapsCodeGenerator extends BaseCodeGenerator {
return macOsFunctionKeyMap.toString().trimRight();
}
/// This generates the map of macOS key codes to physical keys.
String get _macOsKeyCodeMap {
final OutputLines<int> lines = OutputLines<int>('MacOS key code map');
for (final LogicalKeyEntry entry in logicalData.entries) {
for (final int code in entry.macOsKeyCodeValues) {
lines.add(code, ' $code: LogicalKeyboardKey.${entry.constantName},');
}
}
return lines.sortedJoin().trimRight();
}
/// This generates the map of iOS key codes to physical keys.
String get iosScanCodeMap {
final StringBuffer iosScanCodeMap = StringBuffer();
for (final Key entry in keyData.data) {
String get _iosScanCodeMap {
final OutputLines<int> lines = OutputLines<int>('iOS scancode map');
for (final PhysicalKeyEntry entry in keyData.entries) {
if (entry.iosScanCode != null) {
iosScanCodeMap.writeln(' ${toHex(entry.iosScanCode)}: PhysicalKeyboardKey.${entry.constantName},');
lines.add(entry.iosScanCode!, ' ${toHex(entry.iosScanCode)}: PhysicalKeyboardKey.${entry.constantName},');
}
}
return iosScanCodeMap.toString().trimRight();
return lines.sortedJoin().trimRight();
}
/// This generates the map of iOS number pad key codes to logical keys.
String get iosNumpadMap {
final StringBuffer iosNumPadMap = StringBuffer();
for (final Key entry in numpadKeyData) {
String get _iosNumpadMap {
final OutputLines<int> lines = OutputLines<int>('iOS numpad map');
for (final PhysicalKeyEntry entry in _numpadKeyData) {
if (entry.iosScanCode != null) {
iosNumPadMap.writeln(' ${toHex(entry.iosScanCode)}: LogicalKeyboardKey.${entry.constantName},');
lines.add(entry.iosScanCode!,' ${toHex(entry.iosScanCode)}: LogicalKeyboardKey.${entry.constantName},');
}
}
return iosNumPadMap.toString().trimRight();
return lines.sortedJoin().trimRight();
}
/// This generates the map of macOS key codes to physical keys.
String get _iosKeyCodeMap {
final OutputLines<int> lines = OutputLines<int>('iOS key code map');
for (final LogicalKeyEntry entry in logicalData.entries) {
for (final int code in entry.iosKeyCodeValues) {
lines.add(code, ' $code: LogicalKeyboardKey.${entry.constantName},');
}
}
return lines.sortedJoin().trimRight();
}
/// This generates the map of Fuchsia key codes to logical keys.
String get fuchsiaKeyCodeMap {
final StringBuffer fuchsiaKeyCodeMap = StringBuffer();
for (final Key entry in keyData.data) {
if (entry.usbHidCode != null) {
fuchsiaKeyCodeMap.writeln(' ${toHex(entry.flutterId)}: LogicalKeyboardKey.${entry.constantName},');
String get _fuchsiaKeyCodeMap {
final OutputLines<int> lines = OutputLines<int>('Fuchsia key code map');
for (final LogicalKeyEntry entry in logicalData.entries) {
for (final int value in entry.fuchsiaValues) {
lines.add(value, ' ${toHex(value)}: LogicalKeyboardKey.${entry.constantName},');
}
}
return fuchsiaKeyCodeMap.toString().trimRight();
return lines.sortedJoin().trimRight();
}
/// This generates the map of Fuchsia USB HID codes to physical keys.
String get fuchsiaHidCodeMap {
String get _fuchsiaHidCodeMap {
final StringBuffer fuchsiaScanCodeMap = StringBuffer();
for (final Key entry in keyData.data) {
for (final PhysicalKeyEntry entry in keyData.entries) {
if (entry.usbHidCode != null) {
fuchsiaScanCodeMap.writeln(' ${toHex(entry.usbHidCode)}: PhysicalKeyboardKey.${entry.constantName},');
}
......@@ -243,20 +278,20 @@ class KeyboardMapsCodeGenerator extends BaseCodeGenerator {
}
/// This generates the map of Web KeyboardEvent codes to logical keys.
String get webLogicalKeyMap {
final StringBuffer result = StringBuffer();
for (final Key entry in keyData.data) {
if (entry.name != null) {
result.writeln(" '${entry.name}': LogicalKeyboardKey.${entry.constantName},");
String get _webLogicalKeyMap {
final OutputLines<String> lines = OutputLines<String>('Web logical key map');
for (final LogicalKeyEntry entry in logicalData.entries) {
for (final String name in entry.webNames) {
lines.add(name, " '$name': LogicalKeyboardKey.${entry.constantName},");
}
}
return result.toString().trimRight();
return lines.sortedJoin().trimRight();
}
/// This generates the map of Web KeyboardEvent codes to physical keys.
String get webPhysicalKeyMap {
String get _webPhysicalKeyMap {
final StringBuffer result = StringBuffer();
for (final Key entry in keyData.data) {
for (final PhysicalKeyEntry entry in keyData.entries) {
if (entry.name != null) {
result.writeln(" '${entry.name}': PhysicalKeyboardKey.${entry.constantName},");
}
......@@ -264,10 +299,9 @@ class KeyboardMapsCodeGenerator extends BaseCodeGenerator {
return result.toString().trimRight();
}
/// This generates the map of Web number pad codes to logical keys.
String get webNumpadMap {
String get _webNumpadMap {
final StringBuffer result = StringBuffer();
for (final Key entry in numpadKeyData) {
for (final LogicalKeyEntry entry in _numpadLogicalKeyData) {
if (entry.name != null) {
result.writeln(" '${entry.name}': LogicalKeyboardKey.${entry.constantName},");
}
......@@ -275,33 +309,51 @@ class KeyboardMapsCodeGenerator extends BaseCodeGenerator {
return result.toString().trimRight();
}
/// This generates the map of Web number pad codes to logical keys.
String get _webLocationMap {
final String jsonRaw = File(path.join(dataRoot, 'web_logical_location_mapping.json')).readAsStringSync();
final Map<String, List<String?>> locationMap = parseMapOfListOfNullableString(jsonRaw);
final StringBuffer result = StringBuffer();
locationMap.forEach((String key, List<String?> keyNames) {
final String keyStrings = keyNames.map((String? keyName) {
final String? constantName = keyName == null ? null : logicalData.entryByName(keyName).constantName;
return constantName != null ? 'LogicalKeyboardKey.$constantName' : 'null';
}).join(', ');
result.writeln(" '$key': <LogicalKeyboardKey?>[$keyStrings],");
});
return result.toString().trimRight();
}
@override
String get templatePath => path.join(flutterRoot.path, 'dev', 'tools', 'gen_keycodes', 'data', 'keyboard_maps.tmpl');
String get templatePath => path.join(dataRoot, 'keyboard_maps.tmpl');
@override
Map<String, String> mappings() {
return <String, String>{
'ANDROID_SCAN_CODE_MAP': androidScanCodeMap,
'ANDROID_KEY_CODE_MAP': androidKeyCodeMap,
'ANDROID_NUMPAD_MAP': androidNumpadMap,
'FUCHSIA_SCAN_CODE_MAP': fuchsiaHidCodeMap,
'FUCHSIA_KEY_CODE_MAP': fuchsiaKeyCodeMap,
'MACOS_SCAN_CODE_MAP': macOsScanCodeMap,
'MACOS_NUMPAD_MAP': macOsNumpadMap,
'MACOS_FUNCTION_KEY_MAP': macOsFunctionKeyMap,
'IOS_SCAN_CODE_MAP': iosScanCodeMap,
'IOS_NUMPAD_MAP': iosNumpadMap,
'GLFW_KEY_CODE_MAP': glfwKeyCodeMap,
'GLFW_NUMPAD_MAP': glfwNumpadMap,
'GTK_KEY_CODE_MAP': gtkKeyCodeMap,
'GTK_NUMPAD_MAP': gtkNumpadMap,
'XKB_SCAN_CODE_MAP': xkbScanCodeMap,
'WEB_LOGICAL_KEY_MAP': webLogicalKeyMap,
'WEB_PHYSICAL_KEY_MAP': webPhysicalKeyMap,
'WEB_NUMPAD_MAP': webNumpadMap,
'WINDOWS_LOGICAL_KEY_MAP': windowsKeyCodeMap,
'WINDOWS_PHYSICAL_KEY_MAP': windowsScanCodeMap,
'WINDOWS_NUMPAD_MAP': windowsNumpadMap,
'ANDROID_SCAN_CODE_MAP': _androidScanCodeMap,
'ANDROID_KEY_CODE_MAP': _androidKeyCodeMap,
'ANDROID_NUMPAD_MAP': _androidNumpadMap,
'FUCHSIA_SCAN_CODE_MAP': _fuchsiaHidCodeMap,
'FUCHSIA_KEY_CODE_MAP': _fuchsiaKeyCodeMap,
'MACOS_SCAN_CODE_MAP': _macOsScanCodeMap,
'MACOS_NUMPAD_MAP': _macOsNumpadMap,
'MACOS_FUNCTION_KEY_MAP': _macOsFunctionKeyMap,
'MACOS_KEY_CODE_MAP': _macOsKeyCodeMap,
'IOS_SCAN_CODE_MAP': _iosScanCodeMap,
'IOS_NUMPAD_MAP': _iosNumpadMap,
'IOS_KEY_CODE_MAP': _iosKeyCodeMap,
'GLFW_KEY_CODE_MAP': _glfwKeyCodeMap,
'GLFW_NUMPAD_MAP': _glfwNumpadMap,
'GTK_KEY_CODE_MAP': _gtkKeyCodeMap,
'GTK_NUMPAD_MAP': _gtkNumpadMap,
'XKB_SCAN_CODE_MAP': _xkbScanCodeMap,
'WEB_LOGICAL_KEY_MAP': _webLogicalKeyMap,
'WEB_PHYSICAL_KEY_MAP': _webPhysicalKeyMap,
'WEB_NUMPAD_MAP': _webNumpadMap,
'WEB_LOCATION_MAP': _webLocationMap,
'WINDOWS_LOGICAL_KEY_MAP': _windowsKeyCodeMap,
'WINDOWS_PHYSICAL_KEY_MAP': _windowsScanCodeMap,
'WINDOWS_NUMPAD_MAP': _windowsNumpadMap,
};
}
}
// Copyright 2014 The Flutter 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:gen_keycodes/utils.dart';
import 'constants.dart';
import 'physical_key_data.dart';
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);
}
/// A pair of strings that represents left and right modifiers.
class _ModifierPair {
const _ModifierPair(this.left, this.right);
final String left;
final String right;
}
List<T> _toNonEmptyArray<T>(dynamic source) {
final List<dynamic>? dynamicNullableList = source as List<dynamic>?;
final List<dynamic> dynamicList = dynamicNullableList ?? <dynamic>[];
return dynamicList.cast<T>();
}
/// 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
/// [LogicalKeyData.fromJson] constructor and [toJson] method, respectively.
class LogicalKeyData {
factory LogicalKeyData(
String chromiumKeys,
String gtkKeyCodeHeader,
String gtkNameMap,
String windowsKeyCodeHeader,
String windowsNameMap,
String androidKeyCodeHeader,
String androidNameMap,
String macosLogicalToPhysical,
String iosLogicalToPhysical,
PhysicalKeyData physicalKeyData,
) {
final Map<String, LogicalKeyEntry> data = <String, LogicalKeyEntry>{};
_readKeyEntries(data, chromiumKeys);
_readWindowsKeyCodes(data, windowsKeyCodeHeader, parseMapOfListOfString(windowsNameMap));
_readGtkKeyCodes(data, gtkKeyCodeHeader, parseMapOfListOfString(gtkNameMap));
_readAndroidKeyCodes(data, androidKeyCodeHeader, parseMapOfListOfString(androidNameMap));
_readMacOsKeyCodes(data, physicalKeyData, parseMapOfListOfString(macosLogicalToPhysical));
_readIosKeyCodes(data, physicalKeyData, parseMapOfListOfString(iosLogicalToPhysical));
_readFuchsiaKeyCodes(data, physicalKeyData);
// Sort entries by value
final List<MapEntry<String, LogicalKeyEntry>> sortedEntries = data.entries.toList()..sort(
(MapEntry<String, LogicalKeyEntry> a, MapEntry<String, LogicalKeyEntry> b) =>
LogicalKeyEntry.compareByValue(a.value, b.value),
);
data
..clear()
..addEntries(sortedEntries);
return LogicalKeyData._(data);
}
/// Parses the given JSON data and populates the data structure from it.
factory LogicalKeyData.fromJson(Map<String, dynamic> contentMap) {
final Map<String, LogicalKeyEntry> data = <String, LogicalKeyEntry>{};
data.addEntries(contentMap.values.map((dynamic value) {
final LogicalKeyEntry entry = LogicalKeyEntry.fromJsonMapEntry(value as Map<String, dynamic>);
return MapEntry<String, LogicalKeyEntry>(entry.name, entry);
}));
return LogicalKeyData._(data);
}
/// Parses the input data given in from the various data source files,
/// populating the data structure.
///
/// None of the parameters may be null.
LogicalKeyData._(this._data);
/// Converts the data structure into a JSON structure that can be parsed by
/// [LogicalKeyData.fromJson].
Map<String, dynamic> toJson() {
final Map<String, dynamic> outputMap = <String, dynamic>{};
for (final LogicalKeyEntry entry in _data.values) {
outputMap[entry.name] = entry.toJson();
}
return outputMap;
}
/// Find an entry from name.
///
/// Asserts if the name is not found.
LogicalKeyEntry entryByName(String name) {
assert(_data.containsKey(name),
'Unable to find logical entry by name $name.');
return _data[name]!;
}
/// All entries.
Iterable<LogicalKeyEntry> get entries => _data.values;
// Keys mapped from their names.
final Map<String, LogicalKeyEntry> _data;
/// Parses entries from Chromium's key mapping header file.
///
/// Lines in this file look like either of these (without the ///):
/// Key Enum Unicode code point
/// DOM_KEY_UNI("Backspace", BACKSPACE, 0x0008),
/// Key Enum Value
/// DOM_KEY_MAP("Accel", ACCEL, 0x0101),
///
/// Flutter's supplemental_key_data.inc also has a new format
/// that uses a character as the 3rd argument.
/// Key Enum Character
/// DOM_KEY_UNI("KeyB", KEY_B, 'b'),
static void _readKeyEntries(Map<String, LogicalKeyEntry> data, String input) {
final Map<String, String> unusedNumpad = Map<String, String>.from(_printableToNumpads);
final RegExp domKeyRegExp = RegExp(
r'DOM_KEY_(?<kind>UNI|MAP)\s*\(\s*'
r'"(?<name>[^\s]+?)",\s*'
r'(?<enum>[^\s]+?),\s*'
r"(?:0[xX](?<unicode>[a-fA-F0-9]+)|'(?<char>.)')\s*"
r'\)',
// Multiline is necessary because some definitions spread across
// multiple lines.
multiLine: true,
);
final RegExp commentRegExp = RegExp(r'//.*$', multiLine: true);
input = input.replaceAll(commentRegExp, '');
for (final RegExpMatch match in domKeyRegExp.allMatches(input)) {
final String webName = match.namedGroup('name')!;
// ".AltGraphLatch" is consumed internally and not expressed to the Web.
if (webName.startsWith('.')) {
continue;
}
final String name = LogicalKeyEntry.computeName(webName.replaceAll(RegExp('[^A-Za-z0-9]'), ''));
final int value = match.namedGroup('unicode') != null ?
getHex(match.namedGroup('unicode')!) :
match.namedGroup('char')!.codeUnitAt(0);
final String? keyLabel = match.namedGroup('kind')! == 'UNI' ? String.fromCharCode(value) : null;
// If it's a modifier key, add left and right keys instead.
// Don't add web names and values; they're solved with locations.
if (_chromeModifiers.containsKey(name)) {
final _ModifierPair pair = _chromeModifiers[name]!;
data[pair.left] = LogicalKeyEntry.fromName(
value: value + kLeftModifierPlane,
name: pair.left,
keyLabel: null, // Modifier keys don't have keyLabels
)..webNames.add(pair.left)
..webValues.add(value + kLeftModifierPlane);
data[pair.right] = LogicalKeyEntry.fromName(
value: value + kRightModifierPlane,
name: pair.right,
keyLabel: null, // Modifier keys don't have keyLabels
)..webNames.add(pair.right)
..webValues.add(value + kRightModifierPlane);
continue;
}
// If it has a numpad counterpart, also add the numpad key.
final String? char = value < 256 ? String.fromCharCode(value) : null;
if (char != null && _printableToNumpads.containsKey(char)) {
final String numpadName = _printableToNumpads[char]!;
data[numpadName] = LogicalKeyEntry.fromName(
value: char.codeUnitAt(0) + kNumpadPlane,
name: numpadName,
keyLabel: null, // Don't add keyLabel for numpad counterparts
)..webNames.add(numpadName)
..webValues.add(char.codeUnitAt(0) + kNumpadPlane);
unusedNumpad.remove(char);
}
data.putIfAbsent(name, () {
final bool isPrintable = (keyLabel != null && !_isControlCharacter(keyLabel))
|| printable.containsKey(name)
|| value == 0; // "None" key
return LogicalKeyEntry.fromName(
value: value + (isPrintable ? kUnicodePlane : kUnprintablePlane),
name: name,
keyLabel: keyLabel,
)..webNames.add(webName)
..webValues.add(value);
});
}
// Make sure every Numpad key that we care about has been defined.
unusedNumpad.forEach((String key, String value) {
print('Unuadded numpad key $value');
});
}
static void _readMacOsKeyCodes(
Map<String, LogicalKeyEntry> data,
PhysicalKeyData physicalKeyData,
Map<String, List<String>> logicalToPhysical,
) {
final Map<String, String> physicalToLogical = reverseMapOfListOfString(logicalToPhysical,
(String logicalKeyName, String physicalKeyName) { print('Duplicate logical key name $logicalKeyName for macOS'); });
physicalToLogical.forEach((String physicalKeyName, String logicalKeyName) {
final PhysicalKeyEntry physicalEntry = physicalKeyData.entryByName(physicalKeyName);
assert(physicalEntry.macOsScanCode != null,
'Physical entry $physicalKeyName does not have a macOsScanCode.');
final LogicalKeyEntry? logicalEntry = data[logicalKeyName];
assert(logicalEntry != null,
'Unable to find logical entry by name $logicalKeyName.');
logicalEntry!.macOsKeyCodeNames.add(physicalEntry.name);
logicalEntry.macOsKeyCodeValues.add(physicalEntry.macOsScanCode!);
});
}
static void _readIosKeyCodes(
Map<String, LogicalKeyEntry> data,
PhysicalKeyData physicalKeyData,
Map<String, List<String>> logicalToPhysical,
) {
final Map<String, String> physicalToLogical = reverseMapOfListOfString(logicalToPhysical,
(String logicalKeyName, String physicalKeyName) { print('Duplicate logical key name $logicalKeyName for iOS'); });
physicalToLogical.forEach((String physicalKeyName, String logicalKeyName) {
final PhysicalKeyEntry physicalEntry = physicalKeyData.entryByName(physicalKeyName);
assert(physicalEntry.iosScanCode != null,
'Physical entry $physicalKeyName does not have an iosScanCode.');
final LogicalKeyEntry? logicalEntry = data[logicalKeyName];
assert(logicalEntry != null,
'Unable to find logical entry by name $logicalKeyName.');
logicalEntry!.iosKeyCodeNames.add(physicalEntry.name);
logicalEntry.iosKeyCodeValues.add(physicalEntry.iosScanCode!);
});
}
/// Parses entries from GTK's gdkkeysyms.h key code data file.
///
/// Lines in this file look like this (without the ///):
/// /** Space key. */
/// #define GDK_KEY_space 0x020
static void _readGtkKeyCodes(Map<String, LogicalKeyEntry> data, String headerFile, Map<String, List<String>> nameToGtkName) {
final RegExp definedCodes = RegExp(
r'#define '
r'GDK_KEY_(?<name>[a-zA-Z0-9_]+)\s*'
r'0x(?<value>[0-9a-f]+),?',
);
final Map<String, String> gtkNameToFlutterName = reverseMapOfListOfString(nameToGtkName,
(String flutterName, String gtkName) { print('Duplicate GTK logical name $gtkName'); });
for (final RegExpMatch match in definedCodes.allMatches(headerFile)) {
final String gtkName = match.namedGroup('name')!;
final String? name = gtkNameToFlutterName[gtkName];
final int value = int.parse(match.namedGroup('value')!, radix: 16);
if (name == null) {
// print('Unmapped GTK logical entry $gtkName');
continue;
}
final LogicalKeyEntry? entry = data[name];
if (entry == null) {
print('Invalid logical entry by name $name (from GTK $gtkName)');
continue;
}
entry
..gtkNames.add(gtkName)
..gtkValues.add(value);
}
}
static void _readWindowsKeyCodes(Map<String, LogicalKeyEntry> data, String headerFile, Map<String, List<String>> nameMap) {
// The mapping from the Flutter name (e.g. "enter") to the Windows name (e.g.
// "RETURN").
final Map<String, String> nameToFlutterName = reverseMapOfListOfString(nameMap,
(String flutterName, String windowsName) { print('Duplicate Windows logical name $windowsName'); });
final RegExp definedCodes = RegExp(
r'define '
r'VK_(?<name>[A-Z0-9_]+)\s*'
r'(?<value>[A-Z0-9_x]+),?',
);
for (final RegExpMatch match in definedCodes.allMatches(headerFile)) {
final String windowsName = match.namedGroup('name')!;
final String? name = nameToFlutterName[windowsName];
final int value = int.tryParse(match.namedGroup('value')!)!;
if (name == null) {
print('Unmapped Windows logical entry $windowsName');
continue;
}
final LogicalKeyEntry? entry = data[name];
if (entry == null) {
print('Invalid logical entry by name $name (from Windows $windowsName)');
continue;
}
addNameValue(
entry.windowsNames,
entry.windowsValues,
windowsName,
value,
);
}
}
/// 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,
static void _readAndroidKeyCodes(Map<String, LogicalKeyEntry> data, String headerFile, Map<String, List<String>> nameMap) {
final Map<String, String> nameToFlutterName = reverseMapOfListOfString(nameMap,
(String flutterName, String androidName) { print('Duplicate Android logical name $androidName'); });
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_(?<name>[A-Z0-9_]+)\s*'
r'=\s*'
r'(?<value>[0-9]+),?',
);
for (final RegExpMatch match in enumEntry.allMatches(headerFile)) {
final String androidName = match.namedGroup('name')!;
final String? name = nameToFlutterName[androidName];
final int value = int.tryParse(match.namedGroup('value')!)!;
if (name == null) {
print('Unmapped Android logical entry $androidName');
continue;
}
final LogicalKeyEntry? entry = data[name];
if (entry == null) {
print('Invalid logical entry by name $name (from Android $androidName)');
continue;
}
entry
..androidNames.add(androidName)
..androidValues.add(value);
}
}
static void _readFuchsiaKeyCodes(Map<String, LogicalKeyEntry> data, PhysicalKeyData physicalData) {
for (final LogicalKeyEntry entry in data.values) {
final int? value = (() {
if (entry.value == 0) // "None" key
return 0;
final String? keyLabel = printable[entry.constantName];
if (keyLabel != null && !entry.constantName.startsWith('numpad')) {
return kUnicodePlane | (keyLabel.codeUnitAt(0) & kValueMask);
} else {
final PhysicalKeyEntry? physicalEntry = physicalData.tryEntryByName(entry.name);
if (physicalEntry != null) {
return kHidPlane | (physicalEntry.usbHidCode & kValueMask);
}
}
})();
if (value != null)
entry.fuchsiaValues.add(value);
}
}
// Map Web key to the pair of key names
static late final Map<String, _ModifierPair> _chromeModifiers = () {
final String rawJson = File(path.join(dataRoot, 'chromium_modifiers.json',)).readAsStringSync();
return (json.decode(rawJson) as Map<String, dynamic>).map((String key, dynamic value) {
final List<dynamic> pair = value as List<dynamic>;
return MapEntry<String, _ModifierPair>(key, _ModifierPair(pair[0] as String, pair[1] as String));
});
}();
/// Returns the static map of printable representations.
static late final Map<String, String> printable = ((){
final String printableKeys = File(path.join(dataRoot, 'printable.json',)).readAsStringSync();
return (json.decode(printableKeys) as Map<String, dynamic>)
.cast<String, String>();
})();
// Map printable to corresponding numpad key name
static late final Map<String, String> _printableToNumpads = () {
final String rawJson = File(path.join(dataRoot, 'printable_to_numpads.json',)).readAsStringSync();
return (json.decode(rawJson) as Map<String, dynamic>).map((String key, dynamic value) {
return MapEntry<String, String>(key, value as String);
});
}();
/// Returns the static map of synonym representations.
///
/// These include synonyms for keys which don't have printable
/// representations, and appear in more than one place on the keyboard (e.g.
/// SHIFT, ALT, etc.).
static late final Map<String, List<String>> synonyms = ((){
final String synonymKeys = File(path.join(dataRoot, 'synonyms.json',)).readAsStringSync();
final Map<String, dynamic> dynamicSynonym = json.decode(synonymKeys) as Map<String, dynamic>;
return dynamicSynonym.map((String name, dynamic values) {
// The keygen and algorithm of macOS relies on synonyms being pairs.
// See siblingKeyMap in macos_code_gen.dart.
final List<String> names = (values as List<dynamic>).whereType<String>().toList();
assert(names.length == 2);
return MapEntry<String, List<String>>(name, names);
});
})();
}
/// A single entry in the key data structure.
///
/// Can be read from JSON with the [LogicalKeyEntry.fromJsonMapEntry] constructor, or
/// written with the [toJson] method.
class LogicalKeyEntry {
/// Creates a single key entry from available data.
LogicalKeyEntry({
required this.value,
required this.name,
this.keyLabel,
}) : webNames = <String>[],
webValues = <int>[],
macOsKeyCodeNames = <String>[],
macOsKeyCodeValues = <int>[],
iosKeyCodeNames = <String>[],
iosKeyCodeValues = <int>[],
gtkNames = <String>[],
gtkValues = <int>[],
windowsNames = <String>[],
windowsValues = <int>[],
androidNames = <String>[],
androidValues = <int>[],
fuchsiaValues = <int>[];
LogicalKeyEntry.fromName({
required int value,
required String name,
String? keyLabel,
}) : this(
value: value,
name: name,
keyLabel: keyLabel,
);
/// Populates the key from a JSON map.
LogicalKeyEntry.fromJsonMapEntry(Map<String, dynamic> map)
: value = map['value'] as int,
name = map['name'] as String,
webNames = _toNonEmptyArray<String>(map['names']['web']),
webValues = _toNonEmptyArray<int>(map['values']['web']),
macOsKeyCodeNames = _toNonEmptyArray<String>(map['names']['macOs']),
macOsKeyCodeValues = _toNonEmptyArray<int>(map['values']['macOs']),
iosKeyCodeNames = _toNonEmptyArray<String>(map['names']['ios']),
iosKeyCodeValues = _toNonEmptyArray<int>(map['values']['ios']),
gtkNames = _toNonEmptyArray<String>(map['names']['gtk']),
gtkValues = _toNonEmptyArray<int>(map['values']['gtk']),
windowsNames = _toNonEmptyArray<String>(map['names']['windows']),
windowsValues = _toNonEmptyArray<int>(map['values']['windows']),
androidNames = _toNonEmptyArray<String>(map['names']['android']),
androidValues = _toNonEmptyArray<int>(map['values']['android']),
fuchsiaValues = _toNonEmptyArray<int>(map['values']['fuchsia']),
keyLabel = map['keyLabel'] as String?;
final int value;
final String name;
/// The name of the key suitable for placing in comments.
String get commentName => computeCommentName(name);
String get constantName => computeConstantName(commentName);
/// 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.
final List<String> webNames;
/// The value of the key.
final List<int> webValues;
/// The names of the key codes that corresponds to this logical key on macOS,
/// created from the corresponding physical keys.
final List<String> macOsKeyCodeNames;
/// The key codes that corresponds to this logical key on macOS, created from
/// the physical key list substituted with the key mapping.
final List<int> macOsKeyCodeValues;
/// The names of the key codes that corresponds to this logical key on iOS,
/// created from the corresponding physical keys.
final List<String> iosKeyCodeNames;
/// The key codes that corresponds to this logical key on iOS, created from the
/// physical key list substituted with the key mapping.
final List<int> iosKeyCodeValues;
/// The list of names that GTK gives to this key (symbol names minus the
/// prefix).
final List<String> gtkNames;
/// The list of GTK key codes matching this key, created by looking up the
/// Linux name in the GTK data, and substituting the GTK key code
/// value.
final List<int> gtkValues;
/// The list of names that Windows gives to this key (symbol names minus the
/// prefix).
final List<String> windowsNames;
/// The list of Windows key codes matching this key, created by looking up the
/// Windows name in the Chromium data, and substituting the Windows key code
/// value.
final List<int> windowsValues;
/// The list of names that Android gives to this key (symbol names minus the
/// prefix).
final List<String> androidNames;
/// 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.
final List<int> androidValues;
final List<int> fuchsiaValues;
/// A string indicating the letter on the keycap of a letter key.
///
/// This is only used to generate the key label mapping in keyboard_map.dart.
/// [LogicalKeyboardKey.keyLabel] uses a different definition and is generated
/// differently.
final String? keyLabel;
/// Creates a JSON map from the key data.
Map<String, dynamic> toJson() {
return removeEmptyValues(<String, dynamic>{
'name': name,
'value': value,
'keyLabel': keyLabel,
'names': <String, dynamic>{
'web': webNames,
'macOs': macOsKeyCodeNames,
'ios': iosKeyCodeNames,
'gtk': gtkNames,
'windows': windowsNames,
'android': androidNames,
},
'values': <String, List<int>>{
'web': webValues,
'macOs': macOsKeyCodeValues,
'ios': iosKeyCodeValues,
'gtk': gtkValues,
'windows': windowsValues,
'android': androidValues,
'fuchsia': fuchsiaValues,
},
});
}
@override
String toString() {
return "'$name': (value: ${toHex(value)}) ";
}
/// Gets the named used for the key constant in the definitions in
/// keyboard_key.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).
static String computeName(String rawName) {
final String result = rawName.replaceAll('PinP', 'PInP');
if (kDartReservedWords.contains(result)) {
return '${result}Key';
}
return result;
}
/// Takes the [name] and converts it from lower camel case to capitalized
/// separate words (e.g. "wakeUp" converts to "Wake Up").
static String computeCommentName(String name) {
final String replaced = name.replaceAllMapped(
RegExp(r'(Digit|Numpad|Lang|Button|Left|Right)([0-9]+)'), (Match match) => '${match.group(1)} ${match.group(2)}',
);
return replaced
// 'fooBar' => 'foo Bar', 'fooBAR' => 'foo BAR'
.replaceAllMapped(RegExp(r'([^A-Z])([A-Z])'), (Match match) => '${match.group(1)} ${match.group(2)}')
// 'ABCDoo' => 'ABC Doo'
.replaceAllMapped(RegExp(r'([A-Z])([A-Z])([a-z])'), (Match match) => '${match.group(1)} ${match.group(2)}${match.group(3)}')
// 'AB1' => 'AB 1', 'F1' => 'F1'
.replaceAllMapped(RegExp(r'([A-Z]{2,})([0-9])'), (Match match) => '${match.group(1)} ${match.group(2)}')
// 'Foo1' => 'Foo 1'
.replaceAllMapped(RegExp(r'([a-z])([0-9])'), (Match match) => '${match.group(1)} ${match.group(2)}')
.trim();
}
static String computeConstantName(String commentName) {
// Convert the first word in the comment name.
final String lowerCamelSpace = commentName.replaceFirstMapped(RegExp(r'^[^ ]+'),
(Match match) => match[0]!.toLowerCase(),
);
final String result = lowerCamelSpace.replaceAll(' ', '');
if (kDartReservedWords.contains(result)) {
return '${result}Key';
}
return result;
}
static int compareByValue(LogicalKeyEntry a, LogicalKeyEntry b) =>
a.value.compareTo(b.value);
}
......@@ -5,52 +5,108 @@
import 'package:path/path.dart' as path;
import 'base_code_gen.dart';
import 'key_data.dart';
import 'constants.dart';
import 'logical_key_data.dart';
import 'physical_key_data.dart';
import 'utils.dart';
const List<String> kModifiersOfInterest = <String>[
'ShiftLeft',
'ShiftRight',
'ControlLeft',
'ControlRight',
'AltLeft',
'AltRight',
'MetaLeft',
'MetaRight',
];
/// Generates the key mapping of macOS, based on the information in the key
// The name of keys that require special attention.
const List<String> kSpecialPhysicalKeys = <String>['CapsLock'];
const List<String> kSpecialLogicalKeys = <String>['CapsLock'];
String _toConstantVariableName(String variableName) {
return 'k${variableName[0].toUpperCase()}${variableName.substring(1)}';
}
/// Generates the key mapping for macOS, based on the information in the key
/// data structure given to it.
class MacOsCodeGenerator extends PlatformCodeGenerator {
MacOsCodeGenerator(KeyData keyData) : super(keyData);
MacOsCodeGenerator(PhysicalKeyData keyData, LogicalKeyData logicalData)
: super(keyData, logicalData);
/// This generates the map of macOS key codes to physical keys.
String get _macOsScanCodeMap {
final StringBuffer macOsScanCodeMap = StringBuffer();
for (final Key entry in keyData.data) {
String get _scanCodeMap {
final StringBuffer scanCodeMap = StringBuffer();
for (final PhysicalKeyEntry entry in keyData.entries) {
if (entry.macOsScanCode != null) {
macOsScanCodeMap.writeln(' { ${toHex(entry.macOsScanCode)}, ${toHex(entry.usbHidCode)} }, // ${entry.constantName}');
scanCodeMap.writeln(' @${toHex(entry.macOsScanCode)} : @${toHex(entry.usbHidCode)}, // ${entry.constantName}');
}
}
return macOsScanCodeMap.toString().trimRight();
return scanCodeMap.toString().trimRight();
}
/// This generates the map of macOS number pad key codes to logical keys.
String get _macOsNumpadMap {
final StringBuffer macOsNumPadMap = StringBuffer();
for (final Key entry in numpadKeyData) {
if (entry.macOsScanCode != null) {
macOsNumPadMap.writeln(' { ${toHex(entry.macOsScanCode)}, ${toHex(entry.flutterId, digits: 10)} }, // ${entry.constantName}');
String get _keyCodeToLogicalMap {
final StringBuffer result = StringBuffer();
for (final LogicalKeyEntry entry in logicalData.entries) {
zipStrict(entry.macOsKeyCodeValues, entry.macOsKeyCodeNames, (int macOsValue, String macOsName) {
result.writeln(' @${toHex(macOsValue)} : @${toHex(entry.value, digits: 10)}, // $macOsName');
});
}
return result.toString().trimRight();
}
return macOsNumPadMap.toString().trimRight();
/// This generates the mask values for the part of a key code that defines its plane.
String get _maskConstants {
final StringBuffer buffer = StringBuffer();
for (final MaskConstant constant in maskConstants) {
buffer.writeln('/**');
buffer.write(constant.description
.map((String line) => wrapString(line, prefix: ' * '))
.join(' *\n'));
buffer.writeln(' */');
buffer.writeln('const uint64_t ${_toConstantVariableName(constant.name)} = ${toHex(constant.value, digits: 11)};');
buffer.writeln('');
}
return buffer.toString().trimRight();
}
String get _macOsFunctionKeyMap {
final StringBuffer macOsFunctionKeyMap = StringBuffer();
for (final Key entry in functionKeyData) {
if (entry.macOsScanCode != null) {
macOsFunctionKeyMap.writeln(' { ${toHex(entry.macOsScanCode)}, ${toHex(entry.flutterId, digits: 10)} }, // ${entry.constantName}');
/// This generates a map from the key code to a modifier flag.
String get _keyToModifierFlagMap {
final StringBuffer modifierKeyMap = StringBuffer();
for (final String name in kModifiersOfInterest) {
modifierKeyMap.writeln(' @${toHex(logicalData.entryByName(name).macOsKeyCodeValues[0])} : @(kModifierFlag${lowerCamelToUpperCamel(name)}),');
}
return modifierKeyMap.toString().trimRight();
}
/// This generates a map from the modifier flag to the key code.
String get _modifierFlagToKeyMap {
final StringBuffer modifierKeyMap = StringBuffer();
for (final String name in kModifiersOfInterest) {
modifierKeyMap.writeln(' @(kModifierFlag${lowerCamelToUpperCamel(name)}) : @${toHex(logicalData.entryByName(name).macOsKeyCodeValues[0])},');
}
return modifierKeyMap.toString().trimRight();
}
/// This generates some keys that needs special attention.
String get _specialKeyConstants {
final StringBuffer specialKeyConstants = StringBuffer();
for (final String keyName in kSpecialPhysicalKeys) {
specialKeyConstants.writeln('const uint64_t k${keyName}PhysicalKey = ${toHex(keyData.entryByName(keyName).usbHidCode)};');
}
for (final String keyName in kSpecialLogicalKeys) {
specialKeyConstants.writeln('const uint64_t k${lowerCamelToUpperCamel(keyName)}LogicalKey = ${toHex(logicalData.entryByName(keyName).value)};');
}
return macOsFunctionKeyMap.toString().trimRight();
return specialKeyConstants.toString().trimRight();
}
@override
String get templatePath => path.join(flutterRoot.path, 'dev', 'tools', 'gen_keycodes', 'data', 'keyboard_map_macos_cc.tmpl');
String get templatePath => path.join(dataRoot, 'macos_key_code_map_cc.tmpl');
@override
String outputPath(String platform) => path.joinAll(<String>[flutterRoot.path, '..', 'engine', 'src', 'flutter', 'shell', 'platform', 'darwin', platform, 'keycodes', 'keyboard_map_$platform.h']);
String outputPath(String platform) => path.join(PlatformCodeGenerator.engineRoot,
'shell', 'platform', 'darwin', 'macos', 'framework', 'Source', 'KeyCodeMap.mm');
@override
Map<String, String> mappings() {
......@@ -58,9 +114,12 @@ class MacOsCodeGenerator extends PlatformCodeGenerator {
// The LogicalKeyboardKey is generated by raw_keyboard_macos.dart from the unmodified characters
// from NSEvent.
return <String, String>{
'MACOS_SCAN_CODE_MAP': _macOsScanCodeMap,
'MACOS_NUMPAD_MAP': _macOsNumpadMap,
'MACOS_FUNCTION_KEY_MAP': _macOsFunctionKeyMap,
'MACOS_SCAN_CODE_MAP': _scanCodeMap,
'MACOS_KEYCODE_LOGICAL_MAP': _keyCodeToLogicalMap,
'MASK_CONSTANTS': _maskConstants,
'KEYCODE_TO_MODIFIER_FLAG_MAP': _keyToModifierFlagMap,
'MODIFIER_FLAG_TO_KEYCODE_MAP': _modifierFlagToKeyMap,
'SPECIAL_KEY_CONSTANTS': _specialKeyConstants,
};
}
}
// Copyright 2014 The Flutter 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 '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
/// [PhysicalKeyData.fromJson] constructor and [toJson] method, respectively.
class PhysicalKeyData {
factory PhysicalKeyData(
String chromiumHidCodes,
String androidKeyboardLayout,
String androidNameMap,
String glfwHeaderFile,
String glfwNameMap,
) {
final Map<String, List<int>> nameToAndroidScanCodes = _readAndroidScanCodes(androidKeyboardLayout, androidNameMap);
final Map<String, List<int>> nameToGlfwKeyCodes = _readGlfwKeyCodes(glfwHeaderFile, glfwNameMap);
final Map<String, PhysicalKeyEntry> data = _readHidEntries(
chromiumHidCodes,
nameToAndroidScanCodes,
nameToGlfwKeyCodes,
);
final List<MapEntry<String, PhysicalKeyEntry>> sortedEntries = data.entries.toList()..sort(
(MapEntry<String, PhysicalKeyEntry> a, MapEntry<String, PhysicalKeyEntry> b) =>
PhysicalKeyEntry.compareByUsbHidCode(a.value, b.value),
);
data
..clear()
..addEntries(sortedEntries);
return PhysicalKeyData._(data);
}
/// Parses the given JSON data and populates the data structure from it.
factory PhysicalKeyData.fromJson(Map<String, dynamic> contentMap) {
final Map<String, PhysicalKeyEntry> data = <String, PhysicalKeyEntry>{};
for (final MapEntry<String, dynamic> jsonEntry in contentMap.entries) {
final PhysicalKeyEntry entry = PhysicalKeyEntry.fromJsonMapEntry(jsonEntry.value as Map<String, dynamic>);
data[entry.name] = entry;
}
return PhysicalKeyData._(data);
}
PhysicalKeyData._(this._data);
/// Find an entry from name, or null if not found.
PhysicalKeyEntry? tryEntryByName(String name) {
return _data[name];
}
/// Find an entry from name.
///
/// Asserts if the name is not found.
PhysicalKeyEntry entryByName(String name) {
final PhysicalKeyEntry? entry = tryEntryByName(name);
assert(entry != null,
'Unable to find logical entry by name $name.');
return entry!;
}
/// All entries.
Iterable<PhysicalKeyEntry> get entries => _data.values;
// Keys mapped from their names.
final Map<String, PhysicalKeyEntry> _data;
/// Converts the data structure into a JSON structure that can be parsed by
/// [PhysicalKeyData.fromJson].
Map<String, dynamic> toJson() {
final Map<String, dynamic> outputMap = <String, dynamic>{};
for (final PhysicalKeyEntry entry in _data.values) {
outputMap[entry.name] = entry.toJson();
}
return outputMap;
}
/// 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"
/// key 477 F12 FUNCTION
///
/// 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.
static Map<String, List<int>> _readAndroidScanCodes(String keyboardLayout, String nameMap) {
final RegExp keyEntry = RegExp(
r'#?\s*' // Optional comment mark
r'key\s+' // Literal "key"
r'(?<id>[0-9]+)\s*' // ID section
r'"?(?:KEY_)?(?<name>[0-9A-Z_]+|\(undefined\))"?\s*' // Name section
r'(?<function>FUNCTION)?' // Optional literal "FUNCTION"
);
final Map<String, List<int>> androidNameToScanCodes = <String, List<int>>{};
for (final RegExpMatch match in keyEntry.allMatches(keyboardLayout)) {
if (match.namedGroup('function') == 'FUNCTION') {
// Skip odd duplicate Android FUNCTION keys (F1-F12 are already defined).
continue;
}
final String name = match.namedGroup('name')!;
if (name == '(undefined)') {
// Skip undefined scan codes.
continue;
}
androidNameToScanCodes.putIfAbsent(name, () => <int>[])
.add(int.parse(match.namedGroup('id')!));
}
// Cast Android dom map
final Map<String, List<String>> nameToAndroidNames = (json.decode(nameMap) as Map<String, dynamic>)
.cast<String, List<dynamic>>()
.map<String, List<String>>((String key, List<dynamic> value) {
return MapEntry<String, List<String>>(key, value.cast<String>());
});
final Map<String, List<int>> result = nameToAndroidNames.map((String name, List<String> androidNames) {
final Set<int> scanCodes = <int>{};
for (final String androidName in androidNames) {
scanCodes.addAll(androidNameToScanCodes[androidName] ?? <int>[]);
}
return MapEntry<String, List<int>>(name, scanCodes.toList()..sort());
});
return result;
}
/// Parses entries from GLFW's keycodes.h key code data file.
///
/// Lines in this file look like this (without the ///):
/// /** Space key. */
/// #define GLFW_KEY_SPACE 32,
/// #define GLFW_KEY_LAST GLFW_KEY_MENU
static Map<String, List<int>> _readGlfwKeyCodes(String headerFile, String nameMap) {
// Only get the KEY definitions, ignore the rest (mouse, joystick, etc).
final RegExp definedCodes = RegExp(
r'define\s+'
r'GLFW_KEY_(?<name>[A-Z0-9_]+)\s+'
r'(?<value>[A-Z0-9_]+),?',
);
final Map<String, dynamic> replaced = <String, dynamic>{};
for (final RegExpMatch match in definedCodes.allMatches(headerFile)) {
final String name = match.namedGroup('name')!;
final String value = match.namedGroup('value')!;
replaced[name] = int.tryParse(value) ?? value.replaceAll('GLFW_KEY_', '');
}
final Map<String, int> glfwNameToKeyCode = <String, int>{};
replaced.forEach((String key, dynamic value) {
// Some definition values point to other definitions (e.g #define GLFW_KEY_LAST GLFW_KEY_MENU).
if (value is String) {
glfwNameToKeyCode[key] = replaced[value] as int;
} else {
glfwNameToKeyCode[key] = value as int;
}
});
final Map<String, List<String>> nameToGlfwNames = (json.decode(nameMap) as Map<String, dynamic>)
.cast<String, List<dynamic>>()
.map<String, List<String>>((String key, List<dynamic> value) {
return MapEntry<String, List<String>>(key, value.cast<String>());
});
final Map<String, List<int>> result = nameToGlfwNames.map((String name, List<String> glfwNames) {
final Set<int> keyCodes = <int>{};
for (final String glfwName in glfwNames) {
if (glfwNameToKeyCode[glfwName] != null)
keyCodes.add(glfwNameToKeyCode[glfwName]!);
}
return MapEntry<String, List<int>>(name, keyCodes.toList()..sort());
});
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
/// DOM_CODE(0x000010, 0x0000, 0x0000, 0x0000, 0xffff, "Hyper", HYPER),
static Map<String, PhysicalKeyEntry> _readHidEntries(
String input,
Map<String, List<int>> nameToAndroidScanCodes,
Map<String, List<int>> nameToGlfwKeyCodes,
) {
final Map<int, PhysicalKeyEntry> entries = <int, PhysicalKeyEntry>{};
final RegExp usbMapRegExp = RegExp(
r'DOM_CODE\s*\(\s*'
r'0[xX](?<usb>[a-fA-F0-9]+),\s*'
r'0[xX](?<evdev>[a-fA-F0-9]+),\s*'
r'0[xX](?<xkb>[a-fA-F0-9]+),\s*'
r'0[xX](?<win>[a-fA-F0-9]+),\s*'
r'0[xX](?<mac>[a-fA-F0-9]+),\s*'
r'(?:"(?<code>[^\s]+)")?[^")]*?,'
r'\s*(?<enum>[^\s]+?)\s*'
r'\)',
// Multiline is necessary because some definitions spread across
// multiple lines.
multiLine: true,
);
final RegExp commentRegExp = RegExp(r'//.*$', multiLine: true);
input = input.replaceAll(commentRegExp, '');
for (final RegExpMatch match in usbMapRegExp.allMatches(input)) {
final int usbHidCode = getHex(match.namedGroup('usb')!);
final int linuxScanCode = getHex(match.namedGroup('evdev')!);
final int xKbScanCode = getHex(match.namedGroup('xkb')!);
final int windowsScanCode = getHex(match.namedGroup('win')!);
final int macScanCode = getHex(match.namedGroup('mac')!);
final String? chromiumCode = match.namedGroup('code');
// The input data has a typo...
final String enumName = match.namedGroup('enum')!.replaceAll('MINIMIUM', 'MINIMUM');
final String name = chromiumCode ?? shoutingToUpperCamel(enumName);
if (name == 'IntlHash') {
// Skip key that is not actually generated by any keyboard.
continue;
}
final PhysicalKeyEntry newEntry = PhysicalKeyEntry(
usbHidCode: usbHidCode,
androidScanCodes: nameToAndroidScanCodes[name] ?? <int>[],
glfwKeyCodes: nameToGlfwKeyCodes[name] ?? <int>[],
linuxScanCode: linuxScanCode == 0 ? null : linuxScanCode,
xKbScanCode: xKbScanCode == 0 ? null : xKbScanCode,
windowsScanCode: windowsScanCode == 0 ? null : windowsScanCode,
macOsScanCode: macScanCode == 0xffff ? null : macScanCode,
iosScanCode: (usbHidCode & 0x070000) == 0x070000 ? (usbHidCode ^ 0x070000) : null,
name: name,
chromiumCode: chromiumCode,
);
// Remove duplicates: last one wins, so that supplemental codes
// override.
if (entries.containsKey(newEntry.usbHidCode)) {
// This is expected for Fn. Warn for other keys.
if (newEntry.name != 'Fn') {
print('Duplicate usbHidCode ${newEntry.usbHidCode} of key ${newEntry.name} '
'conflicts with existing ${entries[newEntry.usbHidCode]!.name}. Keeping the new one.');
}
}
entries[newEntry.usbHidCode] = newEntry;
}
return entries.map((int code, PhysicalKeyEntry entry) =>
MapEntry<String, PhysicalKeyEntry>(entry.name, entry));
}
}
/// A single entry in the key data structure.
///
/// Can be read from JSON with the [PhysicalKeyEntry.fromJsonMapEntry] constructor, or
/// written with the [toJson] method.
class PhysicalKeyEntry {
/// Creates a single key entry from available data.
///
/// The [usbHidCode] and [chromiumName] parameters must not be null.
PhysicalKeyEntry({
required this.usbHidCode,
required this.name,
required this.androidScanCodes,
required this.linuxScanCode,
required this.xKbScanCode,
required this.windowsScanCode,
required this.macOsScanCode,
required this.iosScanCode,
required this.chromiumCode,
required this.glfwKeyCodes,
});
/// Populates the key from a JSON map.
factory PhysicalKeyEntry.fromJsonMapEntry(Map<String, dynamic> map) {
return PhysicalKeyEntry(
name: map['names']['name'] as String,
chromiumCode: map['names']['chromium'] as String?,
usbHidCode: map['scanCodes']['usb'] as int,
androidScanCodes: (map['scanCodes']['android'] as List<dynamic>?)?.cast<int>() ?? <int>[],
linuxScanCode: map['scanCodes']['linux'] as int?,
xKbScanCode: map['scanCodes']['xkb'] as int?,
windowsScanCode: map['scanCodes']['windows'] as int?,
macOsScanCode: map['scanCodes']['macos'] as int?,
iosScanCode: map['scanCodes']['ios'] as int?,
glfwKeyCodes: (map['keyCodes']?['glfw'] as List<dynamic>?)?.cast<int>() ?? <int>[],
);
}
/// The USB HID code of the key
final int usbHidCode;
/// The Linux scan code of the key, from Chromium's header file.
final int? linuxScanCode;
/// The XKb scan code of the key from Chromium's header file.
final int? xKbScanCode;
/// The Windows scan code of the key from Chromium's header file.
final int? windowsScanCode;
/// The macOS scan code of the key from Chromium's header file.
final int? macOsScanCode;
/// The iOS scan code of the key from UIKey's documentation (USB Hid table)
final int? iosScanCode;
/// 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.
final List<int> androidScanCodes;
/// The list of GLFW key codes matching this key, created by looking up the
/// Linux name in the Chromium data, and substituting the GLFW key code
/// value.
final List<int> glfwKeyCodes;
/// 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.
final String name;
/// The Chromium event code for the key.
final String? chromiumCode;
/// Creates a JSON map from the key data.
Map<String, dynamic> toJson() {
return removeEmptyValues(<String, dynamic>{
'names': <String, dynamic>{
'name': name,
'chromium': chromiumCode,
},
'scanCodes': <String, dynamic>{
'android': androidScanCodes,
'usb': usbHidCode,
'linux': linuxScanCode,
'xkb': xKbScanCode,
'windows': windowsScanCode,
'macos': macOsScanCode,
'ios': iosScanCode,
},
'keyCodes': <String, List<int>>{
'glfw': glfwKeyCodes,
},
});
}
static String getCommentName(String constantName) {
String upperCamel = lowerCamelToUpperCamel(constantName);
upperCamel = upperCamel.replaceAllMapped(
RegExp(r'(Digit|Numpad|Lang|Button|Left|Right)([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 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 => getCommentName(constantName);
/// Gets the named used for the key constant in the definitions in
/// keyboard_key.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).
late final String constantName = ((){
String? result;
if (name.isEmpty) {
// If it doesn't have a DomKey name then use the Chromium symbol name.
result = chromiumCode;
} else {
result = upperCamelToLowerCamel(name);
}
result ??= 'Key${toHex(usbHidCode)}';
if (kDartReservedWords.contains(result)) {
return '${result}Key';
}
return result;
})();
@override
String toString() {
return """'$constantName': (name: "$name", usbHidCode: ${toHex(usbHidCode)}, """
'linuxScanCode: ${toHex(linuxScanCode)}, xKbScanCode: ${toHex(xKbScanCode)}, '
'windowsKeyCode: ${toHex(windowsScanCode)}, macOsScanCode: ${toHex(macOsScanCode)}, '
'windowsScanCode: ${toHex(windowsScanCode)}, chromiumSymbolName: $chromiumCode '
'iOSScanCode: ${toHex(iosScanCode)})';
}
static int compareByUsbHidCode(PhysicalKeyEntry a, PhysicalKeyEntry b) =>
a.usbHidCode.compareTo(b.usbHidCode);
}
......@@ -2,6 +2,7 @@
// 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;
......@@ -9,20 +10,27 @@ import 'package:path/path.dart' as path;
/// The location of the Flutter root directory, based on the known location of
/// this script.
final Directory flutterRoot = Directory(path.dirname(Platform.script.toFilePath())).parent.parent.parent.parent;
final String dataRoot = path.join(flutterRoot.path, 'dev', 'tools', 'gen_keycodes', 'data');
/// Converts `FOO_BAR` to `fooBar`.
String shoutingToLowerCamel(String shouting) {
final RegExp initialLetter = RegExp(r'_([^_])([^_]*)');
/// Converts `FOO_BAR` to `FooBar`.
String shoutingToUpperCamel(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 match.group(1)!.toUpperCase() + match.group(2)!.toLowerCase();
});
return result;
}
/// Converts 'FooBar' to 'fooBar'.
///
/// 'TVFoo' should be convert to 'tvFoo'.
/// 'KeyX' should be convert to 'keyX'.
String upperCamelToLowerCamel(String upperCamel) {
return upperCamel.substring(0, 1).toLowerCase() + upperCamel.substring(1);
final RegExp initialGroup = RegExp(r'^([A-Z]([A-Z]*|[^A-Z]*))([A-Z]([^A-Z]|$)|$)');
return upperCamel.replaceFirstMapped(initialGroup, (Match match) {
return match.group(1)!.toLowerCase() + (match.group(3) ?? '');
});
}
/// Converts 'fooBar' to 'FooBar'.
......@@ -97,7 +105,7 @@ const List<String> kDartReservedWords = <String>[
];
/// Converts an integer into a hex string with the given number of digits.
String toHex(int value, {int digits = 8}) {
String toHex(int? value, {int digits = 8}) {
if (value == null) {
return 'null';
}
......@@ -108,3 +116,137 @@ String toHex(int value, {int digits = 8}) {
int getHex(String input) {
return int.parse(input, radix: 16);
}
/// 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, {required String prefix}) {
final int wrapWidth = 80 - prefix.length;
final StringBuffer result = StringBuffer();
final List<String> words = input.split(RegExp(r'\s+'));
String currentLine = words.removeAt(0);
for (final 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();
}
/// Run `fn` with each corresponding element from list1 and list2.
///
/// If `list1` has a different length from `list2`, the execution is aborted
/// after printing an error.
///
/// An null list is considered a list with length 0.
void zipStrict<T1, T2>(Iterable<T1> list1, Iterable<T2> list2, void Function(T1, T2) fn) {
if (list1 == null && list2 == null)
return;
assert(list1.length == list2.length);
final Iterator<T1> it1 = list1.iterator;
final Iterator<T2> it2 = list2.iterator;
while (it1.moveNext()) {
it2.moveNext();
fn(it1.current, it2.current);
}
}
/// Read a Map<String, String> out of its string representation in JSON.
Map<String, String> parseMapOfString(String jsonString) {
return (json.decode(jsonString) as Map<String, dynamic>).cast<String, String>();
}
/// Read a Map<String, List<String>> out of its string representation in JSON.
Map<String, List<String>> parseMapOfListOfString(String jsonString) {
final Map<String, List<dynamic>> dynamicMap = (json.decode(jsonString) as Map<String, dynamic>).cast<String, List<dynamic>>();
return dynamicMap.map<String, List<String>>((String key, List<dynamic> value) {
return MapEntry<String, List<String>>(key, value.cast<String>());
});
}
Map<String, List<String?>> parseMapOfListOfNullableString(String jsonString) {
final Map<String, List<dynamic>> dynamicMap = (json.decode(jsonString) as Map<String, dynamic>).cast<String, List<dynamic>>();
return dynamicMap.map<String, List<String?>>((String key, List<dynamic> value) {
return MapEntry<String, List<String?>>(key, value.cast<String?>());
});
}
/// Reverse the map of { fromValue -> list of toValue } to { toValue -> fromValue } and return.
Map<String, String> reverseMapOfListOfString(Map<String, List<String>> inMap, void Function(String fromValue, String newToValue) onDuplicate) {
final Map<String, String> result = <String, String>{};
inMap.forEach((String fromValue, List<String> toValues) {
for (final String toValue in toValues) {
if (result.containsKey(toValue)) {
onDuplicate(fromValue, toValue);
continue;
}
result[toValue] = fromValue;
}
});
return result;
}
/// Remove entries whose value `isEmpty` or is null, and return the map.
///
/// Will modify the input map.
Map<String, dynamic> removeEmptyValues(Map<String, dynamic> map) {
return map..removeWhere((String key, dynamic value) {
if (value == null)
return true;
if (value is Map<String, dynamic>) {
final Map<String, dynamic> regularizedMap = removeEmptyValues(value);
return regularizedMap.isEmpty;
}
if (value is Iterable<dynamic>) {
return value.isEmpty;
}
return false;
});
}
void addNameValue(List<String> names, List<int> values, String name, int value) {
final int foundIndex = values.indexOf(value);
if (foundIndex == -1) {
names.add(name);
values.add(value);
} else {
if (!RegExp(r'(^|, )abc1($|, )').hasMatch(name)) {
names[foundIndex] = '${names[foundIndex]}, $name';
}
}
}
/// A utility class to build join a number of lines in a sorted order.
///
/// Use [add] to add a line and associate it with an index. Use [sortedJoin] to
/// get the joined string of these lines joined sorting them in the order of the
/// index.
class OutputLines<T extends Comparable<Object>> {
OutputLines(this.mapName);
/// The name for this map.
///
/// Used in warning messages.
final String mapName;
final Map<T, String> lines = <T, String>{};
void add(T code, String line) {
if (lines.containsKey(code)) {
print('Warn: $mapName is requested to add line $code as:\n $line\n but it already exists as:\n ${lines[code]}');
}
lines[code] = line;
}
String sortedJoin() {
return (lines.entries.toList()
..sort((MapEntry<T, String> a, MapEntry<T, String> b) => a.key.compareTo(b.key)))
.map((MapEntry<T, String> entry) => entry.value)
.join('\n');
}
}
......@@ -5,22 +5,27 @@
import 'package:path/path.dart' as path;
import 'base_code_gen.dart';
import 'key_data.dart';
import 'logical_key_data.dart';
import 'physical_key_data.dart';
import 'utils.dart';
/// Generates the key mapping of Web, based on the information in the key
/// Generates the key mapping for Web, based on the information in the key
/// data structure given to it.
class WebCodeGenerator extends PlatformCodeGenerator {
WebCodeGenerator(KeyData keyData) : super(keyData);
WebCodeGenerator(
PhysicalKeyData keyData,
LogicalKeyData logicalData,
String logicalLocationMap,
) : _logicalLocationMap = parseMapOfListOfNullableString(logicalLocationMap),
super(keyData, logicalData);
/// This generates the map of Web KeyboardEvent codes to logical key ids.
String get _webLogicalKeyCodeMap {
final StringBuffer result = StringBuffer();
for (final Key entry in keyData.data) {
if (entry.name != null) {
result.writeln(" '${entry.name}': ${toHex(entry.flutterId, digits: 10)},");
}
for (final LogicalKeyEntry entry in logicalData.entries) {
zipStrict(entry.webValues, entry.webNames, (int value, String name) {
result.writeln(" '$name': ${toHex(value, digits: 10)},");
});
}
return result.toString().trimRight();
}
......@@ -28,7 +33,7 @@ class WebCodeGenerator extends PlatformCodeGenerator {
/// This generates the map of Web KeyboardEvent codes to physical key USB HID codes.
String get _webPhysicalKeyCodeMap {
final StringBuffer result = StringBuffer();
for (final Key entry in keyData.data) {
for (final PhysicalKeyEntry entry in keyData.entries) {
if (entry.name != null) {
result.writeln(" '${entry.name}': ${toHex(entry.usbHidCode)},");
}
......@@ -37,28 +42,31 @@ class WebCodeGenerator extends PlatformCodeGenerator {
}
/// This generates the map of Web number pad codes to logical key ids.
String get _webNumpadCodeMap {
String get _webLogicalLocationMap {
final StringBuffer result = StringBuffer();
for (final Key entry in numpadKeyData) {
if (entry.name != null) {
result.writeln(" '${entry.name}': ${toHex(entry.flutterId, digits: 10)},");
}
}
_logicalLocationMap.forEach((String webKey, List<String?> locations) {
final String valuesString = locations.map((String? value) {
return value == null ? 'null' : toHex(logicalData.entryByName(value).value, digits: 10);
}).join(', ');
result.writeln(" '$webKey': <int?>[$valuesString],");
});
return result.toString().trimRight();
}
final Map<String, List<String?>> _logicalLocationMap;
@override
String get templatePath => path.join(flutterRoot.path, 'dev', 'tools', 'gen_keycodes', 'data', 'keyboard_map_web.tmpl');
String get templatePath => path.join(dataRoot, 'web_key_map_dart.tmpl');
@override
String outputPath(String platform) => path.join(flutterRoot.path, '..', 'engine', 'src', 'flutter', path.join('lib', 'web_ui', 'lib', 'src', 'engine', 'keycodes', 'keyboard_map_web.dart'));
String outputPath(String platform) => path.join(PlatformCodeGenerator.engineRoot,
'lib', 'web_ui', 'lib', 'src', 'engine', 'key_map.dart');
@override
Map<String, String> mappings() {
return <String, String>{
'WEB_LOGICAL_KEY_CODE_MAP': _webLogicalKeyCodeMap,
'WEB_PHYSICAL_KEY_CODE_MAP': _webPhysicalKeyCodeMap,
'WEB_NUMPAD_CODE_MAP': _webNumpadCodeMap,
'WEB_LOGICAL_LOCATION_MAP': _webLogicalLocationMap,
};
}
}
......@@ -5,59 +5,71 @@
import 'package:path/path.dart' as path;
import 'base_code_gen.dart';
import 'key_data.dart';
import 'logical_key_data.dart';
import 'physical_key_data.dart';
import 'utils.dart';
/// Generates the key mapping of Windows, based on the information in the key
/// Generates the key mapping for Windows, based on the information in the key
/// data structure given to it.
class WindowsCodeGenerator extends PlatformCodeGenerator {
WindowsCodeGenerator(KeyData keyData) : super(keyData);
WindowsCodeGenerator(
PhysicalKeyData keyData,
LogicalKeyData logicalData,
String scancodeToLogical,
) : _scancodeToLogical = parseMapOfString(scancodeToLogical),
super(keyData, logicalData);
/// This generates the map of Windows scan codes to physical keys.
String get _windowsScanCodeMap {
final StringBuffer windowsScanCodeMap = StringBuffer();
for (final Key entry in keyData.data) {
for (final PhysicalKeyEntry entry in keyData.entries) {
if (entry.windowsScanCode != null) {
windowsScanCodeMap.writeln(' { ${entry.windowsScanCode}, ${toHex(entry.usbHidCode)} }, // ${entry.constantName}');
windowsScanCodeMap.writeln(' {${toHex(entry.windowsScanCode)}, ${toHex(entry.usbHidCode)}}, // ${entry.constantName}');
}
}
return windowsScanCodeMap.toString().trimRight();
}
/// This generates the map of Windows number pad key codes to logical keys.
String get _windowsNumpadMap {
final StringBuffer windowsNumPadMap = StringBuffer();
for (final Key entry in numpadKeyData) {
if (entry.windowsScanCode != null) {
windowsNumPadMap.writeln(' { ${toHex(entry.windowsScanCode)}, ${toHex(entry.flutterId, digits: 10)} }, // ${entry.constantName}');
/// This generates the map of Windows key codes to logical keys.
String get _windowsLogicalKeyCodeMap {
final StringBuffer result = StringBuffer();
for (final LogicalKeyEntry entry in logicalData.entries) {
zipStrict(entry.windowsValues, entry.windowsNames, (int windowsValue, String windowsName) {
result.writeln(' {${toHex(windowsValue)}, ${toHex(entry.value, digits: 11)}}, // $windowsName');
});
}
}
return windowsNumPadMap.toString().trimRight();
return result.toString().trimRight();
}
/// This generates the map of Android key codes to logical keys.
String get _windowsKeyCodeMap {
final StringBuffer windowsKeyCodeMap = StringBuffer();
for (final Key entry in keyData.data) {
if (entry.windowsKeyCodes != null) {
for (final int code in entry.windowsKeyCodes.cast<int>()) {
windowsKeyCodeMap.writeln(' { $code, ${toHex(entry.flutterId, digits: 10)} }, // ${entry.constantName}');
}
}
}
return windowsKeyCodeMap.toString().trimRight();
/// This generates the map from scan code to logical keys.
///
/// Normally logical keys should only be derived from key codes, but since some
/// key codes are either 0 or ambiguous (multiple keys using the same key
/// code), these keys are resolved by scan codes.
String get _scanCodeToLogicalMap {
final StringBuffer result = StringBuffer();
_scancodeToLogical.forEach((String scanCodeName, String logicalName) {
final PhysicalKeyEntry physicalEntry = keyData.entryByName(scanCodeName);
final int logicalValue = logicalData.entryByName(logicalName).value;
result.writeln(' {${toHex(physicalEntry.windowsScanCode)}, ${toHex(logicalValue, digits: 10)}}, // ${physicalEntry.name}');
});
return result.toString().trimRight();
}
final Map<String, String> _scancodeToLogical;
@override
String get templatePath => path.join(dataRoot, 'windows_flutter_key_map_cc.tmpl');
@override
String get templatePath => path.join(flutterRoot.path, 'dev', 'tools', 'gen_keycodes', 'data', 'keyboard_map_windows_cc.tmpl');
String outputPath(String platform) => path.join(PlatformCodeGenerator.engineRoot,
'shell', 'platform', 'windows', 'flutter_key_map.cc');
@override
Map<String, String> mappings() {
return <String, String>{
'WINDOWS_SCAN_CODE_MAP': _windowsScanCodeMap,
'WINDOWS_NUMPAD_MAP': _windowsNumpadMap,
'WINDOWS_KEY_CODE_MAP': _windowsKeyCodeMap,
'WINDOWS_SCAN_CODE_TO_LOGICAL_MAP': _scanCodeToLogicalMap,
'WINDOWS_KEY_CODE_MAP': _windowsLogicalKeyCodeMap,
};
}
}
......@@ -2,7 +2,7 @@ name: gen_keycodes
description: Generates keycode source files from various resources.
environment:
sdk: ">=2.2.2 <3.0.0"
sdk: ">=2.12.0-0 <3.0.0"
dependencies:
args: 2.1.0
......
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
......@@ -107,6 +107,14 @@ class RawKeyEventDataIos extends RawKeyEventData {
if (newKey != null) {
return newKey;
}
// Keys that can't be derived with characterIgnoringModifiers will be
// derived from their key codes using this map.
final LogicalKeyboardKey? knownKey = kIosToLogicalKey[keyCode];
if (knownKey != null) {
return knownKey;
}
// If this key is printable, generate the LogicalKeyboardKey from its
// Unicode value. Control keys such as ESC, CRTL, and SHIFT are not
// printable. HOME, DEL, arrow keys, and function keys are considered
......
......@@ -76,6 +76,14 @@ class RawKeyEventDataMacOs extends RawKeyEventData {
if (numPadKey != null) {
return numPadKey;
}
// Keys that can't be derived with characterIgnoringModifiers will be
// derived from their key codes using this map.
final LogicalKeyboardKey? knownKey = kMacOsToLogicalKey[keyCode];
if (knownKey != null) {
return knownKey;
}
// If this key is printable, generate the LogicalKeyboardKey from its
// Unicode value. Control keys such as ESC, CRTL, and SHIFT are not
// printable. HOME, DEL, arrow keys, and function keys are considered
......
......@@ -29,11 +29,11 @@ void main() {
group(LogicalKeyboardKey, () {
test('Various classes of keys can be looked up by code', () async {
// Check a letter key
expect(LogicalKeyboardKey.findKeyByKeyId(0x0000000061), equals(LogicalKeyboardKey.keyA));
expect(LogicalKeyboardKey.findKeyByKeyId(LogicalKeyboardKey.keyA.keyId), equals(LogicalKeyboardKey.keyA));
// Check a control key
expect(LogicalKeyboardKey.findKeyByKeyId(0x0100070029), equals(LogicalKeyboardKey.escape));
expect(LogicalKeyboardKey.findKeyByKeyId(LogicalKeyboardKey.escape.keyId), equals(LogicalKeyboardKey.escape));
// Check a modifier key
expect(LogicalKeyboardKey.findKeyByKeyId(0x01000700e1), equals(LogicalKeyboardKey.shiftLeft));
expect(LogicalKeyboardKey.findKeyByKeyId(LogicalKeyboardKey.shiftLeft.keyId), equals(LogicalKeyboardKey.shiftLeft));
});
test('Control characters are recognized as such', () async {
// Check some common control characters
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment