| // 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_ |