Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add KeyEventDeviceType to KeyData #47315

Merged
merged 8 commits into from
Nov 6, 2023
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
108 changes: 74 additions & 34 deletions lib/ui/key.dart
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,47 @@ enum KeyEventType {
up,

/// The key is held, causing a repeated key input.
repeat,
repeat;

String get label {
return switch (this) {
down => 'Key Down',
up => 'Key Up',
repeat => 'Key Repeat',
};
}
}

/// The source device for the key event.
///
/// Not all platforms supply an accurate type.
// Must match the KeyEventDeviceType enum in ui/window/key_data.h.
enum KeyEventDeviceType {
/// The device is a keyboard.
keyboard,

/// The device is a directional pad on something like a television remote
/// control or similar.
directionalPad,

/// The device is a gamepad button
gamepad,

/// The device is a joystick button
joystick,

/// The device is a device connected to an HDMI bus.
hdmi;

String get label {
return switch (this) {
keyboard => 'Keyboard',
directionalPad => 'Directional Pad',
gamepad => 'Gamepad',
joystick => 'Joystick',
hdmi => 'HDMI',
};
}
}

/// Information about a key event.
Expand All @@ -27,6 +67,7 @@ class KeyData {
required this.logical,
required this.character,
required this.synthesized,
this.deviceType = KeyEventDeviceType.keyboard,
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In order to pass the smoke test for the framework, this needs to be optional. Should we keep it that way, just to make migration easier? I'm leaning towards "yes", although all the other properties are required.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah I think it's fine. At the end of the day it's PlatformDispatcher that creates this object, right?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, usually, although you could write your own test framework or something that used it.

});

