flutter/dev/tools/gen_keycodes
Moritz 3a0897a336
Add workspace (#169451)
Reland after #169357.

Switch Flutter to use pub workspaces as a preparation to unpin selected
packages.

Assumptions:

1. No packages in this repository are published to pub.dev --> We can
use `any` dependencies in most local pubspecs, as the global constraint
defines the version. An exception are the packages used outside of this
repo with an `sdk` dependency, namely `flutter_localizations`,
`flutter_test`, and `flutter`.
2. The "universes" `{flutter_tools}` and `{flutter,
flutter_localizations, flutter_goldens}` can use different packages
versions, as they are not resolved together. --> We do not need to
upgrade them in sync, we can first do one "universe", then the other.

Based on these assumptions, we use
https://github.com/mosuem/pubspec_merger.dart to merge all packages in
the `flutter` universe into a top-level pub workspace.

The `flutter` and `flutter_tools` workspaces being separate also ensures
that changes to `flutter` will not inadvertently break `flutter_tools`,
with not-so-nice consequences for our users which would be unable to run
`flutter upgrade`.

There is a third "top-level" pubspec besides `./pubspec.yaml` and
`packages/flutter_tools/pubspec.yaml`, namely
`packages/flutter_tools/.../widget_preview_scaffold/pubspec.yaml`. This
is an artifact due to it living under `flutter_tools`, so it can't be
part of the `./pubspec.yaml` workspace. Moving it would be a larger
change, and out of the scope of this PR.

This required a rewrite of the update-packages tool, but the main
functionality stays the same, as well as the argument names, to ensure a
seamless transition.

## Pre-launch Checklist

- [x] I read the [Contributor Guide] and followed the process outlined
there for submitting PRs.
- [x] I read the [Tree Hygiene] wiki page, which explains my
responsibilities.
- [x] I read and followed the [Flutter Style Guide], including [Features
we expect every widget to implement].
- [x] I signed the [CLA].
- [x] I listed at least one issue that this PR fixes in the description
above.
- [x] I updated/added relevant documentation (doc comments with `///`).
- [x] I added new tests to check the change I am making, or this PR is
[test-exempt].
- [x] I followed the [breaking change policy] and added [Data Driven
Fixes] where supported.
- [x] All existing and new tests are passing.

If you need help, consider asking for advice on the #hackers-new channel
on [Discord].

<!-- Links -->
[Contributor Guide]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#overview
[Tree Hygiene]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md
[test-exempt]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#tests
[Flutter Style Guide]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Style-guide-for-Flutter-repo.md
[Features we expect every widget to implement]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Style-guide-for-Flutter-repo.md#features-we-expect-every-widget-to-implement
[CLA]: https://cla.developers.google.com/
[flutter/tests]: https://github.com/flutter/tests
[breaking change policy]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#handling-breaking-changes
[Discord]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Chat.md
[Data Driven Fixes]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Data-driven-Fixes.md
2025-05-26 12:46:53 +00:00
..
bin Update gen_keycodes output to new engine location. (#162479) 2025-02-11 00:52:03 +00:00
data Fix Linux keyboard support for AltGr (#162495) 2025-02-11 01:03:48 +00:00
lib Auto-format Framework (#160545) 2024-12-19 20:06:21 +00:00
test Auto-format Framework (#160545) 2024-12-19 20:06:21 +00:00
pubspec.yaml Add workspace (#169451) 2025-05-26 12:46:53 +00:00
README.md Update gen_keycodes output to new engine location. (#162479) 2025-02-11 00:52:03 +00:00

Keycode Generator

This directory contains a keycode generator that can generate Dart code for the LogicalKeyboardKey and PhysicalKeyboardKey classes.

It generates multiple files across Flutter. For framework, it generates

  • keyboard_key.g.dart, which contains the definition and list of logical keys and physical keys; and
  • keyboard_maps.g.dart, which contains platform-specific immutable maps used for the RawKeyboard API.

For engine, it generates one key mapping file for each platform, as well as some files for testing purposes.

It draws information from various source bases, including online repositories, and manual mapping in the data subdirectory. It incorporates this information into a giant list of physical keys (physical_key_data.g.json), and another for logical keys (logical_key_data.g.json). The two files are checked in, and can be used as the data source next time so that output files can be generated without the Internet.

Running the tool

The tool can be run based on the existing database. To do this, run:

/PATH/TO/ROOT/bin/gen_keycodes

The tool can also be run by rebuilding the database by drawing online information anew before generating the files. To do this, run:

/PATH/TO/ROOT/bin/gen_keycodes --collect

This will generate physical_key_data.g.json and logical_key_data.g.json. These files should be checked in.

Other options can be found using --help.

Key ID Scheme

To provide keys with unique ID codes, Flutter uses a scheme to assign keycodes which keeps us out of the business of minting new codes ourselves. This applies both logical keys and physical keys.

The codes are meant to be opaque to the user, and should never be unpacked for meaning, since the coding scheme could change at any time and the meaning is likely to be retrievable more reliably and correctly from the API.

However, if you are porting Flutter to a new platform, you should follow the following guidelines for specifying key codes.

The key code is a 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.

The planes are planned as follows:

  • Plane 0x00: The Unicode plane. For logical keys, this plane contains keys that generate Unicode characters when pressed (this includes dead keys, but not e.g. function keys 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.) For physical keys, this plane contains keys from USB HID usages.

  • Plane 0x01: The unprintable plane. This plane contains logical keys that are defined by the Chromium key list 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. 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
    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 other fields from the platform's native key event.

    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.g.dart instead of using the platform-plane value. However, for a custom platform (see below), since the platform 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.