Skip to content

Commit

Permalink
Hardware keyboard: Web, embedder, and dart:ui (flutter#23466)
Browse files Browse the repository at this point in the history
  • Loading branch information
dkwingsmt authored Jan 29, 2021
1 parent ba1ac54 commit d325ca7
Show file tree
Hide file tree
Showing 46 changed files with 3,295 additions and 5 deletions.
8 changes: 8 additions & 0 deletions ci/licenses_golden/licenses_flutter
Original file line number Diff line number Diff line change
Expand Up @@ -319,6 +319,7 @@ FILE: ../../../flutter/lib/ui/isolate_name_server/isolate_name_server.cc
FILE: ../../../flutter/lib/ui/isolate_name_server/isolate_name_server.h
FILE: ../../../flutter/lib/ui/isolate_name_server/isolate_name_server_natives.cc
FILE: ../../../flutter/lib/ui/isolate_name_server/isolate_name_server_natives.h
FILE: ../../../flutter/lib/ui/key.dart
FILE: ../../../flutter/lib/ui/lerp.dart
FILE: ../../../flutter/lib/ui/natives.dart
FILE: ../../../flutter/lib/ui/painting.dart
Expand Down Expand Up @@ -406,6 +407,10 @@ FILE: ../../../flutter/lib/ui/ui_dart_state.h
FILE: ../../../flutter/lib/ui/volatile_path_tracker.cc
FILE: ../../../flutter/lib/ui/volatile_path_tracker.h
FILE: ../../../flutter/lib/ui/window.dart
FILE: ../../../flutter/lib/ui/window/key_data.cc
FILE: ../../../flutter/lib/ui/window/key_data.h
FILE: ../../../flutter/lib/ui/window/key_data_packet.cc
FILE: ../../../flutter/lib/ui/window/key_data_packet.h
FILE: ../../../flutter/lib/ui/window/platform_configuration.cc
FILE: ../../../flutter/lib/ui/window/platform_configuration.h
FILE: ../../../flutter/lib/ui/window/platform_configuration_unittests.cc
Expand Down Expand Up @@ -500,7 +505,9 @@ FILE: ../../../flutter/lib/web_ui/lib/src/engine/html/surface.dart
FILE: ../../../flutter/lib/web_ui/lib/src/engine/html/surface_stats.dart
FILE: ../../../flutter/lib/web_ui/lib/src/engine/html/transform.dart
FILE: ../../../flutter/lib/web_ui/lib/src/engine/html_image_codec.dart
FILE: ../../../flutter/lib/web_ui/lib/src/engine/key_map.dart
FILE: ../../../flutter/lib/web_ui/lib/src/engine/keyboard.dart
FILE: ../../../flutter/lib/web_ui/lib/src/engine/keyboard_binding.dart
FILE: ../../../flutter/lib/web_ui/lib/src/engine/mouse_cursor.dart
FILE: ../../../flutter/lib/web_ui/lib/src/engine/navigation/history.dart
FILE: ../../../flutter/lib/web_ui/lib/src/engine/navigation/js_url_strategy.dart
Expand Down Expand Up @@ -560,6 +567,7 @@ FILE: ../../../flutter/lib/web_ui/lib/src/ui/compositing.dart
FILE: ../../../flutter/lib/web_ui/lib/src/ui/geometry.dart
FILE: ../../../flutter/lib/web_ui/lib/src/ui/hash_codes.dart
FILE: ../../../flutter/lib/web_ui/lib/src/ui/initialization.dart
FILE: ../../../flutter/lib/web_ui/lib/src/ui/key.dart
FILE: ../../../flutter/lib/web_ui/lib/src/ui/lerp.dart
FILE: ../../../flutter/lib/web_ui/lib/src/ui/natives.dart
FILE: ../../../flutter/lib/web_ui/lib/src/ui/painting.dart
Expand Down
4 changes: 4 additions & 0 deletions lib/ui/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,10 @@ source_set("ui") {
"ui_dart_state.h",
"volatile_path_tracker.cc",
"volatile_path_tracker.h",
"window/key_data.cc",
"window/key_data.h",
"window/key_data_packet.cc",
"window/key_data_packet.h",
"window/platform_configuration.cc",
"window/platform_configuration.h",
"window/platform_message.cc",
Expand Down
1 change: 1 addition & 0 deletions lib/ui/dart_ui.gni
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ dart_ui_files = [
"//flutter/lib/ui/hash_codes.dart",
"//flutter/lib/ui/hooks.dart",
"//flutter/lib/ui/isolate_name_server.dart",
"//flutter/lib/ui/key.dart",
"//flutter/lib/ui/lerp.dart",
"//flutter/lib/ui/natives.dart",
"//flutter/lib/ui/painting.dart",
Expand Down
6 changes: 6 additions & 0 deletions lib/ui/hooks.dart
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,12 @@ void _dispatchPointerDataPacket(ByteData packet) {
PlatformDispatcher.instance._dispatchPointerDataPacket(packet);
}

@pragma('vm:entry-point')
// ignore: unused_element
void _dispatchKeyData(ByteData packet, int responseId) {
PlatformDispatcher.instance._dispatchKeyData(packet, responseId);
}

@pragma('vm:entry-point')
// ignore: unused_element
void _dispatchSemanticsAction(int id, int action, ByteData? args) {
Expand Down
109 changes: 109 additions & 0 deletions lib/ui/key.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
// Copyright 2013 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.

// @dart = 2.12

part of dart.ui;

/// The type of a key event.
// Must match the KeyEventType enum in ui/window/key_data.h.
enum KeyEventType {
/// The key is pressed.
down,

/// The key is released.
up,

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

/// Information about a key event.
class KeyData {
/// Creates an object that represents a key event.
const KeyData({
required this.timeStamp,
required this.type,
required this.physical,
required this.logical,
required this.character,
required this.synthesized,
});

/// Time of event dispatch, relative to an arbitrary timeline.
///
/// For synthesized events, the [timeStamp] might not be the actual time that
/// the key press or release happens.
final Duration timeStamp;

/// The type of the event.
final KeyEventType type;

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

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

/// Character input from the event.
///
/// Ignored for up events.
final String? character;

/// 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).
///
/// 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.
/// If the embedder detects an inconsistancy 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.
///
/// 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.
final bool synthesized;

@override
String toString() => 'KeyData(type: ${_typeToString(type)}, physical: 0x${physical.toRadixString(16)}, '
'logical: 0x${logical.toRadixString(16)}, character: $character)';

/// 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: $character, '
'synthesized: $synthesized'
')';
}

static String _typeToString(KeyEventType type) {
switch (type) {
case KeyEventType.up:
return 'up';
case KeyEventType.down:
return 'down';
case KeyEventType.repeat:
return 'repeat';
}
}
}
65 changes: 64 additions & 1 deletion lib/ui/platform_dispatcher.dart
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,12 @@ typedef TimingsCallback = void Function(List<FrameTiming> timings);
/// Signature for [PlatformDispatcher.onPointerDataPacket].
typedef PointerDataPacketCallback = void Function(PointerDataPacket packet);

// Signature for the response to KeyDataCallback.
typedef _KeyDataResponseCallback = void Function(int responseId, bool handled);

/// Signature for [PlatformDispatcher.onKeyData].
typedef KeyDataCallback = bool Function(KeyData data);

/// Signature for [PlatformDispatcher.onSemanticsAction].
typedef SemanticsActionCallback = void Function(int id, SemanticsAction action, ByteData? args);

Expand Down Expand Up @@ -332,6 +338,63 @@ class PlatformDispatcher {
return PointerDataPacket(data: data);
}

/// Called by [_dispatchKeyData].
void _respondToKeyData(int responseId, bool handled)
native 'PlatformConfiguration_respondToKeyData';

/// A callback that is invoked when key data is available.
///
/// The framework invokes this callback in the same zone in which the callback
/// was set.
KeyDataCallback? get onKeyData => _onKeyData;
KeyDataCallback? _onKeyData;
Zone _onKeyDataZone = Zone.root;
set onKeyData(KeyDataCallback? callback) {
_onKeyData = callback;
_onKeyDataZone = Zone.current;
}

// Called from the engine, via hooks.dart
void _dispatchKeyData(ByteData packet, int responseId) {
_invoke2<KeyData, _KeyDataResponseCallback>(
(KeyData data, _KeyDataResponseCallback callback) {
callback(responseId, onKeyData == null ? false : onKeyData!(data));
},
_onKeyDataZone,
_unpackKeyData(packet),
_respondToKeyData,
);
}

// 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;

// The packet structure is described in `key_data_packet.h`.
static KeyData _unpackKeyData(ByteData packet) {
const int kStride = Int64List.bytesPerElement;

int offset = 0;
final int charDataSize = packet.getUint64(kStride * offset++, _kFakeHostEndian);
final String? character = charDataSize == 0 ? null : utf8.decoder.convert(
packet.buffer.asUint8List(kStride * (offset + _kKeyDataFieldCount), charDataSize));

final KeyData keyData = KeyData(
timeStamp: Duration(microseconds: packet.getUint64(kStride * offset++, _kFakeHostEndian)),
type: KeyEventType.values[packet.getInt64(kStride * offset++, _kFakeHostEndian)],
physical: packet.getUint64(kStride * offset++, _kFakeHostEndian),
logical: packet.getUint64(kStride * offset++, _kFakeHostEndian),
character: character,
synthesized: packet.getUint64(kStride * offset++, _kFakeHostEndian) != 0,
);

return keyData;
}

/// A callback that is invoked to report the [FrameTiming] of recently
/// rasterized frames.
///
Expand Down Expand Up @@ -1547,4 +1610,4 @@ class Locale {
out.write('$separator$countryCode');
return out.toString();
}
}
}
2 changes: 1 addition & 1 deletion lib/ui/ui.dart
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@
// @dart = 2.12
library dart.ui;

import 'dart:_internal' hide Symbol; // ignore: unused_import
import 'dart:async';
import 'dart:collection' as collection;
import 'dart:convert';
Expand All @@ -30,6 +29,7 @@ part 'geometry.dart';
part 'hash_codes.dart';
part 'hooks.dart';
part 'isolate_name_server.dart';
part 'key.dart';
part 'lerp.dart';
part 'natives.dart';
part 'painting.dart';
Expand Down
9 changes: 9 additions & 0 deletions lib/ui/window.dart
Original file line number Diff line number Diff line change
Expand Up @@ -556,6 +556,15 @@ class SingletonFlutterWindow extends FlutterWindow {
platformDispatcher.onPointerDataPacket = callback;
}

/// A callback that is invoked when key data is available.
///
/// The framework invokes this callback in the same zone in which the
/// callback was set.
KeyDataCallback? get onKeyData => platformDispatcher.onKeyData;
set onKeyData(KeyDataCallback? callback) {
platformDispatcher.onKeyData = callback;
}

/// The route or path that the embedder requested when the application was
/// launched.
///
Expand Down
18 changes: 18 additions & 0 deletions lib/ui/window/key_data.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
// Copyright 2013 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.

#include "flutter/lib/ui/window/key_data.h"

#include <cstring>

namespace flutter {

static_assert(sizeof(KeyData) == kBytesPerKeyField * kKeyDataFieldCount,
"KeyData has the wrong size");

void KeyData::Clear() {
memset(this, 0, sizeof(KeyData));
}

} // namespace flutter
48 changes: 48 additions & 0 deletions lib/ui/window/key_data.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
// Copyright 2013 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.

#ifndef FLUTTER_LIB_UI_WINDOW_KEY_DATA_H_
#define FLUTTER_LIB_UI_WINDOW_KEY_DATA_H_

#include <cstdint>

namespace flutter {

// If this value changes, update the key data unpacking code in hooks.dart.
static constexpr int kKeyDataFieldCount = 5;
static constexpr int kBytesPerKeyField = sizeof(int64_t);

// The change of the key event, used by KeyData.
//
// Must match the KeyEventType enum in ui/key.dart.
enum class KeyEventType : int64_t {
kDown = 0,
kUp,
kRepeat,
};

// The fixed-length sections of a KeyDataPacket.
//
// KeyData does not contain `character`, for variable-length data are stored in
// a different way in KeyDataPacket.
//
// This structure is unpacked by hooks.dart.
struct alignas(8) KeyData {
// Timestamp in microseconds from an arbitrary and consistant start point
uint64_t timestamp;
KeyEventType type;
uint64_t physical;
uint64_t logical;
// True if the event does not correspond to a native event.
//
// The value is 1 for true, and 0 for false.
uint64_t synthesized;

// Sets all contents of `Keydata` to 0.
void Clear();
};

} // namespace flutter

#endif // FLUTTER_LIB_UI_WINDOW_POINTER_DATA_H_
26 changes: 26 additions & 0 deletions lib/ui/window/key_data_packet.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
// Copyright 2013 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.

#include "flutter/lib/ui/window/key_data_packet.h"

#include <cstring>

#include "flutter/fml/logging.h"

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));
memcpy(KeyDataStart(), &event, sizeof(KeyData));
if (character != nullptr) {
memcpy(CharacterStart(), character, char_size);
}
}

KeyDataPacket::~KeyDataPacket() = default;

} // namespace flutter
Loading

0 comments on commit d325ca7

Please sign in to comment.