/// Time of event dispatch, relative to an arbitrary timeline.
Expand All @@ -38,6 +79,10 @@ class KeyData {
/// The type of the event.
final KeyEventType type;

/// Describes what type of device (keyboard, directional pad, etc.) this event
/// originated from.
final KeyEventDeviceType deviceType;

/// The key code for the physical key that has changed.
final int physical;

Expand All @@ -49,30 +94,31 @@ class KeyData {
/// Ignored for up events.
final String? character;

/// If [synthesized] is true, this event does not correspond to a native event.
/// If [synthesized] is true, this event does not correspond to a native
/// event.
///
/// Although most of Flutter's keyboard events are transformed from native
/// events, some events are not based on native events, and are synthesized
/// only to conform Flutter's key event model (as documented in
/// the `HardwareKeyboard` class in the framework).
/// only to conform Flutter's key event model (as documented in the
/// `HardwareKeyboard` class in the framework).
///
/// For example, some key downs or ups might be lost when the window loses
/// focus. Some platforms provides ways to query whether a key is being held.
/// focus. Some platforms provide ways to query whether a key is being held.
/// If the embedder detects an inconsistency between its internal record and
/// the state returned by the system, the embedder will synthesize a
/// corresponding event to synchronize the state without breaking the event
/// model.
///
/// As another example, macOS treats CapsLock in a special way by sending
/// down and up events at the down of alterate presses to indicate the
/// direction in which the lock is toggled instead of that the physical key is
/// going. A macOS embedder should normalize the behavior by converting a
/// native down event into a down event followed immediately by a synthesized
/// up event, and the native up event also into a down event followed
/// immediately by a synthesized up event.
/// As another example, macOS treats CapsLock in a special way by sending down
/// and up events at the down of alternate presses to indicate the direction
/// in which the lock is toggled instead of that the physical key is going. A
/// macOS embedder should normalize the behavior by converting a native down
/// event into a down event followed immediately by a synthesized up event,
/// and the native up event also into a down event followed immediately by a
/// synthesized up event.
///
/// Synthesized events do not have a trustworthy [timeStamp], and should not be
/// processed as if the key actually went down or up at the time of the
/// Synthesized events do not have a trustworthy [timeStamp], and should not
/// be processed as if the key actually went down or up at the time of the
/// callback.
///
/// [KeyRepeatEvent] is never synthesized.
Expand Down Expand Up @@ -172,29 +218,23 @@ class KeyData {
}

@override
String toString() => 'KeyData(key ${_typeToString(type)}, physical: 0x${physical.toRadixString(16)}, '
'logical: ${_logicalToString()}, character: ${_escapeCharacter()}${_quotedCharCode()}${synthesized ? ', synthesized' : ''})';
String toString() {
return 'KeyData(${type.label}, '
'physical: 0x${physical.toRadixString(16)}, '
'logical: ${_logicalToString()}, '
'character: ${_escapeCharacter()}${_quotedCharCode()}'
'${synthesized ? ', synthesized' : ''}';
}

/// Returns a complete textual description of the information in this object.
String toStringFull() {
return '$runtimeType('
'type: ${_typeToString(type)}, '
'timeStamp: $timeStamp, '
'physical: 0x${physical.toRadixString(16)}, '
'logical: 0x${logical.toRadixString(16)}, '
'character: ${_escapeCharacter()}, '
'synthesized: $synthesized'
return '$runtimeType(type: ${type.label}, '
'deviceType: ${deviceType.label}, '
'timeStamp: $timeStamp, '
'physical: 0x${physical.toRadixString(16)}, '
'logical: 0x${logical.toRadixString(16)}, '
'character: ${_escapeCharacter()}, '
'synthesized: $synthesized'
')';
}

static String _typeToString(KeyEventType type) {
switch (type) {
case KeyEventType.up:
return 'up';
case KeyEventType.down:
return 'down';
case KeyEventType.repeat:
return 'repeat';
}
}
}
9 changes: 4 additions & 5 deletions lib/ui/platform_dispatcher.dart
Original file line number Diff line number Diff line change
Expand Up @@ -506,11 +506,9 @@ class PlatformDispatcher {

// If this value changes, update the encoding code in the following files:
//
// * key_data.h
// * key.dart (ui)
// * key.dart (web_ui)
// * HardwareKeyboard.java
static const int _kKeyDataFieldCount = 5;
// * key_data.h (kKeyDataFieldCount)
// * KeyData.java (KeyData.FIELD_COUNT)
static const int _kKeyDataFieldCount = 6;

// The packet structure is described in `key_data_packet.h`.
static KeyData _unpackKeyData(ByteData packet) {
Expand All @@ -524,6 +522,7 @@ class PlatformDispatcher {
final KeyData keyData = KeyData(
timeStamp: Duration(microseconds: packet.getUint64(kStride * offset++, _kFakeHostEndian)),
type: KeyEventType.values[packet.getInt64(kStride * offset++, _kFakeHostEndian)],
deviceType: KeyEventDeviceType.keyboard,
physical: packet.getUint64(kStride * offset++, _kFakeHostEndian),
logical: packet.getUint64(kStride * offset++, _kFakeHostEndian),
character: character,
Expand Down
32 changes: 30 additions & 2 deletions lib/ui/window/key_data.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,11 @@

namespace flutter {

// If this value changes, update the key data unpacking code in hooks.dart.
static constexpr int kKeyDataFieldCount = 5;
// If this value changes, update the encoding code in the following files:
//
// * KeyData.java (KeyData.FIELD_COUNT)
// * platform_dispatcher.dart (_kKeyDataFieldCount)
static constexpr int kKeyDataFieldCount = 6;
static constexpr int kBytesPerKeyField = sizeof(int64_t);

// The change of the key event, used by KeyData.
Expand All @@ -22,6 +25,30 @@ enum class KeyEventType : int64_t {
kRepeat,
};

// The source device for the key event.
//
// Not all platforms supply an accurate source.
//
// Defaults to [keyboard].
// Must match the KeyEventDeviceType enum in ui/key.dart.
enum class KeyEventDeviceType : int64_t {
// The source is a keyboard.
kKeyboard = 0,

// The source is a directional pad on something like a television remote
// control or similar.
kDirectionalPad,

// The source is a gamepad button.
kGamepad,

// The source is a joystick button.
kJoystick,

// The source is a device connected to an HDMI bus.
kHdmi,
};

// The fixed-length sections of a KeyDataPacket.
//
// KeyData does not contain `character`, for variable-length data are stored in
Expand All @@ -41,6 +68,7 @@ struct alignas(8) KeyData {
//
// The value is 1 for true, and 0 for false.
uint64_t synthesized;
KeyEventDeviceType device_type;

// Sets all contents of `Keydata` to 0.
void Clear();
Expand Down
5 changes: 3 additions & 2 deletions lib/ui/window/key_data_packet.cc
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
#include "flutter/lib/ui/window/key_data_packet.h"

#include <cstring>
#include <iostream>

#include "flutter/fml/logging.h"

Expand All @@ -13,8 +14,8 @@ namespace flutter {
KeyDataPacket::KeyDataPacket(const KeyData& event, const char* character) {
size_t char_size = character == nullptr ? 0 : strlen(character);
uint64_t char_size_64 = char_size;
data_.resize(sizeof(uint64_t) + sizeof(KeyData) + char_size);
memcpy(CharacterSizeStart(), &char_size_64, sizeof(char_size));
data_.resize(sizeof(char_size_64) + sizeof(KeyData) + char_size);
memcpy(CharacterSizeStart(), &char_size_64, sizeof(char_size_64));
memcpy(KeyDataStart(), &event, sizeof(KeyData));
if (character != nullptr) {
memcpy(CharacterStart(), character, char_size);
Expand Down
Loading