blob: 8f2b543f31121705f66d09029290d7ae1fea5ae4 [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 "src/ui/scenic/lib/input/helper.h"
#include "src/ui/lib/escher/util/type_utils.h"
#include "src/ui/scenic/lib/gfx/resources/compositor/layer.h"
#include "src/ui/scenic/lib/gfx/resources/compositor/layer_stack.h"
namespace scenic_impl {
namespace input {
using PointerEventPhase = fuchsia::ui::input::PointerEventPhase;
using GfxPointerEvent = fuchsia::ui::input::PointerEvent;
using InjectorEventPhase = fuchsia::ui::pointerinjector::EventPhase;
GfxPointerEvent ClonePointerWithCoords(const GfxPointerEvent& event, const glm::vec2& coords) {
GfxPointerEvent clone;
fidl::Clone(event, &clone);
clone.x = coords.x;
clone.y = coords.y;
return clone;
}
glm::vec2 PointerCoords(const GfxPointerEvent& event) { return {event.x, event.y}; }
glm::vec2 TransformPointerCoords(const glm::vec2& pointer, const glm::mat4 transform) {
const glm::vec4 homogenous_pointer{pointer.x, pointer.y, 0, 1};
const glm::vec4 transformed_pointer = transform * homogenous_pointer;
const glm::vec2 homogenized_transformed_pointer{escher::homogenize(transformed_pointer)};
FX_VLOGS(2) << "Coordinate transform (device->view): (" << pointer.x << ", " << pointer.y
<< ")->(" << homogenized_transformed_pointer.x << ", "
<< homogenized_transformed_pointer.y << ")";
return homogenized_transformed_pointer;
}
trace_flow_id_t PointerTraceHACK(float fa, float fb) {
uint32_t ia, ib;
memcpy(&ia, &fa, sizeof(uint32_t));
memcpy(&ib, &fb, sizeof(uint32_t));
return (((uint64_t)ia) << 32) | ib;
}
std::pair<float, float> ReversePointerTraceHACK(trace_flow_id_t trace_id) {
float fhigh, flow;
const uint32_t ihigh = (uint32_t)(trace_id >> 32);
const uint32_t ilow = (uint32_t)trace_id;
memcpy(&fhigh, &ihigh, sizeof(uint32_t));
memcpy(&flow, &ilow, sizeof(uint32_t));
return {fhigh, flow};
}
Phase GfxPhaseToInternalPhase(PointerEventPhase phase) {
switch (phase) {
case PointerEventPhase::ADD:
return Phase::kAdd;
case PointerEventPhase::UP:
return Phase::kUp;
case PointerEventPhase::MOVE:
return Phase::kChange;
case PointerEventPhase::DOWN:
return Phase::kDown;
case PointerEventPhase::REMOVE:
return Phase::kRemove;
case PointerEventPhase::CANCEL:
return Phase::kCancel;
default:
FX_CHECK(false) << "Should never be reached";
return Phase::kInvalid;
}
}
PointerEventPhase InternalPhaseToGfxPhase(Phase phase) {
switch (phase) {
case Phase::kAdd:
return PointerEventPhase::ADD;
case Phase::kUp:
return PointerEventPhase::UP;
case Phase::kChange:
return PointerEventPhase::MOVE;
case Phase::kDown:
return PointerEventPhase::DOWN;
case Phase::kRemove:
return PointerEventPhase::REMOVE;
case Phase::kCancel:
return PointerEventPhase::CANCEL;
case Phase::kInvalid:
FX_CHECK(false) << "Should never be reached.";
return static_cast<PointerEventPhase>(0);
};
}
std::vector<InternalPointerEvent> PointerInjectorEventToInternalPointerEvent(
const fuchsia::ui::pointerinjector::Event& event, uint32_t device_id, const Viewport& viewport,
zx_koid_t context, zx_koid_t target) {
InternalPointerEvent internal_event;
internal_event.timestamp = event.timestamp();
internal_event.device_id = device_id;
const fuchsia::ui::pointerinjector::PointerSample& pointer_sample = event.data().pointer_sample();
internal_event.pointer_id = pointer_sample.pointer_id();
internal_event.viewport = viewport;
internal_event.position_in_viewport = {pointer_sample.position_in_viewport()[0],
pointer_sample.position_in_viewport()[1]};
internal_event.context = context;
internal_event.target = target;
std::vector<InternalPointerEvent> events;
switch (pointer_sample.phase()) {
case InjectorEventPhase::ADD: {
// Insert extra event.
InternalPointerEvent add_clone = internal_event;
add_clone.phase = Phase::kAdd;
events.emplace_back(std::move(add_clone));
internal_event.phase = Phase::kDown;
events.emplace_back(std::move(internal_event));
break;
}
case InjectorEventPhase::CHANGE: {
internal_event.phase = Phase::kChange;
events.emplace_back(std::move(internal_event));
break;
}
case InjectorEventPhase::REMOVE: {
// Insert extra event.
InternalPointerEvent up_clone = internal_event;
up_clone.phase = Phase::kUp;
events.emplace_back(std::move(up_clone));
internal_event.phase = Phase::kRemove;
events.emplace_back(std::move(internal_event));
break;
}
case InjectorEventPhase::CANCEL: {
internal_event.phase = Phase::kCancel;
events.emplace_back(std::move(internal_event));
break;
}
default: {
FX_CHECK(false) << "unsupported phase: " << static_cast<uint32_t>(pointer_sample.phase());
break;
}
}
return events;
}
InternalPointerEvent GfxPointerEventToInternalEvent(
const fuchsia::ui::input::PointerEvent& event, zx_koid_t scene_koid, float screen_width,
float screen_height, const glm::mat4& context_from_screen_transform) {
InternalPointerEvent internal_event;
internal_event.timestamp = event.event_time;
internal_event.device_id = event.device_id;
internal_event.pointer_id = event.pointer_id;
// Define the viewport to match screen dimensions and location.
internal_event.viewport.extents =
Extents({{/*min*/ {0.f, 0.f}, /*max*/ {screen_width, screen_height}}});
internal_event.viewport.context_from_viewport_transform = context_from_screen_transform;
internal_event.position_in_viewport = {event.x, event.y};
// Using scene_koid as both context and target, since it's guaranteed to be the root and thus
// to deliver events to any client in the scene graph.
internal_event.context = scene_koid;
internal_event.target = scene_koid;
internal_event.phase = GfxPhaseToInternalPhase(event.phase);
internal_event.buttons = event.buttons;
return internal_event;
}
GfxPointerEvent InternalPointerEventToGfxPointerEvent(const InternalPointerEvent& internal_event,
const glm::mat4& view_from_context_transform,
fuchsia::ui::input::PointerEventType type,
uint64_t trace_id) {
GfxPointerEvent event;
event.event_time = internal_event.timestamp;
event.device_id = internal_event.device_id;
event.pointer_id = internal_event.pointer_id;
event.type = type;
event.buttons = internal_event.buttons;
// Convert to view-local coordinates.
const glm::mat4 view_from_viewport_transform =
view_from_context_transform * internal_event.viewport.context_from_viewport_transform;
const glm::vec2 local_position =
TransformPointerCoords(internal_event.position_in_viewport, view_from_viewport_transform);
event.x = local_position.x;
event.y = local_position.y;
const auto [high, low] = ReversePointerTraceHACK(trace_id);
event.radius_minor = low; // Lower 32 bits.
event.radius_major = high; // Upper 32 bits.
event.phase = InternalPhaseToGfxPhase(internal_event.phase);
return event;
}
glm::mat4 ColumnMajorMat3VectorToMat4(const std::array<float, 9>& matrix_array) {
// clang-format off
return glm::mat4(matrix_array[0], matrix_array[1], 0.f, matrix_array[2], // first column
matrix_array[3], matrix_array[4], 0.f, matrix_array[5], // second column
0.f, 0.f, 1.f, 0.f, // third column
matrix_array[6], matrix_array[7], 0.f, 1.f); // fourth column
// clang-format on
}
} // namespace input
} // namespace scenic_impl