Skip to content

Commit c8d0b1d

Browse files
authored
New scheme for keyboard logical key ID (flutter#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.
1 parent 252f6a0 commit c8d0b1d

38 files changed

+4776
-4686
lines changed

dev/tools/gen_keycodes/README.md

Lines changed: 69 additions & 102 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ use `--engine-root=/ENGINE/GCLIENT/ROOT` to specify the engine root.
4343

4444
Other options can be found using `--help`.
4545

46-
## Key Code ID Scheme
46+
## Logical Key ID Scheme
4747

4848
To provide logical keys with unique ID codes, Flutter uses a scheme
4949
to assign logical keycodes which keeps us out of the business of minting new
@@ -58,109 +58,76 @@ the API.
5858
However, if you are porting Flutter to a new platform, you should follow the
5959
following guidelines for specifying logical key codes.
6060

61-
The logical key code is a 37-bit integer in a namespace that we control and
62-
define. It has values in the following ranges.
61+
The logical key code is a 52-bit integer (due to the limitation of JavaScript).
62+
The entire namespace is divided into 32-bit *planes*. The upper 20 bits of the
63+
ID represent the plane ID, while the lower 32 bits represent values in the
64+
plane. For example, plane 0x1 refers to the range 0x1 0000 0000 -
65+
0x1 FFFF FFFF. Each plane manages how the values within the range are assigned.
6366

64-
- **0x00 0000 0000 - 0x0 0010 FFFF**: For keys that generate Unicode
67+
The planes are planned as follows:
68+
69+
- **Plane 0x00**: The Unicode plane. This plane contains keys that generate Unicode
6570
characters when pressed (this includes dead keys, but not e.g. function keys
66-
or shift keys), the logical key code is the Unicode code point corresponding
67-
to the representation of the key in the current keyboard mapping. The
68-
Unicode code point might not match the string that is generated for
69-
an unshifted keypress of that key, for example, we would use U+0034 for the
70-
“4 \$” key in the US layout, and also the “4 ;” key in the Russian layout,
71-
and also, maybe less intuitively, for the “' 4 {“ in French layout (wherein
72-
the latter case, an unshifted press gets you a ', not a 4). Similarly, the Q
73-
key in the US layout outputs a q in normal usage, but its code would be 0x0
74-
0000 0051 (U+00051 being the code for the uppercase Q).
75-
76-
- **0x01 0000 0000 - 0x01 FFFF FFFF**: For keys that are defined by the [USB HID
77-
standard](https://www.usb.org/sites/default/files/documents/hut1_12v2.pdf),
78-
the key code consists of the 32 bit USB extended usage code. For
79-
example, the Enter key would have code 0x01 0007 0028. Only keys that fall
80-
into collections "Keyboard", "Keypad", and "Tablet PC System Controls" are
81-
considered for this API; for example, a mixing desk with multiple
82-
collections of volume controls would not be exposed via DOWN and UP events,
83-
nor would a mouse, joystick, or golf simulator control.
84-
85-
- **0x02 0000 0000 - 0xFF FFFF FFFF**: For keys that aren't defined in USB at the
86-
time of implementation, but that we need to support. For example, if Flutter
87-
were ever ported to the Symbolics LM-2, the "thumb up" key might be given
88-
the code 0x14 0000 0001, where 0x14 is defined as the “Symbolics” platform
89-
range. Where possible, we will use specific subranges of this space to reuse
90-
keys from other platforms. When this is not possible, the prefix 0xFF is
91-
reserved for “Custom” codes. Each platform from which we take codes will get
92-
a unique prefix in the range 0x2-0xFE. If multiple systems define keys with
93-
the same usage (not the same number), then the value with the lowest prefix
94-
is used as the defining code.
95-
96-
Prefixes will be:
71+
or shift keys). The value is defined as the Unicode code point corresponding
72+
to the character, lower case and without modifier keys if possible.
73+
Examples are Key A (0x61), Digit 1 (0x31), Colon (0x3A), and Key Ù (0xD9).
74+
(The "Colon" key represents a keyboard key that prints the ":"
75+
character without modifiers, which can be found on the French layout. On the
76+
US layout, the key that prints ":" is the Semicolon key.)
77+
This plane also contains key None (0x0).
78+
79+
- **Plane 0x01**: The unprintable plane. This plane contains keys that are defined
80+
by the [Chromium key list](https://chromium.googlesource.com/codesearch/chromium/src/+/refs/heads/master/ui/events/keycodes/dom/dom_key_data.inc)
81+
and do not generate Unicode characters. The value is defined as the macro
82+
value defined by the Chromium key list. Examples are CapsLock (0x105),
83+
ArrowUp (0x304), F1 (0x801), Hiragata (0x716), and TVPower (0xD4B).
84+
Some keys that exist in the Chromium key list are not present in Flutter in this plane, most notably
85+
modifiers keys (such as Shift). See the Flutter plane below for more
86+
information.
87+
88+
- **Plane 0x02**: The Flutter plane. This plane contains keys that are
89+
defined by Flutter. The values are also manually assigned by Flutter.
90+
Modifier keys are placed in this plane, because Flutter distinguishes
91+
between sided modifier keys (for example "ShiftLeft" and "ShiftRight"),
92+
while the web doesn't (only has "Shift").
93+
Other examples are numpad keys and gamepad keys.
94+
95+
- **Plane 0x03-0x0F**: Reserved.
96+
97+
- **Plane 0x10-0x1F**: Platform planes managed by Flutter. Each platform plane
98+
corresponds to a Flutter embedding officially supported by Flutter. The
99+
platforms are listed as follows:
97100

98101
| Code | Platform |
99102
| ---- | -------- |
100-
| 0x02 | Android |
101-
| 0x03 | Fuchsia |
102-
| 0x04 | iOS |
103-
| 0x05 | macOS |
104-
| 0x06 | Linux |
105-
| 0x07 | Windows |
106-
| 0x08 | Web |
107-
| 0xFF | Custom |
108-
109-
Further ranges will be added as platforms are added. The platform prefix
110-
does not define the platform it is used on, it is just the platform that
111-
decides what the value is: the codes are mapped to the same value on all
112-
platforms.
113-
114-
- **0x100 0000 0000 - 0x1FF FFFF FFFF**: For keys that have no definition yet in
115-
Flutter, but that are encountered in the field, this range is used to embed
116-
the platform-specific keycode in an ID that must be tested for in a
117-
platform-specific way. For instance, if a platform generates a new USB
118-
HID code 0x07 00E8 that a Flutter app wasn’t compiled with, then it would
119-
appear in the app as 0x100 0007 00E8, and the app could test against that
120-
code. Yes, this also means that once they recompile with a version of
121-
Flutter that supports this new HID code, apps looking for this code will
122-
break. This situation is only meant to provide a fallback ability for apps
123-
to handle esoteric codes that their version of Flutter doesn’t support yet.
124-
The prefix for this code is the platform prefix from the previous sections,
125-
plus 0x100.
126-
127-
- **0x200 0000 0000 - 0x2FF FFFF FFFF**: For pseudo-keys which represent
128-
combinations of other keys, and conceptual keys which don't have a physical
129-
representation. This is where things like key synonyms are defined (e.g.
130-
"shiftLeft" is a synonym for "shift": the "shift" key is a pseudo-key
131-
representing either the left or right shift key).
132-
133-
**This is intended to get us out of the business of defining key codes where
134-
possible.** We still have to have mapping tables, but at least the actual minting
135-
of codes is deferred to other organizations to a large extent. Coming up with a
136-
code is a mechanical process consisting of just picking the lowest number code
137-
possible that matches the semantic meaning of the key according to the
138-
definitions above.
139-
140-
Here are some examples:
141-
142-
For example, on a French keyboard layout, pressing CAPS LOCK then pressing
143-
SHIFT + Y would generate the following sequence:
144-
145-
DOWN, code 0x0100070039. (CAPS LOCK DOWN)<br>
146-
UP, code 0x0100070039. (CAPS LOCK UP)<br>
147-
DOWN, code 0x01000700E1 (LEFT SHIFT DOWN)<br>
148-
DOWN, code 0x0000000059, string U+00059 (Y DOWN)<br>
149-
UP, code 0x0000000059 (Y UP)<br>
150-
UP, code 0x01000700E1 (LEFT SHIFT UP)<br>
151-
152-
Here's another example. On a German keyboard layout, you press ^e (the ^ key is
153-
at the top left of the keyboard and is a dead key) to produce an “ê”:
154-
155-
DOWN, code 0x0000000302 (CIRCUMFLEX DOWN) It produces no string, because it's a dead
156-
key. The key code is for "Combining circumflex accent U+0302" in Unicode.<br>
157-
UP, code 0x0000000302 (CIRCUMFLEX UP)<br>
158-
DOWN, code 0x0000000065, string U+000EA (Unicode for ê‬) (E DOWN).<br>
159-
UP, code 0x0000000065. (E UP).<br>
160-
161-
It is an important point that even though we’re representing many keys with USB
162-
HID codes, these are not necessarily the same HID codes produced by the hardware
163-
and presented to the driver, since on most platforms we have to map the platform
164-
representation back to an HID code because we don’t have access to the original
165-
HID code. USB HID is simply a conveniently well-defined standard that includes
166-
many of the keys we would want.
103+
| 0x11 | Android |
104+
| 0x12 | Fuchsia |
105+
| 0x13 | iOS |
106+
| 0x14 | macOS |
107+
| 0x15 | Gtk |
108+
| 0x16 | Windows |
109+
| 0x17 | Web |
110+
| 0x18 | GLFW |
111+
112+
Platform planes store keys that are private to the Flutter embedding of this
113+
platform. This most likely means that these keys have not been officially
114+
recognized by Flutter.
115+
116+
The value scheme within a platform plane is decided by the platform,
117+
typically using the field from the platform's native key event that
118+
represents the key's logical effect (such as `keycode`, `virtual key`, etc).
119+
120+
In time, keys that originally belong to a platform plane might be added to
121+
Flutter, especially if a key is found shared by multiple platforms. The values
122+
of that key will be changed to a new value within the Flutter plane, and all
123+
platforms managed by Flutter will start to send the new value, making it a
124+
breaking change. Therefore, when handling an unrecognized key on a platform
125+
managed by Flutter, it is recommended to file a new issue to add this value
126+
to `keyboard_key.dart` instead of using the platform-plane value. However,
127+
for a custom platform (see below), since the platfrom author has full control
128+
over key mapping, such change will not cause breakage and it is recommended
129+
to use the platform-plane value to avoid adding platform-exclusive values
130+
to the framework.
131+
132+
- **Plane 0x20-0x2F**: Custom platform planes. Similar to Flutter's platform
133+
planes, but for private use by custom platforms.

dev/tools/gen_keycodes/bin/gen_keycodes.dart

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import 'package:gen_keycodes/keyboard_maps_code_gen.dart';
1515
import 'package:gen_keycodes/logical_key_data.dart';
1616
import 'package:gen_keycodes/macos_code_gen.dart';
1717
import 'package:gen_keycodes/physical_key_data.dart';
18+
import 'package:gen_keycodes/testing_key_codes_gen.dart';
1819
import 'package:gen_keycodes/utils.dart';
1920
import 'package:gen_keycodes/web_code_gen.dart';
2021
import 'package:gen_keycodes/windows_code_gen.dart';
@@ -71,7 +72,20 @@ String readDataFile(String fileName) {
7172
return File(path.join(dataRoot, fileName)).readAsStringSync();
7273
}
7374

75+
bool _assertsEnabled() {
76+
bool enabledAsserts = false;
77+
assert(() {
78+
enabledAsserts = true;
79+
return true;
80+
}());
81+
return enabledAsserts;
82+
}
83+
7484
Future<void> main(List<String> rawArguments) async {
85+
if (!_assertsEnabled()) {
86+
print('The gen_keycodes script must be run with --enable-asserts.');
87+
return;
88+
}
7589
final ArgParser argParser = ArgParser();
7690
argParser.addOption(
7791
'engine-root',
@@ -208,6 +222,14 @@ Future<void> main(List<String> rawArguments) async {
208222
print('Writing ${'key maps'.padRight(15)}${mapsFile.absolute}');
209223
await mapsFile.writeAsString(KeyboardMapsCodeGenerator(physicalData, logicalData).generate());
210224

225+
final File keyCodesFile = File(path.join(PlatformCodeGenerator.engineRoot,
226+
'shell', 'platform', 'embedder', 'test_utils', 'key_codes.h'));
227+
if (!mapsFile.existsSync()) {
228+
mapsFile.createSync(recursive: true);
229+
}
230+
print('Writing ${'engine key codes'.padRight(15)}${mapsFile.absolute}');
231+
await keyCodesFile.writeAsString(KeyCodesCcGenerator(physicalData, logicalData).generate());
232+
211233
final Map<String, PlatformCodeGenerator> platforms = <String, PlatformCodeGenerator>{
212234
'android': AndroidCodeGenerator(
213235
physicalData,

dev/tools/gen_keycodes/data/README.md

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@
99
| [`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. |
1010
| [`supplemental_key_data.inc`](supplemental_key_data.inc) | A supplementary key list on top of Chromium's list of keys for extra logical keys.|
1111
| [`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.|
12-
| [`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.|
1312
| [`printable.json`](printable.json) | Maps Flutter key name to its printable character. This character is used as the key label.|
1413
| [`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.|
1514

@@ -58,7 +57,7 @@
5857
| [`gtk_key_mapping_cc.tmpl`](gtk_key_mapping_cc.tmpl) | The template for `key_mapping.cc`. |
5958
| [`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.|
6059
| [`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.|
61-
| [`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.|
60+
| [`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.|
6261
| [`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.|
6362

6463
### Linux (GLFW)

dev/tools/gen_keycodes/data/android_key_name_to_name.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
{
2+
"Add": ["PLUS"],
23
"Again": ["AGAIN"],
34
"AltLeft": ["ALT_LEFT"],
45
"AltRight": ["ALT_RIGHT"],
@@ -7,6 +8,8 @@
78
"ArrowLeft": ["DPAD_LEFT"],
89
"ArrowRight": ["DPAD_RIGHT"],
910
"ArrowUp": ["DPAD_UP"],
11+
"Asterisk": ["STAR"],
12+
"At": ["AT"],
1013
"AudioVolumeDown": ["VOLUME_DOWN"],
1114
"AudioVolumeMute": ["VOLUME_MUTE"],
1215
"AudioVolumeUp": ["VOLUME_UP"],
@@ -201,6 +204,7 @@
201204
"None": ["UNKNOWN"],
202205
"Notification": ["NOTIFICATION"],
203206
"NumLock": ["NUM_LOCK"],
207+
"NumberSign": ["POUND"],
204208
"Numpad0": ["NUMPAD_0"],
205209
"Numpad1": ["NUMPAD_1"],
206210
"Numpad2": ["NUMPAD_2"],

dev/tools/gen_keycodes/data/gtk_key_mapping_cc.tmpl

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@
77
#include <glib.h>
88
#include <map>
99

10+
#include "flutter/shell/platform/linux/fl_key_embedder_responder_private.h"
11+
1012
// DO NOT EDIT -- DO NOT EDIT -- DO NOT EDIT
1113
// This file is generated by
1214
// flutter/flutter@dev/tools/gen_keycodes/bin/gen_keycodes.dart and should not
@@ -15,14 +17,6 @@
1517
// Edit the template dev/tools/gen_keycodes/data/gtk_key_mapping_cc.tmpl
1618
// instead. See dev/tools/gen_keycodes/README.md for more information.
1719

18-
// Insert a new entry into a hashtable from uint64 to uint64.
19-
//
20-
// Returns whether the newly added value was already in the hash table or not.
21-
static bool insert_record(GHashTable* table, guint64 xkb, guint64 fl_key) {
22-
return g_hash_table_insert(table, uint64_to_gpointer(xkb),
23-
uint64_to_gpointer(fl_key));
24-
}
25-
2620
std::map<uint64_t, uint64_t> xkb_to_physical_key_map = {
2721
@@@XKB_SCAN_CODE_MAP@@@
2822
};
@@ -40,3 +34,5 @@ void initialize_lock_bit_to_checked_keys(GHashTable* table) {
4034
FlKeyEmbedderCheckedKey* data;
4135
@@@GTK_MODE_BIT_MAP@@@
4236
}
37+
38+
@@@MASK_CONSTANTS@@@

dev/tools/gen_keycodes/data/ios_key_code_map_cc.tmpl

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// Copyright 2014 The Flutter Authors. All rights reserved.
1+
// Copyright 2013 The Flutter Authors. All rights reserved.
22
// Use of this source code is governed by a BSD-style license that can be
33
// found in the LICENSE file.
44

@@ -36,4 +36,4 @@ const std::map<uint32_t, uint32_t> modifierFlagToKeyCode = {
3636
@@@MODIFIER_FLAG_TO_KEYCODE_MAP@@@
3737
};
3838

39-
@@@SPECIAL_KEY_CONSTANTS@@@
39+
@@@SPECIAL_KEY_CONSTANTS@@@
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
// Copyright 2013 The Flutter Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style license that can be
3+
// found in the LICENSE file.
4+
5+
#ifndef FLUTTER_SHELL_PLATFORM_COMMON_TESTING_KEY_CODES_H_
6+
#define FLUTTER_SHELL_PLATFORM_COMMON_TESTING_KEY_CODES_H_
7+
8+
#include <cinttypes>
9+
10+
// DO NOT EDIT -- DO NOT EDIT -- DO NOT EDIT
11+
// This file is generated by
12+
// flutter/flutter:dev/tools/gen_keycodes/bin/gen_keycodes.dart and should not
13+
// be edited directly.
14+
//
15+
// Edit the template
16+
// flutter/flutter:dev/tools/gen_keycodes/data/key_codes_cc.tmpl
17+
// instead.
18+
//
19+
// See flutter/flutter:dev/tools/gen_keycodes/README.md for more information.
20+
21+
// This file contains keyboard constants to be used in unit tests. They should
22+
// not be used in production code.
23+
24+
namespace flutter {
25+
26+
namespace testing {
27+
28+
namespace keycodes {
29+
30+
@@@PHYSICAL_KEY_DEFINITIONS@@@
31+
32+
@@@LOGICAL_KEY_DEFINITIONS@@@
33+
34+
} // namespace keycodes
35+
36+
} // namespace testing
37+
38+
} // namespace flutter
39+
40+
#endif // FLUTTER_SHELL_PLATFORM_COMMON_TESTING_KEY_CODES_H_

0 commit comments

Comments
 (0)