blob: 232fa3c1de2b75a21c77598dc529a4ae26f70575 [file] [log] [blame]
// Copyright 2018 The Fuchsia 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 "garnet/lib/ui/input/tests/util.h"
#include <hid/hid.h>
#include "garnet/lib/ui/gfx/displays/display_manager.h"
#include "lib/fidl/cpp/clone.h"
#include "lib/fxl/logging.h"
#include "lib/gtest/test_loop_fixture.h"
#include "lib/ui/input/cpp/formatting.h"
namespace lib_ui_input_tests {
using InputCommand = fuchsia::ui::input::Command;
using ScenicEvent = fuchsia::ui::scenic::Event;
using escher::impl::CommandBufferSequencer;
using fuchsia::ui::input::InputEvent;
using fuchsia::ui::input::KeyboardEvent;
using fuchsia::ui::input::KeyboardEventPhase;
using fuchsia::ui::input::PointerEvent;
using fuchsia::ui::input::PointerEventPhase;
using fuchsia::ui::input::PointerEventType;
using fuchsia::ui::input::SendKeyboardInputCmd;
using fuchsia::ui::input::SendPointerInputCmd;
using fuchsia::ui::scenic::SessionListener;
using scenic_impl::ResourceId;
using scenic_impl::Scenic;
using scenic_impl::gfx::DisplayManager;
using scenic_impl::gfx::test::GfxSystemForTest;
using scenic_impl::input::InputSystem;
using scenic_impl::test::ScenicTest;
void CreateTokenPair(zx::eventpair* t1, zx::eventpair* t2) {
zx_status_t status = zx::eventpair::create(/*flags*/ 0u, t1, t2);
FXL_CHECK(status == ZX_OK);
}
void InputSystemTest::RequestToPresent(scenic::Session* session) {
bool scene_presented = false;
session->Present(
/*presentation time*/ 0,
[&scene_presented](fuchsia::images::PresentationInfo info) {
scene_presented = true;
});
RunLoopFor(zx::msec(20)); // Schedule the render task.
EXPECT_TRUE(scene_presented);
}
void InputSystemTest::TearDown() {
// A clean teardown sequence is a little involved but possible.
// 0. Sessions Flush their last resource-release cmds (e.g., ~SessionWrapper).
// 1. Scenic runs the last resource-release cmds.
RunLoopUntilIdle();
// 2. Destroy Scenic before destroying the command buffer sequencer (CBS).
// This ensures no CBS listeners are active by the time CBS is destroyed.
// Scenic is destroyed by the superclass TearDown (now), CBS is destroyed
// by the implicit class destructor (later).
ScenicTest::TearDown();
}
void InputSystemTest::InitializeScenic(Scenic* scenic) {
auto display_manager = std::make_unique<DisplayManager>();
display_manager->SetDefaultDisplayForTests(std::make_unique<TestDisplay>(
/*id*/ 0, test_display_width_px(), test_display_height_px()));
command_buffer_sequencer_ = std::make_unique<CommandBufferSequencer>();
gfx_ = scenic->RegisterSystem<GfxSystemForTest>(
std::move(display_manager), command_buffer_sequencer_.get());
input_ = scenic->RegisterSystem<InputSystem>(gfx_);
}
SessionWrapper::SessionWrapper(Scenic* scenic) {
fuchsia::ui::scenic::SessionPtr session_ptr;
fidl::InterfaceHandle<SessionListener> listener_handle;
fidl::InterfaceRequest<SessionListener> listener_request =
listener_handle.NewRequest();
scenic->CreateSession(session_ptr.NewRequest(), std::move(listener_handle));
session_ = std::make_unique<scenic::Session>(std::move(session_ptr),
std::move(listener_request));
root_node_ = std::make_unique<scenic::EntityNode>(session_.get());
session_->set_event_handler([this](std::vector<ScenicEvent> events) {
for (ScenicEvent& event : events) {
if (event.is_input()) {
events_.push_back(std::move(event.input()));
}
// Ignore other event types for this test.
}
});
}
SessionWrapper::~SessionWrapper() {
root_node_.reset(); // Let go of the resource; enqueue the release cmd.
session_->Flush(); // Ensure Scenic receives the release cmd.
}
void SessionWrapper::RunNow(
fit::function<void(scenic::Session* session, scenic::EntityNode* root_node)>
create_scene_callback) {
create_scene_callback(session_.get(), root_node_.get());
}
void SessionWrapper::ExamineEvents(
fit::function<void(const std::vector<InputEvent>& events)>
examine_events_callback) {
examine_events_callback(events_);
}
PointerCommandGenerator::PointerCommandGenerator(ResourceId compositor_id,
uint32_t device_id,
uint32_t pointer_id,
PointerEventType type)
: compositor_id_(compositor_id) {
blank_.device_id = device_id;
blank_.pointer_id = pointer_id;
blank_.type = type;
}
InputCommand PointerCommandGenerator::Add(float x, float y) {
PointerEvent event;
fidl::Clone(blank_, &event);
event.phase = PointerEventPhase::ADD;
event.x = x;
event.y = y;
return MakeInputCommand(event);
}
InputCommand PointerCommandGenerator::Down(float x, float y) {
PointerEvent event;
fidl::Clone(blank_, &event);
event.phase = PointerEventPhase::DOWN;
event.x = x;
event.y = y;
return MakeInputCommand(event);
}
InputCommand PointerCommandGenerator::Move(float x, float y) {
PointerEvent event;
fidl::Clone(blank_, &event);
event.phase = PointerEventPhase::MOVE;
event.x = x;
event.y = y;
return MakeInputCommand(event);
}
InputCommand PointerCommandGenerator::Up(float x, float y) {
PointerEvent event;
fidl::Clone(blank_, &event);
event.phase = PointerEventPhase::UP;
event.x = x;
event.y = y;
return MakeInputCommand(event);
}
InputCommand PointerCommandGenerator::Remove(float x, float y) {
PointerEvent event;
fidl::Clone(blank_, &event);
event.phase = PointerEventPhase::REMOVE;
event.x = x;
event.y = y;
return MakeInputCommand(event);
}
InputCommand PointerCommandGenerator::MakeInputCommand(PointerEvent event) {
SendPointerInputCmd pointer_cmd;
pointer_cmd.compositor_id = compositor_id_;
pointer_cmd.pointer_event = std::move(event);
InputCommand input_cmd;
input_cmd.set_send_pointer_input(std::move(pointer_cmd));
return input_cmd;
}
KeyboardCommandGenerator::KeyboardCommandGenerator(ResourceId compositor_id,
uint32_t device_id)
: compositor_id_(compositor_id) {
blank_.device_id = device_id;
}
InputCommand KeyboardCommandGenerator::Pressed(uint32_t hid_usage,
uint32_t modifiers) {
KeyboardEvent event;
fidl::Clone(blank_, &event);
event.phase = KeyboardEventPhase::PRESSED;
event.hid_usage = hid_usage;
event.modifiers = modifiers;
return MakeInputCommand(event);
}
InputCommand KeyboardCommandGenerator::Released(uint32_t hid_usage,
uint32_t modifiers) {
KeyboardEvent event;
fidl::Clone(blank_, &event);
event.phase = KeyboardEventPhase::RELEASED;
event.hid_usage = hid_usage;
event.modifiers = modifiers;
return MakeInputCommand(event);
}
InputCommand KeyboardCommandGenerator::Cancelled(uint32_t hid_usage,
uint32_t modifiers) {
KeyboardEvent event;
fidl::Clone(blank_, &event);
event.phase = KeyboardEventPhase::CANCELLED;
event.hid_usage = hid_usage;
event.modifiers = modifiers;
return MakeInputCommand(event);
}
InputCommand KeyboardCommandGenerator::Repeat(uint32_t hid_usage,
uint32_t modifiers) {
KeyboardEvent event;
fidl::Clone(blank_, &event);
event.phase = KeyboardEventPhase::REPEAT;
event.hid_usage = hid_usage;
event.modifiers = modifiers;
return MakeInputCommand(event);
}
InputCommand KeyboardCommandGenerator::MakeInputCommand(KeyboardEvent event) {
// Typically code point is inferred this same way by DeviceState.
event.code_point =
hid_map_key(event.hid_usage,
event.modifiers & (fuchsia::ui::input::kModifierShift |
fuchsia::ui::input::kModifierCapsLock),
qwerty_map);
SendKeyboardInputCmd keyboard_cmd;
keyboard_cmd.compositor_id = compositor_id_;
keyboard_cmd.keyboard_event = std::move(event);
InputCommand input_cmd;
input_cmd.set_send_keyboard_input(std::move(keyboard_cmd));
return input_cmd;
}
bool PointerMatches(const PointerEvent& event, uint32_t pointer_id,
PointerEventPhase phase, float x, float y) {
using fuchsia::ui::input::operator<<;
if (event.pointer_id != pointer_id) {
FXL_LOG(ERROR) << " Actual: " << event.pointer_id;
FXL_LOG(ERROR) << "Expected: " << pointer_id;
return false;
} else if (event.phase != phase) {
FXL_LOG(ERROR) << " Actual: " << event.phase;
FXL_LOG(ERROR) << "Expected: " << phase;
return false;
} else if (event.x != x) {
FXL_LOG(ERROR) << " Actual: " << event.x;
FXL_LOG(ERROR) << "Expected: " << x;
return false;
} else if (event.y != y) {
FXL_LOG(ERROR) << " Actual: " << event.y;
FXL_LOG(ERROR) << "Expected: " << y;
return false;
}
return true;
}
} // namespace lib_ui_input_tests