blob: 0c09813ec6d180dc21871534ece640436e99023f [file] [log] [blame]
// Copyright 2020 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.
#ifndef SRC_UI_BIN_ROOT_PRESENTER_INJECTOR_H_
#define SRC_UI_BIN_ROOT_PRESENTER_INJECTOR_H_
#include <fuchsia/ui/input/cpp/fidl.h>
#include <fuchsia/ui/pointerinjector/cpp/fidl.h>
#include <fuchsia/ui/views/cpp/fidl.h>
#include <lib/sys/cpp/component_context.h>
#include <deque>
#include <unordered_map>
namespace root_presenter {
// Class for handling input injection into Scenic.
//
// We register a touch injector with Scenic for each |device_id| added with OnDeviceAdded.
// On OnDeviceRemoved we close the touch injector channel only after all pending events have been
// delivered.
//
// If a touch injector channel is closed by Scenic, we try to recover by re-establishing the
// connection and injecting pending events. To reduce the risk to performance and infinite loops
// here, we assume the connection is successful, and in the rare case that it's not we drop the
// corresponding events and try to again for the next batch.
//
// We reuse the same context, target, viewport and dispatch policy for all
// fuchsia::ui::pointerinjector::Device channels, since we assume all devices to represent the same
// touchscreen.
class Injector {
public:
// Struct used to define the Viewport used for injection. We define an axis-aligned viewport with
// dimensions of (0,0) to (width, height), scaled and offset from the context view.
struct Viewport {
float width = 1.f;
float height = 1.f;
float scale = 1.f;
float x_offset = 0.f;
float y_offset = 0.f;
};
Injector(sys::ComponentContext* component_context, fuchsia::ui::views::ViewRef context,
fuchsia::ui::views::ViewRef target);
~Injector() = default;
// Not copyable or movable. Since internal closures capture |this| it's not safe.
Injector(const Injector&) = delete;
Injector& operator=(const Injector&) = delete;
Injector(Injector&&) = delete;
Injector& operator=(Injector&&) = delete;
void SetViewport(Viewport viewport);
void OnDeviceAdded(uint32_t device_id);
void OnDeviceRemoved(uint32_t device_id);
void OnEvent(const fuchsia::ui::input::InputEvent& event);
// To be called when the scene is ready for injection.
// All events are buffered until this is called to prevent test flakiness.
void MarkSceneReady();
private:
static constexpr uint64_t kLogFrequency = 100u;
using InjectorId = uint64_t;
struct PerDeviceInjector {
uint32_t device_id = std::numeric_limits<uint32_t>::max();
fuchsia::ui::pointerinjector::DevicePtr touch_injector;
std::deque<fuchsia::ui::pointerinjector::Event> pending_events;
bool injection_in_flight = false;
bool kill_when_empty = false;
};
// Called for each new injector device.
void SetupInputInjection(InjectorId injector_id, uint32_t device_id);
void InjectPending(InjectorId injector_id);
fuchsia::ui::pointerinjector::Viewport GetCurrentViewport();
const sys::ComponentContext* const component_context_;
const fuchsia::ui::views::ViewRef context_view_ref_;
const fuchsia::ui::views::ViewRef target_view_ref_;
// Flaps once, from false to true.
// If scene is disturbed, then Presentation and Injector are both destroyed and recreated.
bool scene_ready_ = false;
Viewport viewport_;
// These internal ID's are never reused, even when the same device is added multiple times.
InjectorId next_injector_id_ = 0;
// Map of all currently active devices to their corresponding |injector_id|.
std::unordered_map<uint32_t, InjectorId> injector_id_by_device_id_;
// Map of all injectors, either with active devices or inactive but with pending events.
std::unordered_map<InjectorId, PerDeviceInjector> injectors_;
// Failed injection attempt counter. Used to reduce log spam.
// We show one log for every |kLogFrequency| failed attempts, and one for every successful
// recovery.
uint64_t num_failed_injection_attempts_ = 0u;
};
} // namespace root_presenter
#endif // SRC_UI_BIN_ROOT_PRESENTER_INJECTOR_H_