Unverified Commit c8d0b1d9 authored by Tong Mu's avatar Tong Mu Committed by GitHub

New scheme for keyboard logical key ID (#85121)

This PR updates the ID used by logical keyboard keys.

The logical key ID is still composed of 2 parts: 32 bits of value, and 8 bits of plane. But the assignment of planes has been drastically changed. HID plane is removed, and unprintable plane and Flutter plane are added. This is to reflect the new generation method for logical key IDs. Now keys that are defined by Flutter but not by dom_key_data are placed into the Flutter plane, including numpad keys, sided modifier keys, and gamepad keys. The values for platform planes have also been adjusted.

The generation script and README have been updated accordingly as well.

A new file, test_utils/key_codes.h is now generated to assist engine unit testing.

All lists generated by the script are now sorted by the key.
parent 252f6a02
......@@ -43,7 +43,7 @@ use `--engine-root=/ENGINE/GCLIENT/ROOT` to specify the engine root.
Other options can be found using `--help`.
## Key Code ID Scheme
## Logical Key ID Scheme
To provide logical keys with unique ID codes, Flutter uses a scheme
to assign logical keycodes which keeps us out of the business of minting new
......@@ -58,109 +58,76 @@ the API.
However, if you are porting Flutter to a new platform, you should follow the
following guidelines for specifying logical key codes.
The logical key code is a 37-bit integer in a namespace that we control and
define. It has values in the following ranges.
The logical key code is a 52-bit integer (due to the limitation of JavaScript).
The entire namespace is divided into 32-bit *planes*. The upper 20 bits of the
ID represent the plane ID, while the lower 32 bits represent values in the
plane. For example, plane 0x1 refers to the range 0x1 0000 0000 -
0x1 FFFF FFFF. Each plane manages how the values within the range are assigned.
- **0x00 0000 0000 - 0x0 0010 FFFF**: For keys that generate Unicode
The planes are planned as follows:
- **Plane 0x00**: The Unicode plane. This plane contains keys that generate Unicode
characters when pressed (this includes dead keys, but not e.g. function keys
or shift keys), the logical key code is the Unicode code point corresponding
to the representation of the key in the current keyboard mapping. The
Unicode code point might not match the string that is generated for
an unshifted keypress of that key, for example, we would use U+0034 for the
“4 \$” key in the US layout, and also the “4 ;” key in the Russian layout,
and also, maybe less intuitively, for the “' 4 {“ in French layout (wherein
the latter case, an unshifted press gets you a ', not a 4). Similarly, the Q
key in the US layout outputs a q in normal usage, but its code would be 0x0
0000 0051 (U+00051 being the code for the uppercase Q).
- **0x01 0000 0000 - 0x01 FFFF FFFF**: For keys that are defined by the [USB HID
standard](https://www.usb.org/sites/default/files/documents/hut1_12v2.pdf),
the key code consists of the 32 bit USB extended usage code. For
example, the Enter key would have code 0x01 0007 0028. Only keys that fall
into collections "Keyboard", "Keypad", and "Tablet PC System Controls" are
considered for this API; for example, a mixing desk with multiple
collections of volume controls would not be exposed via DOWN and UP events,
nor would a mouse, joystick, or golf simulator control.
- **0x02 0000 0000 - 0xFF FFFF FFFF**: For keys that aren't defined in USB at the
time of implementation, but that we need to support. For example, if Flutter
were ever ported to the Symbolics LM-2, the "thumb up" key might be given
the code 0x14 0000 0001, where 0x14 is defined as the “Symbolics” platform
range. Where possible, we will use specific subranges of this space to reuse
keys from other platforms. When this is not possible, the prefix 0xFF is
reserved for “Custom” codes. Each platform from which we take codes will get
a unique prefix in the range 0x2-0xFE. If multiple systems define keys with
the same usage (not the same number), then the value with the lowest prefix
is used as the defining code.
Prefixes will be:
or shift keys). The value is defined as the Unicode code point corresponding
to the character, lower case and without modifier keys if possible.
Examples are Key A (0x61), Digit 1 (0x31), Colon (0x3A), and Key Ù (0xD9).
(The "Colon" key represents a keyboard key that prints the ":"
character without modifiers, which can be found on the French layout. On the
US layout, the key that prints ":" is the Semicolon key.)
This plane also contains key None (0x0).
- **Plane 0x01**: The unprintable plane. This plane contains keys that are defined
by the [Chromium key list](https://chromium.googlesource.com/codesearch/chromium/src/+/refs/heads/master/ui/events/keycodes/dom/dom_key_data.inc)
and do not generate Unicode characters. The value is defined as the macro
value defined by the Chromium key list. Examples are CapsLock (0x105),
ArrowUp (0x304), F1 (0x801), Hiragata (0x716), and TVPower (0xD4B).
Some keys that exist in the Chromium key list are not present in Flutter in this plane, most notably
modifiers keys (such as Shift). See the Flutter plane below for more
information.
- **Plane 0x02**: The Flutter plane. This plane contains keys that are
defined by Flutter. The values are also manually assigned by Flutter.
Modifier keys are placed in this plane, because Flutter distinguishes
between sided modifier keys (for example "ShiftLeft" and "ShiftRight"),
while the web doesn't (only has "Shift").
Other examples are numpad keys and gamepad keys.
- **Plane 0x03-0x0F**: Reserved.
- **Plane 0x10-0x1F**: Platform planes managed by Flutter. Each platform plane
corresponds to a Flutter embedding officially supported by Flutter. The
platforms are listed as follows:
| Code | Platform |
| ---- | -------- |
| 0x02 | Android |
| 0x03 | Fuchsia |
| 0x04 | iOS |
| 0x05 | macOS |
| 0x06 | Linux |
| 0x07 | Windows |
| 0x08 | Web |
| 0xFF | Custom |
Further ranges will be added as platforms are added. The platform prefix
does not define the platform it is used on, it is just the platform that
decides what the value is: the codes are mapped to the same value on all
platforms.
- **0x100 0000 0000 - 0x1FF FFFF FFFF**: For keys that have no definition yet in
Flutter, but that are encountered in the field, this range is used to embed
the platform-specific keycode in an ID that must be tested for in a
platform-specific way. For instance, if a platform generates a new USB
HID code 0x07 00E8 that a Flutter app wasn’t compiled with, then it would
appear in the app as 0x100 0007 00E8, and the app could test against that
code. Yes, this also means that once they recompile with a version of
Flutter that supports this new HID code, apps looking for this code will
break. This situation is only meant to provide a fallback ability for apps
to handle esoteric codes that their version of Flutter doesn’t support yet.
The prefix for this code is the platform prefix from the previous sections,
plus 0x100.
- **0x200 0000 0000 - 0x2FF FFFF FFFF**: For pseudo-keys which represent
combinations of other keys, and conceptual keys which don't have a physical
representation. This is where things like key synonyms are defined (e.g.
"shiftLeft" is a synonym for "shift": the "shift" key is a pseudo-key
representing either the left or right shift key).
**This is intended to get us out of the business of defining key codes where
possible.** We still have to have mapping tables, but at least the actual minting
of codes is deferred to other organizations to a large extent. Coming up with a
code is a mechanical process consisting of just picking the lowest number code
possible that matches the semantic meaning of the key according to the
definitions above.
Here are some examples:
For example, on a French keyboard layout, pressing CAPS LOCK then pressing
SHIFT + Y would generate the following sequence:
DOWN, code 0x0100070039. (CAPS LOCK DOWN)<br>
UP, code 0x0100070039. (CAPS LOCK UP)<br>
DOWN, code 0x01000700E1 (LEFT SHIFT DOWN)<br>
DOWN, code 0x0000000059, string U+00059 (Y DOWN)<br>
UP, code 0x0000000059 (Y UP)<br>
UP, code 0x01000700E1 (LEFT SHIFT UP)<br>
Here's another example. On a German keyboard layout, you press ^e (the ^ key is
at the top left of the keyboard and is a dead key) to produce an “ê”:
DOWN, code 0x0000000302 (CIRCUMFLEX DOWN) It produces no string, because it's a dead
key. The key code is for "Combining circumflex accent U+0302" in Unicode.<br>
UP, code 0x0000000302 (CIRCUMFLEX UP)<br>
DOWN, code 0x0000000065, string U+000EA (Unicode for ê‬) (E DOWN).<br>
UP, code 0x0000000065. (E UP).<br>
It is an important point that even though we’re representing many keys with USB
HID codes, these are not necessarily the same HID codes produced by the hardware
and presented to the driver, since on most platforms we have to map the platform
representation back to an HID code because we don’t have access to the original
HID code. USB HID is simply a conveniently well-defined standard that includes
many of the keys we would want.
| 0x11 | Android |
| 0x12 | Fuchsia |
| 0x13 | iOS |
| 0x14 | macOS |
| 0x15 | Gtk |
| 0x16 | Windows |
| 0x17 | Web |
| 0x18 | GLFW |
Platform planes store keys that are private to the Flutter embedding of this
platform. This most likely means that these keys have not been officially
recognized by Flutter.
The value scheme within a platform plane is decided by the platform,
typically using the field from the platform's native key event that
represents the key's logical effect (such as `keycode`, `virtual key`, etc).
In time, keys that originally belong to a platform plane might be added to
Flutter, especially if a key is found shared by multiple platforms. The values
of that key will be changed to a new value within the Flutter plane, and all
platforms managed by Flutter will start to send the new value, making it a
breaking change. Therefore, when handling an unrecognized key on a platform
managed by Flutter, it is recommended to file a new issue to add this value
to `keyboard_key.dart` instead of using the platform-plane value. However,
for a custom platform (see below), since the platfrom author has full control
over key mapping, such change will not cause breakage and it is recommended
to use the platform-plane value to avoid adding platform-exclusive values
to the framework.
- **Plane 0x20-0x2F**: Custom platform planes. Similar to Flutter's platform
planes, but for private use by custom platforms.
......@@ -15,6 +15,7 @@ import 'package:gen_keycodes/keyboard_maps_code_gen.dart';
import 'package:gen_keycodes/logical_key_data.dart';
import 'package:gen_keycodes/macos_code_gen.dart';
import 'package:gen_keycodes/physical_key_data.dart';
import 'package:gen_keycodes/testing_key_codes_gen.dart';
import 'package:gen_keycodes/utils.dart';
import 'package:gen_keycodes/web_code_gen.dart';
import 'package:gen_keycodes/windows_code_gen.dart';
......@@ -71,7 +72,20 @@ String readDataFile(String fileName) {
return File(path.join(dataRoot, fileName)).readAsStringSync();
}
bool _assertsEnabled() {
bool enabledAsserts = false;
assert(() {
enabledAsserts = true;
return true;
}());
return enabledAsserts;
}
Future<void> main(List<String> rawArguments) async {
if (!_assertsEnabled()) {
print('The gen_keycodes script must be run with --enable-asserts.');
return;
}
final ArgParser argParser = ArgParser();
argParser.addOption(
'engine-root',
......@@ -208,6 +222,14 @@ Future<void> main(List<String> rawArguments) async {
print('Writing ${'key maps'.padRight(15)}${mapsFile.absolute}');
await mapsFile.writeAsString(KeyboardMapsCodeGenerator(physicalData, logicalData).generate());
final File keyCodesFile = File(path.join(PlatformCodeGenerator.engineRoot,
'shell', 'platform', 'embedder', 'test_utils', 'key_codes.h'));
if (!mapsFile.existsSync()) {
mapsFile.createSync(recursive: true);
}
print('Writing ${'engine key codes'.padRight(15)}${mapsFile.absolute}');
await keyCodesFile.writeAsString(KeyCodesCcGenerator(physicalData, logicalData).generate());
final Map<String, PlatformCodeGenerator> platforms = <String, PlatformCodeGenerator>{
'android': AndroidCodeGenerator(
physicalData,
......
......@@ -9,7 +9,6 @@
| [`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.|
......@@ -58,7 +57,7 @@
| [`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_modifier_bit_mapping.json`](gtk_modifier_bit_mapping.json) | Maps a name for GTK's modifier bit macro to Flutter's physical name (element #0), logical name (element #1), and the logical name for the paired key (element #2). This is used to generate checked keys where GTK should keep the pressed state synchronized.|
| [`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)
......
{
"Add": ["PLUS"],
"Again": ["AGAIN"],
"AltLeft": ["ALT_LEFT"],
"AltRight": ["ALT_RIGHT"],
......@@ -7,6 +8,8 @@
"ArrowLeft": ["DPAD_LEFT"],
"ArrowRight": ["DPAD_RIGHT"],
"ArrowUp": ["DPAD_UP"],
"Asterisk": ["STAR"],
"At": ["AT"],
"AudioVolumeDown": ["VOLUME_DOWN"],
"AudioVolumeMute": ["VOLUME_MUTE"],
"AudioVolumeUp": ["VOLUME_UP"],
......@@ -201,6 +204,7 @@
"None": ["UNKNOWN"],
"Notification": ["NOTIFICATION"],
"NumLock": ["NUM_LOCK"],
"NumberSign": ["POUND"],
"Numpad0": ["NUMPAD_0"],
"Numpad1": ["NUMPAD_1"],
"Numpad2": ["NUMPAD_2"],
......
......@@ -7,6 +7,8 @@
#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
......@@ -15,14 +17,6 @@
// 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));
}
std::map<uint64_t, uint64_t> xkb_to_physical_key_map = {
@@@XKB_SCAN_CODE_MAP@@@
};
......@@ -40,3 +34,5 @@ void initialize_lock_bit_to_checked_keys(GHashTable* table) {
FlKeyEmbedderCheckedKey* data;
@@@GTK_MODE_BIT_MAP@@@
}
@@@MASK_CONSTANTS@@@
// 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.
......@@ -36,4 +36,4 @@ const std::map<uint32_t, uint32_t> modifierFlagToKeyCode = {
@@@MODIFIER_FLAG_TO_KEYCODE_MAP@@@
};
@@@SPECIAL_KEY_CONSTANTS@@@
\ No newline at end of file
@@@SPECIAL_KEY_CONSTANTS@@@
// 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_COMMON_TESTING_KEY_CODES_H_
#define FLUTTER_SHELL_PLATFORM_COMMON_TESTING_KEY_CODES_H_
#include <cinttypes>
// 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
// flutter/flutter:dev/tools/gen_keycodes/data/key_codes_cc.tmpl
// instead.
//
// See flutter/flutter:dev/tools/gen_keycodes/README.md for more information.
// This file contains keyboard constants to be used in unit tests. They should
// not be used in production code.
namespace flutter {
namespace testing {
namespace keycodes {
@@@PHYSICAL_KEY_DEFINITIONS@@@
@@@LOGICAL_KEY_DEFINITIONS@@@
} // namespace keycodes
} // namespace testing
} // namespace flutter
#endif // FLUTTER_SHELL_PLATFORM_COMMON_TESTING_KEY_CODES_H_
......@@ -280,7 +280,7 @@ class LogicalKeyboardKey extends KeyboardKey {
/// would be 0x1020000ffff, but once Flutter added the "doWhatIMean" key to
/// the definitions below, the new code would be 0x0020000ffff for all
/// platforms that had a "do what I mean" key from then on.
bool get isAutogenerated => (keyId & autogeneratedMask) != 0;
bool get isAutogenerated => (keyId & planeMask) >= startOfPlatformPlanes;
/// Returns a set of pseudo-key synonyms for the given `key`.
///
......@@ -323,45 +323,10 @@ class LogicalKeyboardKey extends KeyboardKey {
properties.add(StringProperty('debugName', debugName, showName: true, defaultValue: null));
}
/// Mask for the 32-bit value portion of the key code.
///
/// This is used by platform-specific code to generate Flutter key codes.
static const int valueMask = 0x000FFFFFFFF;
/// Mask for the platform prefix portion of the key code.
///
/// This is used by platform-specific code to generate Flutter key codes.
static const int platformMask = 0x0FF00000000;
/// Mask for the 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.
static const int autogeneratedMask = 0x10000000000;
/// 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.
static const int synonymMask = 0x20000000000;
@@@MASK_CONSTANTS@@@
/// The code prefix for keys which have a Unicode representation.
///
/// This is used by platform-specific code to generate Flutter key codes.
static const int unicodePlane = 0x00000000000;
/// The code prefix for keys which do not have a Unicode representation.
///
/// This is used by platform-specific code to generate Flutter key codes using
/// HID Usage codes.
static const int hidPlane = 0x00100000000;
@@@LOGICAL_KEY_DEFINITIONS@@@
// A list of all predefined constant LogicalKeyboardKeys so they can be
// searched.
static const Map<int, LogicalKeyboardKey> _knownLogicalKeys = <int, LogicalKeyboardKey>{
......@@ -373,7 +338,8 @@ class LogicalKeyboardKey extends KeyboardKey {
@@@LOGICAL_KEY_SYNONYMS@@@ };
static const Map<int, String> _keyLabels = <int, String>{
@@@LOGICAL_KEY_KEY_LABELS@@@ };
@@@LOGICAL_KEY_KEY_LABELS@@@
};
}
/// A class with static values that describe the keys that are returned from
......@@ -520,7 +486,9 @@ class PhysicalKeyboardKey extends KeyboardKey {
// Key constants for all keyboard keys in the USB HID specification at the
// time Flutter was built.
@@@PHYSICAL_KEY_DEFINITIONS@@@
// A list of all the predefined constant PhysicalKeyboardKeys so that they
// can be searched.
static const Map<int, PhysicalKeyboardKey> _knownPhysicalKeys = <int, PhysicalKeyboardKey>{
......@@ -530,5 +498,6 @@ class PhysicalKeyboardKey extends KeyboardKey {
static const Map<int, String> _debugNames = kReleaseMode ?
<int, String>{} :
<int, String>{
@@@PHYSICAL_KEY_DEBUG_NAMES@@@ };
@@@PHYSICAL_KEY_DEBUG_NAMES@@@
};
}
This source diff could not be displayed because it is too large. You can view the blob instead.
{
"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",
......
{
"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 data to be added to those that Chromium
// defines.
// This file contains supplemental key data to be added to those that
// Chromium defines.
// ============================================================
// Printable keys
// Printable keys (Unicode plane)
// ============================================================
// Key Enum Unicode code point
......@@ -76,63 +76,105 @@
DOM_KEY_UNI("Tilde", TILDE, '~'),
DOM_KEY_UNI("Bar", BAR, '|'),
// The following keys reside in the Flutter plane (0x0100000000).
// ============================================================
// Game controller buttons
// Miscellaneous (0x000__)
// ============================================================
// 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),
// Key Enum Value
FLUTTER_KEY_MAP("Suspend", SUSPEND, 0x00000),
FLUTTER_KEY_MAP("Resume", RESUME, 0x00001),
FLUTTER_KEY_MAP("Sleep", SLEEP, 0x00002),
FLUTTER_KEY_MAP("Abort", ABORT, 0x00003),
FLUTTER_KEY_MAP("Lang1", LANG1, 0x00010),
FLUTTER_KEY_MAP("Lang2", LANG2, 0x00011),
FLUTTER_KEY_MAP("Lang3", LANG3, 0x00012),
FLUTTER_KEY_MAP("Lang4", LANG4, 0x00013),
FLUTTER_KEY_MAP("Lang5", LANG5, 0x00014),
FLUTTER_KEY_MAP("IntlBackslash", INTL_BACKSLASH, 0x00020),
FLUTTER_KEY_MAP("IntlRo", INTL_RO, 0x00021),
FLUTTER_KEY_MAP("IntlYen", INTL_YEN, 0x00022),
// ============================================================
// Modifiers (0x001__)
// ============================================================
// Key Enum Value
FLUTTER_KEY_MAP("ControlLeft", CONTROL_LEFT, 0x00100),
FLUTTER_KEY_MAP("ControlRight", CONTROL_RIGHT, 0x00101),
FLUTTER_KEY_MAP("ShiftLeft", SHIFT_LEFT, 0x00102),
FLUTTER_KEY_MAP("ShiftRight", SHIFT_RIGHT, 0x00103),
FLUTTER_KEY_MAP("AltLeft", ALT_LEFT, 0x00104),
FLUTTER_KEY_MAP("AltRight", ALT_RIGHT, 0x00105),
FLUTTER_KEY_MAP("MetaLeft", META_LEFT, 0x00106),
FLUTTER_KEY_MAP("MetaRight", META_RIGHT, 0x00107),
// Synonym keys are added for compatibility and will be removed in the future.
FLUTTER_KEY_MAP("Control", CONTROL, 0x001F0),
FLUTTER_KEY_MAP("Shift", SHIFT, 0x001F2),
FLUTTER_KEY_MAP("Alt", ALT, 0x001F4),
FLUTTER_KEY_MAP("Meta", META, 0x001F6),
// ============================================================
// Number pad (0x002__)
// ============================================================
// The value for number pad buttons are derived from their unicode code
// points.
FLUTTER_KEY_MAP("NumpadEnter", NUMPAD_ENTER, 0x0020D),
FLUTTER_KEY_MAP("NumpadParenLeft", NUMPAD_PAREN_LEFT, 0x00228),
FLUTTER_KEY_MAP("NumpadParenRight", NUMPAD_PAREN_RIGHT, 0x00229),
FLUTTER_KEY_MAP("NumpadMultiply", NUMPAD_MULTIPLY, 0x0022A),
FLUTTER_KEY_MAP("NumpadAdd", NUMPAD_ADD, 0x0022B),
FLUTTER_KEY_MAP("NumpadComma", NUMPAD_COMMA, 0x0022C),
FLUTTER_KEY_MAP("NumpadSubtract", NUMPAD_SUBTRACT, 0x0022D),
FLUTTER_KEY_MAP("NumpadDecimal", NUMPAD_DECIMAL, 0x0022E),
FLUTTER_KEY_MAP("NumpadDivide", NUMPAD_DIVIDE, 0x0022F),
FLUTTER_KEY_MAP("Numpad0", NUMPAD_0, 0x00230),
FLUTTER_KEY_MAP("Numpad1", NUMPAD_1, 0x00231),
FLUTTER_KEY_MAP("Numpad2", NUMPAD_2, 0x00232),
FLUTTER_KEY_MAP("Numpad3", NUMPAD_3, 0x00233),
FLUTTER_KEY_MAP("Numpad4", NUMPAD_4, 0x00234),
FLUTTER_KEY_MAP("Numpad5", NUMPAD_5, 0x00235),
FLUTTER_KEY_MAP("Numpad6", NUMPAD_6, 0x00236),
FLUTTER_KEY_MAP("Numpad7", NUMPAD_7, 0x00237),
FLUTTER_KEY_MAP("Numpad8", NUMPAD_8, 0x00238),
FLUTTER_KEY_MAP("Numpad9", NUMPAD_9, 0x00239),
FLUTTER_KEY_MAP("NumpadEqual", NUMPAD_EQUAL, 0x0023D),
// ============================================================
// Other buttons
// Game controller buttons (0x003__)
// ============================================================
// 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),
// The value for game controller buttons are derived from the last 8 bit
// of its USB HID usage.
// Key Enum Value
FLUTTER_KEY_MAP("GameButton1", GAME_BUTTON_1, 0x00301),
FLUTTER_KEY_MAP("GameButton2", GAME_BUTTON_2, 0x00302),
FLUTTER_KEY_MAP("GameButton3", GAME_BUTTON_3, 0x00303),
FLUTTER_KEY_MAP("GameButton4", GAME_BUTTON_4, 0x00304),
FLUTTER_KEY_MAP("GameButton5", GAME_BUTTON_5, 0x00305),
FLUTTER_KEY_MAP("GameButton6", GAME_BUTTON_6, 0x00306),
FLUTTER_KEY_MAP("GameButton7", GAME_BUTTON_7, 0x00307),
FLUTTER_KEY_MAP("GameButton8", GAME_BUTTON_8, 0x00308),
FLUTTER_KEY_MAP("GameButton9", GAME_BUTTON_9, 0x00309),
FLUTTER_KEY_MAP("GameButton10", GAME_BUTTON_10, 0x0030a),
FLUTTER_KEY_MAP("GameButton11", GAME_BUTTON_11, 0x0030b),
FLUTTER_KEY_MAP("GameButton12", GAME_BUTTON_12, 0x0030c),
FLUTTER_KEY_MAP("GameButton13", GAME_BUTTON_13, 0x0030d),
FLUTTER_KEY_MAP("GameButton14", GAME_BUTTON_14, 0x0030e),
FLUTTER_KEY_MAP("GameButton15", GAME_BUTTON_15, 0x0030f),
FLUTTER_KEY_MAP("GameButton16", GAME_BUTTON_16, 0x00310),
FLUTTER_KEY_MAP("GameButtonA", GAME_BUTTON_A, 0x00311),
FLUTTER_KEY_MAP("GameButtonB", GAME_BUTTON_B, 0x00312),
FLUTTER_KEY_MAP("GameButtonC", GAME_BUTTON_C, 0x00313),
FLUTTER_KEY_MAP("GameButtonLeft1", GAME_BUTTON_L1, 0x00314),
FLUTTER_KEY_MAP("GameButtonLeft2", GAME_BUTTON_L2, 0x00315),
FLUTTER_KEY_MAP("GameButtonMode", GAME_BUTTON_MODE, 0x00316),
FLUTTER_KEY_MAP("GameButtonRight1", GAME_BUTTON_R1, 0x00317),
FLUTTER_KEY_MAP("GameButtonRight2", GAME_BUTTON_R2, 0x00318),
FLUTTER_KEY_MAP("GameButtonSelect", GAME_BUTTON_SELECT, 0x00319),
FLUTTER_KEY_MAP("GameButtonStart", GAME_BUTTON_START, 0x0031a),
FLUTTER_KEY_MAP("GameButtonThumbLeft", GAME_BUTTON_THUMBL, 0x0031b),
FLUTTER_KEY_MAP("GameButtonThumbRight", GAME_BUTTON_THUMBR, 0x0031c),
FLUTTER_KEY_MAP("GameButtonX", GAME_BUTTON_X, 0x0031d),
FLUTTER_KEY_MAP("GameButtonY", GAME_BUTTON_Y, 0x0031e),
FLUTTER_KEY_MAP("GameButtonZ", GAME_BUTTON_Z, 0x0031f),
......@@ -9,7 +9,7 @@
// Edit the template dev/tools/gen_keycodes/data/web_key_map_dart.tmpl instead.
// See dev/tools/gen_keycodes/README.md for more information.
/// Maps Web KeyboardEvent codes to the matching LogicalKeyboardKey id.
/// Maps Web KeyboardEvent keys to the matching LogicalKeyboardKey id.
const Map<String, int> kWebToLogicalKey = <String, int>{
@@@WEB_LOGICAL_KEY_CODE_MAP@@@
};
......
......@@ -37,6 +37,8 @@ std::map<uint64_t, uint64_t> KeyboardKeyEmbedderHandler::scanCodeToLogicalMap_ =
@@@WINDOWS_SCAN_CODE_TO_LOGICAL_MAP@@@
};
@@@MASK_CONSTANTS@@@
} // namespace flutter
#endif
......@@ -10,8 +10,7 @@
"Lang1": ["HANGUL", "HANGEUL", "KANA"],
"JunjaMode": ["JUNJA"],
"FinalMode": ["FINAL"],
"HanjaMode": ["HANJA"],
"KanjiMode": ["KANJI"],
"KanjiMode": ["KANJI", "HANJA"],
"Escape": ["ESCAPE"],
"Convert": ["CONVERT"],
"Nonconvert": ["NONCONVERT"],
......@@ -49,7 +48,7 @@
"Numpad9": ["NUMPAD9"],
"NumpadMultiply": ["MULTIPLY"],
"NumpadAdd": ["ADD"],
"NumpadSeparator": ["SEPARATOR"],
"NumpadComma": ["SEPARATOR"],
"NumpadSubtract": ["SUBTRACT"],
"NumpadDecimal": ["DECIMAL"],
"NumpadDivide": ["DIVIDE"],
......
......@@ -2,147 +2,107 @@
// 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;
import 'utils.dart';
class MaskConstant {
const MaskConstant({required this.name, required this.value, required this.description});
const MaskConstant.platform({required String platform, required int value})
: this(
name: '$platform Plane',
value: value,
description: 'The plane value for the private keys defined by the $platform embedding.'
);
final String name;
final int value;
final List<String> description;
final String description;
String get upperCamelName {
return name
.split(' ')
.map<String>((String word) => lowerCamelToUpperCamel(word.toLowerCase()))
.join('');
}
String get lowerCamelName {
final String upperCamel = upperCamelName;
return upperCamel.substring(0, 1).toLowerCase() + upperCamel.substring(1);
}
}
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.'
],
),
];
const MaskConstant kValueMask = MaskConstant(
name: 'Value Mask',
value: 0x00FFFFFFFF,
description: 'Mask for the 32-bit value portion of the key code.',
);
const MaskConstant kPlaneMask = MaskConstant(
name: 'Plane Mask',
value: 0xFF00000000,
description: 'Mask for the plane prefix portion of the key code.',
);
const MaskConstant kUnicodePlane = MaskConstant(
name: 'Unicode Plane',
value: 0x0000000000,
description: 'The plane value for keys which have a Unicode representation.',
);
const MaskConstant kUnprintablePlane = MaskConstant(
name: 'Unprintable Plane',
value: 0x0100000000,
description: 'The plane value for keys defined by Chromium and does not have a Unicode representation.',
);
const MaskConstant kFlutterPlane = MaskConstant(
name: 'Flutter Plane',
value: 0x0200000000,
description: 'The plane value for keys defined by Flutter.',
);
const MaskConstant kStartOfPlatformPlanes = MaskConstant(
name: 'Start Of Platform Planes',
value: 0x1100000000,
description: 'The platform plane with the lowest mask value, beyond which the keys are considered autogenerated.',
);
const MaskConstant kAndroidPlane = MaskConstant.platform(
platform: 'Android',
value: 0x1100000000,
);
const MaskConstant kFuchsiaPlane = MaskConstant.platform(
platform: 'Fuchsia',
value: 0x1200000000,
);
const MaskConstant kIosPlane = MaskConstant.platform(
platform: 'iOS',
value: 0x1300000000,
);
const MaskConstant kMacosPlane = MaskConstant.platform(
platform: 'macOS',
value: 0x1400000000,
);
const MaskConstant kGtkPlane = MaskConstant.platform(
platform: 'Gtk',
value: 0x1500000000,
);
const MaskConstant kWindowsPlane = MaskConstant.platform(
platform: 'Windows',
value: 0x1600000000,
);
const MaskConstant kWebPlane = MaskConstant.platform(
platform: 'Web',
value: 0x1700000000,
);
const MaskConstant kGlfwPlane = MaskConstant.platform(
platform: 'GLFW',
value: 0x1800000000,
);
......@@ -5,6 +5,7 @@
import 'package:path/path.dart' as path;
import 'base_code_gen.dart';
import 'constants.dart';
import 'logical_key_data.dart';
import 'physical_key_data.dart';
import 'utils.dart';
......@@ -55,27 +56,27 @@ class GtkCodeGenerator extends PlatformCodeGenerator {
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 String primaryPhysicalName = keyNames[0];
final String primaryLogicalName = keyNames[1];
final String? secondaryLogicalName = keyNames.length == 3 ? keyNames[2] : null;
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.');
final LogicalKeyEntry primaryLogical = logicalData.entryByName(primaryLogicalName);
final LogicalKeyEntry? secondaryLogical = secondaryLogicalName == null ? null : logicalData.entryByName(secondaryLogicalName);
if (secondaryLogical == null && secondaryLogicalName != null) {
print('Unrecognized secondary logical key $secondaryLogicalName specified for $debugFunctionName.');
return;
}
final String pad = secondaryPhysical == null ? '' : ' ';
final String pad = secondaryLogical == 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) {
data->primary_physical_key = ${toHex(primaryPhysical.usbHidCode, digits: 9)};$pad // ${primaryPhysical.constantName}
data->primary_logical_key = ${toHex(primaryLogical.value, digits: 11)};$pad // ${primaryLogical.constantName}''');
if (secondaryLogical != null) {
result.writeln('''
data->secondary_physical_key = ${toHex(secondaryPhysical.usbHidCode, digits: 9)}; // ${secondaryPhysical.constantName}''');
data->secondary_logical_key = ${toHex(secondaryLogical.value, digits: 11)}; // ${secondaryLogical.constantName}''');
}
});
return result.toString().trimRight();
......@@ -91,6 +92,20 @@ class GtkCodeGenerator extends PlatformCodeGenerator {
}
final Map<String, List<String>> _lockBitMapping;
/// This generates the mask values for the part of a key code that defines its plane.
String get _maskConstants {
final StringBuffer buffer = StringBuffer();
const List<MaskConstant> maskConstants = <MaskConstant>[
kValueMask,
kUnicodePlane,
kGtkPlane,
];
for (final MaskConstant constant in maskConstants) {
buffer.writeln('const uint64_t k${constant.upperCamelName} = ${toHex(constant.value, digits: 11)};');
}
return buffer.toString().trimRight();
}
@override
String get templatePath => path.join(dataRoot, 'gtk_key_mapping_cc.tmpl');
......@@ -105,6 +120,7 @@ class GtkCodeGenerator extends PlatformCodeGenerator {
'GTK_KEYVAL_CODE_MAP': _gtkKeyvalCodeMap,
'GTK_MODIFIER_BIT_MAP': _gtkModifierBitMap,
'GTK_MODE_BIT_MAP': _gtkModeBitMap,
'MASK_CONSTANTS': _maskConstants,
};
}
}
......@@ -25,10 +25,6 @@ const List<String> kModifiersOfInterest = <String>[
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 iOS, based on the information in the key
/// data structure given to it.
class IOSCodeGenerator extends PlatformCodeGenerator {
......@@ -37,35 +33,39 @@ class IOSCodeGenerator extends PlatformCodeGenerator {
/// This generates the map of iOS key codes to physical keys.
String get _scanCodeMap {
final StringBuffer scanCodeMap = StringBuffer();
final OutputLines<int> lines = OutputLines<int>('iOS scancode map');
for (final PhysicalKeyEntry entry in keyData.entries) {
if (entry.iOSScanCode != null) {
scanCodeMap.writeln(' {${toHex(entry.iOSScanCode)}, ${toHex(entry.usbHidCode)}}, // ${entry.constantName}');
lines.add(entry.iOSScanCode!,
' {${toHex(entry.iOSScanCode)}, ${toHex(entry.usbHidCode)}}, // ${entry.constantName}');
}
}
return scanCodeMap.toString().trimRight();
return lines.sortedJoin().trimRight();
}
String get _keyCodeToLogicalMap {
final StringBuffer result = StringBuffer();
final OutputLines<int> lines = OutputLines<int>('iOS keycode map');
for (final LogicalKeyEntry entry in logicalData.entries) {
zipStrict(entry.iOSKeyCodeValues, entry.iOSKeyCodeNames, (int iOSValue, String iOSName) {
result.writeln(' {${toHex(iOSValue)}, ${toHex(entry.value, digits: 11)}}, // $iOSName');
lines.add(iOSValue, ' {${toHex(iOSValue)}, ${toHex(entry.value, digits: 11)}}, // $iOSName');
});
}
return result.toString().trimRight();
return lines.sortedJoin().trimRight();
}
/// This generates the mask values for the part of a key code that defines its plane.
String get _maskConstants {
final StringBuffer buffer = StringBuffer();
const List<MaskConstant> maskConstants = <MaskConstant>[
kValueMask,
kUnicodePlane,
kIosPlane,
];
for (final MaskConstant constant in maskConstants) {
buffer.writeln('/**');
buffer.write(constant.description
.map((String line) => wrapString(line, prefix: ' * '))
.join(' *\n'));
buffer.write(wrapString(constant.description, prefix: ' * '));
buffer.writeln(' */');
buffer.writeln('const uint64_t ${_toConstantVariableName(constant.name)} = ${toHex(constant.value, digits: 11)};');
buffer.writeln('const uint64_t k${constant.upperCamelName} = ${toHex(constant.value, digits: 11)};');
buffer.writeln('');
}
return buffer.toString().trimRight();
......@@ -75,7 +75,8 @@ class IOSCodeGenerator extends PlatformCodeGenerator {
String get _keyToModifierFlagMap {
final StringBuffer modifierKeyMap = StringBuffer();
for (final String name in kModifiersOfInterest) {
modifierKeyMap.writeln(' {${toHex(logicalData.entryByName(name).iOSKeyCodeValues[0])}, kModifierFlag${lowerCamelToUpperCamel(name)}},');
final String line = '{${toHex(logicalData.entryByName(name).iOSKeyCodeValues[0])}, kModifierFlag${lowerCamelToUpperCamel(name)}},';
modifierKeyMap.writeln(' ${line.padRight(42)}// $name');
}
return modifierKeyMap.toString().trimRight();
}
......@@ -84,7 +85,8 @@ class IOSCodeGenerator extends PlatformCodeGenerator {
String get _modifierFlagToKeyMap {
final StringBuffer modifierKeyMap = StringBuffer();
for (final String name in kModifiersOfInterest) {
modifierKeyMap.writeln(' {kModifierFlag${lowerCamelToUpperCamel(name)}, ${toHex(logicalData.entryByName(name).iOSKeyCodeValues[0])}},');
final String line = '{kModifierFlag${lowerCamelToUpperCamel(name)}, ${toHex(logicalData.entryByName(name).iOSKeyCodeValues[0])}},';
modifierKeyMap.writeln(' ${line.padRight(42)}// $name');
}
return modifierKeyMap.toString().trimRight();
}
......
......@@ -16,6 +16,23 @@ String _wrapString(String input) {
return wrapString(input, prefix: ' /// ');
}
final List<MaskConstant> _maskConstants = <MaskConstant>[
kValueMask,
kPlaneMask,
kUnicodePlane,
kUnprintablePlane,
kFlutterPlane,
kStartOfPlatformPlanes,
kAndroidPlane,
kFuchsiaPlane,
kIosPlane,
kMacosPlane,
kGtkPlane,
kWindowsPlane,
kWebPlane,
kGlfwPlane,
];
class SynonymKeyInfo {
SynonymKeyInfo(this.keys, this.name);
......@@ -26,7 +43,6 @@ class SynonymKeyInfo {
// 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);
}
......@@ -37,39 +53,36 @@ class KeyboardKeysCodeGenerator extends BaseCodeGenerator {
/// Gets the generated definitions of PhysicalKeyboardKeys.
String get _physicalDefinitions {
final StringBuffer definitions = StringBuffer();
final OutputLines<int> lines = OutputLines<int>('Physical Key Definition');
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 '
'[KeyEvent.physical] for more information.');
definitions.write('''
'[RawKeyEvent.physicalKey] for more information.');
lines.add(entry.usbHidCode, '''
$firstComment ///
$otherComments static const PhysicalKeyboardKey ${entry.constantName} = PhysicalKeyboardKey(${toHex(entry.usbHidCode, digits: 8)});
''');
}
return definitions.toString();
return lines.sortedJoin().trimRight();
}
String get _physicalDebugNames {
final StringBuffer result = StringBuffer();
final OutputLines<int> lines = OutputLines<int>('Physical debug names');
for (final PhysicalKeyEntry entry in keyData.entries) {
result.write('''
${toHex(entry.usbHidCode, digits: 8)}: '${entry.commentName}',
''');
lines.add(entry.usbHidCode, '''
${toHex(entry.usbHidCode, digits: 8)}: '${entry.commentName}',''');
}
return result.toString();
return lines.sortedJoin().trimRight();
}
/// Gets the generated definitions of LogicalKeyboardKeys.
String get _logicalDefinitions {
final StringBuffer definitions = StringBuffer();
final OutputLines<int> lines = OutputLines<int>('Logical debug names');
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 [KeyEvent.logical] for more information.');
definitions.write('''
otherComments ??= _wrapString('See the function [RawKeyEvent.logicalKey] for more information.');
lines.add(flutterId, '''
$firstComment ///
$otherComments static const LogicalKeyboardKey $constantName = LogicalKeyboardKey(${toHex(flutterId, digits: 11)});
''');
......@@ -80,67 +93,71 @@ $otherComments static const LogicalKeyboardKey $constantName = LogicalKeyboardK
entry.value,
entry.constantName,
entry.commentName,
otherComments: _otherComments(entry.name),
);
}
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 Set<String> unionNames = synonymInfo.keys.map(
return lines.sortedJoin().trimRight();
}
String? _otherComments(String name) {
if (synonyms.containsKey(name)) {
final Set<String> unionNames = synonyms[name]!.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 '
return _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.'));
'directly, its main use is in defining key maps.');
}
return definitions.toString();
return null;
}
String get _logicalSynonyms {
final StringBuffer result = StringBuffer();
for (final SynonymKeyInfo synonymInfo in synonyms) {
for (final SynonymKeyInfo synonymInfo in synonyms.values) {
for (final LogicalKeyEntry key in synonymInfo.keys) {
final String synonymName = upperCamelToLowerCamel(synonymInfo.name);
result.writeln(' ${key.constantName}: $synonymName,');
final LogicalKeyEntry synonnym = logicalData.entryByName(synonymInfo.name);
result.writeln(' ${key.constantName}: ${synonnym.constantName},');
}
}
return result.toString();
}
String get _logicalKeyLabels {
final StringBuffer result = StringBuffer();
final OutputLines<int> lines = OutputLines<int>('Logical key labels');
for (final LogicalKeyEntry entry in logicalData.entries) {
result.write('''
${toHex(entry.value, digits: 11)}: '${entry.commentName}',
''');
}
for (final SynonymKeyInfo synonymInfo in synonyms) {
result.write('''
${toHex(synonymInfo.value)}: '${synonymInfo.name}',
''');
lines.add(entry.value, '''
${toHex(entry.value, digits: 11)}: '${entry.commentName}',''');
}
return result.toString();
return lines.sortedJoin().trimRight();
}
/// This generates the map of USB HID codes to physical keys.
String get _predefinedHidCodeMap {
final StringBuffer scanCodeMap = StringBuffer();
final OutputLines<int> lines = OutputLines<int>('Physical key map');
for (final PhysicalKeyEntry entry in keyData.entries) {
scanCodeMap.writeln(' ${toHex(entry.usbHidCode)}: ${entry.constantName},');
lines.add(entry.usbHidCode, ' ${toHex(entry.usbHidCode)}: ${entry.constantName},');
}
return scanCodeMap.toString().trimRight();
return lines.sortedJoin().trimRight();
}
/// This generates the map of Flutter key codes to logical keys.
String get _predefinedKeyCodeMap {
final StringBuffer keyCodeMap = StringBuffer();
final OutputLines<int> lines = OutputLines<int>('Logical key map');
for (final LogicalKeyEntry entry in logicalData.entries) {
keyCodeMap.writeln(' ${toHex(entry.value, digits: 11)}: ${entry.constantName},');
lines.add(entry.value, ' ${toHex(entry.value, digits: 11)}: ${entry.constantName},');
}
for (final SynonymKeyInfo synonymInfo in synonyms) {
keyCodeMap.writeln(' ${toHex(synonymInfo.value, digits: 11)}: ${synonymInfo.constantName},');
return lines.sortedJoin().trimRight();
}
String get _maskConstantVariables {
final OutputLines<int> lines = OutputLines<int>('Mask constants', checkDuplicate: false);
for (final MaskConstant constant in _maskConstants) {
lines.add(constant.value, '''
${_wrapString(constant.description)} ///
/// This is used by platform-specific code to generate Flutter key codes.
static const int ${constant.lowerCamelName} = ${toHex(constant.value, digits: 11)};
''');
}
return keyCodeMap.toString().trimRight();
return lines.join().trimRight();
}
@override
......@@ -156,17 +173,21 @@ $otherComments static const LogicalKeyboardKey $constantName = LogicalKeyboardK
'PHYSICAL_KEY_MAP': _predefinedHidCodeMap,
'PHYSICAL_KEY_DEFINITIONS': _physicalDefinitions,
'PHYSICAL_KEY_DEBUG_NAMES': _physicalDebugNames,
'MASK_CONSTANTS': _maskConstantVariables,
};
}
late final List<SynonymKeyInfo> synonyms = LogicalKeyData.synonyms.entries.map(
(MapEntry<String, List<String>> synonymDefinition) {
late final Map<String, SynonymKeyInfo> synonyms = Map<String, SynonymKeyInfo>.fromEntries(
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,
return MapEntry<String, SynonymKeyInfo>(
synonymDefinition.key,
SynonymKeyInfo(
entries,
synonymDefinition.key,
),
);
}
).toList();
}),
);
}
......@@ -51,24 +51,24 @@ class KeyboardMapsCodeGenerator extends BaseCodeGenerator {
/// This generates the map of GLFW number pad key codes to logical keys.
String get _glfwNumpadMap {
final StringBuffer glfwNumpadMap = StringBuffer();
final OutputLines<int> lines = OutputLines<int>('GLFW numpad map');
for (final PhysicalKeyEntry entry in _numpadKeyData) {
for (final int code in entry.glfwKeyCodes) {
glfwNumpadMap.writeln(' $code: LogicalKeyboardKey.${entry.constantName},');
lines.add(code, ' $code: LogicalKeyboardKey.${entry.constantName},');
}
}
return glfwNumpadMap.toString().trimRight();
return lines.sortedJoin().trimRight();
}
/// This generates the map of GLFW key codes to logical keys.
String get _glfwKeyCodeMap {
final StringBuffer glfwKeyCodeMap = StringBuffer();
final OutputLines<int> lines = OutputLines<int>('GLFW key code map');
for (final PhysicalKeyEntry entry in keyData.entries) {
for (final int code in entry.glfwKeyCodes) {
glfwKeyCodeMap.writeln(' $code: LogicalKeyboardKey.${entry.constantName},');
lines.add(code, ' $code: LogicalKeyboardKey.${entry.constantName},');
}
}
return glfwKeyCodeMap.toString().trimRight();
return lines.sortedJoin().trimRight();
}
/// This generates the map of GTK number pad key codes to logical keys.
......@@ -95,13 +95,14 @@ class KeyboardMapsCodeGenerator extends BaseCodeGenerator {
/// This generates the map of XKB USB HID codes to physical keys.
String get _xkbScanCodeMap {
final StringBuffer xkbScanCodeMap = StringBuffer();
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)}: PhysicalKeyboardKey.${entry.constantName},');
lines.add(entry.xKbScanCode!,
' ${toHex(entry.xKbScanCode)}: PhysicalKeyboardKey.${entry.constantName},');
}
}
return xkbScanCodeMap.toString().trimRight();
return lines.sortedJoin().trimRight();
}
/// This generates the map of Android key codes to logical keys.
......@@ -128,26 +129,26 @@ class KeyboardMapsCodeGenerator extends BaseCodeGenerator {
/// This generates the map of Android scan codes to physical keys.
String get _androidScanCodeMap {
final StringBuffer androidScanCodeMap = StringBuffer();
final OutputLines<int> lines = OutputLines<int>('Android scancode map');
for (final PhysicalKeyEntry entry in keyData.entries) {
if (entry.androidScanCodes != null) {
for (final int code in entry.androidScanCodes) {
androidScanCodeMap.writeln(' $code: PhysicalKeyboardKey.${entry.constantName},');
lines.add(code, ' $code: PhysicalKeyboardKey.${entry.constantName},');
}
}
}
return androidScanCodeMap.toString().trimRight();
return lines.sortedJoin().trimRight();
}
/// This generates the map of Windows scan codes to physical keys.
String get _windowsScanCodeMap {
final StringBuffer windowsScanCodeMap = StringBuffer();
final OutputLines<int> lines = OutputLines<int>('Windows scancode map');
for (final PhysicalKeyEntry entry in keyData.entries) {
if (entry.windowsScanCode != null) {
windowsScanCodeMap.writeln(' ${toHex(entry.windowsScanCode)}: PhysicalKeyboardKey.${entry.constantName},');
lines.add(entry.windowsScanCode!, ' ${entry.windowsScanCode}: PhysicalKeyboardKey.${entry.constantName},');
}
}
return windowsScanCodeMap.toString().trimRight();
return lines.sortedJoin().trimRight();
}
/// This generates the map of Windows number pad key codes to logical keys.
......@@ -181,34 +182,34 @@ class KeyboardMapsCodeGenerator extends BaseCodeGenerator {
/// This generates the map of macOS key codes to physical keys.
String get _macOSScanCodeMap {
final StringBuffer macOSScanCodeMap = StringBuffer();
final OutputLines<int> lines = OutputLines<int>('macOS scancode map');
for (final PhysicalKeyEntry entry in keyData.entries) {
if (entry.macOSScanCode != null) {
macOSScanCodeMap.writeln(' ${toHex(entry.macOSScanCode)}: PhysicalKeyboardKey.${entry.constantName},');
lines.add(entry.macOSScanCode!, ' ${toHex(entry.macOSScanCode)}: PhysicalKeyboardKey.${entry.constantName},');
}
}
return macOSScanCodeMap.toString().trimRight();
return lines.sortedJoin().trimRight();
}
/// This generates the map of macOS number pad key codes to logical keys.
String get _macOSNumpadMap {
final StringBuffer macOSNumPadMap = StringBuffer();
final OutputLines<int> lines = OutputLines<int>('macOS numpad map');
for (final PhysicalKeyEntry entry in _numpadKeyData) {
if (entry.macOSScanCode != null) {
macOSNumPadMap.writeln(' ${toHex(entry.macOSScanCode)}: LogicalKeyboardKey.${entry.constantName},');
lines.add(entry.macOSScanCode!, ' ${toHex(entry.macOSScanCode)}: LogicalKeyboardKey.${entry.constantName},');
}
}
return macOSNumPadMap.toString().trimRight();
return lines.sortedJoin().trimRight();
}
String get _macOSFunctionKeyMap {
final StringBuffer macOSFunctionKeyMap = StringBuffer();
final OutputLines<int> lines = OutputLines<int>('macOS function key map');
for (final PhysicalKeyEntry entry in _functionKeyData) {
if (entry.macOSScanCode != null) {
macOSFunctionKeyMap.writeln(' ${toHex(entry.macOSScanCode)}: LogicalKeyboardKey.${entry.constantName},');
lines.add(entry.macOSScanCode!, ' ${toHex(entry.macOSScanCode)}: LogicalKeyboardKey.${entry.constantName},');
}
}
return macOSFunctionKeyMap.toString().trimRight();
return lines.sortedJoin().trimRight();
}
/// This generates the map of macOS key codes to physical keys.
......@@ -290,38 +291,38 @@ class KeyboardMapsCodeGenerator extends BaseCodeGenerator {
/// This generates the map of Web KeyboardEvent codes to physical keys.
String get _webPhysicalKeyMap {
final StringBuffer result = StringBuffer();
final OutputLines<String> lines = OutputLines<String>('Web physical key map');
for (final PhysicalKeyEntry entry in keyData.entries) {
if (entry.name != null) {
result.writeln(" '${entry.name}': PhysicalKeyboardKey.${entry.constantName},");
lines.add(entry.name, " '${entry.name}': PhysicalKeyboardKey.${entry.constantName},");
}
}
return result.toString().trimRight();
return lines.sortedJoin().trimRight();
}
String get _webNumpadMap {
final StringBuffer result = StringBuffer();
final OutputLines<String> lines = OutputLines<String>('Web numpad map');
for (final LogicalKeyEntry entry in _numpadLogicalKeyData) {
if (entry.name != null) {
result.writeln(" '${entry.name}': LogicalKeyboardKey.${entry.constantName},");
lines.add(entry.name, " '${entry.name}': LogicalKeyboardKey.${entry.constantName},");
}
}
return result.toString().trimRight();
return lines.sortedJoin().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();
final OutputLines<String> lines = OutputLines<String>('Web location map');
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],");
lines.add(key, " '$key': <LogicalKeyboardKey?>[$keyStrings],");
});
return result.toString().trimRight();
return lines.sortedJoin().trimRight();
}
@override
......
......@@ -120,15 +120,17 @@ class LogicalKeyData {
/// 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.
/// Flutter's supplemental_key_data.inc also has some new formats.
/// The following format uses a character as the 3rd argument.
/// Key Enum Character
/// DOM_KEY_UNI("KeyB", KEY_B, 'b'),
///
/// The following format should be mapped to the Flutter plane.
/// Key Enum Character
/// FLUTTER_KEY_MAP("Lang4", LANG4, 0x00013),
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'(?<source>DOM|FLUTTER)_KEY_(?<kind>UNI|MAP)\s*\(\s*'
r'"(?<name>[^\s]+?)",\s*'
r'(?<enum>[^\s]+?),\s*'
r"(?:0[xX](?<unicode>[a-fA-F0-9]+)|'(?<char>.)')\s*"
......@@ -140,6 +142,7 @@ class LogicalKeyData {
final RegExp commentRegExp = RegExp(r'//.*$', multiLine: true);
input = input.replaceAll(commentRegExp, '');
for (final RegExpMatch match in domKeyRegExp.allMatches(input)) {
final String source = match.namedGroup('source')!;
final String webName = match.namedGroup('name')!;
// ".AltGraphLatch" is consumed internally and not expressed to the Web.
if (webName.startsWith('.')) {
......@@ -150,51 +153,21 @@ class LogicalKeyData {
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);
data[pair.right] = LogicalKeyEntry.fromName(
value: value + kRightModifierPlane,
name: pair.right,
keyLabel: null, // Modifier keys don't have keyLabels
)..webNames.add(pair.right);
// Skip modifier keys from DOM. They will be added with supplemental data.
if (_chromeModifiers.containsKey(name) && source == 'DOM') {
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);
unusedNumpad.remove(char);
}
final bool isPrintable = (keyLabel != null && !_isControlCharacter(keyLabel))
|| printable.containsKey(name);
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),
value: toPlane(value, _sourceToPlane(source, isPrintable)),
name: name,
keyLabel: keyLabel,
)..webNames.add(webName);
});
}
// Make sure every Numpad key that we care about has been defined.
unusedNumpad.forEach((String key, String value) {
print('Undefined numpad key $value');
});
}
static void _readMacOsKeyCodes(
......@@ -347,11 +320,11 @@ class LogicalKeyData {
return 0;
final String? keyLabel = printable[entry.constantName];
if (keyLabel != null && !entry.constantName.startsWith('numpad')) {
return kUnicodePlane | (keyLabel.codeUnitAt(0) & kValueMask);
return toPlane(keyLabel.codeUnitAt(0), kUnicodePlane.value);
} else {
final PhysicalKeyEntry? physicalEntry = physicalData.tryEntryByName(entry.name);
if (physicalEntry != null) {
return kHidPlane | (physicalEntry.usbHidCode & kValueMask);
return toPlane(physicalEntry.usbHidCode, kFuchsiaPlane.value);
}
}
})();
......@@ -376,14 +349,6 @@ class LogicalKeyData {
.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
......@@ -400,6 +365,18 @@ class LogicalKeyData {
return MapEntry<String, List<String>>(name, names);
});
})();
static int _sourceToPlane(String source, bool isPrintable) {
switch (source) {
case 'DOM':
return isPrintable ? kUnicodePlane.value : kUnprintablePlane.value;
case 'FLUTTER':
return kFlutterPlane.value;
default:
assert(false, 'Unrecognized logical key source $source');
return kFlutterPlane.value;
}
}
}
......
......@@ -25,10 +25,6 @@ const List<String> kModifiersOfInterest = <String>[
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 {
......@@ -37,36 +33,36 @@ class MacOSCodeGenerator extends PlatformCodeGenerator {
/// This generates the map of macOS key codes to physical keys.
String get _scanCodeMap {
final StringBuffer scanCodeMap = StringBuffer();
final OutputLines<int> lines = OutputLines<int>('macOS scancode map');
for (final PhysicalKeyEntry entry in keyData.entries) {
if (entry.macOSScanCode != null) {
scanCodeMap.writeln(' @${toHex(entry.macOSScanCode)} : @${toHex(entry.usbHidCode)}, // ${entry.constantName}');
lines.add(entry.macOSScanCode!, ' @${toHex(entry.macOSScanCode)} : @${toHex(entry.usbHidCode)}, // ${entry.constantName}');
}
}
return scanCodeMap.toString().trimRight();
return lines.sortedJoin().trimRight();
}
String get _keyCodeToLogicalMap {
final StringBuffer result = StringBuffer();
final OutputLines<int> lines = OutputLines<int>('macOS keycode map');
for (final LogicalKeyEntry entry in logicalData.entries) {
zipStrict(entry.macOSKeyCodeValues, entry.macOSKeyCodeNames, (int macOSValue, String macOSName) {
result.writeln(' @${toHex(macOSValue)} : @${toHex(entry.value, digits: 11)}, // $macOSName');
lines.add(macOSValue,
' @${toHex(macOSValue)} : @${toHex(entry.value, digits: 11)}, // $macOSName -> ${entry.constantName}');
});
}
return result.toString().trimRight();
return lines.sortedJoin().trimRight();
}
/// This generates the mask values for the part of a key code that defines its plane.
String get _maskConstants {
final StringBuffer buffer = StringBuffer();
const List<MaskConstant> maskConstants = <MaskConstant>[
kValueMask,
kUnicodePlane,
kMacosPlane,
];
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('');
buffer.writeln('const uint64_t k${constant.upperCamelName} = ${toHex(constant.value, digits: 11)};');
}
return buffer.toString().trimRight();
}
......
......@@ -220,7 +220,7 @@ class PhysicalKeyData {
final String enumName = match.namedGroup('enum')!.replaceAll('MINIMIUM', 'MINIMUM');
final String name = chromiumCode ?? shoutingToUpperCamel(enumName);
if (name == 'IntlHash') {
if (name == 'IntlHash' || name == 'None') {
// Skip key that is not actually generated by any keyboard.
continue;
}
......
// 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 'logical_key_data.dart';
import 'physical_key_data.dart';
import 'utils.dart';
String _toUpperCammel(String lowerCammel) {
return lowerCammel.substring(0, 1).toUpperCase() + lowerCammel.substring(1);
}
/// Generates the common/testing/key_codes.h based on the information in the key
/// data structure given to it.
class KeyCodesCcGenerator extends BaseCodeGenerator {
KeyCodesCcGenerator(PhysicalKeyData keyData, LogicalKeyData logicalData) : super(keyData, logicalData);
/// Gets the generated definitions of PhysicalKeyboardKeys.
String get _physicalDefinitions {
final OutputLines<int> lines = OutputLines<int>('Physical Key list');
for (final PhysicalKeyEntry entry in keyData.entries) {
lines.add(entry.usbHidCode, '''
constexpr uint64_t kPhysical${_toUpperCammel(entry.constantName)} = ${toHex(entry.usbHidCode, digits: 8)};''');
}
return lines.sortedJoin().trimRight();
}
/// Gets the generated definitions of PhysicalKeyboardKeys.
String get _logicalDefinitions {
final OutputLines<int> lines = OutputLines<int>('Logical Key list');
for (final LogicalKeyEntry entry in logicalData.entries) {
lines.add(entry.value, '''
constexpr uint64_t kLogical${_toUpperCammel(entry.constantName)} = ${toHex(entry.value, digits: 11)};''');
}
return lines.sortedJoin().trimRight();
}
@override
String get templatePath => path.join(dataRoot, 'key_codes_h.tmpl');
@override
Map<String, String> mappings() {
return <String, String>{
'LOGICAL_KEY_DEFINITIONS': _logicalDefinitions,
'PHYSICAL_KEY_DEFINITIONS': _physicalDefinitions,
};
}
}
......@@ -7,6 +7,8 @@ import 'dart:io';
import 'package:path/path.dart' as path;
import 'constants.dart';
/// 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;
......@@ -221,32 +223,63 @@ void addNameValue(List<String> names, List<int> values, String name, int value)
}
}
/// The information for a line used by [OutputLines].
class OutputLine<T extends Comparable<Object>> {
const OutputLine(this.key, this.value);
final T key;
final String value;
}
/// 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);
OutputLines(this.mapName, {this.checkDuplicate = true});
/// If true, then lines with duplicate keys will be warned and discarded.
///
/// Default to true.
final bool checkDuplicate;
/// The name for this map.
///
/// Used in warning messages.
final String mapName;
final Map<T, String> lines = <T, String>{};
final Set<T> keys = <T>{};
final List<OutputLine<T>> lines = <OutputLine<T>>[];
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]}');
if (checkDuplicate) {
if (keys.contains(code)) {
final OutputLine<T> existing = lines.firstWhere((OutputLine<T> line) => line.key == code);
print('Warn: $mapName is requested to add line $code as:\n $line\n but it already exists as:\n ${existing.value}');
return;
}
keys.add(code);
}
lines[code] = line;
lines.add(OutputLine<T>(code, line));
}
String join() {
return lines.map((OutputLine<T> line) => line.value).join('\n');
}
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)
return (lines.sublist(0)
..sort((OutputLine<T> a, OutputLine<T> b) => a.key.compareTo(b.key)))
.map((OutputLine<T> line) => line.value)
.join('\n');
}
}
int toPlane(int value, int plane) {
return (value & kValueMask.value) + (plane & kPlaneMask.value);
}
int getPlane(int value) {
return value & kPlaneMask.value;
}
......@@ -5,6 +5,7 @@
import 'package:path/path.dart' as path;
import 'base_code_gen.dart';
import 'constants.dart';
import 'logical_key_data.dart';
import 'physical_key_data.dart';
import 'utils.dart';
......@@ -23,8 +24,11 @@ class WebCodeGenerator extends PlatformCodeGenerator {
String get _webLogicalKeyCodeMap {
final OutputLines<String> lines = OutputLines<String>('Web logical map');
for (final LogicalKeyEntry entry in logicalData.entries) {
for (final String name in entry.webNames) {
lines.add(name, " '$name': ${toHex(entry.value, digits: 11)},");
final int plane = getPlane(entry.value);
if (plane == kUnprintablePlane.value) {
for (final String name in entry.webNames) {
lines.add(name, " '$name': ${toHex(entry.value, digits: 11)},");
}
}
}
return lines.sortedJoin().trimRight();
......@@ -32,25 +36,29 @@ 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();
final OutputLines<String> lines = OutputLines<String>('Web physical map');
for (final PhysicalKeyEntry entry in keyData.entries) {
if (entry.name != null) {
result.writeln(" '${entry.name}': ${toHex(entry.usbHidCode)},");
lines.add(entry.name,
" '${entry.name}': ${toHex(entry.usbHidCode)}, // ${entry.constantName}");
}
}
return result.toString().trimRight();
return lines.sortedJoin().trimRight();
}
/// This generates the map of Web number pad codes to logical key ids.
String get _webLogicalLocationMap {
final StringBuffer result = StringBuffer();
final OutputLines<String> lines = OutputLines<String>('Web logical location map');
_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);
return value == null ? 'null' : toHex(logicalData.entryByName(value).value, digits: 11);
}).join(', ');
final String namesString = locations.map((String? value) {
return value == null ? 'null' : logicalData.entryByName(value).constantName;
}).join(', ');
result.writeln(" '$webKey': <int?>[$valuesString],");
lines.add(webKey, " '$webKey': <int?>[$valuesString], // $namesString");
});
return result.toString().trimRight();
return lines.sortedJoin().trimRight();
}
final Map<String, List<String?>> _logicalLocationMap;
......
......@@ -5,6 +5,7 @@
import 'package:path/path.dart' as path;
import 'base_code_gen.dart';
import 'constants.dart';
import 'logical_key_data.dart';
import 'physical_key_data.dart';
import 'utils.dart';
......@@ -21,24 +22,29 @@ class WindowsCodeGenerator extends PlatformCodeGenerator {
/// This generates the map of Windows scan codes to physical keys.
String get _windowsScanCodeMap {
final StringBuffer windowsScanCodeMap = StringBuffer();
final OutputLines<int> lines = OutputLines<int>('Windows scancode map');
for (final PhysicalKeyEntry entry in keyData.entries) {
if (entry.windowsScanCode != null) {
windowsScanCodeMap.writeln(' {${toHex(entry.windowsScanCode)}, ${toHex(entry.usbHidCode)}}, // ${entry.constantName}');
lines.add(entry.windowsScanCode!,
' {${toHex(entry.windowsScanCode)}, ${toHex(entry.usbHidCode)}}, // ${entry.constantName}');
}
}
return windowsScanCodeMap.toString().trimRight();
return lines.sortedJoin().trimRight();
}
/// This generates the map of Windows key codes to logical keys.
String get _windowsLogicalKeyCodeMap {
final StringBuffer result = StringBuffer();
final OutputLines<int> lines = OutputLines<int>('Windows logical map');
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');
});
zipStrict(entry.windowsValues, entry.windowsNames,
(int windowsValue, String windowsName) {
lines.add(windowsValue,
' {${toHex(windowsValue)}, ${toHex(entry.value, digits: 11)}}, '
'// $windowsName -> ${entry.constantName}');
},
);
}
return result.toString().trimRight();
return lines.sortedJoin().trimRight();
}
/// This generates the map from scan code to logical keys.
......@@ -47,16 +53,32 @@ class WindowsCodeGenerator extends PlatformCodeGenerator {
/// 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();
final OutputLines<int> lines = OutputLines<int>('Windows scancode to logical map');
_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}');
final LogicalKeyEntry logicalEntry = logicalData.entryByName(logicalName);
lines.add(physicalEntry.windowsScanCode!,
' {${toHex(physicalEntry.windowsScanCode)}, ${toHex(logicalEntry.value, digits: 11)}}, '
'// ${physicalEntry.constantName} -> ${logicalEntry.constantName}');
});
return result.toString().trimRight();
return lines.sortedJoin().trimRight();
}
final Map<String, String> _scancodeToLogical;
/// This generates the mask values for the part of a key code that defines its plane.
String get _maskConstants {
final StringBuffer buffer = StringBuffer();
const List<MaskConstant> maskConstants = <MaskConstant>[
kValueMask,
kUnicodePlane,
kWindowsPlane,
];
for (final MaskConstant constant in maskConstants) {
buffer.writeln('const uint64_t KeyboardKeyEmbedderHandler::${constant.lowerCamelName} = ${toHex(constant.value, digits: 11)};');
}
return buffer.toString().trimRight();
}
@override
String get templatePath => path.join(dataRoot, 'windows_flutter_key_map_cc.tmpl');
......@@ -70,6 +92,7 @@ class WindowsCodeGenerator extends PlatformCodeGenerator {
'WINDOWS_SCAN_CODE_MAP': _windowsScanCodeMap,
'WINDOWS_SCAN_CODE_TO_LOGICAL_MAP': _scanCodeToLogicalMap,
'WINDOWS_KEY_CODE_MAP': _windowsLogicalKeyCodeMap,
'MASK_CONSTANTS': _maskConstants,
};
}
}
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.
......@@ -152,7 +152,7 @@ class RawKeyEventDataAndroid extends RawKeyEventData {
@override
PhysicalKeyboardKey get physicalKey {
if (kAndroidToPhysicalKey.containsKey(scanCode)) {
return kAndroidToPhysicalKey[scanCode] ?? PhysicalKeyboardKey.none;
return kAndroidToPhysicalKey[scanCode]!;
}
// Android sends DPAD_UP, etc. as the keyCode for joystick DPAD events, but
......@@ -174,7 +174,7 @@ class RawKeyEventDataAndroid extends RawKeyEventData {
return PhysicalKeyboardKey.arrowRight;
}
}
return PhysicalKeyboardKey.none;
return PhysicalKeyboardKey(LogicalKeyboardKey.androidPlane + scanCode);
}
@override
......@@ -198,16 +198,12 @@ class RawKeyEventDataAndroid extends RawKeyEventData {
}
// Look to see if the keyCode is one we know about and have a mapping for.
LogicalKeyboardKey? newKey = kAndroidToLogicalKey[keyCode];
final LogicalKeyboardKey? newKey = kAndroidToLogicalKey[keyCode];
if (newKey != null) {
return newKey;
}
// This is a non-printable key that we don't know about, so we mint a new
// code with the autogenerated bit set.
const int androidKeyIdPlane = 0x00200000000;
newKey ??= LogicalKeyboardKey(androidKeyIdPlane | keyCode | LogicalKeyboardKey.autogeneratedMask);
return newKey;
return LogicalKeyboardKey(keyCode | LogicalKeyboardKey.androidPlane);
}
bool _isLeftRightModifierPressed(KeyboardSide side, int anyMask, int leftMask, int rightMask) {
......
......@@ -73,20 +73,18 @@ class RawKeyEventDataFuchsia extends RawKeyEventData {
}
// Look to see if the hidUsage is one we know about and have a mapping for.
LogicalKeyboardKey? newKey = kFuchsiaToLogicalKey[hidUsage | LogicalKeyboardKey.hidPlane];
final LogicalKeyboardKey? newKey = kFuchsiaToLogicalKey[hidUsage | LogicalKeyboardKey.fuchsiaPlane];
if (newKey != null) {
return newKey;
}
// This is a non-printable key that we don't know about, so we mint a new
// code with the autogenerated bit set.
const int fuchsiaKeyIdPlane = 0x00300000000;
newKey ??= LogicalKeyboardKey(fuchsiaKeyIdPlane | hidUsage | LogicalKeyboardKey.autogeneratedMask);
return newKey;
// code.
return LogicalKeyboardKey(hidUsage | LogicalKeyboardKey.fuchsiaPlane);
}
@override
PhysicalKeyboardKey get physicalKey => kFuchsiaToPhysicalKey[hidUsage] ?? PhysicalKeyboardKey.none;
PhysicalKeyboardKey get physicalKey => kFuchsiaToPhysicalKey[hidUsage] ?? PhysicalKeyboardKey(LogicalKeyboardKey.fuchsiaPlane + hidUsage);
bool _isLeftRightModifierPressed(KeyboardSide side, int anyMask, int leftMask, int rightMask) {
if (modifiers & anyMask == 0) {
......
......@@ -90,7 +90,7 @@ class RawKeyEventDataIos extends RawKeyEventData {
String get keyLabel => charactersIgnoringModifiers;
@override
PhysicalKeyboardKey get physicalKey => kIosToPhysicalKey[keyCode] ?? PhysicalKeyboardKey.none;
PhysicalKeyboardKey get physicalKey => kIosToPhysicalKey[keyCode] ?? PhysicalKeyboardKey(LogicalKeyboardKey.iosPlane + keyCode);
@override
LogicalKeyboardKey get logicalKey {
......@@ -137,22 +137,9 @@ class RawKeyEventDataIos extends RawKeyEventData {
return LogicalKeyboardKey.findKeyByKeyId(keyId) ?? LogicalKeyboardKey(keyId);
}
// Control keys like "backspace" and movement keys like arrow keys don't
// have a printable representation, but are present on the physical
// keyboard. Since there is no logical keycode map for iOS (iOS uses the
// keycode to reference physical keys), a LogicalKeyboardKey is created with
// the physical key's HID usage and debugName. This avoids duplicating the
// physical key map.
if (physicalKey != PhysicalKeyboardKey.none) {
final int keyId = physicalKey.usbHidUsage | LogicalKeyboardKey.hidPlane;
return LogicalKeyboardKey.findKeyByKeyId(keyId) ?? LogicalKeyboardKey(keyId);
}
// This is a non-printable key that is unrecognized, so a new code is minted
// with the autogenerated bit set.
const int iosKeyIdPlane = 0x00400000000;
return LogicalKeyboardKey(iosKeyIdPlane | keyCode | LogicalKeyboardKey.autogeneratedMask);
// This is a non-printable key that we don't know about, so we mint a new
// code.
return LogicalKeyboardKey(keyCode | LogicalKeyboardKey.iosPlane);
}
/// Returns true if the given label represents an unprintable key.
......
......@@ -73,7 +73,7 @@ class RawKeyEventDataLinux extends RawKeyEventData {
String get keyLabel => unicodeScalarValues == 0 ? '' : String.fromCharCode(unicodeScalarValues);
@override
PhysicalKeyboardKey get physicalKey => kLinuxToPhysicalKey[scanCode] ?? PhysicalKeyboardKey.none;
PhysicalKeyboardKey get physicalKey => kLinuxToPhysicalKey[scanCode] ?? PhysicalKeyboardKey(LogicalKeyboardKey.webPlane + scanCode);
@override
LogicalKeyboardKey get logicalKey {
......@@ -96,17 +96,14 @@ class RawKeyEventDataLinux extends RawKeyEventData {
}
// Look to see if the keyCode is one we know about and have a mapping for.
LogicalKeyboardKey? newKey = keyHelper.logicalKey(keyCode);
final LogicalKeyboardKey? newKey = keyHelper.logicalKey(keyCode);
if (newKey != null) {
return newKey;
}
const int linuxKeyIdPlane = 0x00600000000;
// This is a non-printable key that we don't know about, so we mint a new
// code with the autogenerated bit set.
newKey ??= LogicalKeyboardKey(linuxKeyIdPlane | keyCode | LogicalKeyboardKey.autogeneratedMask);
return newKey;
// code.
return LogicalKeyboardKey(keyCode | keyHelper.platformPlane);
}
@override
......@@ -157,6 +154,9 @@ abstract class KeyHelper {
/// The logical key from the specific key code mapping.
LogicalKeyboardKey? logicalKey(int keyCode);
/// The platform plane mask value of this platform.
int get platformPlane;
}
/// Helper class that uses GLFW-specific key mappings.
......@@ -294,6 +294,9 @@ class GLFWKeyHelper implements KeyHelper {
LogicalKeyboardKey? logicalKey(int keyCode) {
return kGlfwToLogicalKey[keyCode];
}
@override
int get platformPlane => LogicalKeyboardKey.glfwPlane;
}
/// Helper class that uses GTK-specific key mappings.
......@@ -432,4 +435,7 @@ class GtkKeyHelper implements KeyHelper {
LogicalKeyboardKey? logicalKey(int keyCode) {
return kGtkToLogicalKey[keyCode];
}
@override
int get platformPlane => LogicalKeyboardKey.gtkPlane;
}
......@@ -65,7 +65,7 @@ class RawKeyEventDataMacOs extends RawKeyEventData {
String get keyLabel => charactersIgnoringModifiers;
@override
PhysicalKeyboardKey get physicalKey => kMacOsToPhysicalKey[keyCode] ?? PhysicalKeyboardKey.none;
PhysicalKeyboardKey get physicalKey => kMacOsToPhysicalKey[keyCode] ?? PhysicalKeyboardKey(LogicalKeyboardKey.windowsPlane + keyCode);
@override
LogicalKeyboardKey get logicalKey {
......@@ -106,22 +106,9 @@ class RawKeyEventDataMacOs extends RawKeyEventData {
return LogicalKeyboardKey.findKeyByKeyId(keyId) ?? LogicalKeyboardKey(keyId);
}
// Control keys like "backspace" and movement keys like arrow keys don't
// have a printable representation, but are present on the physical
// keyboard. Since there is no logical keycode map for macOS (macOS uses the
// keycode to reference physical keys), a LogicalKeyboardKey is created with
// the physical key's HID usage and debugName. This avoids duplicating the
// physical key map.
if (physicalKey != PhysicalKeyboardKey.none) {
final int keyId = physicalKey.usbHidUsage | LogicalKeyboardKey.hidPlane;
return LogicalKeyboardKey.findKeyByKeyId(keyId) ?? LogicalKeyboardKey(keyId);
}
// This is a non-printable key that is unrecognized, so a new code is minted
// with the autogenerated bit set.
const int macOsKeyIdPlane = 0x00500000000;
return LogicalKeyboardKey(macOsKeyIdPlane | keyCode | LogicalKeyboardKey.autogeneratedMask);
// This is a non-printable key that we don't know about, so we mint a new
// code.
return LogicalKeyboardKey(keyCode | LogicalKeyboardKey.macosPlane);
}
bool _isLeftRightModifierPressed(KeyboardSide side, int anyMask, int leftMask, int rightMask) {
......
......@@ -60,7 +60,7 @@ class RawKeyEventDataWeb extends RawKeyEventData {
@override
PhysicalKeyboardKey get physicalKey {
return kWebToPhysicalKey[code] ?? PhysicalKeyboardKey.none;
return kWebToPhysicalKey[code] ?? PhysicalKeyboardKey(LogicalKeyboardKey.webPlane + code.hashCode);
}
@override
......@@ -80,9 +80,8 @@ class RawKeyEventDataWeb extends RawKeyEventData {
}
// This is a non-printable key that we don't know about, so we mint a new
// code with the autogenerated bit set.
const int webKeyIdPlane = 0x00800000000;
return LogicalKeyboardKey(webKeyIdPlane | code.hashCode | LogicalKeyboardKey.autogeneratedMask);
// code.
return LogicalKeyboardKey(code.hashCode | LogicalKeyboardKey.webPlane);
}
@override
......
......@@ -60,7 +60,7 @@ class RawKeyEventDataWindows extends RawKeyEventData {
String get keyLabel => characterCodePoint == 0 ? '' : String.fromCharCode(characterCodePoint);
@override
PhysicalKeyboardKey get physicalKey => kWindowsToPhysicalKey[scanCode] ?? PhysicalKeyboardKey.none;
PhysicalKeyboardKey get physicalKey => kWindowsToPhysicalKey[scanCode] ?? PhysicalKeyboardKey(LogicalKeyboardKey.windowsPlane + scanCode);
@override
LogicalKeyboardKey get logicalKey {
......@@ -81,16 +81,14 @@ class RawKeyEventDataWindows extends RawKeyEventData {
return LogicalKeyboardKey.findKeyByKeyId(keyId) ?? LogicalKeyboardKey(keyId);
}
// Look to see if the keyCode is one we know about and have a mapping for.
LogicalKeyboardKey? newKey = kWindowsToLogicalKey[keyCode];
final LogicalKeyboardKey? newKey = kWindowsToLogicalKey[keyCode];
if (newKey != null) {
return newKey;
}
// This is a non-printable key that we don't know about, so we mint a new
// code with the autogenerated bit set.
const int windowsKeyIdPlane = 0x00700000000;
newKey ??= LogicalKeyboardKey(windowsKeyIdPlane | keyCode | LogicalKeyboardKey.autogeneratedMask);
return newKey;
// code.
return LogicalKeyboardKey(keyCode | LogicalKeyboardKey.windowsPlane);
}
bool _isLeftRightModifierPressed(KeyboardSide side, int anyMask, int leftMask, int rightMask) {
......
......@@ -50,14 +50,13 @@ void main() {
expect(LogicalKeyboardKey.isControlCharacter('\xa0'), isFalse); // NO-BREAK SPACE
});
test('Control characters are not using incorrect values', () async {
const int kUnprintablePlane = 0x01000000000;
// Check some common control characters to make sure they're using
// their char code values, and not something else.
expect(LogicalKeyboardKey.backspace.keyId, equals(kUnprintablePlane + 0x08));
expect(LogicalKeyboardKey.tab.keyId, equals(kUnprintablePlane + 0x09));
expect(LogicalKeyboardKey.enter.keyId, equals(kUnprintablePlane + 0x0d));
expect(LogicalKeyboardKey.escape.keyId, equals(kUnprintablePlane + 0x1b));
expect(LogicalKeyboardKey.delete.keyId, equals(kUnprintablePlane + 0x7f));
expect(LogicalKeyboardKey.backspace.keyId, equals(LogicalKeyboardKey.unprintablePlane + 0x08));
expect(LogicalKeyboardKey.tab.keyId, equals(LogicalKeyboardKey.unprintablePlane + 0x09));
expect(LogicalKeyboardKey.enter.keyId, equals(LogicalKeyboardKey.unprintablePlane + 0x0d));
expect(LogicalKeyboardKey.escape.keyId, equals(LogicalKeyboardKey.unprintablePlane + 0x1b));
expect(LogicalKeyboardKey.delete.keyId, equals(LogicalKeyboardKey.unprintablePlane + 0x7f));
});
test('Basic synonyms can be looked up.', () async {
expect(LogicalKeyboardKey.shiftLeft.synonyms.first, equals(LogicalKeyboardKey.shift));
......
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