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

Adds macOS raw keyboard mapping (#29231)

parent 75c50da5
......@@ -92,6 +92,11 @@ class _HardwareKeyDemoState extends State<RawKeyboardDemo> {
dataText.add(Text('codePoint: ${data.codePoint} (${_asHex(data.codePoint)})'));
dataText.add(Text('hidUsage: ${data.hidUsage} (${_asHex(data.hidUsage)})'));
dataText.add(Text('modifiers: ${data.modifiers} (${_asHex(data.modifiers)})'));
} else if (data is RawKeyEventDataMacOs) {
dataText.add(Text('keyCode: ${data.keyCode} (${_asHex(data.keyCode)})'));
dataText.add(Text('characters: ${data.characters}'));
dataText.add(Text('charactersIgnoringModifiers: ${data.charactersIgnoringModifiers}'));
dataText.add(Text('modifiers: ${data.modifiers} (${_asHex(data.modifiers)})'));
}
dataText.add(Text('logical: ${_event.logicalKey}'));
dataText.add(Text('physical: ${_event.physicalKey}'));
......
......@@ -38,3 +38,15 @@ const Map<int, LogicalKeyboardKey> kFuchsiaToLogicalKey = <int, LogicalKeyboardK
const Map<int, PhysicalKeyboardKey> kFuchsiaToPhysicalKey = <int, PhysicalKeyboardKey>{
@@@FUCHSIA_SCAN_CODE_MAP@@@
};
/// Maps macOS-specific key code values representing [PhysicalKeyboardKey].
const Map<int, PhysicalKeyboardKey> kMacOsToPhysicalKey = <int, PhysicalKeyboardKey>{
@@@MACOS_SCAN_CODE_MAP@@@
};
/// A map of macOS key codes which have printable representations, but appear
/// on the number pad. Used to provide different key objects for keys like
/// KEY_EQUALS and NUMPAD_EQUALS.
const Map<int, LogicalKeyboardKey> kMacOsNumPadMap = <int, LogicalKeyboardKey>{
@@@MACOS_NUMPAD_MAP@@@
};
......@@ -135,6 +135,31 @@ $otherComments static const LogicalKeyboardKey ${entry.constantName} = LogicalK
return androidScanCodeMap.toString().trimRight();
}
/// This generates the map of macOS key codes to physical keys.
String get macOsScanCodeMap {
final StringBuffer macOsScanCodeMap = StringBuffer();
for (Key entry in keyData.data) {
if (entry.macOsScanCode != null) {
macOsScanCodeMap.writeln(' ${toHex(entry.macOsScanCode)}: PhysicalKeyboardKey.${entry.constantName},');
}
}
return macOsScanCodeMap.toString().trimRight();
}
/// This generates the map of macOS number pad key codes to logical keys.
String get macOsNumpadMap {
final StringBuffer macOsNumPadMap = StringBuffer();
final List<Key> onlyNumpads = keyData.data.where((Key entry) {
return entry.constantName.startsWith('numpad') && entry.keyLabel != null;
}).toList();
for (Key entry in onlyNumpads) {
if (entry.macOsScanCode != null) {
macOsNumPadMap.writeln(' ${toHex(entry.macOsScanCode)}: LogicalKeyboardKey.${entry.constantName},');
}
}
return macOsNumPadMap.toString().trimRight();
}
/// This generates the map of Fuchsia key codes to logical keys.
String get fuchsiaKeyCodeMap {
final StringBuffer fuchsiaKeyCodeMap = StringBuffer();
......@@ -174,12 +199,17 @@ $otherComments static const LogicalKeyboardKey ${entry.constantName} = LogicalK
/// Substitutes the various platform specific maps into the template file for
/// keyboard_maps.dart.
String generateKeyboardMaps() {
// There is no macOS keycode map since macOS uses keycode to represent a physical key.
// The LogicalKeyboardKey is generated by raw_keyboard_macos.dart from the unmodified characters
// from NSEvent.
final Map<String, String> mappings = <String, String>{
'ANDROID_SCAN_CODE_MAP': androidScanCodeMap,
'ANDROID_KEY_CODE_MAP': androidKeyCodeMap,
'ANDROID_NUMPAD_MAP': androidNumpadMap,
'FUCHSIA_SCAN_CODE_MAP': fuchsiaHidCodeMap,
'FUCHSIA_KEY_CODE_MAP': fuchsiaKeyCodeMap,
'MACOS_SCAN_CODE_MAP': macOsScanCodeMap,
'MACOS_NUMPAD_MAP': macOsNumpadMap,
};
final String template = File(path.join(flutterRoot.path, 'dev', 'tools', 'gen_keycodes', 'data', 'keyboard_maps.tmpl')).readAsStringSync();
......
......@@ -24,6 +24,7 @@ export 'src/services/platform_views.dart';
export 'src/services/raw_keyboard.dart';
export 'src/services/raw_keyboard_android.dart';
export 'src/services/raw_keyboard_fuchsia.dart';
export 'src/services/raw_keyboard_macos.dart';
export 'src/services/system_channels.dart';
export 'src/services/system_chrome.dart';
export 'src/services/system_navigator.dart';
......
......@@ -842,3 +842,150 @@ const Map<int, PhysicalKeyboardKey> kFuchsiaToPhysicalKey = <int, PhysicalKeyboa
0x000c028b: PhysicalKeyboardKey.mailForward,
0x000c028c: PhysicalKeyboardKey.mailSend,
};
/// Maps macOS-specific key code values representing [PhysicalKeyboardKey].
/// MacOS doesn't provide a scan code, but a virtual keycode to represent a physical key.
const Map<int, PhysicalKeyboardKey> kMacOsToPhysicalKey = <int, PhysicalKeyboardKey>{
0x00000000: PhysicalKeyboardKey.keyA,
0x0000000b: PhysicalKeyboardKey.keyB,
0x00000008: PhysicalKeyboardKey.keyC,
0x00000002: PhysicalKeyboardKey.keyD,
0x0000000e: PhysicalKeyboardKey.keyE,
0x00000003: PhysicalKeyboardKey.keyF,
0x00000005: PhysicalKeyboardKey.keyG,
0x00000004: PhysicalKeyboardKey.keyH,
0x00000022: PhysicalKeyboardKey.keyI,
0x00000026: PhysicalKeyboardKey.keyJ,
0x00000028: PhysicalKeyboardKey.keyK,
0x00000025: PhysicalKeyboardKey.keyL,
0x0000002e: PhysicalKeyboardKey.keyM,
0x0000002d: PhysicalKeyboardKey.keyN,
0x0000001f: PhysicalKeyboardKey.keyO,
0x00000023: PhysicalKeyboardKey.keyP,
0x0000000c: PhysicalKeyboardKey.keyQ,
0x0000000f: PhysicalKeyboardKey.keyR,
0x00000001: PhysicalKeyboardKey.keyS,
0x00000011: PhysicalKeyboardKey.keyT,
0x00000020: PhysicalKeyboardKey.keyU,
0x00000009: PhysicalKeyboardKey.keyV,
0x0000000d: PhysicalKeyboardKey.keyW,
0x00000007: PhysicalKeyboardKey.keyX,
0x00000010: PhysicalKeyboardKey.keyY,
0x00000006: PhysicalKeyboardKey.keyZ,
0x00000012: PhysicalKeyboardKey.digit1,
0x00000013: PhysicalKeyboardKey.digit2,
0x00000014: PhysicalKeyboardKey.digit3,
0x00000015: PhysicalKeyboardKey.digit4,
0x00000017: PhysicalKeyboardKey.digit5,
0x00000016: PhysicalKeyboardKey.digit6,
0x0000001a: PhysicalKeyboardKey.digit7,
0x0000001c: PhysicalKeyboardKey.digit8,
0x00000019: PhysicalKeyboardKey.digit9,
0x0000001d: PhysicalKeyboardKey.digit0,
0x00000024: PhysicalKeyboardKey.enter,
0x00000035: PhysicalKeyboardKey.escape,
0x00000033: PhysicalKeyboardKey.backspace,
0x00000030: PhysicalKeyboardKey.tab,
0x00000031: PhysicalKeyboardKey.space,
0x0000001b: PhysicalKeyboardKey.minus,
0x00000018: PhysicalKeyboardKey.equal,
0x00000021: PhysicalKeyboardKey.bracketLeft,
0x0000001e: PhysicalKeyboardKey.bracketRight,
0x0000002a: PhysicalKeyboardKey.backslash,
0x00000029: PhysicalKeyboardKey.semicolon,
0x00000027: PhysicalKeyboardKey.quote,
0x00000032: PhysicalKeyboardKey.backquote,
0x0000002b: PhysicalKeyboardKey.comma,
0x0000002f: PhysicalKeyboardKey.period,
0x0000002c: PhysicalKeyboardKey.slash,
0x00000039: PhysicalKeyboardKey.capsLock,
0x0000007a: PhysicalKeyboardKey.f1,
0x00000078: PhysicalKeyboardKey.f2,
0x00000063: PhysicalKeyboardKey.f3,
0x00000076: PhysicalKeyboardKey.f4,
0x00000060: PhysicalKeyboardKey.f5,
0x00000061: PhysicalKeyboardKey.f6,
0x00000062: PhysicalKeyboardKey.f7,
0x00000064: PhysicalKeyboardKey.f8,
0x00000065: PhysicalKeyboardKey.f9,
0x0000006d: PhysicalKeyboardKey.f10,
0x00000067: PhysicalKeyboardKey.f11,
0x0000006f: PhysicalKeyboardKey.f12,
0x00000072: PhysicalKeyboardKey.insert,
0x00000073: PhysicalKeyboardKey.home,
0x00000074: PhysicalKeyboardKey.pageUp,
0x00000075: PhysicalKeyboardKey.delete,
0x00000077: PhysicalKeyboardKey.end,
0x00000079: PhysicalKeyboardKey.pageDown,
0x0000007c: PhysicalKeyboardKey.arrowRight,
0x0000007b: PhysicalKeyboardKey.arrowLeft,
0x0000007d: PhysicalKeyboardKey.arrowDown,
0x0000007e: PhysicalKeyboardKey.arrowUp,
0x00000047: PhysicalKeyboardKey.numLock,
0x0000004b: PhysicalKeyboardKey.numpadDivide,
0x00000043: PhysicalKeyboardKey.numpadMultiply,
0x0000004e: PhysicalKeyboardKey.numpadSubtract,
0x00000045: PhysicalKeyboardKey.numpadAdd,
0x0000004c: PhysicalKeyboardKey.numpadEnter,
0x00000053: PhysicalKeyboardKey.numpad1,
0x00000054: PhysicalKeyboardKey.numpad2,
0x00000055: PhysicalKeyboardKey.numpad3,
0x00000056: PhysicalKeyboardKey.numpad4,
0x00000057: PhysicalKeyboardKey.numpad5,
0x00000058: PhysicalKeyboardKey.numpad6,
0x00000059: PhysicalKeyboardKey.numpad7,
0x0000005b: PhysicalKeyboardKey.numpad8,
0x0000005c: PhysicalKeyboardKey.numpad9,
0x00000052: PhysicalKeyboardKey.numpad0,
0x00000041: PhysicalKeyboardKey.numpadDecimal,
0x0000000a: PhysicalKeyboardKey.intlBackslash,
0x0000006e: PhysicalKeyboardKey.contextMenu,
0x00000051: PhysicalKeyboardKey.numpadEqual,
0x00000069: PhysicalKeyboardKey.f13,
0x0000006b: PhysicalKeyboardKey.f14,
0x00000071: PhysicalKeyboardKey.f15,
0x0000006a: PhysicalKeyboardKey.f16,
0x00000040: PhysicalKeyboardKey.f17,
0x0000004f: PhysicalKeyboardKey.f18,
0x00000050: PhysicalKeyboardKey.f19,
0x0000005a: PhysicalKeyboardKey.f20,
0x0000004a: PhysicalKeyboardKey.audioVolumeMute,
0x00000048: PhysicalKeyboardKey.audioVolumeUp,
0x00000049: PhysicalKeyboardKey.audioVolumeDown,
0x0000005f: PhysicalKeyboardKey.numpadComma,
0x0000005e: PhysicalKeyboardKey.intlRo,
0x00000068: PhysicalKeyboardKey.kanaMode,
0x0000005d: PhysicalKeyboardKey.intlYen,
0x0000003b: PhysicalKeyboardKey.controlLeft,
0x00000038: PhysicalKeyboardKey.shiftLeft,
0x0000003a: PhysicalKeyboardKey.altLeft,
0x00000037: PhysicalKeyboardKey.metaLeft,
0x0000003e: PhysicalKeyboardKey.controlRight,
0x0000003c: PhysicalKeyboardKey.shiftRight,
0x0000003d: PhysicalKeyboardKey.altRight,
0x00000036: PhysicalKeyboardKey.metaRight,
};
/// A map of macOS key codes which have printable representations, but appear
/// on the number pad. Used to provide different key objects for keys like
/// KEY_EQUALS and NUMPAD_EQUALS.
const Map<int, LogicalKeyboardKey> kMacOsNumPadMap = <int, LogicalKeyboardKey>{
0x0000004b: LogicalKeyboardKey.numpadDivide,
0x00000043: LogicalKeyboardKey.numpadMultiply,
0x0000004e: LogicalKeyboardKey.numpadSubtract,
0x00000045: LogicalKeyboardKey.numpadAdd,
0x00000053: LogicalKeyboardKey.numpad1,
0x00000054: LogicalKeyboardKey.numpad2,
0x00000055: LogicalKeyboardKey.numpad3,
0x00000056: LogicalKeyboardKey.numpad4,
0x00000057: LogicalKeyboardKey.numpad5,
0x00000058: LogicalKeyboardKey.numpad6,
0x00000059: LogicalKeyboardKey.numpad7,
0x0000005b: LogicalKeyboardKey.numpad8,
0x0000005c: LogicalKeyboardKey.numpad9,
0x00000052: LogicalKeyboardKey.numpad0,
0x00000041: LogicalKeyboardKey.numpadDecimal,
0x00000051: LogicalKeyboardKey.numpadEqual,
0x0000005f: LogicalKeyboardKey.numpadComma,
};
......@@ -9,6 +9,7 @@ import 'package:flutter/foundation.dart';
import 'keyboard_key.dart';
import 'raw_keyboard_android.dart';
import 'raw_keyboard_fuchsia.dart';
import 'raw_keyboard_macos.dart';
import 'system_channels.dart';
/// An enum describing the side of the keyboard that a key is on, to allow
......@@ -263,6 +264,14 @@ abstract class RawKeyEvent {
modifiers: message['modifiers'] ?? 0,
);
break;
case 'macos':
data = RawKeyEventDataMacOs(
characters: message['characters'] ?? '',
charactersIgnoringModifiers:
message['charactersIgnoringModifiers'] ?? '',
keyCode: message['keyCode'] ?? 0,
modifiers: message['modifiers'] ?? 0);
break;
default:
// We don't yet implement raw key events on iOS or other platforms, but
// we don't hit this exception because the engine never sends us these
......
// Copyright 2019 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'package:flutter/foundation.dart';
import 'keyboard_key.dart';
import 'keyboard_maps.dart';
import 'raw_keyboard.dart';
/// Platform-specific key event data for macOS.
///
/// This object contains information about key events obtained from macOS's
/// `NSEvent` interface.
///
/// See also:
///
/// * [RawKeyboard], which uses this interface to expose key data.
class RawKeyEventDataMacOs extends RawKeyEventData {
/// Creates a key event data structure specific for macOS.
///
/// The [characters], [charactersIgnoringModifiers], and [modifiers], arguments
/// must not be null.
const RawKeyEventDataMacOs({
this.characters = '',
this.charactersIgnoringModifiers = '',
this.keyCode = 0,
this.modifiers = 0,
}) : assert(characters != null),
assert(charactersIgnoringModifiers != null),
assert(keyCode != null),
assert(modifiers != null);
/// The Unicode characters associated with a key-up or key-down event.
///
/// See also:
///
/// * [Apple's NSEvent documentation](https://developer.apple.com/documentation/appkit/nsevent/1534183-characters?language=objc)
final String characters;
/// The characters generated by a key event as if no modifier key (except for
/// Shift) applies.
///
/// See also:
///
/// * [Apple's NSEvent documentation](https://developer.apple.com/documentation/appkit/nsevent/1524605-charactersignoringmodifiers?language=objc)
final String charactersIgnoringModifiers;
/// The virtual key code for the keyboard key associated with a key event.
///
/// See also:
///
/// * [Apple's NSEvent documentation](https://developer.apple.com/documentation/appkit/nsevent/1534513-keycode?language=objc)
final int keyCode;
/// A mask of the current modifiers using the values in Modifier Flags.
///
/// See also:
///
/// * [Apple's NSEvent documentation](https://developer.apple.com/documentation/appkit/nsevent/1535211-modifierflags?language=objc)
final int modifiers;
@override
String get keyLabel => charactersIgnoringModifiers.isEmpty ? null : charactersIgnoringModifiers;
@override
PhysicalKeyboardKey get physicalKey => kMacOsToPhysicalKey[keyCode] ?? PhysicalKeyboardKey.none;
@override
LogicalKeyboardKey get logicalKey {
// Look to see if the keyCode is a printable number pad key, so that a
// difference between regular keys (e.g. "=") and the number pad version
// (e.g. the "=" on the number pad) can be determined.
final LogicalKeyboardKey numPadKey = kMacOsNumPadMap[keyCode];
if (numPadKey != null) {
return numPadKey;
}
// Look to see if the keyCode is one we know about and have a mapping for.
if (keyLabel != null &&
!LogicalKeyboardKey.isControlCharacter(keyLabel)) {
assert(charactersIgnoringModifiers.length <= 2);
int codeUnit = charactersIgnoringModifiers.codeUnitAt(0);
if (charactersIgnoringModifiers.length == 2) {
// Not covering length > 2 case since > 1 is already unlikely.
final int secondCode = charactersIgnoringModifiers.codeUnitAt(1);
codeUnit = (codeUnit << 16) | secondCode;
}
final int keyId = LogicalKeyboardKey.unicodePlane | (codeUnit & LogicalKeyboardKey.valueMask);
return LogicalKeyboardKey.findKeyByKeyId(keyId) ?? LogicalKeyboardKey(
keyId,
keyLabel: keyLabel,
debugName: kReleaseMode ? null : 'Key ${keyLabel.toUpperCase()}',
);
}
// 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;
// Keys like "backspace" won't have a character, but it's known by the physical keyboard.
// 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) {
final int keyId = physicalKey.usbHidUsage | LogicalKeyboardKey.hidPlane;
return LogicalKeyboardKey.findKeyByKeyId(keyId) ?? LogicalKeyboardKey(
keyId,
keyLabel: physicalKey.debugName,
debugName: physicalKey.debugName,
);
}
return LogicalKeyboardKey(
macOsKeyIdPlane | keyCode | LogicalKeyboardKey.autogeneratedMask,
debugName: kReleaseMode ? null : 'Unknown macOS key code $keyCode',
);
}
bool _isLeftRightModifierPressed(KeyboardSide side, int anyMask, int leftMask, int rightMask) {
if (modifiers & anyMask == 0) {
return false;
}
switch (side) {
case KeyboardSide.any:
return true;
case KeyboardSide.all:
return modifiers & leftMask != 0 && modifiers & rightMask != 0;
case KeyboardSide.left:
return modifiers & leftMask != 0;
case KeyboardSide.right:
return modifiers & rightMask != 0;
}
return false;
}
@override
bool isModifierPressed(ModifierKey key, {KeyboardSide side = KeyboardSide.any}) {
final int independentModifier = modifiers & deviceIndependentMask;
switch (key) {
case ModifierKey.controlModifier:
return _isLeftRightModifierPressed(side, independentModifier & modifierControl, modifierLeftControl, modifierRightControl);
case ModifierKey.shiftModifier:
return _isLeftRightModifierPressed(side, independentModifier & modifierShift, modifierLeftShift, modifierRightShift);
case ModifierKey.altModifier:
return _isLeftRightModifierPressed(side, independentModifier & modifierOption, modifierLeftOption, modifierRightOption);
case ModifierKey.metaModifier:
return _isLeftRightModifierPressed(side, independentModifier & modifierCommand, modifierLeftCommand, modifierRightCommand);
case ModifierKey.capsLockModifier:
return independentModifier & modifierCapsLock != 0;
case ModifierKey.numLockModifier:
return independentModifier & modifierNumericPad != 0;
case ModifierKey.functionModifier:
return independentModifier & modifierFunction != 0;
case ModifierKey.symbolModifier:
case ModifierKey.scrollLockModifier:
// These are not used in macOS keyboards.
return false;
}
return false;
}
@override
KeyboardSide getModifierSide(ModifierKey key) {
KeyboardSide findSide(int leftMask, int rightMask) {
final int combinedMask = leftMask | rightMask;
final int combined = modifiers & combinedMask;
if (combined == leftMask) {
return KeyboardSide.left;
} else if (combined == rightMask) {
return KeyboardSide.right;
} else if (combined == combinedMask) {
return KeyboardSide.all;
}
return null;
}
switch (key) {
case ModifierKey.controlModifier:
return findSide(modifierLeftControl, modifierRightControl);
case ModifierKey.shiftModifier:
return findSide(modifierLeftShift, modifierRightShift);
case ModifierKey.altModifier:
return findSide(modifierLeftOption, modifierRightOption);
case ModifierKey.metaModifier:
return findSide(modifierLeftCommand, modifierRightCommand);
case ModifierKey.capsLockModifier:
case ModifierKey.numLockModifier:
case ModifierKey.scrollLockModifier:
case ModifierKey.functionModifier:
case ModifierKey.symbolModifier:
return KeyboardSide.all;
}
assert(false, 'Not handling $key type properly.');
return null;
}
// Modifier key masks. See Apple's NSEvent documentation
// https://developer.apple.com/documentation/appkit/nseventmodifierflags?language=objc
// https://opensource.apple.com/source/IOHIDFamily/IOHIDFamily-86/IOHIDSystem/IOKit/hidsystem/IOLLEvent.h.auto.html
/// This mask is used to check the [modifiers] field to test whether the CAPS
/// LOCK modifier key is on.
///
/// {@template flutter.services.logicalKeyboardKey.modifiers}
/// Use this value if you need to decode the [modifiers] field yourself, but
/// it's much easier to use [isModifierPressed] if you just want to know if
/// a modifier is pressed.
/// {@endtemplate}
static const int modifierCapsLock = 0x10000;
/// This mask is used to check the [modifiers] field to test whether one of the
/// SHIFT modifier keys is pressed.
///
/// {@macro flutter.services.logicalKeyboardKey.modifiers}
static const int modifierShift = 0x20000;
/// This mask is used to check the [modifiers] field to test whether the left
/// SHIFT modifier key is pressed.
///
/// {@macro flutter.services.logicalKeyboardKey.modifiers}
static const int modifierLeftShift = 0x02;
/// This mask is used to check the [modifiers] field to test whether the right
/// SHIFT modifier key is pressed.
///
/// {@macro flutter.services.logicalKeyboardKey.modifiers}
static const int modifierRightShift = 0x04;
/// This mask is used to check the [modifiers] field to test whether one of the
/// CTRL modifier keys is pressed.
///
/// {@macro flutter.services.logicalKeyboardKey.modifiers}
static const int modifierControl = 0x40000;
/// This mask is used to check the [modifiers] field to test whether the left
/// CTRL modifier key is pressed.
///
/// {@macro flutter.services.logicalKeyboardKey.modifiers}
static const int modifierLeftControl = 0x01;
/// This mask is used to check the [modifiers] field to test whether the right
/// CTRL modifier key is pressed.
///
/// {@macro flutter.services.logicalKeyboardKey.modifiers}
static const int modifierRightControl = 0x2000;
/// This mask is used to check the [modifiers] field to test whether one of the
/// ALT modifier keys is pressed.
///
/// {@macro flutter.services.logicalKeyboardKey.modifiers}
static const int modifierOption = 0x80000;
/// This mask is used to check the [modifiers] field to test whether the left
/// ALT modifier key is pressed.
///
/// {@macro flutter.services.logicalKeyboardKey.modifiers}
static const int modifierLeftOption = 0x20;
/// This mask is used to check the [modifiers] field to test whether the right
/// ALT modifier key is pressed.
///
/// {@macro flutter.services.logicalKeyboardKey.modifiers}
static const int modifierRightOption = 0x40;
/// This mask is used to check the [modifiers] field to test whether one of the
/// CMD modifier keys is pressed.
///
/// {@macro flutter.services.logicalKeyboardKey.modifiers}
static const int modifierCommand = 0x100000;
/// This mask is used to check the [modifiers] field to test whether the left
/// CMD modifier keys is pressed.
///
/// {@macro flutter.services.logicalKeyboardKey.modifiers}
static const int modifierLeftCommand = 0x08;
/// This mask is used to check the [modifiers] field to test whether the right
/// CMD modifier keys is pressed.
///
/// {@macro flutter.services.logicalKeyboardKey.modifiers}
static const int modifierRightCommand = 0x10;
/// This mask is used to check the [modifiers] field to test whether any key in
/// the numeric keypad is pressed.
///
/// {@macro flutter.services.logicalKeyboardKey.modifiers}
static const int modifierNumericPad = 0x200000;
/// This mask is used to check the [modifiers] field to test whether the
/// HELP modifier key is pressed.
///
/// {@macro flutter.services.logicalKeyboardKey.modifiers}
static const int modifierHelp = 0x400000;
/// This mask is used to check the [modifiers] field to test whether one of the
/// FUNCTION modifier keys is pressed.
///
/// {@macro flutter.services.logicalKeyboardKey.modifiers}
static const int modifierFunction = 0x800000;
/// Used to retrieve only the device-independent modifier flags, allowing
/// applications to mask off the device-dependent modifier flags, including
/// event coalescing information.
static const int deviceIndependentMask = 0xffff0000;
@override
String toString() {
return '$runtimeType(keyLabel: $keyLabel, keyCode: $keyCode, characters: $characters,'
' unmodifiedCharacters: $charactersIgnoringModifiers, modifiers: $modifiers, '
'modifiers down: $modifiersPressed)';
}
}
......@@ -264,4 +264,133 @@ void main() {
expect(data.keyLabel, isNull);
});
});
group('RawKeyEventDataMacOs', () {
const Map<int, _ModifierCheck> modifierTests = <int, _ModifierCheck>{
RawKeyEventDataMacOs.modifierOption | RawKeyEventDataMacOs.modifierLeftOption: _ModifierCheck(ModifierKey.altModifier, KeyboardSide.left),
RawKeyEventDataMacOs.modifierOption | RawKeyEventDataMacOs.modifierRightOption: _ModifierCheck(ModifierKey.altModifier, KeyboardSide.right),
RawKeyEventDataMacOs.modifierShift | RawKeyEventDataMacOs.modifierLeftShift: _ModifierCheck(ModifierKey.shiftModifier, KeyboardSide.left),
RawKeyEventDataMacOs.modifierShift | RawKeyEventDataMacOs.modifierRightShift: _ModifierCheck(ModifierKey.shiftModifier, KeyboardSide.right),
RawKeyEventDataMacOs.modifierFunction: _ModifierCheck(ModifierKey.functionModifier, KeyboardSide.all),
RawKeyEventDataMacOs.modifierControl | RawKeyEventDataMacOs.modifierLeftControl: _ModifierCheck(ModifierKey.controlModifier, KeyboardSide.left),
RawKeyEventDataMacOs.modifierControl | RawKeyEventDataMacOs.modifierRightControl: _ModifierCheck(ModifierKey.controlModifier, KeyboardSide.right),
RawKeyEventDataMacOs.modifierCommand | RawKeyEventDataMacOs.modifierLeftCommand: _ModifierCheck(ModifierKey.metaModifier, KeyboardSide.left),
RawKeyEventDataMacOs.modifierCommand | RawKeyEventDataMacOs.modifierRightCommand: _ModifierCheck(ModifierKey.metaModifier, KeyboardSide.right),
RawKeyEventDataMacOs.modifierCapsLock: _ModifierCheck(ModifierKey.capsLockModifier, KeyboardSide.all),
};
test('modifier keys are recognized individually', () {
for (int modifier in modifierTests.keys) {
final RawKeyEvent event = RawKeyEvent.fromMessage(<String, dynamic>{
'type': 'keydown',
'keymap': 'macos',
'keyCode': 0x04,
'characters': 'a',
'charactersIgnoringModifiers': 'a',
'modifiers': modifier,
});
final RawKeyEventDataMacOs data = event.data;
for (ModifierKey key in ModifierKey.values) {
if (modifierTests[modifier].key == key) {
expect(
data.isModifierPressed(key, side: modifierTests[modifier].side),
isTrue,
reason: "$key should be pressed with metaState $modifier, but isn't.",
);
expect(data.getModifierSide(key), equals(modifierTests[modifier].side));
} else {
expect(
data.isModifierPressed(key, side: modifierTests[modifier].side),
isFalse,
reason: '$key should not be pressed with metaState $modifier.',
);
}
}
}
});
test('modifier keys are recognized when combined', () {
for (int modifier in modifierTests.keys) {
if (modifier == RawKeyEventDataMacOs.modifierFunction) {
// No need to combine function key with itself.
continue;
}
final RawKeyEvent event = RawKeyEvent.fromMessage(<String, dynamic>{
'type': 'keydown',
'keymap': 'macos',
'keyCode': 0x04,
'plainCodePoint': 0x64,
'characters': 'a',
'charactersIgnoringModifiers': 'a',
'modifiers': modifier | RawKeyEventDataMacOs.modifierFunction,
});
final RawKeyEventDataMacOs data = event.data;
for (ModifierKey key in ModifierKey.values) {
if (modifierTests[modifier].key == key || key == ModifierKey.functionModifier) {
expect(
data.isModifierPressed(key, side: modifierTests[modifier].side),
isTrue,
reason: '$key should be pressed with metaState $modifier '
"and additional key ${RawKeyEventDataMacOs.modifierFunction}, but isn't.",
);
if (key != ModifierKey.functionModifier) {
expect(data.getModifierSide(key), equals(modifierTests[modifier].side));
} else {
expect(data.getModifierSide(key), equals(KeyboardSide.all));
}
} else {
expect(
data.isModifierPressed(key, side: modifierTests[modifier].side),
isFalse,
reason: '$key should not be pressed with metaState $modifier with metaState $modifier '
'and additional key ${RawKeyEventDataMacOs.modifierFunction}.',
);
}
}
}
});
test('Printable keyboard keys are correctly translated', () {
const String unmodifiedCharacter = 'a';
final RawKeyEvent keyAEvent = RawKeyEvent.fromMessage(const <String, dynamic>{
'type': 'keydown',
'keymap': 'macos',
'keyCode': 0x00000000,
'characters': 'a',
'charactersIgnoringModifiers': unmodifiedCharacter,
'modifiers': 0x0,
});
final RawKeyEventDataMacOs data = keyAEvent.data;
expect(data.physicalKey, equals(PhysicalKeyboardKey.keyA));
expect(data.logicalKey, equals(LogicalKeyboardKey.keyA));
expect(data.keyLabel, equals('a'));
});
test('Control keyboard keys are correctly translated', () {
final RawKeyEvent escapeKeyEvent = RawKeyEvent.fromMessage(const <String, dynamic>{
'type': 'keydown',
'keymap': 'macos',
'keyCode': 0x00000035,
'characters': '',
'charactersIgnoringModifiers': '',
'character': null,
'modifiers': 0x0,
});
final RawKeyEventDataMacOs data = escapeKeyEvent.data;
expect(data.physicalKey, equals(PhysicalKeyboardKey.escape));
expect(data.logicalKey, equals(LogicalKeyboardKey.escape));
expect(data.keyLabel, isNull);
});
test('Modifier keyboard keys are correctly translated', () {
final RawKeyEvent shiftLeftKeyEvent = RawKeyEvent.fromMessage(const <String, dynamic>{
'type': 'keydown',
'keymap': 'macos',
'keyCode': 0x00000038,
'characters': '',
'charactersIgnoringModifiers': '',
'character': null,
'modifiers': RawKeyEventDataMacOs.modifierLeftShift,
});
final RawKeyEventDataMacOs data = shiftLeftKeyEvent.data;
expect(data.physicalKey, equals(PhysicalKeyboardKey.shiftLeft));
expect(data.logicalKey, equals(LogicalKeyboardKey.shiftLeft));
expect(data.keyLabel, isNull);
});
});
}
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