Unverified Commit deb155e8 authored by Francisco Magdaleno's avatar Francisco Magdaleno Committed by GitHub

[macos] Check for special keys before creating a logical key (#37901)

parent c8af729d
...@@ -75,14 +75,18 @@ class RawKeyEventDataMacOs extends RawKeyEventData { ...@@ -75,14 +75,18 @@ class RawKeyEventDataMacOs extends RawKeyEventData {
if (numPadKey != null) { if (numPadKey != null) {
return numPadKey; return numPadKey;
} }
// If this key is printable, generate the LogicalKeyboardKey from its Unicode value.
// Look to see if the keyCode is one we know about and have a mapping for. // Control keys such as ESC, CRTL, and SHIFT are not printable. HOME, DEL, arrow keys, and function
// keys are considered modifier function keys, which generate invalid Unicode scalar values.
if (keyLabel != null && if (keyLabel != null &&
!LogicalKeyboardKey.isControlCharacter(keyLabel)) { !LogicalKeyboardKey.isControlCharacter(keyLabel) &&
!_isUnprintableKey(keyLabel)) {
// Given that charactersIgnoringModifiers can contain a String of arbitrary length,
// limit to a maximum of two Unicode scalar values. It is unlikely that a keyboard would produce a code point
// bigger than 32 bits, but it is still worth defending against this case.
assert(charactersIgnoringModifiers.length <= 2); assert(charactersIgnoringModifiers.length <= 2);
int codeUnit = charactersIgnoringModifiers.codeUnitAt(0); int codeUnit = charactersIgnoringModifiers.codeUnitAt(0);
if (charactersIgnoringModifiers.length == 2) { if (charactersIgnoringModifiers.length == 2) {
// Not covering length > 2 case since > 1 is already unlikely.
final int secondCode = charactersIgnoringModifiers.codeUnitAt(1); final int secondCode = charactersIgnoringModifiers.codeUnitAt(1);
codeUnit = (codeUnit << 16) | secondCode; codeUnit = (codeUnit << 16) | secondCode;
} }
...@@ -95,14 +99,11 @@ class RawKeyEventDataMacOs extends RawKeyEventData { ...@@ -95,14 +99,11 @@ class RawKeyEventDataMacOs extends RawKeyEventData {
); );
} }
// This is a non-printable key that we don't know about, so we mint a new // Control keys like "backspace" and movement keys like arrow keys don't have a printable representation,
// code with the autogenerated bit set. // but are present on the physical keyboard. Since there is no logical keycode map for macOS
const int macOsKeyIdPlane = 0x00500000000; // (macOS uses the keycode to reference physical keys), a LogicalKeyboardKey is created with
// the physical key's HID usage and debugName. This avoids duplicating the physical
// Keys like "backspace" won't have a character, but it's known by the physical keyboard. // key map.
// Since there is no logical keycode map for macOS (macOS uses the keycode to reference
// physical keys), a LogicalKeyboardKey is created with the physical key's HID usage and
// debugName. This avoids the need for duplicating the physical key map.
if (physicalKey != PhysicalKeyboardKey.none) { if (physicalKey != PhysicalKeyboardKey.none) {
final int keyId = physicalKey.usbHidUsage | LogicalKeyboardKey.hidPlane; final int keyId = physicalKey.usbHidUsage | LogicalKeyboardKey.hidPlane;
return LogicalKeyboardKey.findKeyByKeyId(keyId) ?? LogicalKeyboardKey( return LogicalKeyboardKey.findKeyByKeyId(keyId) ?? LogicalKeyboardKey(
...@@ -112,6 +113,10 @@ class RawKeyEventDataMacOs extends RawKeyEventData { ...@@ -112,6 +113,10 @@ class RawKeyEventDataMacOs extends RawKeyEventData {
); );
} }
// This is a non-printable key that we don't know about, so we mint a new
// code with the autogenerated bit set.
const int macOsKeyIdPlane = 0x00500000000;
return LogicalKeyboardKey( return LogicalKeyboardKey(
macOsKeyIdPlane | keyCode | LogicalKeyboardKey.autogeneratedMask, macOsKeyIdPlane | keyCode | LogicalKeyboardKey.autogeneratedMask,
debugName: kReleaseMode ? null : 'Unknown macOS key code $keyCode', debugName: kReleaseMode ? null : 'Unknown macOS key code $keyCode',
...@@ -197,6 +202,23 @@ class RawKeyEventDataMacOs extends RawKeyEventData { ...@@ -197,6 +202,23 @@ class RawKeyEventDataMacOs extends RawKeyEventData {
return null; return null;
} }
/// Returns true if the given label represents an unprintable key.
///
/// Examples of unprintable keys are "NSUpArrowFunctionKey = 0xF700"
/// or "NSHomeFunctionKey = 0xF729".
///
/// See <https://developer.apple.com/documentation/appkit/1535851-function-key_unicodes?language=objc> for more
/// information.
///
/// Used by [RawKeyEvent] subclasses to help construct IDs.
static bool _isUnprintableKey(String label) {
if (label.length > 1) {
return false;
}
final int codeUnit = label.codeUnitAt(0);
return codeUnit >= 0xF700 && codeUnit <= 0xF8FF;
}
// Modifier key masks. See Apple's NSEvent documentation // Modifier key masks. See Apple's NSEvent documentation
// https://developer.apple.com/documentation/appkit/nseventmodifierflags?language=objc // https://developer.apple.com/documentation/appkit/nseventmodifierflags?language=objc
// https://opensource.apple.com/source/IOHIDFamily/IOHIDFamily-86/IOHIDSystem/IOKit/hidsystem/IOLLEvent.h.auto.html // https://opensource.apple.com/source/IOHIDFamily/IOHIDFamily-86/IOHIDSystem/IOKit/hidsystem/IOLLEvent.h.auto.html
......
...@@ -450,6 +450,22 @@ void main() { ...@@ -450,6 +450,22 @@ void main() {
expect(data.logicalKey, equals(LogicalKeyboardKey.shiftLeft)); expect(data.logicalKey, equals(LogicalKeyboardKey.shiftLeft));
expect(data.keyLabel, isNull); expect(data.keyLabel, isNull);
}); });
test('Unprintable keyboard keys are correctly translated', () {
final RawKeyEvent leftArrowKey = RawKeyEvent.fromMessage(const <String, dynamic>{
'type': 'keydown',
'keymap': 'macos',
'keyCode': 0x0000007B,
'characters': '',
'charactersIgnoringModifiers': '', // NSLeftArrowFunctionKey = 0xF702
'character': null,
'modifiers': RawKeyEventDataMacOs.modifierFunction,
});
final RawKeyEventDataMacOs data = leftArrowKey.data;
expect(data.physicalKey, equals(PhysicalKeyboardKey.arrowLeft));
expect(data.logicalKey, equals(LogicalKeyboardKey.arrowLeft));
expect(data.logicalKey.keyLabel, isNull);
});
}); });
group('RawKeyEventDataLinux-GFLW', () { group('RawKeyEventDataLinux-GFLW', () {
const Map<int, _ModifierCheck> modifierTests = <int, _ModifierCheck>{ const Map<int, _ModifierCheck> modifierTests = <int, _ModifierCheck>{
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment