diff --git a/dev/tools/gen_keycodes/data/key_data.json b/dev/tools/gen_keycodes/data/key_data.json index accd0d8db6..d17e005354 100644 --- a/dev/tools/gen_keycodes/data/key_data.json +++ b/dev/tools/gen_keycodes/data/key_data.json @@ -66,33 +66,6 @@ "glfw": null } }, - "fn": { - "names": { - "domkey": "Fn", - "android": [ - "FUNCTION" - ], - "english": "Fn", - "chromium": "fn", - "glfw": null - }, - "scanCodes": { - "android": [ - 464 - ], - "usb": 18, - "linux": null, - "xkb": null, - "windows": null, - "macos": null - }, - "keyCodes": { - "android": [ - 119 - ], - "glfw": null - } - }, "fnLock": { "names": { "domkey": "FnLock", @@ -7222,5 +7195,32 @@ ], "glfw": null } + }, + "fn": { + "names": { + "domkey": "Fn", + "android": [ + "FUNCTION" + ], + "english": "Fn", + "chromium": "fn", + "glfw": null + }, + "scanCodes": { + "android": [ + 464 + ], + "usb": 18, + "linux": null, + "xkb": null, + "windows": null, + "macos": 63 + }, + "keyCodes": { + "android": [ + 119 + ], + "glfw": null + } } } \ No newline at end of file diff --git a/dev/tools/gen_keycodes/data/keyboard_key.tmpl b/dev/tools/gen_keycodes/data/keyboard_key.tmpl index 562d408c4b..8d5ae35d91 100644 --- a/dev/tools/gen_keycodes/data/keyboard_key.tmpl +++ b/dev/tools/gen_keycodes/data/keyboard_key.tmpl @@ -238,6 +238,21 @@ class LogicalKeyboardKey extends KeyboardKey { return result == null ? {} : {result}; } + /// Takes a set of keys, and returns the same set, but with any keys that have + /// synonyms replaced. + /// + /// It is used, for example, to make sets of keys with members like + /// [controlRight] and [controlLeft] and convert that set to contain just + /// [control], so that the question "is any control key down?" can be asked. + static Set collapseSynonyms(Set input) { + final Set result = {}; + for (LogicalKeyboardKey key in input) { + final LogicalKeyboardKey synonym = _synonyms[key]; + result.add(synonym ?? key); + } + return result; + } + @override void debugFillProperties(DiagnosticPropertiesBuilder properties) { super.debugFillProperties(properties); diff --git a/dev/tools/gen_keycodes/data/keyboard_maps.tmpl b/dev/tools/gen_keycodes/data/keyboard_maps.tmpl index 45d90a897e..a6c3dfc61a 100644 --- a/dev/tools/gen_keycodes/data/keyboard_maps.tmpl +++ b/dev/tools/gen_keycodes/data/keyboard_maps.tmpl @@ -53,6 +53,12 @@ const Map kMacOsNumPadMap = { @@@MACOS_NUMPAD_MAP@@@ }; +/// A map of macOS key codes which are numbered function keys, so that they +/// can be excluded when asking "is the Fn modifier down?". +const Map kMacOsFunctionKeyMap = { +@@@MACOS_FUNCTION_KEY_MAP@@@ +}; + /// Maps GLFW-specific key codes to the matching [LogicalKeyboardKey]. const Map kGlfwToLogicalKey = { @@@GLFW_KEY_CODE_MAP@@@ diff --git a/dev/tools/gen_keycodes/data/supplemental_hid_codes.inc b/dev/tools/gen_keycodes/data/supplemental_hid_codes.inc index da271bc806..b9c99dc814 100644 --- a/dev/tools/gen_keycodes/data/supplemental_hid_codes.inc +++ b/dev/tools/gen_keycodes/data/supplemental_hid_codes.inc @@ -43,3 +43,10 @@ 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), + + // The Mac defines a key code for the Fn key on Mac keyboards, but it's not + // defined on other platforms. Chromium does define an "Fn" row, but doesn't + // give it a Mac keycode. This overrides their definition. + // USB HID evdev XKB Win Mac DOMKey Code + USB_KEYMAP(0x000012, 0x0000, 0x0000, 0x0000, 0x003f, "Fn", FN), + diff --git a/dev/tools/gen_keycodes/lib/code_gen.dart b/dev/tools/gen_keycodes/lib/code_gen.dart index 1afe2f7809..98897cf4c4 100644 --- a/dev/tools/gen_keycodes/lib/code_gen.dart +++ b/dev/tools/gen_keycodes/lib/code_gen.dart @@ -114,6 +114,13 @@ $otherComments static const LogicalKeyboardKey $constantName = LogicalKeyboardK }).toList(); } + List get functionKeyData { + final RegExp functionKeyRe = RegExp(r'^f[0-9]+$'); + return keyData.data.where((Key entry) { + return functionKeyRe.hasMatch(entry.constantName); + }).toList(); + } + /// This generates the map of USB HID codes to physical keys. String get predefinedHidCodeMap { final StringBuffer scanCodeMap = StringBuffer(); @@ -240,6 +247,16 @@ $otherComments static const LogicalKeyboardKey $constantName = LogicalKeyboardK return macOsNumPadMap.toString().trimRight(); } + String get macOsFunctionKeyMap { + final StringBuffer macOsFunctionKeyMap = StringBuffer(); + for (Key entry in functionKeyData) { + if (entry.macOsScanCode != null) { + macOsFunctionKeyMap.writeln(' ${toHex(entry.macOsScanCode)}: LogicalKeyboardKey.${entry.constantName},'); + } + } + return macOsFunctionKeyMap.toString().trimRight(); + } + /// This generates the map of Fuchsia key codes to logical keys. String get fuchsiaKeyCodeMap { final StringBuffer fuchsiaKeyCodeMap = StringBuffer(); @@ -324,6 +341,7 @@ $otherComments static const LogicalKeyboardKey $constantName = LogicalKeyboardK 'FUCHSIA_KEY_CODE_MAP': fuchsiaKeyCodeMap, 'MACOS_SCAN_CODE_MAP': macOsScanCodeMap, 'MACOS_NUMPAD_MAP': macOsNumpadMap, + 'MACOS_FUNCTION_KEY_MAP': macOsFunctionKeyMap, 'GLFW_KEY_CODE_MAP': glfwKeyCodeMap, 'GLFW_NUMPAD_MAP': glfwNumpadMap, 'XKB_SCAN_CODE_MAP': xkbScanCodeMap, diff --git a/dev/tools/gen_keycodes/lib/key_data.dart b/dev/tools/gen_keycodes/lib/key_data.dart index ced5fb0e4f..56b4d88566 100644 --- a/dev/tools/gen_keycodes/lib/key_data.dart +++ b/dev/tools/gen_keycodes/lib/key_data.dart @@ -244,6 +244,9 @@ class KeyData { // Skip key that is not actually generated by any keyboard. return ''; } + // Remove duplicates: last one wins, so that supplemental codes + // override. + entries.removeWhere((Key entry) => entry.usbHidCode == newEntry.usbHidCode); entries.add(newEntry); } return match.group(0); diff --git a/packages/flutter/lib/src/services/keyboard_key.dart b/packages/flutter/lib/src/services/keyboard_key.dart index 3bf138b0a6..41446b563b 100644 --- a/packages/flutter/lib/src/services/keyboard_key.dart +++ b/packages/flutter/lib/src/services/keyboard_key.dart @@ -315,11 +315,6 @@ class LogicalKeyboardKey extends KeyboardKey { /// See the function [RawKeyEvent.logicalKey] for more information. static const LogicalKeyboardKey superKey = LogicalKeyboardKey(0x00100000011, debugName: kReleaseMode ? null : 'Super Key'); - /// Represents the logical "Fn" key on the keyboard. - /// - /// See the function [RawKeyEvent.logicalKey] for more information. - static const LogicalKeyboardKey fn = LogicalKeyboardKey(0x00100000012, debugName: kReleaseMode ? null : 'Fn'); - /// Represents the logical "Fn Lock" key on the keyboard. /// /// See the function [RawKeyEvent.logicalKey] for more information. @@ -1625,6 +1620,11 @@ class LogicalKeyboardKey extends KeyboardKey { /// See the function [RawKeyEvent.logicalKey] for more information. static const LogicalKeyboardKey gameButtonZ = LogicalKeyboardKey(0x0010005ff1f, debugName: kReleaseMode ? null : 'Game Button Z'); + /// Represents the logical "Fn" key on the keyboard. + /// + /// See the function [RawKeyEvent.logicalKey] for more information. + static const LogicalKeyboardKey fn = LogicalKeyboardKey(0x00100000012, debugName: kReleaseMode ? null : 'Fn'); + /// Represents the logical "Shift" key on the keyboard. /// /// This key represents the union of the keys {shiftLeft, shiftRight} when @@ -1659,7 +1659,6 @@ class LogicalKeyboardKey extends KeyboardKey { 0x0100000000: none, 0x0100000010: hyper, 0x0100000011: superKey, - 0x0100000012: fn, 0x0100000013: fnLock, 0x0100000014: suspend, 0x0100000015: resume, @@ -1921,6 +1920,7 @@ class LogicalKeyboardKey extends KeyboardKey { 0x010005ff1d: gameButtonX, 0x010005ff1e: gameButtonY, 0x010005ff1f: gameButtonZ, + 0x0100000012: fn, 0x201000700e1: shift, 0x201000700e3: meta, 0x201000700e2: alt, @@ -2102,11 +2102,6 @@ class PhysicalKeyboardKey extends KeyboardKey { /// See the function [RawKeyEvent.physicalKey] for more information. static const PhysicalKeyboardKey superKey = PhysicalKeyboardKey(0x00000011, debugName: kReleaseMode ? null : 'Super Key'); - /// Represents the location of the "Fn" key on a generalized keyboard. - /// - /// See the function [RawKeyEvent.physicalKey] for more information. - static const PhysicalKeyboardKey fn = PhysicalKeyboardKey(0x00000012, debugName: kReleaseMode ? null : 'Fn'); - /// Represents the location of the "Fn Lock" key on a generalized keyboard. /// /// See the function [RawKeyEvent.physicalKey] for more information. @@ -3526,13 +3521,17 @@ class PhysicalKeyboardKey extends KeyboardKey { /// See the function [RawKeyEvent.physicalKey] for more information. static const PhysicalKeyboardKey gameButtonZ = PhysicalKeyboardKey(0x0005ff1f, debugName: kReleaseMode ? null : 'Game Button Z'); + /// Represents the location of the "Fn" key on a generalized keyboard. + /// + /// See the function [RawKeyEvent.physicalKey] for more information. + static const PhysicalKeyboardKey fn = PhysicalKeyboardKey(0x00000012, debugName: kReleaseMode ? null : 'Fn'); + // A list of all the predefined constant PhysicalKeyboardKeys so that they // can be searched. static const Map _knownPhysicalKeys = { 0x00000000: none, 0x00000010: hyper, 0x00000011: superKey, - 0x00000012: fn, 0x00000013: fnLock, 0x00000014: suspend, 0x00000015: resume, @@ -3794,5 +3793,6 @@ class PhysicalKeyboardKey extends KeyboardKey { 0x0005ff1d: gameButtonX, 0x0005ff1e: gameButtonY, 0x0005ff1f: gameButtonZ, + 0x00000012: fn, }; } diff --git a/packages/flutter/lib/src/services/keyboard_maps.dart b/packages/flutter/lib/src/services/keyboard_maps.dart index f5030d959d..099039e2c2 100644 --- a/packages/flutter/lib/src/services/keyboard_maps.dart +++ b/packages/flutter/lib/src/services/keyboard_maps.dart @@ -14,7 +14,6 @@ import 'keyboard_key.dart'; /// Maps Android-specific key codes to the matching [LogicalKeyboardKey]. const Map kAndroidToLogicalKey = { 0: LogicalKeyboardKey.none, - 119: LogicalKeyboardKey.fn, 223: LogicalKeyboardKey.sleep, 224: LogicalKeyboardKey.wakeUp, 29: LogicalKeyboardKey.keyA, @@ -194,11 +193,11 @@ const Map kAndroidToLogicalKey = kAndroidToPhysicalKey = { - 464: PhysicalKeyboardKey.fn, 205: PhysicalKeyboardKey.suspend, 142: PhysicalKeyboardKey.sleep, 143: PhysicalKeyboardKey.wakeUp, @@ -425,6 +424,7 @@ const Map kAndroidToPhysicalKey = kFuchsiaToLogicalKey = kFuchsiaToLogicalKey = kFuchsiaToPhysicalKey = kFuchsiaToPhysicalKey = kMacOsToPhysicalKey = kMacOsNumPadMap = { 0x0000005f: LogicalKeyboardKey.numpadComma, }; +/// A map of macOS key codes which are numbered function keys, so that they +/// can be excluded when asking "is the Fn modifier down?". +const Map kMacOsFunctionKeyMap = { + 0x0000007a: LogicalKeyboardKey.f1, + 0x00000078: LogicalKeyboardKey.f2, + 0x00000063: LogicalKeyboardKey.f3, + 0x00000076: LogicalKeyboardKey.f4, + 0x00000060: LogicalKeyboardKey.f5, + 0x00000061: LogicalKeyboardKey.f6, + 0x00000062: LogicalKeyboardKey.f7, + 0x00000064: LogicalKeyboardKey.f8, + 0x00000065: LogicalKeyboardKey.f9, + 0x0000006d: LogicalKeyboardKey.f10, + 0x00000067: LogicalKeyboardKey.f11, + 0x0000006f: LogicalKeyboardKey.f12, + 0x00000069: LogicalKeyboardKey.f13, + 0x0000006b: LogicalKeyboardKey.f14, + 0x00000071: LogicalKeyboardKey.f15, + 0x0000006a: LogicalKeyboardKey.f16, + 0x00000040: LogicalKeyboardKey.f17, + 0x0000004f: LogicalKeyboardKey.f18, + 0x00000050: LogicalKeyboardKey.f19, + 0x0000005a: LogicalKeyboardKey.f20, +}; + /// Maps GLFW-specific key codes to the matching [LogicalKeyboardKey]. const Map kGlfwToLogicalKey = { 65: LogicalKeyboardKey.keyA, @@ -1498,7 +1524,6 @@ const Map kWebToLogicalKey = kWebToLogicalKey = kWebToPhysicalKey = kWebToPhysicalKey = {LogicalKeyboardKey.shiftLeft}, + { LogicalKeyboardKey.shiftLeft }, ), reason: 'on $platform', ); @@ -64,12 +64,44 @@ void main() { expect( RawKeyboard.instance.keysPressed, equals( - {LogicalKeyboardKey.shiftLeft}, + { LogicalKeyboardKey.shiftLeft }, ), reason: 'on $platform', ); await simulateKeyUpEvent(LogicalKeyboardKey.shiftLeft, platform: platform); expect(RawKeyboard.instance.keysPressed, isEmpty, reason: 'on $platform'); + // The Fn key isn't mapped on linux. + if (platform != 'linux') { + await simulateKeyDownEvent(LogicalKeyboardKey.fn, platform: platform); + expect(RawKeyboard.instance.keysPressed, + equals( + { + if (platform != 'macos') LogicalKeyboardKey.fn, + }, + ), + reason: 'on $platform'); + await simulateKeyDownEvent(LogicalKeyboardKey.f12, platform: platform); + expect( + RawKeyboard.instance.keysPressed, + equals( + { + if (platform != 'macos') LogicalKeyboardKey.fn, + LogicalKeyboardKey.f12, + }, + ), + reason: 'on $platform', + ); + await simulateKeyUpEvent(LogicalKeyboardKey.fn, platform: platform); + expect( + RawKeyboard.instance.keysPressed, + equals( + { LogicalKeyboardKey.f12 }, + ), + reason: 'on $platform', + ); + await simulateKeyUpEvent(LogicalKeyboardKey.f12, platform: platform); + expect(RawKeyboard.instance.keysPressed, isEmpty, reason: 'on $platform'); + } } }, skip: kIsWeb);