blob: ad3b50c10aed5275f64997e1d3a9bcdf97bcda7b [file] [log] [blame]
// Copyright 2022 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 SRC_EMBEDDER_TOUCH_DELEGATE_H_
#define SRC_EMBEDDER_TOUCH_DELEGATE_H_
#include <fuchsia/ui/pointer/cpp/fidl.h>
#include <array>
#include <functional>
#include <optional>
#include <unordered_map>
#include <unordered_set>
#include <vector>
#include "src/embedder/engine/embedder.h"
#include "src/embedder/logging.h"
namespace embedder {
// Helper class for keying into a map.
struct TouchInteractionHasher {
std::size_t operator()(const fuchsia::ui::pointer::TouchInteractionId& ixn) const {
return std::hash<uint32_t>()(ixn.device_id) ^ std::hash<uint32_t>()(ixn.pointer_id) ^
std::hash<uint32_t>()(ixn.interaction_id);
}
};
/// Channel processor for fuchsia.ui.pointer.TouchSource protocol
/// protocol. It manages the channel state, collects touch events, and
/// converts them to generic FlutterPointerEvent types before
/// returning them to the embedder's main function, which
/// dispatches them to the flutter engine using the embedder API
class TouchDelegate {
public:
/// Initializes the TouchDelegate. Does NOT start watching for events
/// |touch_source| - the FIDL handle for touch input events
TouchDelegate(fuchsia::ui::pointer::TouchSourceHandle touch_source);
/// This function collects Fuchsia's ui::pointer::MouseEvent's and
/// transforms them into FlutterPointerEvent structs. It then calls
/// the supplied callback with a vector of FlutterPointerEvent, which
/// uses the embedder API to forward these to the flutter engine
void WatchLoop(std::function<void(std::vector<FlutterPointerEvent>)> callback);
private:
/// Channel for touch events from Scenic.
fuchsia::ui::pointer::TouchSourcePtr touch_source_;
/// Receive touch events from Scenic. Must be copyable as the FIDL protocol
/// accepting this callback will take this by value.
std::function<void(std::vector<fuchsia::ui::pointer::TouchEvent>)> touch_responder_;
/// Per-interaction buffer of touch events from Scenic. When an interaction
/// starts with event.pointer_sample.phase == ADD, we allocate a buffer and
/// store samples. When interaction ownership becomes
/// event.interaction_result.status == GRANTED, we flush the buffer to client,
/// delete the buffer, and all future events in this interaction are flushed
/// direct to client. When interaction ownership becomes DENIED, we delete the
/// buffer, and the client does not get any previous or future events in this
/// interaction.
///
/// There are three basic interaction forms that we need to handle, and the API
/// guarantees we see only these three forms. S=sample, R(g)=result-granted,
/// R(d)=result-denied, and + means packaged in the same table. Time flows from
/// left to right. Samples start with ADD, and end in REMOVE or CANCEL. Each
/// interaction receives just one ownership result.
/// (1) Late grant. S S S R(g) S S S
/// (1-a) Combo. S S S+R(g) S S S
/// (2) Early grant. S+R(g) S S S S S
/// (3) Late deny. S S S R(d)
/// (3-a) Combo. S S S+R(d)
///
/// This results in the following high-level algorithm to correctly deal with
/// buffer allocation and deletion, and event flushing or event dropping based
/// on ownership.
/// if event.sample.phase == ADD && !event.result
/// allocate buffer[event.sample.interaction]
/// if buffer[event.sample.interaction]
/// buffer[event.sample.interaction].push(event.sample)
/// else
/// flush_to_client(event.sample)
/// if event.result
/// if event.result == GRANTED
/// flush_to_client(buffer[event.result.interaction])
/// delete buffer[event.result.interaction]
std::unordered_map<fuchsia::ui::pointer::TouchInteractionId, std::vector<FlutterPointerEvent>,
TouchInteractionHasher>
touch_buffer_;
/// The fuchsia.ui.pointer.TouchSource protocol allows one in-flight
/// hanging-get Watch() call to gather touch events, and the client is expected
/// to respond with consumption intent on the following hanging-get Watch()
/// call. Store responses here for the next call.
std::vector<fuchsia::ui::pointer::TouchResponse> touch_responses_;
/// The fuchsia.ui.pointer.TouchSource protocol issues channel-global view
/// parameters on connection and on change. Events must apply these view
/// parameters to correctly map to logical view coordinates. The "nullopt"
/// state represents the absence of view parameters, early in the protocol
/// lifecycle.
std::optional<fuchsia::ui::pointer::ViewParameters> touch_view_parameters_;
};
} // namespace embedder
#endif // SRC_EMBEDDER_TOUCH_DELEGATE_H_