From f0978c225ad80e53eec6e9886f987c996e113408 Mon Sep 17 00:00:00 2001 From: Greg Spencer Date: Wed, 5 Jun 2019 16:23:17 -0700 Subject: [PATCH] Game controller button support (#33868) This adds support for game controller buttons. It adds some supplemental USB HID codes that aren't available from the Chromium source code, and maps those on Android to the game pad buttons that Android supports. Other platforms are not supported yet. --- dev/manual_tests/lib/raw_keyboard.dart | 11 +- dev/tools/gen_keycodes/bin/gen_keycodes.dart | 9 + dev/tools/gen_keycodes/data/key_data.json | 865 +++++++++++++++++- .../data/key_name_to_android_name.json | 32 + .../data/supplemental_hid_codes.inc | 45 + dev/tools/gen_keycodes/lib/key_data.dart | 2 +- .../lib/src/services/keyboard_key.dart | 403 ++++++++ .../lib/src/services/keyboard_maps.dart | 142 +++ .../lib/src/services/raw_keyboard.dart | 3 + .../src/services/raw_keyboard_android.dart | 53 +- .../test/services/raw_keyboard_test.dart | 56 ++ 11 files changed, 1613 insertions(+), 8 deletions(-) create mode 100644 dev/tools/gen_keycodes/data/supplemental_hid_codes.inc diff --git a/dev/manual_tests/lib/raw_keyboard.dart b/dev/manual_tests/lib/raw_keyboard.dart index 72948323c4..8507bf2394 100644 --- a/dev/manual_tests/lib/raw_keyboard.dart +++ b/dev/manual_tests/lib/raw_keyboard.dart @@ -36,10 +36,11 @@ class _HardwareKeyDemoState extends State { super.dispose(); } - void _handleKeyEvent(RawKeyEvent event) { + bool _handleKeyEvent(FocusNode node, RawKeyEvent event) { setState(() { _event = event; }); + return false; } String _asHex(int value) => value != null ? '0x${value.toRadixString(16)}' : 'null'; @@ -53,9 +54,10 @@ class _HardwareKeyDemoState extends State { @override Widget build(BuildContext context) { final TextTheme textTheme = Theme.of(context).textTheme; - return RawKeyboardListener( + return Focus( focusNode: _focusNode, onKey: _handleKeyEvent, + autofocus: true, child: AnimatedBuilder( animation: _focusNode, builder: (BuildContext context, Widget child) { @@ -87,6 +89,9 @@ class _HardwareKeyDemoState extends State { dataText.add(Text('keyCode: ${data.keyCode} (${_asHex(data.keyCode)})')); dataText.add(Text('scanCode: ${data.scanCode} (${_asHex(data.scanCode)})')); dataText.add(Text('metaState: ${data.metaState} (${_asHex(data.metaState)})')); + dataText.add(Text('source: ${data.eventSource} (${_asHex(data.eventSource)})')); + dataText.add(Text('vendorId: ${data.vendorId} (${_asHex(data.vendorId)})')); + dataText.add(Text('productId: ${data.productId} (${_asHex(data.productId)})')); dataText.add(Text('flags: ${data.flags} (${_asHex(data.flags)})')); } else if (data is RawKeyEventDataFuchsia) { dataText.add(Text('codePoint: ${data.codePoint} (${_asHex(data.codePoint)})')); @@ -118,7 +123,7 @@ class _HardwareKeyDemoState extends State { } } return DefaultTextStyle( - style: textTheme.headline, + style: textTheme.subhead, child: Column( mainAxisAlignment: MainAxisAlignment.center, children: dataText, diff --git a/dev/tools/gen_keycodes/bin/gen_keycodes.dart b/dev/tools/gen_keycodes/bin/gen_keycodes.dart index dd1d6c304a..4e04faa76d 100644 --- a/dev/tools/gen_keycodes/bin/gen_keycodes.dart +++ b/dev/tools/gen_keycodes/bin/gen_keycodes.dart @@ -52,6 +52,12 @@ Future main(List rawArguments) async { 'read. If --chromium-hid-codes is not specified, the input will be read ' 'from the correct file in the Chromium repository.', ); + argParser.addOption( + 'supplemental-hid-codes', + defaultsTo: path.join(flutterRoot.path, 'dev', 'tools', 'gen_keycodes', 'data', 'supplemental_hid_codes.inc'), + help: "The path to where the supplemental HID codes that don't appear in the " + 'Chromium map should be read.', + ); argParser.addOption( 'android-keycodes', defaultsTo: null, @@ -140,6 +146,9 @@ Future main(List rawArguments) async { hidCodes = File(parsedArguments['chromium-hid-codes']).readAsStringSync(); } + final String supplementalHidCodes = File(parsedArguments['supplemental-hid-codes']).readAsStringSync(); + hidCodes = '$hidCodes\n$supplementalHidCodes'; + String androidKeyCodes; if (parsedArguments['android-keycodes'] == null) { androidKeyCodes = await getAndroidKeyCodes(); diff --git a/dev/tools/gen_keycodes/data/key_data.json b/dev/tools/gen_keycodes/data/key_data.json index 8ea2f21823..7aee204085 100644 --- a/dev/tools/gen_keycodes/data/key_data.json +++ b/dev/tools/gen_keycodes/data/key_data.json @@ -3794,13 +3794,17 @@ "select": { "names": { "domkey": "Select", - "android": null, + "android": [ + "DPAD_CENTER" + ], "english": "Select", "chromium": "select", "glfw": null }, "scanCodes": { - "android": null, + "android": [ + 353 + ], "usb": 458871, "linux": 132, "xkb": 140, @@ -3808,7 +3812,9 @@ "macos": null }, "keyCodes": { - "android": null, + "android": [ + 23 + ], "glfw": null } }, @@ -6355,5 +6361,858 @@ "android": null, "glfw": null } + }, + "gameButton1": { + "names": { + "domkey": "GameButton1", + "android": [ + "BUTTON_1" + ], + "english": "Game Button 1", + "chromium": "button1", + "glfw": null + }, + "scanCodes": { + "android": [ + 256, + 288 + ], + "usb": 341761, + "linux": null, + "xkb": null, + "windows": null, + "macos": null + }, + "keyCodes": { + "android": [ + 188 + ], + "glfw": null + } + }, + "gameButton2": { + "names": { + "domkey": "GameButton2", + "android": [ + "BUTTON_2" + ], + "english": "Game Button 2", + "chromium": "button2", + "glfw": null + }, + "scanCodes": { + "android": [ + 257, + 289 + ], + "usb": 341762, + "linux": null, + "xkb": null, + "windows": null, + "macos": null + }, + "keyCodes": { + "android": [ + 189 + ], + "glfw": null + } + }, + "gameButton3": { + "names": { + "domkey": "GameButton3", + "android": [ + "BUTTON_3" + ], + "english": "Game Button 3", + "chromium": "button3", + "glfw": null + }, + "scanCodes": { + "android": [ + 258, + 290 + ], + "usb": 341763, + "linux": null, + "xkb": null, + "windows": null, + "macos": null + }, + "keyCodes": { + "android": [ + 190 + ], + "glfw": null + } + }, + "gameButton4": { + "names": { + "domkey": "GameButton4", + "android": [ + "BUTTON_4" + ], + "english": "Game Button 4", + "chromium": "button4", + "glfw": null + }, + "scanCodes": { + "android": [ + 259, + 291 + ], + "usb": 341764, + "linux": null, + "xkb": null, + "windows": null, + "macos": null + }, + "keyCodes": { + "android": [ + 191 + ], + "glfw": null + } + }, + "gameButton5": { + "names": { + "domkey": "GameButton5", + "android": [ + "BUTTON_5" + ], + "english": "Game Button 5", + "chromium": "button5", + "glfw": null + }, + "scanCodes": { + "android": [ + 260, + 292 + ], + "usb": 341765, + "linux": null, + "xkb": null, + "windows": null, + "macos": null + }, + "keyCodes": { + "android": [ + 192 + ], + "glfw": null + } + }, + "gameButton6": { + "names": { + "domkey": "GameButton6", + "android": [ + "BUTTON_6" + ], + "english": "Game Button 6", + "chromium": "button6", + "glfw": null + }, + "scanCodes": { + "android": [ + 261, + 293 + ], + "usb": 341766, + "linux": null, + "xkb": null, + "windows": null, + "macos": null + }, + "keyCodes": { + "android": [ + 193 + ], + "glfw": null + } + }, + "gameButton7": { + "names": { + "domkey": "GameButton7", + "android": [ + "BUTTON_7" + ], + "english": "Game Button 7", + "chromium": "button7", + "glfw": null + }, + "scanCodes": { + "android": [ + 262, + 294 + ], + "usb": 341767, + "linux": null, + "xkb": null, + "windows": null, + "macos": null + }, + "keyCodes": { + "android": [ + 194 + ], + "glfw": null + } + }, + "gameButton8": { + "names": { + "domkey": "GameButton8", + "android": [ + "BUTTON_8" + ], + "english": "Game Button 8", + "chromium": "button8", + "glfw": null + }, + "scanCodes": { + "android": [ + 263, + 295 + ], + "usb": 341768, + "linux": null, + "xkb": null, + "windows": null, + "macos": null + }, + "keyCodes": { + "android": [ + 195 + ], + "glfw": null + } + }, + "gameButton9": { + "names": { + "domkey": "GameButton9", + "android": [ + "BUTTON_9" + ], + "english": "Game Button 9", + "chromium": "button9", + "glfw": null + }, + "scanCodes": { + "android": [ + 264, + 296 + ], + "usb": 341769, + "linux": null, + "xkb": null, + "windows": null, + "macos": null + }, + "keyCodes": { + "android": [ + 196 + ], + "glfw": null + } + }, + "gameButton10": { + "names": { + "domkey": "GameButton10", + "android": [ + "BUTTON_10" + ], + "english": "Game Button 10", + "chromium": "button10", + "glfw": null + }, + "scanCodes": { + "android": [ + 265, + 297 + ], + "usb": 341770, + "linux": null, + "xkb": null, + "windows": null, + "macos": null + }, + "keyCodes": { + "android": [ + 197 + ], + "glfw": null + } + }, + "gameButton11": { + "names": { + "domkey": "GameButton11", + "android": [ + "BUTTON_11" + ], + "english": "Game Button 11", + "chromium": "button11", + "glfw": null + }, + "scanCodes": { + "android": [ + 266, + 298 + ], + "usb": 341771, + "linux": null, + "xkb": null, + "windows": null, + "macos": null + }, + "keyCodes": { + "android": [ + 198 + ], + "glfw": null + } + }, + "gameButton12": { + "names": { + "domkey": "GameButton12", + "android": [ + "BUTTON_12" + ], + "english": "Game Button 12", + "chromium": "button12", + "glfw": null + }, + "scanCodes": { + "android": [ + 267, + 299 + ], + "usb": 341772, + "linux": null, + "xkb": null, + "windows": null, + "macos": null + }, + "keyCodes": { + "android": [ + 199 + ], + "glfw": null + } + }, + "gameButton13": { + "names": { + "domkey": "GameButton13", + "android": [ + "BUTTON_13" + ], + "english": "Game Button 13", + "chromium": "button13", + "glfw": null + }, + "scanCodes": { + "android": [ + 268, + 300 + ], + "usb": 341773, + "linux": null, + "xkb": null, + "windows": null, + "macos": null + }, + "keyCodes": { + "android": [ + 200 + ], + "glfw": null + } + }, + "gameButton14": { + "names": { + "domkey": "GameButton14", + "android": [ + "BUTTON_14" + ], + "english": "Game Button 14", + "chromium": "button14", + "glfw": null + }, + "scanCodes": { + "android": [ + 269, + 301 + ], + "usb": 341774, + "linux": null, + "xkb": null, + "windows": null, + "macos": null + }, + "keyCodes": { + "android": [ + 201 + ], + "glfw": null + } + }, + "gameButton15": { + "names": { + "domkey": "GameButton15", + "android": [ + "BUTTON_15" + ], + "english": "Game Button 15", + "chromium": "button15", + "glfw": null + }, + "scanCodes": { + "android": [ + 270, + 302 + ], + "usb": 341775, + "linux": null, + "xkb": null, + "windows": null, + "macos": null + }, + "keyCodes": { + "android": [ + 202 + ], + "glfw": null + } + }, + "gameButton16": { + "names": { + "domkey": "GameButton16", + "android": [ + "BUTTON_16" + ], + "english": "Game Button 16", + "chromium": "button16", + "glfw": null + }, + "scanCodes": { + "android": [ + 271, + 303 + ], + "usb": 341776, + "linux": null, + "xkb": null, + "windows": null, + "macos": null + }, + "keyCodes": { + "android": [ + 203 + ], + "glfw": null + } + }, + "gameButtonA": { + "names": { + "domkey": "GameButtonA", + "android": [ + "BUTTON_A" + ], + "english": "Game Button A", + "chromium": "buttonA", + "glfw": null + }, + "scanCodes": { + "android": [ + 304 + ], + "usb": 341777, + "linux": null, + "xkb": null, + "windows": null, + "macos": null + }, + "keyCodes": { + "android": [ + 96 + ], + "glfw": null + } + }, + "gameButtonB": { + "names": { + "domkey": "GameButtonB", + "android": [ + "BUTTON_B" + ], + "english": "Game Button B", + "chromium": "buttonB", + "glfw": null + }, + "scanCodes": { + "android": [ + 305 + ], + "usb": 341778, + "linux": null, + "xkb": null, + "windows": null, + "macos": null + }, + "keyCodes": { + "android": [ + 97 + ], + "glfw": null + } + }, + "gameButtonC": { + "names": { + "domkey": "GameButtonC", + "android": [ + "BUTTON_C" + ], + "english": "Game Button C", + "chromium": "buttonC", + "glfw": null + }, + "scanCodes": { + "android": [ + 306 + ], + "usb": 341779, + "linux": null, + "xkb": null, + "windows": null, + "macos": null + }, + "keyCodes": { + "android": [ + 98 + ], + "glfw": null + } + }, + "gameButtonLeft1": { + "names": { + "domkey": "GameButtonLeft1", + "android": [ + "BUTTON_L1" + ], + "english": "Game Button Left 1", + "chromium": "buttonL1", + "glfw": null + }, + "scanCodes": { + "android": [ + 310 + ], + "usb": 341780, + "linux": null, + "xkb": null, + "windows": null, + "macos": null + }, + "keyCodes": { + "android": [ + 102 + ], + "glfw": null + } + }, + "gameButtonLeft2": { + "names": { + "domkey": "GameButtonLeft2", + "android": [ + "BUTTON_L2" + ], + "english": "Game Button Left 2", + "chromium": "buttonL2", + "glfw": null + }, + "scanCodes": { + "android": [ + 312 + ], + "usb": 341781, + "linux": null, + "xkb": null, + "windows": null, + "macos": null + }, + "keyCodes": { + "android": [ + 104 + ], + "glfw": null + } + }, + "gameButtonMode": { + "names": { + "domkey": "GameButtonMode", + "android": [ + "BUTTON_MODE" + ], + "english": "Game Button Mode", + "chromium": "buttonMode", + "glfw": null + }, + "scanCodes": { + "android": [ + 316 + ], + "usb": 341782, + "linux": null, + "xkb": null, + "windows": null, + "macos": null + }, + "keyCodes": { + "android": [ + 110 + ], + "glfw": null + } + }, + "gameButtonRight1": { + "names": { + "domkey": "GameButtonRight1", + "android": [ + "BUTTON_R1" + ], + "english": "Game Button Right 1", + "chromium": "buttonR1", + "glfw": null + }, + "scanCodes": { + "android": [ + 311 + ], + "usb": 341783, + "linux": null, + "xkb": null, + "windows": null, + "macos": null + }, + "keyCodes": { + "android": [ + 103 + ], + "glfw": null + } + }, + "gameButtonRight2": { + "names": { + "domkey": "GameButtonRight2", + "android": [ + "BUTTON_R2" + ], + "english": "Game Button Right 2", + "chromium": "buttonR2", + "glfw": null + }, + "scanCodes": { + "android": [ + 313 + ], + "usb": 341784, + "linux": null, + "xkb": null, + "windows": null, + "macos": null + }, + "keyCodes": { + "android": [ + 105 + ], + "glfw": null + } + }, + "gameButtonSelect": { + "names": { + "domkey": "GameButtonSelect", + "android": [ + "BUTTON_SELECT" + ], + "english": "Game Button Select", + "chromium": "buttonSelect", + "glfw": null + }, + "scanCodes": { + "android": [ + 314 + ], + "usb": 341785, + "linux": null, + "xkb": null, + "windows": null, + "macos": null + }, + "keyCodes": { + "android": [ + 109 + ], + "glfw": null + } + }, + "gameButtonStart": { + "names": { + "domkey": "GameButtonStart", + "android": [ + "BUTTON_START" + ], + "english": "Game Button Start", + "chromium": "buttonStart", + "glfw": null + }, + "scanCodes": { + "android": [ + 315 + ], + "usb": 341786, + "linux": null, + "xkb": null, + "windows": null, + "macos": null + }, + "keyCodes": { + "android": [ + 108 + ], + "glfw": null + } + }, + "gameButtonThumbLeft": { + "names": { + "domkey": "GameButtonThumbLeft", + "android": [ + "BUTTON_THUMBL" + ], + "english": "Game Button Thumb Left", + "chromium": "buttonThumbl", + "glfw": null + }, + "scanCodes": { + "android": [ + 317 + ], + "usb": 341787, + "linux": null, + "xkb": null, + "windows": null, + "macos": null + }, + "keyCodes": { + "android": [ + 106 + ], + "glfw": null + } + }, + "gameButtonThumbRight": { + "names": { + "domkey": "GameButtonThumbRight", + "android": [ + "BUTTON_THUMBR" + ], + "english": "Game Button Thumb Right", + "chromium": "buttonThumbr", + "glfw": null + }, + "scanCodes": { + "android": [ + 318 + ], + "usb": 341788, + "linux": null, + "xkb": null, + "windows": null, + "macos": null + }, + "keyCodes": { + "android": [ + 107 + ], + "glfw": null + } + }, + "gameButtonX": { + "names": { + "domkey": "GameButtonX", + "android": [ + "BUTTON_X" + ], + "english": "Game Button X", + "chromium": "buttonX", + "glfw": null + }, + "scanCodes": { + "android": [ + 307 + ], + "usb": 341789, + "linux": null, + "xkb": null, + "windows": null, + "macos": null + }, + "keyCodes": { + "android": [ + 99 + ], + "glfw": null + } + }, + "gameButtonY": { + "names": { + "domkey": "GameButtonY", + "android": [ + "BUTTON_Y" + ], + "english": "Game Button Y", + "chromium": "buttonY", + "glfw": null + }, + "scanCodes": { + "android": [ + 308 + ], + "usb": 341790, + "linux": null, + "xkb": null, + "windows": null, + "macos": null + }, + "keyCodes": { + "android": [ + 100 + ], + "glfw": null + } + }, + "gameButtonZ": { + "names": { + "domkey": "GameButtonZ", + "android": [ + "BUTTON_Z" + ], + "english": "Game Button Z", + "chromium": "buttonZ", + "glfw": null + }, + "scanCodes": { + "android": [ + 309 + ], + "usb": 341791, + "linux": null, + "xkb": null, + "windows": null, + "macos": null + }, + "keyCodes": { + "android": [ + 101 + ], + "glfw": null + } } } \ No newline at end of file diff --git a/dev/tools/gen_keycodes/data/key_name_to_android_name.json b/dev/tools/gen_keycodes/data/key_name_to_android_name.json index f51ec02005..856a4f9f64 100644 --- a/dev/tools/gen_keycodes/data/key_name_to_android_name.json +++ b/dev/tools/gen_keycodes/data/key_name_to_android_name.json @@ -90,6 +90,37 @@ "f24": ["F24"], "find": ["FIND"], "fn": ["FUNCTION"], + "gameButton1": ["BUTTON_1"], + "gameButton2": ["BUTTON_2"], + "gameButton3": ["BUTTON_3"], + "gameButton4": ["BUTTON_4"], + "gameButton5": ["BUTTON_5"], + "gameButton6": ["BUTTON_6"], + "gameButton7": ["BUTTON_7"], + "gameButton8": ["BUTTON_8"], + "gameButton9": ["BUTTON_9"], + "gameButton10": ["BUTTON_10"], + "gameButton11": ["BUTTON_11"], + "gameButton12": ["BUTTON_12"], + "gameButton13": ["BUTTON_13"], + "gameButton14": ["BUTTON_14"], + "gameButton15": ["BUTTON_15"], + "gameButton16": ["BUTTON_16"], + "gameButtonA": ["BUTTON_A"], + "gameButtonB": ["BUTTON_B"], + "gameButtonC": ["BUTTON_C"], + "gameButtonLeft1": ["BUTTON_L1"], + "gameButtonLeft2": ["BUTTON_L2"], + "gameButtonMode": ["BUTTON_MODE"], + "gameButtonRight1": ["BUTTON_R1"], + "gameButtonRight2": ["BUTTON_R2"], + "gameButtonSelect": ["BUTTON_SELECT"], + "gameButtonStart": ["BUTTON_START"], + "gameButtonThumbLeft": ["BUTTON_THUMBL"], + "gameButtonThumbRight": ["BUTTON_THUMBR"], + "gameButtonX": ["BUTTON_X"], + "gameButtonY": ["BUTTON_Y"], + "gameButtonZ": ["BUTTON_Z"], "goBack": ["BACK"], "goHome": ["HOME"], "groupNext": ["LANGUAGE_SWITCH"], @@ -200,6 +231,7 @@ "quote": ["APOSTROPHE"], "redo": ["REDO"], "scrollLock": ["SCROLL_LOCK"], + "select": ["DPAD_CENTER"], "semicolon": ["SEMICOLON"], "settings": ["SETTINGS"], "shiftLeft": ["SHIFT_LEFT"], diff --git a/dev/tools/gen_keycodes/data/supplemental_hid_codes.inc b/dev/tools/gen_keycodes/data/supplemental_hid_codes.inc new file mode 100644 index 0000000000..da271bc806 --- /dev/null +++ b/dev/tools/gen_keycodes/data/supplemental_hid_codes.inc @@ -0,0 +1,45 @@ + // These are supplemental key codes to be added to those that Chromium + // defines. Since the web doesn't have game controller buttons defined in the + // same way, these map USB HID codes for game controller buttons to + // Android/Linux button names. + // + // The HID codes here are not real USB HID codes, because the USB HID standard + // doesn't define game controller buttons in this way. It defines only two + // button "collections" (fire/jump and trigger), with the button number for + // each collection sent as extra data. Since we're just using USB HID as a + // convenient namespace, and not using these HID codes for interfacing with a + // USB protocol, we can define new ones to enumerate the buttons. These don't + // collide with any currently defined HID codes. + // + // USB HID evdev XKB Win Mac DOMKey Code + USB_KEYMAP(0x05ff01, 0x0000, 0x0000, 0x0000, 0xffff, "GameButton1", BUTTON_1), + USB_KEYMAP(0x05ff02, 0x0000, 0x0000, 0x0000, 0xffff, "GameButton2", BUTTON_2), + USB_KEYMAP(0x05ff03, 0x0000, 0x0000, 0x0000, 0xffff, "GameButton3", BUTTON_3), + USB_KEYMAP(0x05ff04, 0x0000, 0x0000, 0x0000, 0xffff, "GameButton4", BUTTON_4), + USB_KEYMAP(0x05ff05, 0x0000, 0x0000, 0x0000, 0xffff, "GameButton5", BUTTON_5), + USB_KEYMAP(0x05ff06, 0x0000, 0x0000, 0x0000, 0xffff, "GameButton6", BUTTON_6), + USB_KEYMAP(0x05ff07, 0x0000, 0x0000, 0x0000, 0xffff, "GameButton7", BUTTON_7), + USB_KEYMAP(0x05ff08, 0x0000, 0x0000, 0x0000, 0xffff, "GameButton8", BUTTON_8), + USB_KEYMAP(0x05ff09, 0x0000, 0x0000, 0x0000, 0xffff, "GameButton9", BUTTON_9), + USB_KEYMAP(0x05ff0a, 0x0000, 0x0000, 0x0000, 0xffff, "GameButton10", BUTTON_10), + USB_KEYMAP(0x05ff0b, 0x0000, 0x0000, 0x0000, 0xffff, "GameButton11", BUTTON_11), + USB_KEYMAP(0x05ff0c, 0x0000, 0x0000, 0x0000, 0xffff, "GameButton12", BUTTON_12), + USB_KEYMAP(0x05ff0d, 0x0000, 0x0000, 0x0000, 0xffff, "GameButton13", BUTTON_13), + USB_KEYMAP(0x05ff0e, 0x0000, 0x0000, 0x0000, 0xffff, "GameButton14", BUTTON_14), + USB_KEYMAP(0x05ff0f, 0x0000, 0x0000, 0x0000, 0xffff, "GameButton15", BUTTON_15), + USB_KEYMAP(0x05ff10, 0x0000, 0x0000, 0x0000, 0xffff, "GameButton16", BUTTON_16), + USB_KEYMAP(0x05ff11, 0x0000, 0x0000, 0x0000, 0xffff, "GameButtonA", BUTTON_A), + USB_KEYMAP(0x05ff12, 0x0000, 0x0000, 0x0000, 0xffff, "GameButtonB", BUTTON_B), + USB_KEYMAP(0x05ff13, 0x0000, 0x0000, 0x0000, 0xffff, "GameButtonC", BUTTON_C), + USB_KEYMAP(0x05ff14, 0x0000, 0x0000, 0x0000, 0xffff, "GameButtonLeft1", BUTTON_L1), + USB_KEYMAP(0x05ff15, 0x0000, 0x0000, 0x0000, 0xffff, "GameButtonLeft2", BUTTON_L2), + USB_KEYMAP(0x05ff16, 0x0000, 0x0000, 0x0000, 0xffff, "GameButtonMode", BUTTON_MODE), + USB_KEYMAP(0x05ff17, 0x0000, 0x0000, 0x0000, 0xffff, "GameButtonRight1", BUTTON_R1), + USB_KEYMAP(0x05ff18, 0x0000, 0x0000, 0x0000, 0xffff, "GameButtonRight2", BUTTON_R2), + USB_KEYMAP(0x05ff19, 0x0000, 0x0000, 0x0000, 0xffff, "GameButtonSelect", BUTTON_SELECT), + USB_KEYMAP(0x05ff1a, 0x0000, 0x0000, 0x0000, 0xffff, "GameButtonStart", BUTTON_START), + USB_KEYMAP(0x05ff1b, 0x0000, 0x0000, 0x0000, 0xffff, "GameButtonThumbLeft", BUTTON_THUMBL), + USB_KEYMAP(0x05ff1c, 0x0000, 0x0000, 0x0000, 0xffff, "GameButtonThumbRight", BUTTON_THUMBR), + USB_KEYMAP(0x05ff1d, 0x0000, 0x0000, 0x0000, 0xffff, "GameButtonX", BUTTON_X), + USB_KEYMAP(0x05ff1e, 0x0000, 0x0000, 0x0000, 0xffff, "GameButtonY", BUTTON_Y), + USB_KEYMAP(0x05ff1f, 0x0000, 0x0000, 0x0000, 0xffff, "GameButtonZ", BUTTON_Z), diff --git a/dev/tools/gen_keycodes/lib/key_data.dart b/dev/tools/gen_keycodes/lib/key_data.dart index 3b9c8cac0f..71492ca3c5 100644 --- a/dev/tools/gen_keycodes/lib/key_data.dart +++ b/dev/tools/gen_keycodes/lib/key_data.dart @@ -375,7 +375,7 @@ class Key { static String getCommentName(String constantName) { String upperCamel = lowerCamelToUpperCamel(constantName); - upperCamel = upperCamel.replaceAllMapped(RegExp(r'(Digit|Numpad|Lang)([0-9]+)'), (Match match) => '${match.group(1)} ${match.group(2)}'); + upperCamel = upperCamel.replaceAllMapped(RegExp(r'(Digit|Numpad|Lang|Button|Left|Right)([0-9]+)'), (Match match) => '${match.group(1)} ${match.group(2)}'); return upperCamel.replaceAllMapped(RegExp(r'([A-Z])'), (Match match) => ' ${match.group(1)}').trim(); } diff --git a/packages/flutter/lib/src/services/keyboard_key.dart b/packages/flutter/lib/src/services/keyboard_key.dart index bc69d8c406..0f88afbd99 100644 --- a/packages/flutter/lib/src/services/keyboard_key.dart +++ b/packages/flutter/lib/src/services/keyboard_key.dart @@ -1455,6 +1455,161 @@ class LogicalKeyboardKey extends KeyboardKey { /// See the function [RawKeyEvent.logicalKey] for more information. static const LogicalKeyboardKey showAllWindows = LogicalKeyboardKey(0x001000c029f, debugName: kReleaseMode ? null : 'Show All Windows'); + /// Represents the logical "Game Button 1" key on the keyboard. + /// + /// See the function [RawKeyEvent.logicalKey] for more information. + static const LogicalKeyboardKey gameButton1 = LogicalKeyboardKey(0x00100053701, debugName: kReleaseMode ? null : 'Game Button 1'); + + /// Represents the logical "Game Button 2" key on the keyboard. + /// + /// See the function [RawKeyEvent.logicalKey] for more information. + static const LogicalKeyboardKey gameButton2 = LogicalKeyboardKey(0x00100053702, debugName: kReleaseMode ? null : 'Game Button 2'); + + /// Represents the logical "Game Button 3" key on the keyboard. + /// + /// See the function [RawKeyEvent.logicalKey] for more information. + static const LogicalKeyboardKey gameButton3 = LogicalKeyboardKey(0x00100053703, debugName: kReleaseMode ? null : 'Game Button 3'); + + /// Represents the logical "Game Button 4" key on the keyboard. + /// + /// See the function [RawKeyEvent.logicalKey] for more information. + static const LogicalKeyboardKey gameButton4 = LogicalKeyboardKey(0x00100053704, debugName: kReleaseMode ? null : 'Game Button 4'); + + /// Represents the logical "Game Button 5" key on the keyboard. + /// + /// See the function [RawKeyEvent.logicalKey] for more information. + static const LogicalKeyboardKey gameButton5 = LogicalKeyboardKey(0x00100053705, debugName: kReleaseMode ? null : 'Game Button 5'); + + /// Represents the logical "Game Button 6" key on the keyboard. + /// + /// See the function [RawKeyEvent.logicalKey] for more information. + static const LogicalKeyboardKey gameButton6 = LogicalKeyboardKey(0x00100053706, debugName: kReleaseMode ? null : 'Game Button 6'); + + /// Represents the logical "Game Button 7" key on the keyboard. + /// + /// See the function [RawKeyEvent.logicalKey] for more information. + static const LogicalKeyboardKey gameButton7 = LogicalKeyboardKey(0x00100053707, debugName: kReleaseMode ? null : 'Game Button 7'); + + /// Represents the logical "Game Button 8" key on the keyboard. + /// + /// See the function [RawKeyEvent.logicalKey] for more information. + static const LogicalKeyboardKey gameButton8 = LogicalKeyboardKey(0x00100053708, debugName: kReleaseMode ? null : 'Game Button 8'); + + /// Represents the logical "Game Button 9" key on the keyboard. + /// + /// See the function [RawKeyEvent.logicalKey] for more information. + static const LogicalKeyboardKey gameButton9 = LogicalKeyboardKey(0x00100053709, debugName: kReleaseMode ? null : 'Game Button 9'); + + /// Represents the logical "Game Button 10" key on the keyboard. + /// + /// See the function [RawKeyEvent.logicalKey] for more information. + static const LogicalKeyboardKey gameButton10 = LogicalKeyboardKey(0x0010005370a, debugName: kReleaseMode ? null : 'Game Button 10'); + + /// Represents the logical "Game Button 11" key on the keyboard. + /// + /// See the function [RawKeyEvent.logicalKey] for more information. + static const LogicalKeyboardKey gameButton11 = LogicalKeyboardKey(0x0010005370b, debugName: kReleaseMode ? null : 'Game Button 11'); + + /// Represents the logical "Game Button 12" key on the keyboard. + /// + /// See the function [RawKeyEvent.logicalKey] for more information. + static const LogicalKeyboardKey gameButton12 = LogicalKeyboardKey(0x0010005370c, debugName: kReleaseMode ? null : 'Game Button 12'); + + /// Represents the logical "Game Button 13" key on the keyboard. + /// + /// See the function [RawKeyEvent.logicalKey] for more information. + static const LogicalKeyboardKey gameButton13 = LogicalKeyboardKey(0x0010005370d, debugName: kReleaseMode ? null : 'Game Button 13'); + + /// Represents the logical "Game Button 14" key on the keyboard. + /// + /// See the function [RawKeyEvent.logicalKey] for more information. + static const LogicalKeyboardKey gameButton14 = LogicalKeyboardKey(0x0010005370e, debugName: kReleaseMode ? null : 'Game Button 14'); + + /// Represents the logical "Game Button 15" key on the keyboard. + /// + /// See the function [RawKeyEvent.logicalKey] for more information. + static const LogicalKeyboardKey gameButton15 = LogicalKeyboardKey(0x0010005370f, debugName: kReleaseMode ? null : 'Game Button 15'); + + /// Represents the logical "Game Button 16" key on the keyboard. + /// + /// See the function [RawKeyEvent.logicalKey] for more information. + static const LogicalKeyboardKey gameButton16 = LogicalKeyboardKey(0x00100053710, debugName: kReleaseMode ? null : 'Game Button 16'); + + /// Represents the logical "Game Button A" key on the keyboard. + /// + /// See the function [RawKeyEvent.logicalKey] for more information. + static const LogicalKeyboardKey gameButtonA = LogicalKeyboardKey(0x00100053711, debugName: kReleaseMode ? null : 'Game Button A'); + + /// Represents the logical "Game Button B" key on the keyboard. + /// + /// See the function [RawKeyEvent.logicalKey] for more information. + static const LogicalKeyboardKey gameButtonB = LogicalKeyboardKey(0x00100053712, debugName: kReleaseMode ? null : 'Game Button B'); + + /// Represents the logical "Game Button C" key on the keyboard. + /// + /// See the function [RawKeyEvent.logicalKey] for more information. + static const LogicalKeyboardKey gameButtonC = LogicalKeyboardKey(0x00100053713, debugName: kReleaseMode ? null : 'Game Button C'); + + /// Represents the logical "Game Button Left 1" key on the keyboard. + /// + /// See the function [RawKeyEvent.logicalKey] for more information. + static const LogicalKeyboardKey gameButtonLeft1 = LogicalKeyboardKey(0x00100053714, debugName: kReleaseMode ? null : 'Game Button Left 1'); + + /// Represents the logical "Game Button Left 2" key on the keyboard. + /// + /// See the function [RawKeyEvent.logicalKey] for more information. + static const LogicalKeyboardKey gameButtonLeft2 = LogicalKeyboardKey(0x00100053715, debugName: kReleaseMode ? null : 'Game Button Left 2'); + + /// Represents the logical "Game Button Mode" key on the keyboard. + /// + /// See the function [RawKeyEvent.logicalKey] for more information. + static const LogicalKeyboardKey gameButtonMode = LogicalKeyboardKey(0x00100053716, debugName: kReleaseMode ? null : 'Game Button Mode'); + + /// Represents the logical "Game Button Right 1" key on the keyboard. + /// + /// See the function [RawKeyEvent.logicalKey] for more information. + static const LogicalKeyboardKey gameButtonRight1 = LogicalKeyboardKey(0x00100053717, debugName: kReleaseMode ? null : 'Game Button Right 1'); + + /// Represents the logical "Game Button Right 2" key on the keyboard. + /// + /// See the function [RawKeyEvent.logicalKey] for more information. + static const LogicalKeyboardKey gameButtonRight2 = LogicalKeyboardKey(0x00100053718, debugName: kReleaseMode ? null : 'Game Button Right 2'); + + /// Represents the logical "Game Button Select" key on the keyboard. + /// + /// See the function [RawKeyEvent.logicalKey] for more information. + static const LogicalKeyboardKey gameButtonSelect = LogicalKeyboardKey(0x00100053719, debugName: kReleaseMode ? null : 'Game Button Select'); + + /// Represents the logical "Game Button Start" key on the keyboard. + /// + /// See the function [RawKeyEvent.logicalKey] for more information. + static const LogicalKeyboardKey gameButtonStart = LogicalKeyboardKey(0x0010005371a, debugName: kReleaseMode ? null : 'Game Button Start'); + + /// Represents the logical "Game Button Thumb Left" key on the keyboard. + /// + /// See the function [RawKeyEvent.logicalKey] for more information. + static const LogicalKeyboardKey gameButtonThumbLeft = LogicalKeyboardKey(0x0010005371b, debugName: kReleaseMode ? null : 'Game Button Thumb Left'); + + /// Represents the logical "Game Button Thumb Right" key on the keyboard. + /// + /// See the function [RawKeyEvent.logicalKey] for more information. + static const LogicalKeyboardKey gameButtonThumbRight = LogicalKeyboardKey(0x0010005371c, debugName: kReleaseMode ? null : 'Game Button Thumb Right'); + + /// Represents the logical "Game Button X" key on the keyboard. + /// + /// See the function [RawKeyEvent.logicalKey] for more information. + static const LogicalKeyboardKey gameButtonX = LogicalKeyboardKey(0x0010005371d, debugName: kReleaseMode ? null : 'Game Button X'); + + /// Represents the logical "Game Button Y" key on the keyboard. + /// + /// See the function [RawKeyEvent.logicalKey] for more information. + static const LogicalKeyboardKey gameButtonY = LogicalKeyboardKey(0x0010005371e, debugName: kReleaseMode ? null : 'Game Button Y'); + + /// Represents the logical "Game Button Z" key on the keyboard. + /// + /// See the function [RawKeyEvent.logicalKey] for more information. + static const LogicalKeyboardKey gameButtonZ = LogicalKeyboardKey(0x0010005371f, debugName: kReleaseMode ? null : 'Game Button Z'); + /// Represents the logical "Shift" key on the keyboard. /// /// This key represents the union of the keys {shiftLeft, shiftRight} when @@ -1720,6 +1875,37 @@ class LogicalKeyboardKey extends KeyboardKey { 0x01000c028c: mailSend, 0x01000c029d: keyboardLayoutSelect, 0x01000c029f: showAllWindows, + 0x0100053701: gameButton1, + 0x0100053702: gameButton2, + 0x0100053703: gameButton3, + 0x0100053704: gameButton4, + 0x0100053705: gameButton5, + 0x0100053706: gameButton6, + 0x0100053707: gameButton7, + 0x0100053708: gameButton8, + 0x0100053709: gameButton9, + 0x010005370a: gameButton10, + 0x010005370b: gameButton11, + 0x010005370c: gameButton12, + 0x010005370d: gameButton13, + 0x010005370e: gameButton14, + 0x010005370f: gameButton15, + 0x0100053710: gameButton16, + 0x0100053711: gameButtonA, + 0x0100053712: gameButtonB, + 0x0100053713: gameButtonC, + 0x0100053714: gameButtonLeft1, + 0x0100053715: gameButtonLeft2, + 0x0100053716: gameButtonMode, + 0x0100053717: gameButtonRight1, + 0x0100053718: gameButtonRight2, + 0x0100053719: gameButtonSelect, + 0x010005371a: gameButtonStart, + 0x010005371b: gameButtonThumbLeft, + 0x010005371c: gameButtonThumbRight, + 0x010005371d: gameButtonX, + 0x010005371e: gameButtonY, + 0x010005371f: gameButtonZ, 0x201000700e1: shift, 0x201000700e3: meta, 0x201000700e2: alt, @@ -3139,6 +3325,192 @@ class PhysicalKeyboardKey extends KeyboardKey { /// See the function [RawKeyEvent.physicalKey] for more information. static const PhysicalKeyboardKey showAllWindows = PhysicalKeyboardKey(0x000c029f, debugName: kReleaseMode ? null : 'Show All Windows'); + /// Represents the location of the "Game Button 1" key on a generalized + /// keyboard. + /// + /// See the function [RawKeyEvent.physicalKey] for more information. + static const PhysicalKeyboardKey gameButton1 = PhysicalKeyboardKey(0x00053701, debugName: kReleaseMode ? null : 'Game Button 1'); + + /// Represents the location of the "Game Button 2" key on a generalized + /// keyboard. + /// + /// See the function [RawKeyEvent.physicalKey] for more information. + static const PhysicalKeyboardKey gameButton2 = PhysicalKeyboardKey(0x00053702, debugName: kReleaseMode ? null : 'Game Button 2'); + + /// Represents the location of the "Game Button 3" key on a generalized + /// keyboard. + /// + /// See the function [RawKeyEvent.physicalKey] for more information. + static const PhysicalKeyboardKey gameButton3 = PhysicalKeyboardKey(0x00053703, debugName: kReleaseMode ? null : 'Game Button 3'); + + /// Represents the location of the "Game Button 4" key on a generalized + /// keyboard. + /// + /// See the function [RawKeyEvent.physicalKey] for more information. + static const PhysicalKeyboardKey gameButton4 = PhysicalKeyboardKey(0x00053704, debugName: kReleaseMode ? null : 'Game Button 4'); + + /// Represents the location of the "Game Button 5" key on a generalized + /// keyboard. + /// + /// See the function [RawKeyEvent.physicalKey] for more information. + static const PhysicalKeyboardKey gameButton5 = PhysicalKeyboardKey(0x00053705, debugName: kReleaseMode ? null : 'Game Button 5'); + + /// Represents the location of the "Game Button 6" key on a generalized + /// keyboard. + /// + /// See the function [RawKeyEvent.physicalKey] for more information. + static const PhysicalKeyboardKey gameButton6 = PhysicalKeyboardKey(0x00053706, debugName: kReleaseMode ? null : 'Game Button 6'); + + /// Represents the location of the "Game Button 7" key on a generalized + /// keyboard. + /// + /// See the function [RawKeyEvent.physicalKey] for more information. + static const PhysicalKeyboardKey gameButton7 = PhysicalKeyboardKey(0x00053707, debugName: kReleaseMode ? null : 'Game Button 7'); + + /// Represents the location of the "Game Button 8" key on a generalized + /// keyboard. + /// + /// See the function [RawKeyEvent.physicalKey] for more information. + static const PhysicalKeyboardKey gameButton8 = PhysicalKeyboardKey(0x00053708, debugName: kReleaseMode ? null : 'Game Button 8'); + + /// Represents the location of the "Game Button 9" key on a generalized + /// keyboard. + /// + /// See the function [RawKeyEvent.physicalKey] for more information. + static const PhysicalKeyboardKey gameButton9 = PhysicalKeyboardKey(0x00053709, debugName: kReleaseMode ? null : 'Game Button 9'); + + /// Represents the location of the "Game Button 10" key on a generalized + /// keyboard. + /// + /// See the function [RawKeyEvent.physicalKey] for more information. + static const PhysicalKeyboardKey gameButton10 = PhysicalKeyboardKey(0x0005370a, debugName: kReleaseMode ? null : 'Game Button 10'); + + /// Represents the location of the "Game Button 11" key on a generalized + /// keyboard. + /// + /// See the function [RawKeyEvent.physicalKey] for more information. + static const PhysicalKeyboardKey gameButton11 = PhysicalKeyboardKey(0x0005370b, debugName: kReleaseMode ? null : 'Game Button 11'); + + /// Represents the location of the "Game Button 12" key on a generalized + /// keyboard. + /// + /// See the function [RawKeyEvent.physicalKey] for more information. + static const PhysicalKeyboardKey gameButton12 = PhysicalKeyboardKey(0x0005370c, debugName: kReleaseMode ? null : 'Game Button 12'); + + /// Represents the location of the "Game Button 13" key on a generalized + /// keyboard. + /// + /// See the function [RawKeyEvent.physicalKey] for more information. + static const PhysicalKeyboardKey gameButton13 = PhysicalKeyboardKey(0x0005370d, debugName: kReleaseMode ? null : 'Game Button 13'); + + /// Represents the location of the "Game Button 14" key on a generalized + /// keyboard. + /// + /// See the function [RawKeyEvent.physicalKey] for more information. + static const PhysicalKeyboardKey gameButton14 = PhysicalKeyboardKey(0x0005370e, debugName: kReleaseMode ? null : 'Game Button 14'); + + /// Represents the location of the "Game Button 15" key on a generalized + /// keyboard. + /// + /// See the function [RawKeyEvent.physicalKey] for more information. + static const PhysicalKeyboardKey gameButton15 = PhysicalKeyboardKey(0x0005370f, debugName: kReleaseMode ? null : 'Game Button 15'); + + /// Represents the location of the "Game Button 16" key on a generalized + /// keyboard. + /// + /// See the function [RawKeyEvent.physicalKey] for more information. + static const PhysicalKeyboardKey gameButton16 = PhysicalKeyboardKey(0x00053710, debugName: kReleaseMode ? null : 'Game Button 16'); + + /// Represents the location of the "Game Button A" key on a generalized + /// keyboard. + /// + /// See the function [RawKeyEvent.physicalKey] for more information. + static const PhysicalKeyboardKey gameButtonA = PhysicalKeyboardKey(0x00053711, debugName: kReleaseMode ? null : 'Game Button A'); + + /// Represents the location of the "Game Button B" key on a generalized + /// keyboard. + /// + /// See the function [RawKeyEvent.physicalKey] for more information. + static const PhysicalKeyboardKey gameButtonB = PhysicalKeyboardKey(0x00053712, debugName: kReleaseMode ? null : 'Game Button B'); + + /// Represents the location of the "Game Button C" key on a generalized + /// keyboard. + /// + /// See the function [RawKeyEvent.physicalKey] for more information. + static const PhysicalKeyboardKey gameButtonC = PhysicalKeyboardKey(0x00053713, debugName: kReleaseMode ? null : 'Game Button C'); + + /// Represents the location of the "Game Button Left 1" key on a generalized + /// keyboard. + /// + /// See the function [RawKeyEvent.physicalKey] for more information. + static const PhysicalKeyboardKey gameButtonLeft1 = PhysicalKeyboardKey(0x00053714, debugName: kReleaseMode ? null : 'Game Button Left 1'); + + /// Represents the location of the "Game Button Left 2" key on a generalized + /// keyboard. + /// + /// See the function [RawKeyEvent.physicalKey] for more information. + static const PhysicalKeyboardKey gameButtonLeft2 = PhysicalKeyboardKey(0x00053715, debugName: kReleaseMode ? null : 'Game Button Left 2'); + + /// Represents the location of the "Game Button Mode" key on a generalized + /// keyboard. + /// + /// See the function [RawKeyEvent.physicalKey] for more information. + static const PhysicalKeyboardKey gameButtonMode = PhysicalKeyboardKey(0x00053716, debugName: kReleaseMode ? null : 'Game Button Mode'); + + /// Represents the location of the "Game Button Right 1" key on a generalized + /// keyboard. + /// + /// See the function [RawKeyEvent.physicalKey] for more information. + static const PhysicalKeyboardKey gameButtonRight1 = PhysicalKeyboardKey(0x00053717, debugName: kReleaseMode ? null : 'Game Button Right 1'); + + /// Represents the location of the "Game Button Right 2" key on a generalized + /// keyboard. + /// + /// See the function [RawKeyEvent.physicalKey] for more information. + static const PhysicalKeyboardKey gameButtonRight2 = PhysicalKeyboardKey(0x00053718, debugName: kReleaseMode ? null : 'Game Button Right 2'); + + /// Represents the location of the "Game Button Select" key on a generalized + /// keyboard. + /// + /// See the function [RawKeyEvent.physicalKey] for more information. + static const PhysicalKeyboardKey gameButtonSelect = PhysicalKeyboardKey(0x00053719, debugName: kReleaseMode ? null : 'Game Button Select'); + + /// Represents the location of the "Game Button Start" key on a generalized + /// keyboard. + /// + /// See the function [RawKeyEvent.physicalKey] for more information. + static const PhysicalKeyboardKey gameButtonStart = PhysicalKeyboardKey(0x0005371a, debugName: kReleaseMode ? null : 'Game Button Start'); + + /// Represents the location of the "Game Button Thumb Left" key on a + /// generalized keyboard. + /// + /// See the function [RawKeyEvent.physicalKey] for more information. + static const PhysicalKeyboardKey gameButtonThumbLeft = PhysicalKeyboardKey(0x0005371b, debugName: kReleaseMode ? null : 'Game Button Thumb Left'); + + /// Represents the location of the "Game Button Thumb Right" key on a + /// generalized keyboard. + /// + /// See the function [RawKeyEvent.physicalKey] for more information. + static const PhysicalKeyboardKey gameButtonThumbRight = PhysicalKeyboardKey(0x0005371c, debugName: kReleaseMode ? null : 'Game Button Thumb Right'); + + /// Represents the location of the "Game Button X" key on a generalized + /// keyboard. + /// + /// See the function [RawKeyEvent.physicalKey] for more information. + static const PhysicalKeyboardKey gameButtonX = PhysicalKeyboardKey(0x0005371d, debugName: kReleaseMode ? null : 'Game Button X'); + + /// Represents the location of the "Game Button Y" key on a generalized + /// keyboard. + /// + /// See the function [RawKeyEvent.physicalKey] for more information. + static const PhysicalKeyboardKey gameButtonY = PhysicalKeyboardKey(0x0005371e, debugName: kReleaseMode ? null : 'Game Button Y'); + + /// Represents the location of the "Game Button Z" key on a generalized + /// keyboard. + /// + /// See the function [RawKeyEvent.physicalKey] for more information. + static const PhysicalKeyboardKey gameButtonZ = PhysicalKeyboardKey(0x0005371f, debugName: kReleaseMode ? null : 'Game Button Z'); + // A list of all the predefined constant PhysicalKeyboardKeys so that they // can be searched. static const Map _knownPhysicalKeys = { @@ -3376,5 +3748,36 @@ class PhysicalKeyboardKey extends KeyboardKey { 0x000c028c: mailSend, 0x000c029d: keyboardLayoutSelect, 0x000c029f: showAllWindows, + 0x00053701: gameButton1, + 0x00053702: gameButton2, + 0x00053703: gameButton3, + 0x00053704: gameButton4, + 0x00053705: gameButton5, + 0x00053706: gameButton6, + 0x00053707: gameButton7, + 0x00053708: gameButton8, + 0x00053709: gameButton9, + 0x0005370a: gameButton10, + 0x0005370b: gameButton11, + 0x0005370c: gameButton12, + 0x0005370d: gameButton13, + 0x0005370e: gameButton14, + 0x0005370f: gameButton15, + 0x00053710: gameButton16, + 0x00053711: gameButtonA, + 0x00053712: gameButtonB, + 0x00053713: gameButtonC, + 0x00053714: gameButtonLeft1, + 0x00053715: gameButtonLeft2, + 0x00053716: gameButtonMode, + 0x00053717: gameButtonRight1, + 0x00053718: gameButtonRight2, + 0x00053719: gameButtonSelect, + 0x0005371a: gameButtonStart, + 0x0005371b: gameButtonThumbLeft, + 0x0005371c: gameButtonThumbRight, + 0x0005371d: gameButtonX, + 0x0005371e: gameButtonY, + 0x0005371f: gameButtonZ, }; } diff --git a/packages/flutter/lib/src/services/keyboard_maps.dart b/packages/flutter/lib/src/services/keyboard_maps.dart index 175e9a186b..d5b3cb50f2 100644 --- a/packages/flutter/lib/src/services/keyboard_maps.dart +++ b/packages/flutter/lib/src/services/keyboard_maps.dart @@ -116,6 +116,7 @@ const Map kAndroidToLogicalKey = kAndroidToLogicalKey = kAndroidToPhysicalKey = kAndroidToPhysicalKey = kFuchsiaToLogicalKey = kFuchsiaToPhysicalKey = + /// for the numerical values of the `source`. Many of these constants are also + /// replicated as static constants in this class. + final int eventSource; + + /// The vendor ID of the device that produced the event. + /// + /// See + /// for the numerical values of the `vendorId`. + final int vendorId; + + /// The product ID of the device that produced the event. + /// + /// See + /// for the numerical values of the `productId`. + final int productId; + + // The source code that indicates that an event came from a joystick. + // from https://developer.android.com/reference/android/view/InputDevice.html#SOURCE_JOYSTICK + static const int _sourceJoystick = 0x01000010; + // Android only reports a single code point for the key label. @override String get keyLabel => plainCodePoint == 0 ? null : String.fromCharCode(plainCodePoint & _kCombiningCharacterMask); @override - PhysicalKeyboardKey get physicalKey => kAndroidToPhysicalKey[scanCode] ?? PhysicalKeyboardKey.none; + PhysicalKeyboardKey get physicalKey { + if (kAndroidToPhysicalKey.containsKey(scanCode)) { + return kAndroidToPhysicalKey[scanCode] ?? PhysicalKeyboardKey.none; + } + + // Android sends DPAD_UP, etc. as the keyCode for joystick DPAD events, but + // it doesn't set the scanCode for those, so we have to detect this, and set + // our own DPAD physical keys. The logical key will still match "arrowUp", + // etc. + if (eventSource & _sourceJoystick == _sourceJoystick) { + final LogicalKeyboardKey foundKey = kAndroidToLogicalKey[keyCode]; + if (foundKey == LogicalKeyboardKey.arrowUp) { + return PhysicalKeyboardKey.arrowUp; + } + if (foundKey == LogicalKeyboardKey.arrowDown) { + return PhysicalKeyboardKey.arrowDown; + } + if (foundKey == LogicalKeyboardKey.arrowLeft) { + return PhysicalKeyboardKey.arrowLeft; + } + if (foundKey == LogicalKeyboardKey.arrowRight) { + return PhysicalKeyboardKey.arrowRight; + } + } + return PhysicalKeyboardKey.none; + } @override LogicalKeyboardKey get logicalKey { diff --git a/packages/flutter/test/services/raw_keyboard_test.dart b/packages/flutter/test/services/raw_keyboard_test.dart index fec0a66bad..02e2ade5ec 100644 --- a/packages/flutter/test/services/raw_keyboard_test.dart +++ b/packages/flutter/test/services/raw_keyboard_test.dart @@ -39,6 +39,7 @@ void main() { 'codePoint': 0x44, 'scanCode': 0x20, 'metaState': modifier, + 'source': 0x101, // Keyboard source. }); final RawKeyEventDataAndroid data = event.data; for (ModifierKey key in ModifierKey.values) { @@ -73,6 +74,7 @@ void main() { 'codePoint': 0x44, 'scanCode': 0x20, 'metaState': modifier | RawKeyEventDataAndroid.modifierFunction, + 'source': 0x101, // Keyboard source. }); final RawKeyEventDataAndroid data = event.data; for (ModifierKey key in ModifierKey.values) { @@ -109,6 +111,7 @@ void main() { 'character': 'A', 'scanCode': 30, 'metaState': 0x0, + 'source': 0x101, // Keyboard source. }); final RawKeyEventDataAndroid data = keyAEvent.data; expect(data.physicalKey, equals(PhysicalKeyboardKey.keyA)); @@ -124,6 +127,7 @@ void main() { 'character': null, 'scanCode': 1, 'metaState': 0x0, + 'source': 0x101, // Keyboard source. }); final RawKeyEventDataAndroid data = escapeKeyEvent.data; expect(data.physicalKey, equals(PhysicalKeyboardKey.escape)); @@ -140,12 +144,64 @@ void main() { 'character': null, 'scanCode': 42, 'metaState': RawKeyEventDataAndroid.modifierLeftShift, + 'source': 0x101, // Keyboard source. }); final RawKeyEventDataAndroid data = shiftLeftKeyEvent.data; expect(data.physicalKey, equals(PhysicalKeyboardKey.shiftLeft)); expect(data.logicalKey, equals(LogicalKeyboardKey.shiftLeft)); expect(data.keyLabel, isNull); }); + test('DPAD keys from a joystick give physical key mappings', () { + final RawKeyEvent joystickDpadDown = RawKeyEvent.fromMessage(const { + 'type': 'keydown', + 'keymap': 'android', + 'keyCode': 20, + 'plainCodePoint': 0, + 'codePoint': 0, + 'character': null, + 'scanCode': 0, + 'metaState': 0, + 'source': 0x1000010, // Joystick source. + }); + final RawKeyEventDataAndroid data = joystickDpadDown.data; + expect(data.physicalKey, equals(PhysicalKeyboardKey.arrowDown)); + expect(data.logicalKey, equals(LogicalKeyboardKey.arrowDown)); + expect(data.keyLabel, isNull); + }); + test('Arrow keys from a keyboard give correct physical key mappings', () { + final RawKeyEvent joystickDpadDown = RawKeyEvent.fromMessage(const { + 'type': 'keydown', + 'keymap': 'android', + 'keyCode': 20, + 'plainCodePoint': 0, + 'codePoint': 0, + 'character': null, + 'scanCode': 108, + 'metaState': 0, + 'source': 0x101, // Keyboard source. + }); + final RawKeyEventDataAndroid data = joystickDpadDown.data; + expect(data.physicalKey, equals(PhysicalKeyboardKey.arrowDown)); + expect(data.logicalKey, equals(LogicalKeyboardKey.arrowDown)); + expect(data.keyLabel, isNull); + }); + test('DPAD center from a game pad gives physical key mappings', () { + final RawKeyEvent joystickDpadCenter = RawKeyEvent.fromMessage(const { + 'type': 'keydown', + 'keymap': 'android', + 'keyCode': 23, // DPAD_CENTER code. + 'plainCodePoint': 0, + 'codePoint': 0, + 'character': null, + 'scanCode': 317, // Left side thumb joystick center click button. + 'metaState': 0, + 'source': 0x501, // Gamepad and keyboard source. + }); + final RawKeyEventDataAndroid data = joystickDpadCenter.data; + expect(data.physicalKey, equals(PhysicalKeyboardKey.gameButtonThumbLeft)); + expect(data.logicalKey, equals(LogicalKeyboardKey.select)); + expect(data.keyLabel, isNull); + }); }); group('RawKeyEventDataFuchsia', () { const Map modifierTests = {