mirror of
https://github.com/flutter/flutter.git
synced 2026-02-20 02:29:02 +08:00
[fuchsia][input] Migrate Flutter to "input3" (flutter/engine#23262)
This commit is contained in:
parent
0c179e5d66
commit
943ae09365
@ -1230,6 +1230,9 @@ FILE: ../../../flutter/shell/platform/fuchsia/flutter/isolate_configurator.h
|
||||
FILE: ../../../flutter/shell/platform/fuchsia/flutter/kernel/extract_far.dart
|
||||
FILE: ../../../flutter/shell/platform/fuchsia/flutter/kernel/framework_shim.dart
|
||||
FILE: ../../../flutter/shell/platform/fuchsia/flutter/kernel/libraries.json
|
||||
FILE: ../../../flutter/shell/platform/fuchsia/flutter/keyboard.cc
|
||||
FILE: ../../../flutter/shell/platform/fuchsia/flutter/keyboard.h
|
||||
FILE: ../../../flutter/shell/platform/fuchsia/flutter/keyboard_unittest.cc
|
||||
FILE: ../../../flutter/shell/platform/fuchsia/flutter/logging.h
|
||||
FILE: ../../../flutter/shell/platform/fuchsia/flutter/loop.cc
|
||||
FILE: ../../../flutter/shell/platform/fuchsia/flutter/loop.h
|
||||
|
||||
@ -63,6 +63,8 @@ template("runner_sources") {
|
||||
"fuchsia_intl.h",
|
||||
"isolate_configurator.cc",
|
||||
"isolate_configurator.h",
|
||||
"keyboard.cc",
|
||||
"keyboard.h",
|
||||
"logging.h",
|
||||
"loop.cc",
|
||||
"loop.h",
|
||||
@ -446,6 +448,7 @@ executable("flutter_runner_unittests") {
|
||||
"component_unittest.cc",
|
||||
"flutter_runner_fakes.h",
|
||||
"fuchsia_intl_unittest.cc",
|
||||
"keyboard_unittest.cc",
|
||||
"platform_view_unittest.cc",
|
||||
"runner_unittest.cc",
|
||||
"tests/engine_unittests.cc",
|
||||
|
||||
@ -101,10 +101,16 @@ Engine::Engine(Delegate& delegate,
|
||||
scenic->CreateSession2(session.NewRequest(), session_listener.Bind(),
|
||||
focuser.NewRequest());
|
||||
|
||||
// Make clones of the `ViewRef` before sending it down to Scenic.
|
||||
fuchsia::ui::views::ViewRef platform_view_ref, isolate_view_ref;
|
||||
// Make clones of the `ViewRef` before sending it down to Scenic, since the
|
||||
// refs are not copyable, and multiple consumers need view refs.
|
||||
fuchsia::ui::views::ViewRef platform_view_ref;
|
||||
view_ref_pair.view_ref.Clone(&platform_view_ref);
|
||||
fuchsia::ui::views::ViewRef isolate_view_ref;
|
||||
view_ref_pair.view_ref.Clone(&isolate_view_ref);
|
||||
// Input3 keyboard listener registration requires a ViewRef as an event
|
||||
// filter. So we clone it here, as ViewRefs can not be reused, only cloned.
|
||||
fuchsia::ui::views::ViewRef keyboard_view_ref;
|
||||
view_ref_pair.view_ref.Clone(&keyboard_view_ref);
|
||||
|
||||
// Session is terminated on the raster thread, but we must terminate ourselves
|
||||
// on the platform thread.
|
||||
@ -203,6 +209,26 @@ Engine::Engine(Delegate& delegate,
|
||||
auto run_configuration = flutter::RunConfiguration::InferFromSettings(
|
||||
settings, task_runners.GetIOTaskRunner());
|
||||
|
||||
// Connect to fuchsia.ui.input3.Keyboard to hand out a listener.
|
||||
using fuchsia::ui::input3::Keyboard;
|
||||
using fuchsia::ui::input3::KeyboardListener;
|
||||
|
||||
// Keyboard client-side stub.
|
||||
keyboard_svc_ = svc->Connect<Keyboard>();
|
||||
ZX_ASSERT(keyboard_svc_.is_bound());
|
||||
// KeyboardListener handle pair is not initialized until NewRequest() is
|
||||
// called.
|
||||
fidl::InterfaceHandle<KeyboardListener> keyboard_listener;
|
||||
|
||||
// Server side of KeyboardListener. Initializes the keyboard_listener
|
||||
// handle.
|
||||
fidl::InterfaceRequest<KeyboardListener> keyboard_listener_request =
|
||||
keyboard_listener.NewRequest();
|
||||
ZX_ASSERT(keyboard_listener_request.is_valid());
|
||||
|
||||
keyboard_svc_->AddListener(std::move(keyboard_view_ref),
|
||||
keyboard_listener.Bind(), [] {});
|
||||
|
||||
// Setup the callback that will instantiate the platform view.
|
||||
flutter::Shell::CreateCallback<flutter::PlatformView>
|
||||
on_create_platform_view = fml::MakeCopyable(
|
||||
@ -222,7 +248,9 @@ Engine::Engine(Delegate& delegate,
|
||||
on_create_surface_callback = std::move(on_create_surface_callback),
|
||||
external_view_embedder = GetExternalViewEmbedder(),
|
||||
vsync_offset = product_config.get_vsync_offset(),
|
||||
vsync_handle = vsync_event_.get()](flutter::Shell& shell) mutable {
|
||||
vsync_handle = vsync_event_.get(),
|
||||
keyboard_listener_request = std::move(keyboard_listener_request)](
|
||||
flutter::Shell& shell) mutable {
|
||||
return std::make_unique<flutter_runner::PlatformView>(
|
||||
shell, // delegate
|
||||
debug_label, // debug label
|
||||
@ -232,6 +260,9 @@ Engine::Engine(Delegate& delegate,
|
||||
std::move(parent_environment_service_provider), // services
|
||||
std::move(session_listener_request), // session listener
|
||||
std::move(focuser),
|
||||
// Server-side part of the fuchsia.ui.input3.KeyboardListener
|
||||
// connection.
|
||||
std::move(keyboard_listener_request),
|
||||
std::move(on_session_listener_error_callback),
|
||||
std::move(on_enable_wireframe_callback),
|
||||
std::move(on_create_view_callback),
|
||||
|
||||
@ -114,6 +114,8 @@ class Engine final {
|
||||
|
||||
friend class testing::EngineTest;
|
||||
|
||||
fidl::InterfacePtr<fuchsia::ui::input3::Keyboard> keyboard_svc_;
|
||||
|
||||
FML_DISALLOW_COPY_AND_ASSIGN(Engine);
|
||||
};
|
||||
|
||||
|
||||
318
engine/src/flutter/shell/platform/fuchsia/flutter/keyboard.cc
Normal file
318
engine/src/flutter/shell/platform/fuchsia/flutter/keyboard.cc
Normal file
@ -0,0 +1,318 @@
|
||||
// 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/shell/platform/fuchsia/flutter/keyboard.h"
|
||||
|
||||
#include <fuchsia/input/cpp/fidl.h>
|
||||
#include <fuchsia/ui/input/cpp/fidl.h>
|
||||
#include <fuchsia/ui/input3/cpp/fidl.h>
|
||||
|
||||
#include <iostream>
|
||||
|
||||
namespace flutter_runner {
|
||||
|
||||
using fuchsia::input::Key;
|
||||
using fuchsia::ui::input::kModifierCapsLock;
|
||||
using fuchsia::ui::input::kModifierLeftAlt;
|
||||
using fuchsia::ui::input::kModifierLeftControl;
|
||||
using fuchsia::ui::input::kModifierLeftShift;
|
||||
using fuchsia::ui::input::kModifierNone;
|
||||
using fuchsia::ui::input::kModifierRightAlt;
|
||||
using fuchsia::ui::input::kModifierRightControl;
|
||||
using fuchsia::ui::input::kModifierRightShift;
|
||||
using fuchsia::ui::input3::KeyEvent;
|
||||
using fuchsia::ui::input3::KeyEventType;
|
||||
|
||||
namespace {
|
||||
|
||||
// A simple keymap from a QWERTY keyboard to code points. A value 0 means no
|
||||
// code point has been assigned for the respective keypress. Column 0 is the
|
||||
// code point without a level modifier active, and Column 1 is the code point
|
||||
// with a level modifier (e.g. Shift key) active.
|
||||
static const uint32_t QWERTY_TO_CODE_POINTS[][2] = {
|
||||
// 0x00
|
||||
{},
|
||||
{},
|
||||
{},
|
||||
{},
|
||||
// 0x04,
|
||||
{'a', 'A'},
|
||||
{'b', 'B'},
|
||||
{'c', 'C'},
|
||||
{'d', 'D'},
|
||||
// 0x08
|
||||
{'e', 'E'},
|
||||
{'f', 'F'},
|
||||
{'g', 'G'},
|
||||
{'h', 'H'},
|
||||
// 0x0c
|
||||
{'i', 'I'},
|
||||
{'j', 'J'},
|
||||
{'k', 'K'},
|
||||
{'l', 'L'},
|
||||
// 0x10
|
||||
{'m', 'M'},
|
||||
{'n', 'N'},
|
||||
{'o', 'O'},
|
||||
{'p', 'P'},
|
||||
// 0x14
|
||||
{'q', 'Q'},
|
||||
{'r', 'R'},
|
||||
{'s', 'S'},
|
||||
{'t', 'T'},
|
||||
// 0x18
|
||||
{'u', 'U'},
|
||||
{'v', 'V'},
|
||||
{'w', 'W'},
|
||||
{'x', 'X'},
|
||||
// 0x1c
|
||||
{'y', 'Y'},
|
||||
{'z', 'Z'},
|
||||
{'1', '!'},
|
||||
{'2', '@'},
|
||||
// 0x20
|
||||
{'3', '#'},
|
||||
{'4', '$'},
|
||||
{'5', '%'},
|
||||
{'6', '^'},
|
||||
// 0x24
|
||||
{'7', '&'},
|
||||
{'8', '*'},
|
||||
{'9', '('},
|
||||
{'0', ')'},
|
||||
// 0x28
|
||||
{},
|
||||
{},
|
||||
{},
|
||||
{},
|
||||
// 0x2c
|
||||
{' ', ' '},
|
||||
{'-', '_'},
|
||||
{'=', '+'},
|
||||
{'[', '{'},
|
||||
// 0x30
|
||||
{']', '}'},
|
||||
{'\\', '|'},
|
||||
{},
|
||||
{';', ':'},
|
||||
// 0x34
|
||||
{'\'', '"'},
|
||||
{'`', '~'},
|
||||
{',', '<'},
|
||||
{'.', '>'},
|
||||
// 0x38
|
||||
{'/', '?'},
|
||||
{},
|
||||
{},
|
||||
{},
|
||||
// 0x3c
|
||||
{},
|
||||
{},
|
||||
{},
|
||||
{},
|
||||
// 0x40
|
||||
{},
|
||||
{},
|
||||
{},
|
||||
{},
|
||||
// 0x44
|
||||
{},
|
||||
{},
|
||||
{},
|
||||
{},
|
||||
// 0x48
|
||||
{},
|
||||
{},
|
||||
{},
|
||||
{},
|
||||
// 0x4c
|
||||
{},
|
||||
{},
|
||||
{},
|
||||
{},
|
||||
// 0x50
|
||||
{},
|
||||
{},
|
||||
{},
|
||||
{},
|
||||
// 0x54
|
||||
{'/', 0},
|
||||
{'*', 0},
|
||||
{'-', 0},
|
||||
{'+', 0},
|
||||
// 0x58
|
||||
{},
|
||||
{'1', 0},
|
||||
{'2', 0},
|
||||
{'3', 0},
|
||||
// 0x5c
|
||||
{'4', 0},
|
||||
{'5', 0},
|
||||
{'6', 0},
|
||||
{'7', 0},
|
||||
// 0x60
|
||||
{'8', 0},
|
||||
{'9', 0},
|
||||
{'0', 0},
|
||||
{'.', 0},
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
Keyboard::Keyboard()
|
||||
: any_events_received_(false),
|
||||
stateful_caps_lock_(false),
|
||||
left_shift_(false),
|
||||
right_shift_(false),
|
||||
left_alt_(false),
|
||||
right_alt_(false),
|
||||
left_ctrl_(false),
|
||||
right_ctrl_(false),
|
||||
last_event_() {}
|
||||
|
||||
bool Keyboard::ConsumeEvent(KeyEvent event) {
|
||||
if (!event.has_type()) {
|
||||
return false;
|
||||
}
|
||||
if (!event.has_key()) {
|
||||
return false;
|
||||
}
|
||||
// Check if the time sequence of the events is correct.
|
||||
|
||||
last_event_ = std::move(event);
|
||||
any_events_received_ = true;
|
||||
const Key& key = last_event_.key();
|
||||
const KeyEventType& event_type = last_event_.type();
|
||||
switch (event_type) {
|
||||
// For modifier keys, a SYNC is the same as a press.
|
||||
case KeyEventType::SYNC:
|
||||
switch (key) {
|
||||
case Key::CAPS_LOCK:
|
||||
stateful_caps_lock_ = true;
|
||||
break;
|
||||
case Key::LEFT_ALT:
|
||||
left_alt_ = true;
|
||||
break;
|
||||
case Key::LEFT_CTRL:
|
||||
left_ctrl_ = true;
|
||||
break;
|
||||
case Key::LEFT_SHIFT:
|
||||
left_shift_ = true;
|
||||
break;
|
||||
case Key::RIGHT_ALT:
|
||||
right_alt_ = true;
|
||||
break;
|
||||
case Key::RIGHT_CTRL:
|
||||
right_ctrl_ = true;
|
||||
break;
|
||||
case Key::RIGHT_SHIFT:
|
||||
right_shift_ = true;
|
||||
break;
|
||||
default:
|
||||
// no-op
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case KeyEventType::PRESSED:
|
||||
switch (key) {
|
||||
case Key::CAPS_LOCK:
|
||||
stateful_caps_lock_ = !stateful_caps_lock_;
|
||||
break;
|
||||
case Key::LEFT_ALT:
|
||||
left_alt_ = true;
|
||||
break;
|
||||
case Key::LEFT_CTRL:
|
||||
left_ctrl_ = true;
|
||||
break;
|
||||
case Key::LEFT_SHIFT:
|
||||
left_shift_ = true;
|
||||
break;
|
||||
case Key::RIGHT_ALT:
|
||||
right_alt_ = true;
|
||||
break;
|
||||
case Key::RIGHT_CTRL:
|
||||
right_ctrl_ = true;
|
||||
break;
|
||||
case Key::RIGHT_SHIFT:
|
||||
right_shift_ = true;
|
||||
break;
|
||||
default:
|
||||
// No-op
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case KeyEventType::RELEASED:
|
||||
switch (key) {
|
||||
case Key::CAPS_LOCK:
|
||||
// No-op.
|
||||
break;
|
||||
case Key::LEFT_ALT:
|
||||
left_alt_ = false;
|
||||
break;
|
||||
case Key::LEFT_CTRL:
|
||||
left_ctrl_ = false;
|
||||
break;
|
||||
case Key::LEFT_SHIFT:
|
||||
left_shift_ = false;
|
||||
break;
|
||||
case Key::RIGHT_ALT:
|
||||
right_alt_ = false;
|
||||
break;
|
||||
case Key::RIGHT_CTRL:
|
||||
right_ctrl_ = false;
|
||||
break;
|
||||
case Key::RIGHT_SHIFT:
|
||||
right_shift_ = false;
|
||||
break;
|
||||
default:
|
||||
// No-op
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case KeyEventType::CANCEL:
|
||||
// No-op?
|
||||
break;
|
||||
default:
|
||||
// No-op
|
||||
break;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Keyboard::IsShift() {
|
||||
return left_shift_ | right_shift_ | stateful_caps_lock_;
|
||||
}
|
||||
|
||||
bool Keyboard::IsKeys() {
|
||||
return (static_cast<uint64_t>(last_event_.key()) & 0xFFFF0000) == 0x00070000;
|
||||
}
|
||||
|
||||
uint32_t Keyboard::Modifiers() {
|
||||
return kModifierNone + (kModifierLeftShift * left_shift_) +
|
||||
(kModifierLeftAlt * left_alt_) + (kModifierLeftControl * left_ctrl_) +
|
||||
(kModifierRightShift * right_shift_) +
|
||||
(kModifierRightAlt * right_alt_) +
|
||||
(kModifierRightControl * right_ctrl_) +
|
||||
(kModifierCapsLock * stateful_caps_lock_);
|
||||
}
|
||||
|
||||
uint32_t Keyboard::LastCodePoint() {
|
||||
static const int qwerty_map_size =
|
||||
sizeof(QWERTY_TO_CODE_POINTS) / sizeof(QWERTY_TO_CODE_POINTS[0]);
|
||||
if (!IsKeys()) {
|
||||
return 0;
|
||||
}
|
||||
const auto usage = LastHIDUsage();
|
||||
if (usage < qwerty_map_size) {
|
||||
return QWERTY_TO_CODE_POINTS[usage][IsShift() & 1];
|
||||
}
|
||||
// Any other keys don't have a code point.
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint32_t Keyboard::LastHIDUsage() {
|
||||
return static_cast<uint64_t>(last_event_.key()) & 0xFFFF;
|
||||
}
|
||||
|
||||
} // namespace flutter_runner
|
||||
63
engine/src/flutter/shell/platform/fuchsia/flutter/keyboard.h
Normal file
63
engine/src/flutter/shell/platform/fuchsia/flutter/keyboard.h
Normal file
@ -0,0 +1,63 @@
|
||||
// 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_SHELL_PLATFORM_FUCHSIA_KEYBOARD_H_
|
||||
#define FLUTTER_SHELL_PLATFORM_FUCHSIA_KEYBOARD_H_
|
||||
|
||||
#include <fuchsia/ui/input3/cpp/fidl.h>
|
||||
|
||||
namespace flutter_runner {
|
||||
|
||||
// Keyboard handles the keyboard signals from fuchsia.ui.input3. Specifically,
|
||||
// input3 has no notion of a code point, and does not track stateful versions
|
||||
// of the modifier keys.
|
||||
class Keyboard final {
|
||||
public:
|
||||
explicit Keyboard();
|
||||
|
||||
// Consumes the given keyboard event. Keyboard will adjust the modifier
|
||||
// state based on the info given in the event. Returns true if the event has
|
||||
// been integrated into the internal state successfully, or false otherwise.
|
||||
bool ConsumeEvent(fuchsia::ui::input3::KeyEvent event);
|
||||
|
||||
// Gets the currently active modifier keys.
|
||||
uint32_t Modifiers();
|
||||
|
||||
// Gets the last encountered code point. The reported code point depends on
|
||||
// the state of the modifier keys.
|
||||
uint32_t LastCodePoint();
|
||||
|
||||
// Gets the last encountered HID usage.
|
||||
uint32_t LastHIDUsage();
|
||||
|
||||
private:
|
||||
// Return true if any level shift is active.
|
||||
bool IsShift();
|
||||
|
||||
// Returns true if the last key event was about a key that may have a code
|
||||
// point associated.
|
||||
bool IsKeys();
|
||||
|
||||
// Set to false until any event is received.
|
||||
bool any_events_received_ : 1;
|
||||
|
||||
// The flags below show the state of the keyboard modifiers after the last
|
||||
// event has been processed. Stateful keys remain in the same state after
|
||||
// a release and require an additional press to toggle.
|
||||
bool stateful_caps_lock_ : 1;
|
||||
bool left_shift_ : 1;
|
||||
bool right_shift_ : 1;
|
||||
bool left_alt_ : 1;
|
||||
bool right_alt_ : 1;
|
||||
bool left_ctrl_ : 1;
|
||||
bool right_ctrl_ : 1;
|
||||
|
||||
// The last received key event. If any_events_received_ is not set, this is
|
||||
// not valid.
|
||||
fuchsia::ui::input3::KeyEvent last_event_;
|
||||
};
|
||||
|
||||
} // namespace flutter_runner
|
||||
|
||||
#endif // FLUTTER_SHELL_PLATFORM_FUCHSIA_KEYBOARD_H_
|
||||
@ -0,0 +1,172 @@
|
||||
// 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.
|
||||
|
||||
// Testing the stateful Fuchsia Input3 keyboard interactions. This test case
|
||||
// is not intended to be exhaustive: it is intended to capture the tests that
|
||||
// demonstrate how we think Input3 interaction should work, and possibly
|
||||
// regression tests if we catch some behavior that needs to be guarded long
|
||||
// term. Pragmatically, this should be enough to ensure no specific bug
|
||||
// happens twice.
|
||||
|
||||
#include "flutter/shell/platform/fuchsia/flutter/keyboard.h"
|
||||
|
||||
#include <fuchsia/input/cpp/fidl.h>
|
||||
#include <fuchsia/ui/input/cpp/fidl.h>
|
||||
#include <fuchsia/ui/input3/cpp/fidl.h>
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
#include <zircon/time.h>
|
||||
#include <vector>
|
||||
|
||||
namespace flutter_runner {
|
||||
namespace {
|
||||
|
||||
using fuchsia::input::Key;
|
||||
using fuchsia::ui::input::kModifierCapsLock;
|
||||
using fuchsia::ui::input::kModifierLeftAlt;
|
||||
using fuchsia::ui::input::kModifierLeftControl;
|
||||
using fuchsia::ui::input::kModifierLeftShift;
|
||||
using fuchsia::ui::input::kModifierNone;
|
||||
using fuchsia::ui::input::kModifierRightAlt;
|
||||
using fuchsia::ui::input::kModifierRightControl;
|
||||
using fuchsia::ui::input::kModifierRightShift;
|
||||
using fuchsia::ui::input3::KeyEvent;
|
||||
using fuchsia::ui::input3::KeyEventType;
|
||||
|
||||
class KeyboardTest : public testing::Test {
|
||||
protected:
|
||||
static void SetUpTestCase() { testing::Test::SetUpTestCase(); }
|
||||
|
||||
// Creates a new key event for testing.
|
||||
KeyEvent NewKeyEvent(KeyEventType event_type, Key key) {
|
||||
KeyEvent event;
|
||||
// Assume events are delivered with correct timing.
|
||||
event.set_timestamp(++timestamp_);
|
||||
event.set_type(event_type);
|
||||
event.set_key(key);
|
||||
return event;
|
||||
}
|
||||
|
||||
// Makes the keyboard consume all the provided `events`. The end state of
|
||||
// the keyboard is as if all of the specified events happened between the
|
||||
// start state of the keyboard and its end state.
|
||||
void ConsumeEvents(Keyboard* keyboard, const std::vector<KeyEvent>& events) {
|
||||
for (const auto& event : events) {
|
||||
KeyEvent e;
|
||||
event.Clone(&e);
|
||||
keyboard->ConsumeEvent(std::move(e));
|
||||
}
|
||||
}
|
||||
|
||||
// Converts a pressed key to usage value.
|
||||
uint32_t ToUsage(Key key) { return static_cast<uint64_t>(key) & 0xFFFF; }
|
||||
|
||||
private:
|
||||
zx_time_t timestamp_ = 0;
|
||||
};
|
||||
|
||||
// This test checks that if a caps lock has been pressed when we didn't have
|
||||
// focus, the effect of caps lock remains. Only this first test case is
|
||||
// commented to explain how the test case works.
|
||||
TEST_F(KeyboardTest, CapsLockSync) {
|
||||
// Place the key events since the beginning of time into `keys`.
|
||||
std::vector<KeyEvent> keys;
|
||||
keys.emplace_back(NewKeyEvent(KeyEventType::SYNC, Key::CAPS_LOCK));
|
||||
|
||||
// Replay them on the keyboard.
|
||||
Keyboard keyboard;
|
||||
ConsumeEvents(&keyboard, keys);
|
||||
|
||||
// Verify the state of the keyboard's public API:
|
||||
// - check that the key sync had no code point (it was a caps lock press).
|
||||
// - check that the registered usage was that of caps lock.
|
||||
// - check that the net effect is that the caps lock modifier is locked
|
||||
// active.
|
||||
EXPECT_EQ(0u, keyboard.LastCodePoint());
|
||||
EXPECT_EQ(ToUsage(Key::CAPS_LOCK), keyboard.LastHIDUsage());
|
||||
EXPECT_EQ(kModifierCapsLock, keyboard.Modifiers());
|
||||
}
|
||||
|
||||
TEST_F(KeyboardTest, CapsLockPress) {
|
||||
std::vector<KeyEvent> keys;
|
||||
keys.emplace_back(NewKeyEvent(KeyEventType::PRESSED, Key::CAPS_LOCK));
|
||||
|
||||
Keyboard keyboard;
|
||||
ConsumeEvents(&keyboard, keys);
|
||||
|
||||
EXPECT_EQ(0u, keyboard.LastCodePoint());
|
||||
EXPECT_EQ(ToUsage(Key::CAPS_LOCK), keyboard.LastHIDUsage());
|
||||
EXPECT_EQ(kModifierCapsLock, keyboard.Modifiers());
|
||||
}
|
||||
|
||||
TEST_F(KeyboardTest, CapsLockPressRelease) {
|
||||
std::vector<KeyEvent> keys;
|
||||
keys.emplace_back(NewKeyEvent(KeyEventType::PRESSED, Key::CAPS_LOCK));
|
||||
keys.emplace_back(NewKeyEvent(KeyEventType::RELEASED, Key::CAPS_LOCK));
|
||||
|
||||
Keyboard keyboard;
|
||||
ConsumeEvents(&keyboard, keys);
|
||||
|
||||
EXPECT_EQ(0u, keyboard.LastCodePoint());
|
||||
EXPECT_EQ(ToUsage(Key::CAPS_LOCK), keyboard.LastHIDUsage());
|
||||
EXPECT_EQ(kModifierCapsLock, keyboard.Modifiers());
|
||||
}
|
||||
|
||||
TEST_F(KeyboardTest, ShiftA) {
|
||||
std::vector<KeyEvent> keys;
|
||||
keys.emplace_back(NewKeyEvent(KeyEventType::PRESSED, Key::LEFT_SHIFT));
|
||||
keys.emplace_back(NewKeyEvent(KeyEventType::PRESSED, Key::A));
|
||||
|
||||
Keyboard keyboard;
|
||||
ConsumeEvents(&keyboard, keys);
|
||||
|
||||
EXPECT_EQ(static_cast<uint32_t>('A'), keyboard.LastCodePoint());
|
||||
EXPECT_EQ(ToUsage(Key::A), keyboard.LastHIDUsage());
|
||||
EXPECT_EQ(kModifierLeftShift, keyboard.Modifiers());
|
||||
}
|
||||
|
||||
TEST_F(KeyboardTest, ShiftAWithRelease) {
|
||||
std::vector<KeyEvent> keys;
|
||||
keys.emplace_back(NewKeyEvent(KeyEventType::PRESSED, Key::LEFT_SHIFT));
|
||||
keys.emplace_back(NewKeyEvent(KeyEventType::PRESSED, Key::A));
|
||||
keys.emplace_back(NewKeyEvent(KeyEventType::RELEASED, Key::A));
|
||||
|
||||
Keyboard keyboard;
|
||||
ConsumeEvents(&keyboard, keys);
|
||||
|
||||
EXPECT_EQ(static_cast<uint32_t>('A'), keyboard.LastCodePoint());
|
||||
EXPECT_EQ(ToUsage(Key::A), keyboard.LastHIDUsage());
|
||||
EXPECT_EQ(kModifierLeftShift, keyboard.Modifiers());
|
||||
}
|
||||
|
||||
TEST_F(KeyboardTest, ShiftAWithReleaseShift) {
|
||||
std::vector<KeyEvent> keys;
|
||||
keys.emplace_back(NewKeyEvent(KeyEventType::PRESSED, Key::LEFT_SHIFT));
|
||||
keys.emplace_back(NewKeyEvent(KeyEventType::PRESSED, Key::A));
|
||||
keys.emplace_back(NewKeyEvent(KeyEventType::RELEASED, Key::LEFT_SHIFT));
|
||||
keys.emplace_back(NewKeyEvent(KeyEventType::RELEASED, Key::A));
|
||||
|
||||
Keyboard keyboard;
|
||||
ConsumeEvents(&keyboard, keys);
|
||||
|
||||
EXPECT_EQ(static_cast<uint32_t>('a'), keyboard.LastCodePoint());
|
||||
EXPECT_EQ(ToUsage(Key::A), keyboard.LastHIDUsage());
|
||||
EXPECT_EQ(kModifierNone, keyboard.Modifiers());
|
||||
}
|
||||
|
||||
TEST_F(KeyboardTest, LowcaseA) {
|
||||
std::vector<KeyEvent> keys;
|
||||
keys.emplace_back(NewKeyEvent(KeyEventType::PRESSED, Key::A));
|
||||
keys.emplace_back(NewKeyEvent(KeyEventType::RELEASED, Key::A));
|
||||
|
||||
Keyboard keyboard;
|
||||
ConsumeEvents(&keyboard, keys);
|
||||
|
||||
EXPECT_EQ(static_cast<uint32_t>('a'), keyboard.LastCodePoint());
|
||||
EXPECT_EQ(ToUsage(Key::A), keyboard.LastHIDUsage());
|
||||
EXPECT_EQ(kModifierNone, keyboard.Modifiers());
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace flutter_runner
|
||||
@ -40,13 +40,13 @@ template <class T>
|
||||
void SetInterfaceErrorHandler(fidl::InterfacePtr<T>& interface,
|
||||
std::string name) {
|
||||
interface.set_error_handler([name](zx_status_t status) {
|
||||
FML_LOG(ERROR) << "Interface error on: " << name;
|
||||
FML_LOG(ERROR) << "Interface error on: " << name << "status: " << status;
|
||||
});
|
||||
}
|
||||
template <class T>
|
||||
void SetInterfaceErrorHandler(fidl::Binding<T>& binding, std::string name) {
|
||||
binding.set_error_handler([name](zx_status_t status) {
|
||||
FML_LOG(ERROR) << "Interface error on: " << name;
|
||||
FML_LOG(ERROR) << "Interface error on: " << name << ", status: " << status;
|
||||
});
|
||||
}
|
||||
|
||||
@ -61,6 +61,8 @@ PlatformView::PlatformView(
|
||||
fidl::InterfaceRequest<fuchsia::ui::scenic::SessionListener>
|
||||
session_listener_request,
|
||||
fidl::InterfaceHandle<fuchsia::ui::views::Focuser> focuser,
|
||||
fidl::InterfaceRequest<fuchsia::ui::input3::KeyboardListener>
|
||||
keyboard_listener_request,
|
||||
fit::closure session_listener_error_callback,
|
||||
OnEnableWireframe wireframe_enabled_callback,
|
||||
OnCreateView on_create_view_callback,
|
||||
@ -85,13 +87,16 @@ PlatformView::PlatformView(
|
||||
external_view_embedder_(external_view_embedder),
|
||||
ime_client_(this),
|
||||
vsync_offset_(std::move(vsync_offset)),
|
||||
vsync_event_handle_(vsync_event_handle) {
|
||||
vsync_event_handle_(vsync_event_handle),
|
||||
keyboard_listener_binding_(this, std::move(keyboard_listener_request)) {
|
||||
// Register all error handlers.
|
||||
SetInterfaceErrorHandler(session_listener_binding_, "SessionListener");
|
||||
SetInterfaceErrorHandler(ime_, "Input Method Editor");
|
||||
SetInterfaceErrorHandler(text_sync_service_, "Text Sync Service");
|
||||
SetInterfaceErrorHandler(parent_environment_service_provider_,
|
||||
"Parent Environment Service Provider");
|
||||
SetInterfaceErrorHandler(keyboard_listener_binding_,
|
||||
"KeyboardListener Service");
|
||||
// Access the IME service.
|
||||
parent_environment_service_provider_ =
|
||||
parent_environment_service_provider_handle.Bind();
|
||||
@ -174,12 +179,6 @@ void PlatformView::DidUpdateState(
|
||||
);
|
||||
last_text_state_ =
|
||||
std::make_unique<fuchsia::ui::input::TextInputState>(state);
|
||||
|
||||
// Handle keyboard input events for HID keys only.
|
||||
// TODO(SCN-1189): Are we done here?
|
||||
if (input_event && input_event->keyboard().hid_usage != 0) {
|
||||
OnHandleKeyboardEvent(input_event->keyboard());
|
||||
}
|
||||
}
|
||||
|
||||
// |fuchsia::ui::input::InputMethodEditorClient|
|
||||
@ -308,7 +307,9 @@ void PlatformView::OnScenicEvent(
|
||||
break;
|
||||
}
|
||||
case fuchsia::ui::input::InputEvent::Tag::kKeyboard: {
|
||||
OnHandleKeyboardEvent(event.input().keyboard());
|
||||
// All devices should receive key events via input3.KeyboardListener
|
||||
// instead.
|
||||
FML_LOG(WARNING) << "Keyboard event from Scenic: ignored";
|
||||
break;
|
||||
}
|
||||
case fuchsia::ui::input::InputEvent::Tag::Invalid: {
|
||||
@ -501,31 +502,40 @@ bool PlatformView::OnHandlePointerEvent(
|
||||
return true;
|
||||
}
|
||||
|
||||
bool PlatformView::OnHandleKeyboardEvent(
|
||||
const fuchsia::ui::input::KeyboardEvent& keyboard) {
|
||||
// |fuchsia::ui:input3::KeyboardListener|
|
||||
void PlatformView::OnKeyEvent(
|
||||
fuchsia::ui::input3::KeyEvent key_event,
|
||||
fuchsia::ui::input3::KeyboardListener::OnKeyEventCallback callback) {
|
||||
const char* type = nullptr;
|
||||
if (keyboard.phase == fuchsia::ui::input::KeyboardEventPhase::PRESSED) {
|
||||
type = "keydown";
|
||||
} else if (keyboard.phase == fuchsia::ui::input::KeyboardEventPhase::REPEAT) {
|
||||
type = "keydown"; // TODO change this to keyrepeat
|
||||
} else if (keyboard.phase ==
|
||||
fuchsia::ui::input::KeyboardEventPhase::RELEASED) {
|
||||
type = "keyup";
|
||||
switch (key_event.type()) {
|
||||
case fuchsia::ui::input3::KeyEventType::PRESSED:
|
||||
type = "keydown";
|
||||
break;
|
||||
case fuchsia::ui::input3::KeyEventType::RELEASED:
|
||||
type = "keyup";
|
||||
break;
|
||||
case fuchsia::ui::input3::KeyEventType::SYNC:
|
||||
// What, if anything, should happen here?
|
||||
case fuchsia::ui::input3::KeyEventType::CANCEL:
|
||||
// What, if anything, should happen here?
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (type == nullptr) {
|
||||
FML_DLOG(ERROR) << "Unknown key event phase.";
|
||||
return false;
|
||||
callback(fuchsia::ui::input3::KeyEventStatus::NOT_HANDLED);
|
||||
return;
|
||||
}
|
||||
keyboard_.ConsumeEvent(std::move(key_event));
|
||||
|
||||
rapidjson::Document document;
|
||||
auto& allocator = document.GetAllocator();
|
||||
document.SetObject();
|
||||
document.AddMember("type", rapidjson::Value(type, strlen(type)), allocator);
|
||||
document.AddMember("keymap", rapidjson::Value("fuchsia"), allocator);
|
||||
document.AddMember("hidUsage", keyboard.hid_usage, allocator);
|
||||
document.AddMember("codePoint", keyboard.code_point, allocator);
|
||||
document.AddMember("modifiers", keyboard.modifiers, allocator);
|
||||
document.AddMember("hidUsage", keyboard_.LastHIDUsage(), allocator);
|
||||
document.AddMember("codePoint", keyboard_.LastCodePoint(), allocator);
|
||||
document.AddMember("modifiers", keyboard_.Modifiers(), allocator);
|
||||
rapidjson::StringBuffer buffer;
|
||||
rapidjson::Writer<rapidjson::StringBuffer> writer(buffer);
|
||||
document.Accept(writer);
|
||||
@ -536,8 +546,7 @@ bool PlatformView::OnHandleKeyboardEvent(
|
||||
std::vector<uint8_t>(data, data + buffer.GetSize()), // data
|
||||
nullptr) // response
|
||||
);
|
||||
|
||||
return true;
|
||||
callback(fuchsia::ui::input3::KeyEventStatus::HANDLED);
|
||||
}
|
||||
|
||||
bool PlatformView::OnHandleFocusEvent(
|
||||
|
||||
@ -6,6 +6,7 @@
|
||||
#define FLUTTER_SHELL_PLATFORM_FUCHSIA_PLATFORM_VIEW_H_
|
||||
|
||||
#include <fuchsia/ui/input/cpp/fidl.h>
|
||||
#include <fuchsia/ui/input3/cpp/fidl.h>
|
||||
#include <fuchsia/ui/scenic/cpp/fidl.h>
|
||||
#include <lib/fidl/cpp/binding.h>
|
||||
#include <lib/fit/function.h>
|
||||
@ -19,6 +20,7 @@
|
||||
#include "flutter/fml/time/time_delta.h"
|
||||
#include "flutter/shell/common/platform_view.h"
|
||||
#include "flutter/shell/platform/fuchsia/flutter/fuchsia_external_view_embedder.h"
|
||||
#include "flutter/shell/platform/fuchsia/flutter/keyboard.h"
|
||||
|
||||
#include "accessibility_bridge.h"
|
||||
|
||||
@ -45,6 +47,7 @@ using OnCreateSurface = fit::function<std::unique_ptr<flutter::Surface>()>;
|
||||
class PlatformView final : public flutter::PlatformView,
|
||||
public AccessibilityBridge::Delegate,
|
||||
private fuchsia::ui::scenic::SessionListener,
|
||||
private fuchsia::ui::input3::KeyboardListener,
|
||||
private fuchsia::ui::input::InputMethodEditorClient {
|
||||
public:
|
||||
PlatformView(flutter::PlatformView::Delegate& delegate,
|
||||
@ -57,6 +60,8 @@ class PlatformView final : public flutter::PlatformView,
|
||||
fidl::InterfaceRequest<fuchsia::ui::scenic::SessionListener>
|
||||
session_listener_request,
|
||||
fidl::InterfaceHandle<fuchsia::ui::views::Focuser> focuser,
|
||||
fidl::InterfaceRequest<fuchsia::ui::input3::KeyboardListener>
|
||||
keyboard_listener,
|
||||
fit::closure on_session_listener_error_callback,
|
||||
OnEnableWireframe wireframe_enabled_callback,
|
||||
OnCreateView on_create_view_callback,
|
||||
@ -87,6 +92,12 @@ class PlatformView final : public flutter::PlatformView,
|
||||
private:
|
||||
void RegisterPlatformMessageHandlers();
|
||||
|
||||
// |fuchsia.ui.input3.KeyboardListener|
|
||||
// Called by the embedder every time there is a key event to process.
|
||||
void OnKeyEvent(fuchsia::ui::input3::KeyEvent key_event,
|
||||
fuchsia::ui::input3::KeyboardListener::OnKeyEventCallback
|
||||
callback) override;
|
||||
|
||||
// |fuchsia::ui::input::InputMethodEditorClient|
|
||||
void DidUpdateState(
|
||||
fuchsia::ui::input::TextInputState state,
|
||||
@ -105,8 +116,6 @@ class PlatformView final : public flutter::PlatformView,
|
||||
|
||||
bool OnHandlePointerEvent(const fuchsia::ui::input::PointerEvent& pointer);
|
||||
|
||||
bool OnHandleKeyboardEvent(const fuchsia::ui::input::KeyboardEvent& keyboard);
|
||||
|
||||
bool OnHandleFocusEvent(const fuchsia::ui::input::FocusEvent& focus);
|
||||
|
||||
// Gets a new input method editor from the input connection. Run when both
|
||||
@ -204,6 +213,13 @@ class PlatformView final : public flutter::PlatformView,
|
||||
fml::TimeDelta vsync_offset_;
|
||||
zx_handle_t vsync_event_handle_ = 0;
|
||||
|
||||
// The registered binding for serving the keyboard listener server endpoint.
|
||||
fidl::Binding<fuchsia::ui::input3::KeyboardListener>
|
||||
keyboard_listener_binding_;
|
||||
|
||||
// The keyboard translation for fuchsia.ui.input3.KeyEvent.
|
||||
Keyboard keyboard_;
|
||||
|
||||
FML_DISALLOW_COPY_AND_ASSIGN(PlatformView);
|
||||
};
|
||||
|
||||
|
||||
@ -162,6 +162,129 @@ class MockResponse : public flutter::PlatformMessageResponse {
|
||||
MOCK_METHOD0(CompleteEmpty, void());
|
||||
};
|
||||
|
||||
// Used to construct partial instances of PlatformView for testing. The
|
||||
// PlatformView constructor has many parameters, not all of which need to
|
||||
// be filled out for each test. The builder allows you to initialize only
|
||||
// those that matter to your specific test. Not all builder methods are
|
||||
// provided: if you find some that are missing, feel free to add them.
|
||||
class PlatformViewBuilder {
|
||||
public:
|
||||
PlatformViewBuilder(flutter::PlatformView::Delegate& delegate,
|
||||
flutter::TaskRunners task_runners,
|
||||
std::shared_ptr<sys::ServiceDirectory> runner_services)
|
||||
: delegate_(delegate),
|
||||
debug_label_("test_platform_view"),
|
||||
view_ref_(fuchsia::ui::views::ViewRef()),
|
||||
task_runners_(task_runners),
|
||||
runner_services_(runner_services) {}
|
||||
|
||||
// Add builder methods as required.
|
||||
|
||||
PlatformViewBuilder& SetServiceProvider(
|
||||
fidl::InterfaceHandle<fuchsia::sys::ServiceProvider> service_provider) {
|
||||
parent_environment_service_provider_ = std::move(service_provider);
|
||||
return *this;
|
||||
}
|
||||
|
||||
PlatformViewBuilder& SetFocuser(
|
||||
fidl::InterfaceHandle<fuchsia::ui::views::Focuser> focuser) {
|
||||
focuser_ = std::move(focuser);
|
||||
return *this;
|
||||
}
|
||||
|
||||
PlatformViewBuilder& SetDestroyViewCallback(OnDestroyView callback) {
|
||||
on_destroy_view_callback_ = std::move(callback);
|
||||
return *this;
|
||||
}
|
||||
|
||||
PlatformViewBuilder& SetUpdateViewCallback(OnUpdateView callback) {
|
||||
on_update_view_callback_ = std::move(callback);
|
||||
return *this;
|
||||
}
|
||||
|
||||
PlatformViewBuilder& SetEnableWireframeCallback(OnEnableWireframe callback) {
|
||||
wireframe_enabled_callback_ = std::move(callback);
|
||||
return *this;
|
||||
}
|
||||
|
||||
PlatformViewBuilder& SetCreateViewCallback(OnCreateView callback) {
|
||||
on_create_view_callback_ = std::move(callback);
|
||||
return *this;
|
||||
}
|
||||
|
||||
PlatformViewBuilder& SetSessionListenerRequest(
|
||||
fidl::InterfaceRequest<fuchsia::ui::scenic::SessionListener> request) {
|
||||
session_listener_request_ = std::move(request);
|
||||
return *this;
|
||||
}
|
||||
|
||||
PlatformViewBuilder& SetCreateSurfaceCallback(OnCreateSurface callback) {
|
||||
on_create_surface_callback_ = std::move(callback);
|
||||
return *this;
|
||||
}
|
||||
|
||||
PlatformViewBuilder& SetViewEmbedder(
|
||||
std::shared_ptr<flutter::ExternalViewEmbedder> embedder) {
|
||||
view_embedder_ = embedder;
|
||||
return *this;
|
||||
}
|
||||
|
||||
PlatformViewBuilder& SetKeyboardListener(
|
||||
fidl::InterfaceRequest<fuchsia::ui::input3::KeyboardListener> listener) {
|
||||
keyboard_listener_ = std::move(listener);
|
||||
return *this;
|
||||
}
|
||||
|
||||
// Once Build is called, the instance is no longer usable.
|
||||
PlatformView Build() {
|
||||
EXPECT_EQ(false, built_)
|
||||
<< "Build() was already called, this buider is good for one use only.";
|
||||
built_ = true;
|
||||
return PlatformView(delegate_, debug_label_, std::move(view_ref_),
|
||||
task_runners_, runner_services_,
|
||||
std::move(parent_environment_service_provider_),
|
||||
std::move(session_listener_request_),
|
||||
std::move(focuser_), std::move(keyboard_listener_),
|
||||
std::move(on_session_listener_error_callback_),
|
||||
std::move(wireframe_enabled_callback_),
|
||||
std::move(on_create_view_callback_),
|
||||
std::move(on_update_view_callback_),
|
||||
std::move(on_destroy_view_callback_),
|
||||
std::move(on_create_surface_callback_), view_embedder_,
|
||||
std::move(vsync_offset_), vsync_event_handle_);
|
||||
}
|
||||
|
||||
private:
|
||||
PlatformViewBuilder() = delete;
|
||||
|
||||
bool built_{false};
|
||||
|
||||
// Required elements. Make sure to initialize them.
|
||||
flutter::PlatformView::Delegate& delegate_;
|
||||
std::string debug_label_;
|
||||
fuchsia::ui::views::ViewRef view_ref_;
|
||||
flutter::TaskRunners task_runners_;
|
||||
std::shared_ptr<sys::ServiceDirectory> runner_services_{nullptr};
|
||||
fidl::InterfaceHandle<fuchsia::sys::ServiceProvider>
|
||||
parent_environment_service_provider_{nullptr};
|
||||
|
||||
// Optional elements.
|
||||
fidl::InterfaceRequest<fuchsia::ui::scenic::SessionListener>
|
||||
session_listener_request_{nullptr};
|
||||
fidl::InterfaceHandle<fuchsia::ui::views::Focuser> focuser_{nullptr};
|
||||
fidl::InterfaceRequest<fuchsia::ui::input3::KeyboardListener>
|
||||
keyboard_listener_{nullptr};
|
||||
fit::closure on_session_listener_error_callback_{nullptr};
|
||||
OnEnableWireframe wireframe_enabled_callback_{nullptr};
|
||||
OnCreateView on_create_view_callback_{nullptr};
|
||||
OnUpdateView on_update_view_callback_{nullptr};
|
||||
OnDestroyView on_destroy_view_callback_{nullptr};
|
||||
OnCreateSurface on_create_surface_callback_{nullptr};
|
||||
std::shared_ptr<flutter::ExternalViewEmbedder> view_embedder_{nullptr};
|
||||
fml::TimeDelta vsync_offset_{fml::TimeDelta::Zero()};
|
||||
zx_handle_t vsync_event_handle_{ZX_HANDLE_INVALID};
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
class PlatformViewTests : public ::testing::Test {
|
||||
@ -175,9 +298,25 @@ class PlatformViewTests : public ::testing::Test {
|
||||
loop_.ResetQuit();
|
||||
}
|
||||
|
||||
fuchsia::ui::input3::KeyEvent MakeEvent(
|
||||
fuchsia::ui::input3::KeyEventType event_type,
|
||||
std::optional<fuchsia::ui::input3::Modifiers> modifiers,
|
||||
fuchsia::input::Key key) {
|
||||
fuchsia::ui::input3::KeyEvent event;
|
||||
event.set_timestamp(++event_timestamp_);
|
||||
event.set_type(event_type);
|
||||
if (modifiers.has_value()) {
|
||||
event.set_modifiers(modifiers.value());
|
||||
}
|
||||
event.set_key(key);
|
||||
return event;
|
||||
}
|
||||
|
||||
private:
|
||||
async::Loop loop_;
|
||||
|
||||
uint64_t event_timestamp_{42};
|
||||
|
||||
FML_DISALLOW_COPY_AND_ASSIGN(PlatformViewTests);
|
||||
};
|
||||
|
||||
@ -206,25 +345,12 @@ TEST_F(PlatformViewTests, CreateSurfaceTest) {
|
||||
"PlatformViewTest", view_embedder, gr_context.get());
|
||||
};
|
||||
|
||||
auto platform_view = flutter_runner::PlatformView(
|
||||
delegate, // delegate
|
||||
"test_platform_view", // label
|
||||
fuchsia::ui::views::ViewRef(), // view_ref
|
||||
std::move(task_runners), // task_runners
|
||||
services_provider.service_directory(), // runner_services
|
||||
nullptr, // parent_environment_service_provider_handle
|
||||
nullptr, // session_listener_request
|
||||
nullptr, // focuser,
|
||||
nullptr, // on_session_listener_error_callback
|
||||
nullptr, // on_enable_wireframe_callback,
|
||||
nullptr, // on_create_view_callback,
|
||||
nullptr, // on_update_view_callback,
|
||||
nullptr, // on_destroy_view_callback,
|
||||
CreateSurfaceCallback, // on_create_surface_callback,
|
||||
view_embedder, // external_view_embedder,
|
||||
fml::TimeDelta::Zero(), // vsync_offset
|
||||
ZX_HANDLE_INVALID // vsync_event_handle
|
||||
);
|
||||
flutter_runner::PlatformView platform_view =
|
||||
PlatformViewBuilder(delegate, std::move(task_runners),
|
||||
services_provider.service_directory())
|
||||
.SetCreateSurfaceCallback(CreateSurfaceCallback)
|
||||
.SetViewEmbedder(view_embedder)
|
||||
.Build();
|
||||
platform_view.NotifyCreated();
|
||||
|
||||
RunLoopUntilIdle();
|
||||
@ -251,25 +377,11 @@ TEST_F(PlatformViewTests, SetViewportMetrics) {
|
||||
sys::testing::ServiceDirectoryProvider services_provider(dispatcher());
|
||||
flutter::TaskRunners task_runners("test_runners", nullptr, nullptr, nullptr,
|
||||
nullptr);
|
||||
flutter_runner::PlatformView platform_view(
|
||||
delegate, // delegate
|
||||
"test_platform_view", // label
|
||||
fuchsia::ui::views::ViewRef(), // view_ref
|
||||
std::move(task_runners), // task_runners
|
||||
services_provider.service_directory(), // runner_services
|
||||
nullptr, // parent_environment_service_provider_handle
|
||||
session_listener.NewRequest(), // session_listener_request
|
||||
nullptr, // focuser,
|
||||
nullptr, // on_session_listener_error_callback
|
||||
nullptr, // on_enable_wireframe_callback,
|
||||
nullptr, // on_create_view_callback,
|
||||
nullptr, // on_update_view_callback,
|
||||
nullptr, // on_destroy_view_callback,
|
||||
nullptr, // on_create_surface_callback,
|
||||
nullptr, // external_view_embedder,
|
||||
fml::TimeDelta::Zero(), // vsync_offset
|
||||
ZX_HANDLE_INVALID // vsync_event_handle
|
||||
);
|
||||
flutter_runner::PlatformView platform_view =
|
||||
PlatformViewBuilder(delegate, std::move(task_runners),
|
||||
services_provider.service_directory())
|
||||
.SetSessionListenerRequest(session_listener.NewRequest())
|
||||
.Build();
|
||||
RunLoopUntilIdle();
|
||||
EXPECT_EQ(delegate.metrics(), flutter::ViewportMetrics());
|
||||
|
||||
@ -384,25 +496,13 @@ TEST_F(PlatformViewTests, ChangesAccessibilitySettings) {
|
||||
EXPECT_FALSE(delegate.semantics_enabled());
|
||||
EXPECT_EQ(delegate.semantics_features(), 0);
|
||||
|
||||
auto platform_view = flutter_runner::PlatformView(
|
||||
delegate, // delegate
|
||||
"test_platform_view", // label
|
||||
fuchsia::ui::views::ViewRef(), // view_ref
|
||||
std::move(task_runners), // task_runners
|
||||
services_provider.service_directory(), // runner_services
|
||||
nullptr, // parent_environment_service_provider_handle
|
||||
nullptr, // session_listener_request
|
||||
nullptr, // focuser,
|
||||
nullptr, // on_session_listener_error_callback
|
||||
nullptr, // on_enable_wireframe_callback,
|
||||
nullptr, // on_create_view_callback,
|
||||
nullptr, // on_update_view_callback,
|
||||
nullptr, // on_destroy_view_callback,
|
||||
nullptr, // on_create_surface_callback,
|
||||
nullptr, // external_view_embedder,
|
||||
fml::TimeDelta::Zero(), // vsync_offset
|
||||
ZX_HANDLE_INVALID // vsync_event_handle
|
||||
);
|
||||
flutter_runner::PlatformView platform_view =
|
||||
PlatformViewBuilder(
|
||||
delegate, // delegate
|
||||
std::move(task_runners), // task_runners
|
||||
services_provider.service_directory() // runner_services
|
||||
)
|
||||
.Build();
|
||||
|
||||
RunLoopUntilIdle();
|
||||
|
||||
@ -435,25 +535,11 @@ TEST_F(PlatformViewTests, EnableWireframeTest) {
|
||||
wireframe_enabled = should_enable;
|
||||
};
|
||||
|
||||
auto platform_view = flutter_runner::PlatformView(
|
||||
delegate, // delegate
|
||||
"test_platform_view", // label
|
||||
fuchsia::ui::views::ViewRef(), // view_ref
|
||||
std::move(task_runners), // task_runners
|
||||
services_provider.service_directory(), // runner_services
|
||||
nullptr, // parent_environment_service_provider_handle
|
||||
nullptr, // session_listener_request
|
||||
nullptr, // focuser,
|
||||
nullptr, // on_session_listener_error_callback
|
||||
EnableWireframeCallback, // on_enable_wireframe_callback,
|
||||
nullptr, // on_create_view_callback,
|
||||
nullptr, // on_update_view_callback,
|
||||
nullptr, // on_destroy_view_callback,
|
||||
nullptr, // on_create_surface_callback,
|
||||
nullptr, // external_view_embedder,
|
||||
fml::TimeDelta::Zero(), // vsync_offset
|
||||
ZX_HANDLE_INVALID // vsync_event_handle
|
||||
);
|
||||
flutter_runner::PlatformView platform_view =
|
||||
PlatformViewBuilder(delegate, std::move(task_runners),
|
||||
services_provider.service_directory())
|
||||
.SetEnableWireframeCallback(EnableWireframeCallback)
|
||||
.Build();
|
||||
|
||||
// Cast platform_view to its base view so we can have access to the public
|
||||
// "HandlePlatformMessage" function.
|
||||
@ -497,25 +583,11 @@ TEST_F(PlatformViewTests, CreateViewTest) {
|
||||
int64_t view_id, bool hit_testable,
|
||||
bool focusable) { create_view_called = true; };
|
||||
|
||||
auto platform_view = flutter_runner::PlatformView(
|
||||
delegate, // delegate
|
||||
"test_platform_view", // label
|
||||
fuchsia::ui::views::ViewRef(), // view_ref
|
||||
std::move(task_runners), // task_runners
|
||||
services_provider.service_directory(), // runner_services
|
||||
nullptr, // parent_environment_service_provider_handle
|
||||
nullptr, // session_listener_request
|
||||
nullptr, // focuser,
|
||||
nullptr, // on_session_listener_error_callback
|
||||
nullptr, // on_enable_wireframe_callback,
|
||||
CreateViewCallback, // on_create_view_callback,
|
||||
nullptr, // on_update_view_callback,
|
||||
nullptr, // on_destroy_view_callback,
|
||||
nullptr, // on_create_surface_callback,
|
||||
nullptr, // external_view_embedder,
|
||||
fml::TimeDelta::Zero(), // vsync_offset
|
||||
ZX_HANDLE_INVALID // vsync_event_handle
|
||||
);
|
||||
flutter_runner::PlatformView platform_view =
|
||||
PlatformViewBuilder(delegate, std::move(task_runners),
|
||||
services_provider.service_directory())
|
||||
.SetCreateViewCallback(CreateViewCallback)
|
||||
.Build();
|
||||
|
||||
// Cast platform_view to its base view so we can have access to the public
|
||||
// "HandlePlatformMessage" function.
|
||||
@ -561,25 +633,11 @@ TEST_F(PlatformViewTests, UpdateViewTest) {
|
||||
int64_t view_id, bool hit_testable,
|
||||
bool focusable) { update_view_called = true; };
|
||||
|
||||
auto platform_view = flutter_runner::PlatformView(
|
||||
delegate, // delegate
|
||||
"test_platform_view", // label
|
||||
fuchsia::ui::views::ViewRef(), // view_ref
|
||||
std::move(task_runners), // task_runners
|
||||
services_provider.service_directory(), // runner_services
|
||||
nullptr, // parent_environment_service_provider_handle
|
||||
nullptr, // session_listener_request
|
||||
nullptr, // focuser,
|
||||
nullptr, // on_session_listener_error_callback
|
||||
nullptr, // on_enable_wireframe_callback,
|
||||
nullptr, // on_create_view_callback,
|
||||
UpdateViewCallback, // on_update_view_callback,
|
||||
nullptr, // on_destroy_view_callback,
|
||||
nullptr, // on_create_surface_callback,
|
||||
nullptr, // external_view_embedder,
|
||||
fml::TimeDelta::Zero(), // vsync_offset
|
||||
ZX_HANDLE_INVALID // vsync_event_handle
|
||||
);
|
||||
flutter_runner::PlatformView platform_view =
|
||||
PlatformViewBuilder(delegate, std::move(task_runners),
|
||||
services_provider.service_directory())
|
||||
.SetUpdateViewCallback(UpdateViewCallback)
|
||||
.Build();
|
||||
|
||||
// Cast platform_view to its base view so we can have access to the public
|
||||
// "HandlePlatformMessage" function.
|
||||
@ -625,25 +683,11 @@ TEST_F(PlatformViewTests, DestroyViewTest) {
|
||||
destroy_view_called = true;
|
||||
};
|
||||
|
||||
auto platform_view = flutter_runner::PlatformView(
|
||||
delegate, // delegate
|
||||
"test_platform_view", // label
|
||||
fuchsia::ui::views::ViewRef(), // view_ref
|
||||
std::move(task_runners), // task_runners
|
||||
services_provider.service_directory(), // runner_services
|
||||
nullptr, // parent_environment_service_provider_handle
|
||||
nullptr, // session_listener_request
|
||||
nullptr, // focuser,
|
||||
nullptr, // on_session_listener_error_callback
|
||||
nullptr, // on_enable_wireframe_callback,
|
||||
nullptr, // on_create_view_callback,
|
||||
nullptr, // on_update_view_callback,
|
||||
DestroyViewCallback, // on_destroy_view_callback,
|
||||
nullptr, // on_create_surface_callback,
|
||||
nullptr, // external_view_embedder,
|
||||
fml::TimeDelta::Zero(), // vsync_offset
|
||||
ZX_HANDLE_INVALID // vsync_event_handle
|
||||
);
|
||||
flutter_runner::PlatformView platform_view =
|
||||
PlatformViewBuilder(delegate, std::move(task_runners),
|
||||
services_provider.service_directory())
|
||||
.SetDestroyViewCallback(DestroyViewCallback)
|
||||
.Build();
|
||||
|
||||
// Cast platform_view to its base view so we can have access to the public
|
||||
// "HandlePlatformMessage" function.
|
||||
@ -686,25 +730,12 @@ TEST_F(PlatformViewTests, ViewEventsTest) {
|
||||
flutter_runner::CreateFMLTaskRunner(async_get_default_dispatcher()),
|
||||
nullptr);
|
||||
|
||||
auto platform_view = flutter_runner::PlatformView(
|
||||
delegate, // delegate
|
||||
"test_platform_view", // label
|
||||
fuchsia::ui::views::ViewRef(), // view_ref
|
||||
std::move(task_runners), // task_runners
|
||||
services_provider.service_directory(), // runner_services
|
||||
nullptr, // parent_environment_service_provider_handle
|
||||
session_listener.NewRequest(), // session_listener_request
|
||||
nullptr, // focuser,
|
||||
nullptr, // on_session_listener_error_callback
|
||||
nullptr, // on_enable_wireframe_callback,
|
||||
nullptr, // on_create_view_callback,
|
||||
nullptr, // on_update_view_callback,
|
||||
nullptr, // on_destroy_view_callback,
|
||||
nullptr, // on_create_surface_callback,
|
||||
nullptr, // external_view_embedder,
|
||||
fml::TimeDelta::Zero(), // vsync_offset
|
||||
ZX_HANDLE_INVALID // vsync_event_handle
|
||||
);
|
||||
flutter_runner::PlatformView platform_view =
|
||||
PlatformViewBuilder(delegate, std::move(task_runners),
|
||||
services_provider.service_directory())
|
||||
.SetSessionListenerRequest(session_listener.NewRequest())
|
||||
.Build();
|
||||
|
||||
RunLoopUntilIdle();
|
||||
|
||||
// ViewConnected event.
|
||||
@ -769,25 +800,11 @@ TEST_F(PlatformViewTests, RequestFocusTest) {
|
||||
fidl::BindingSet<fuchsia::ui::views::Focuser> focuser_bindings;
|
||||
auto focuser_handle = focuser_bindings.AddBinding(&mock_focuser);
|
||||
|
||||
auto platform_view = flutter_runner::PlatformView(
|
||||
delegate, // delegate
|
||||
"test_platform_view", // label
|
||||
fuchsia::ui::views::ViewRef(), // view_ref
|
||||
std::move(task_runners), // task_runners
|
||||
services_provider.service_directory(), // runner_services
|
||||
nullptr, // parent_environment_service_provider_handle
|
||||
nullptr, // session_listener_request
|
||||
std::move(focuser_handle), // focuser,
|
||||
nullptr, // on_session_listener_error_callback
|
||||
nullptr, // on_enable_wireframe_callback,
|
||||
nullptr, // on_create_view_callback,
|
||||
nullptr, // on_update_view_callback,
|
||||
nullptr, // on_destroy_view_callback,
|
||||
nullptr, // on_create_surface_callback,
|
||||
nullptr, // external_view_embedder,
|
||||
fml::TimeDelta::Zero(), // vsync_offset
|
||||
ZX_HANDLE_INVALID // vsync_event_handle
|
||||
);
|
||||
flutter_runner::PlatformView platform_view =
|
||||
PlatformViewBuilder(delegate, std::move(task_runners),
|
||||
services_provider.service_directory())
|
||||
.SetFocuser(std::move(focuser_handle))
|
||||
.Build();
|
||||
|
||||
// Cast platform_view to its base view so we can have access to the public
|
||||
// "HandlePlatformMessage" function.
|
||||
@ -846,25 +863,11 @@ TEST_F(PlatformViewTests, RequestFocusFailTest) {
|
||||
fidl::BindingSet<fuchsia::ui::views::Focuser> focuser_bindings;
|
||||
auto focuser_handle = focuser_bindings.AddBinding(&mock_focuser);
|
||||
|
||||
auto platform_view = flutter_runner::PlatformView(
|
||||
delegate, // delegate
|
||||
"test_platform_view", // label
|
||||
fuchsia::ui::views::ViewRef(), // view_ref
|
||||
std::move(task_runners), // task_runners
|
||||
services_provider.service_directory(), // runner_services
|
||||
nullptr, // parent_environment_service_provider_handle
|
||||
nullptr, // session_listener_request
|
||||
std::move(focuser_handle), // focuser,
|
||||
nullptr, // on_session_listener_error_callback
|
||||
nullptr, // on_enable_wireframe_callback,
|
||||
nullptr, // on_create_view_callback,
|
||||
nullptr, // on_update_view_callback,
|
||||
nullptr, // on_destroy_view_callback,
|
||||
nullptr, // on_create_surface_callback,
|
||||
nullptr, // external_view_embedder,
|
||||
fml::TimeDelta::Zero(), // vsync_offset
|
||||
ZX_HANDLE_INVALID // vsync_event_handle
|
||||
);
|
||||
flutter_runner::PlatformView platform_view =
|
||||
PlatformViewBuilder(delegate, std::move(task_runners),
|
||||
services_provider.service_directory())
|
||||
.SetFocuser(std::move(focuser_handle))
|
||||
.Build();
|
||||
|
||||
// Cast platform_view to its base view so we can have access to the public
|
||||
// "HandlePlatformMessage" function.
|
||||
@ -916,4 +919,88 @@ TEST_F(PlatformViewTests, RequestFocusFailTest) {
|
||||
EXPECT_EQ(out.str(), result);
|
||||
}
|
||||
|
||||
struct EventFlow {
|
||||
fuchsia::ui::input3::KeyEvent event;
|
||||
fuchsia::ui::input3::KeyEventStatus expected_key_event_status;
|
||||
std::string expected_platform_message;
|
||||
};
|
||||
|
||||
// Makes sure that OnKeyEvent is dispatched as a platform message.
|
||||
TEST_F(PlatformViewTests, OnKeyEvent) {
|
||||
sys::testing::ServiceDirectoryProvider services_provider(dispatcher());
|
||||
MockPlatformViewDelegate delegate;
|
||||
flutter::TaskRunners task_runners =
|
||||
flutter::TaskRunners("test_runners", nullptr, nullptr, nullptr, nullptr);
|
||||
|
||||
fidl::InterfacePtr<fuchsia::ui::input3::KeyboardListener> keyboard_listener;
|
||||
|
||||
flutter_runner::PlatformView platform_view =
|
||||
PlatformViewBuilder(delegate, std::move(task_runners),
|
||||
services_provider.service_directory())
|
||||
.SetKeyboardListener(keyboard_listener.NewRequest(dispatcher()))
|
||||
.Build();
|
||||
|
||||
using fuchsia::input::Key;
|
||||
using fuchsia::ui::input3::KeyEvent;
|
||||
using fuchsia::ui::input3::KeyEventStatus;
|
||||
using fuchsia::ui::input3::KeyEventType;
|
||||
using fuchsia::ui::input3::Modifiers;
|
||||
|
||||
std::vector<EventFlow> events;
|
||||
// Press A. Get 'a'.
|
||||
events.emplace_back(EventFlow{
|
||||
MakeEvent(KeyEventType::PRESSED, std::nullopt, Key::A),
|
||||
KeyEventStatus::HANDLED,
|
||||
R"({"type":"keydown","keymap":"fuchsia","hidUsage":4,"codePoint":97,"modifiers":0})",
|
||||
});
|
||||
// Release A. Get 'a' release.
|
||||
events.emplace_back(EventFlow{
|
||||
MakeEvent(KeyEventType::RELEASED, std::nullopt, Key::A),
|
||||
KeyEventStatus::HANDLED,
|
||||
R"({"type":"keyup","keymap":"fuchsia","hidUsage":4,"codePoint":97,"modifiers":0})",
|
||||
});
|
||||
// Press CAPS_LOCK. Modifier now active.
|
||||
events.emplace_back(EventFlow{
|
||||
MakeEvent(KeyEventType::PRESSED, Modifiers::CAPS_LOCK, Key::CAPS_LOCK),
|
||||
KeyEventStatus::HANDLED,
|
||||
R"({"type":"keydown","keymap":"fuchsia","hidUsage":57,"codePoint":0,"modifiers":1})",
|
||||
});
|
||||
// Pres A. Get 'A'.
|
||||
events.emplace_back(EventFlow{
|
||||
MakeEvent(KeyEventType::PRESSED, std::nullopt, Key::A),
|
||||
KeyEventStatus::HANDLED,
|
||||
R"({"type":"keydown","keymap":"fuchsia","hidUsage":4,"codePoint":65,"modifiers":1})",
|
||||
});
|
||||
// Release CAPS_LOCK.
|
||||
events.emplace_back(EventFlow{
|
||||
MakeEvent(KeyEventType::RELEASED, Modifiers::CAPS_LOCK, Key::CAPS_LOCK),
|
||||
KeyEventStatus::HANDLED,
|
||||
R"({"type":"keyup","keymap":"fuchsia","hidUsage":57,"codePoint":0,"modifiers":1})",
|
||||
});
|
||||
// Press A again. This time get 'A'.
|
||||
// CAPS_LOCK is latched active even if it was just released.
|
||||
events.emplace_back(EventFlow{
|
||||
MakeEvent(KeyEventType::PRESSED, std::nullopt, Key::A),
|
||||
KeyEventStatus::HANDLED,
|
||||
R"({"type":"keydown","keymap":"fuchsia","hidUsage":4,"codePoint":65,"modifiers":1})",
|
||||
});
|
||||
|
||||
for (const auto& event : events) {
|
||||
KeyEvent e;
|
||||
event.event.Clone(&e);
|
||||
fuchsia::ui::input3::KeyEventStatus key_event_status;
|
||||
keyboard_listener->OnKeyEvent(
|
||||
std::move(e),
|
||||
[&key_event_status](fuchsia::ui::input3::KeyEventStatus status) {
|
||||
key_event_status = status;
|
||||
});
|
||||
RunLoopUntilIdle();
|
||||
const std::vector<uint8_t> data = delegate.message()->data();
|
||||
const std::string message = std::string(data.begin(), data.end());
|
||||
|
||||
EXPECT_EQ(event.expected_platform_message, message);
|
||||
EXPECT_EQ(key_event_status, event.expected_key_event_status);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace flutter_runner::testing
|
||||
|
||||
@ -12,15 +12,15 @@
|
||||
"services": [
|
||||
"fuchsia.accessibility.semantics.SemanticsManager",
|
||||
"fuchsia.deprecatedtimezone.Timezone",
|
||||
"fuchsia.fonts.Provider",
|
||||
"fuchsia.intl.PropertyProvider",
|
||||
"fuchsia.logger.LogSink",
|
||||
"fuchsia.process.Launcher",
|
||||
"fuchsia.settings.Intl",
|
||||
"fuchsia.sysmem.Allocator",
|
||||
"fuchsia.tracing.provider.Registry",
|
||||
"fuchsia.vulkan.loader.Loader",
|
||||
"fuchsia.intl.PropertyProvider",
|
||||
"fuchsia.fonts.Provider"
|
||||
"fuchsia.ui.input3.Keyboard",
|
||||
"fuchsia.vulkan.loader.Loader"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user