| // Copyright 2014 The Flutter 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 Windows. |
| /// |
| /// This object contains information about key events obtained from Windows's |
| /// win32 API. |
| /// |
| /// See also: |
| /// |
| /// * [RawKeyboard], which uses this interface to expose key data. |
| class RawKeyEventDataWindows extends RawKeyEventData { |
| /// Creates a key event data structure specific for Windows. |
| /// |
| /// The [keyCode], [scanCode], [characterCodePoint], and [modifiers], arguments |
| /// must not be null. |
| const RawKeyEventDataWindows({ |
| this.keyCode = 0, |
| this.scanCode = 0, |
| this.characterCodePoint = 0, |
| this.modifiers = 0, |
| }) : assert(keyCode != null), |
| assert(scanCode != null), |
| assert(characterCodePoint != null), |
| assert(modifiers != null); |
| |
| /// The hardware key code corresponding to this key event. |
| /// |
| /// This is the physical key that was pressed, not the Unicode character. |
| /// See [characterCodePoint] for the Unicode character. |
| final int keyCode; |
| |
| /// The hardware scan code id corresponding to this key event. |
| /// |
| /// These values are not reliable and vary from device to device, so this |
| /// information is mainly useful for debugging. |
| final int scanCode; |
| |
| /// The Unicode code point represented by the key event, if any. |
| /// |
| /// If there is no Unicode code point, this value is zero. |
| final int characterCodePoint; |
| |
| /// A mask of the current modifiers. The modifier values must be in sync with |
| /// the ones defined in https://github.com/flutter/engine/blob/master/shell/platform/windows/key_event_handler.cc |
| final int modifiers; |
| |
| @override |
| String get keyLabel => characterCodePoint == 0 ? null : String.fromCharCode(characterCodePoint); |
| |
| @override |
| PhysicalKeyboardKey get physicalKey => kWindowsToPhysicalKey[scanCode] ?? 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 = kWindowsNumPadMap[keyCode]; |
| if (numPadKey != null) { |
| return numPadKey; |
| } |
| |
| // If it has a non-control-character label, then either return the existing |
| // constant, or construct a new Unicode-based key from it. Don't mark it as |
| // autogenerated, since the label uniquely identifies an ID from the Unicode |
| // plane. |
| if (keyLabel != null && keyLabel.isNotEmpty && !LogicalKeyboardKey.isControlCharacter(keyLabel)) { |
| final int keyId = LogicalKeyboardKey.unicodePlane | (characterCodePoint & LogicalKeyboardKey.valueMask); |
| return LogicalKeyboardKey.findKeyByKeyId(keyId) ?? LogicalKeyboardKey( |
| keyId, |
| keyLabel: keyLabel, |
| debugName: kReleaseMode ? null : 'Key ${keyLabel.toUpperCase()}', |
| ); |
| } |
| // Look to see if the keyCode is one we know about and have a mapping for. |
| LogicalKeyboardKey newKey = kWindowsToLogicalKey[keyCode]; |
| if (newKey != null) { |
| return newKey; |
| } |
| |
| // 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 windowsKeyIdPlane = 0x00700000000; |
| newKey ??= LogicalKeyboardKey( |
| windowsKeyIdPlane | keyCode | LogicalKeyboardKey.autogeneratedMask, |
| debugName: kReleaseMode ? null : 'Unknown Windows key code $keyCode', |
| ); |
| return newKey; |
| } |
| |
| bool _isLeftRightModifierPressed(KeyboardSide side, int anyMask, int leftMask, int rightMask) { |
| if (modifiers & anyMask == 0 && |
| modifiers & leftMask == 0 && |
| modifiers & rightMask == 0) { |
| return false; |
| } |
| // If only the "anyMask" bit is set, then we respond true for requests of |
| // whether either left or right is pressed. |
| // Handles the case where Windows supplies just the "either" modifier flag, |
| // but not the left/right flag. (e.g. modifierShift but not |
| // modifierLeftShift). |
| final bool anyOnly = modifiers & (leftMask | rightMask | anyMask) == anyMask; |
| switch (side) { |
| case KeyboardSide.any: |
| return true; |
| case KeyboardSide.all: |
| return modifiers & leftMask != 0 && modifiers & rightMask != 0 || anyOnly; |
| case KeyboardSide.left: |
| return modifiers & leftMask != 0 || anyOnly; |
| case KeyboardSide.right: |
| return modifiers & rightMask != 0 || anyOnly; |
| } |
| return false; |
| } |
| |
| @override |
| bool isModifierPressed(ModifierKey key, {KeyboardSide side = KeyboardSide.any}) { |
| bool result; |
| switch (key) { |
| case ModifierKey.controlModifier: |
| result = _isLeftRightModifierPressed(side, modifierControl, modifierLeftControl, modifierRightControl); |
| break; |
| case ModifierKey.shiftModifier: |
| result = _isLeftRightModifierPressed(side, modifierShift, modifierLeftShift, modifierRightShift); |
| break; |
| case ModifierKey.altModifier: |
| result = _isLeftRightModifierPressed(side, modifierAlt, modifierLeftAlt, modifierRightAlt); |
| break; |
| case ModifierKey.metaModifier: |
| // Windows does not provide an "any" key for win key press. |
| result = _isLeftRightModifierPressed(side, modifierLeftMeta | modifierRightMeta , modifierLeftMeta, modifierRightMeta); |
| break; |
| case ModifierKey.capsLockModifier: |
| result = modifiers & modifierCaps != 0; |
| break; |
| case ModifierKey.scrollLockModifier: |
| result = modifiers & modifierScrollLock != 0; |
| break; |
| case ModifierKey.numLockModifier: |
| result = modifiers & modifierNumLock != 0; |
| break; |
| // The OS does not expose the Fn key to the drivers, it doesn't generate a key message. |
| case ModifierKey.functionModifier: |
| case ModifierKey.symbolModifier: |
| // These modifier masks are not used in Windows keyboards. |
| result = false; |
| break; |
| } |
| assert(!result || getModifierSide(key) != null, "$runtimeType thinks that a modifier is pressed, but can't figure out what side it's on."); |
| return result; |
| } |
| |
| |
| @override |
| KeyboardSide getModifierSide(ModifierKey key) { |
| KeyboardSide findSide(int leftMask, int rightMask, int anyMask) { |
| 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; |
| } else if (modifiers & (combinedMask | anyMask) == anyMask) { |
| // Handles the case where Windows supplies just the "either" modifier |
| // flag, but not the left/right flag. (e.g. modifierShift but not |
| // modifierLeftShift). |
| return KeyboardSide.any; |
| } |
| return null; |
| } |
| |
| switch (key) { |
| case ModifierKey.controlModifier: |
| return findSide(modifierLeftControl, modifierRightControl, modifierControl); |
| case ModifierKey.shiftModifier: |
| return findSide(modifierLeftShift, modifierRightShift, modifierShift); |
| case ModifierKey.altModifier: |
| return findSide(modifierLeftAlt, modifierRightAlt, modifierAlt); |
| case ModifierKey.metaModifier: |
| return findSide(modifierLeftMeta, modifierRightMeta, 0); |
| 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; |
| } |
| |
| // These are not the values defined by the Windows header for each modifier. Since they |
| // can't be packaged into a single int, we are re-defining them here to reduce the size |
| // of the message from the embedder. Embedders should map these values to the native key codes. |
| // Keep this in sync with https://github.com/flutter/engine/blob/master/shell/platform/windows/key_event_handler.cc |
| |
| /// This mask is used to check the [modifiers] field to test whether one of the |
| /// SHIFT modifier keys is pressed. |
| /// |
| /// {@macro flutter.services.rawKeyEventDataWindows.modifiers} |
| static const int modifierShift = 1 << 0; |
| |
| /// This mask is used to check the [modifiers] field to test whether the left |
| /// SHIFT modifier key is pressed. |
| /// |
| /// {@macro flutter.services.rawKeyEventDataWindows.modifiers} |
| static const int modifierLeftShift = 1 << 1; |
| |
| /// This mask is used to check the [modifiers] field to test whether the right |
| /// SHIFT modifier key is pressed. |
| /// |
| /// {@macro flutter.services.rawKeyEventDataWindows.modifiers} |
| static const int modifierRightShift = 1 << 2; |
| |
| /// This mask is used to check the [modifiers] field to test whether one of the |
| /// CTRL modifier keys is pressed. |
| /// |
| /// {@macro flutter.services.rawKeyEventDataWindows.modifiers} |
| static const int modifierControl = 1 << 3; |
| |
| /// This mask is used to check the [modifiers] field to test whether the left |
| /// CTRL modifier key is pressed. |
| /// |
| /// {@macro flutter.services.rawKeyEventDataWindows.modifiers} |
| static const int modifierLeftControl = 1 << 4; |
| |
| /// This mask is used to check the [modifiers] field to test whether the right |
| /// CTRL modifier key is pressed. |
| /// |
| /// {@macro flutter.services.rawKeyEventDataWindows.modifiers} |
| static const int modifierRightControl = 1 << 5; |
| |
| /// This mask is used to check the [modifiers] field to test whether one of the |
| /// ALT modifier keys is pressed. |
| /// |
| /// {@macro flutter.services.rawKeyEventDataWindows.modifiers} |
| static const int modifierAlt = 1 << 6; |
| |
| /// This mask is used to check the [modifiers] field to test whether the left |
| /// ALT modifier key is pressed. |
| /// |
| /// {@macro flutter.services.rawKeyEventDataWindows.modifiers} |
| static const int modifierLeftAlt = 1 << 7; |
| |
| /// This mask is used to check the [modifiers] field to test whether the right |
| /// ALT modifier key is pressed. |
| /// |
| /// {@macro flutter.services.rawKeyEventDataWindows.modifiers} |
| static const int modifierRightAlt = 1 << 8; |
| |
| /// This mask is used to check the [modifiers] field to test whether the left |
| /// WIN modifier keys is pressed. |
| /// |
| /// {@macro flutter.services.rawKeyEventDataWindows.modifiers} |
| static const int modifierLeftMeta = 1 << 9; |
| |
| /// This mask is used to check the [modifiers] field to test whether the right |
| /// WIN modifier keys is pressed. |
| /// |
| /// {@macro flutter.services.rawKeyEventDataWindows.modifiers} |
| static const int modifierRightMeta = 1 << 10; |
| |
| /// This mask is used to check the [modifiers] field to test whether the CAPS LOCK key |
| /// is pressed. |
| /// |
| /// {@macro flutter.services.rawKeyEventDataWindows.modifiers} |
| static const int modifierCaps = 1 << 11; |
| |
| /// This mask is used to check the [modifiers] field to test whether the NUM LOCK key |
| /// is pressed. |
| /// |
| /// {@macro flutter.services.rawKeyEventDataWindows.modifiers} |
| static const int modifierNumLock = 1 << 12; |
| |
| /// This mask is used to check the [modifiers] field to test whether the SCROLL LOCK key |
| /// is pressed. |
| /// |
| /// {@macro flutter.services.rawKeyEventDataWindows.modifiers} |
| static const int modifierScrollLock = 1 << 13; |
| } |