| // Copyright 2013 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 FLUTTER_SHELL_PLATFORM_FUCHSIA_POINTER_DELEGATE_H_ |
| #define FLUTTER_SHELL_PLATFORM_FUCHSIA_POINTER_DELEGATE_H_ |
| |
| #include <fuchsia/ui/pointer/cpp/fidl.h> |
| |
| #include <array> |
| #include <functional> |
| #include <optional> |
| #include <unordered_map> |
| #include <unordered_set> |
| #include <vector> |
| |
| #include "flutter/lib/ui/window/pointer_data.h" |
| |
| namespace flutter_runner { |
| |
| // Helper class for keying into a map. |
| struct IxnHasher { |
| 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 processors for fuchsia.ui.pointer.TouchSource and MouseSource |
| // protocols. It manages the channel state, collects touch and mouse events, and |
| // surfaces them to PlatformView as flutter::PointerData events for further |
| // processing and dispatch. |
| class PointerDelegate { |
| public: |
| PointerDelegate(fuchsia::ui::pointer::TouchSourceHandle touch_source, |
| fuchsia::ui::pointer::MouseSourceHandle mouse_source); |
| |
| // This function collects Fuchsia's TouchPointerSample and MousePointerSample |
| // data and transforms them into flutter::PointerData structs. It then calls |
| // the supplied callback with a vector of flutter::PointerData, which (1) does |
| // last processing (applies metrics), and (2) packs these flutter::PointerData |
| // in a flutter::PointerDataPacket for transport to the Engine. |
| void WatchLoop( |
| std::function<void(std::vector<flutter::PointerData>)> callback); |
| |
| private: |
| /***** TOUCH STATE *****/ |
| |
| // Channel for touch events from Scenic. |
| fuchsia::ui::pointer::TouchSourcePtr touch_source_; |
| |
| // Receive touch events from Scenic. Must be copyable. |
| 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<flutter::PointerData>, |
| IxnHasher> |
| 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_; |
| |
| /***** MOUSE STATE *****/ |
| |
| // Channel for mouse events from Scenic. |
| fuchsia::ui::pointer::MouseSourcePtr mouse_source_; |
| |
| // Receive mouse events from Scenic. Must be copyable. |
| std::function<void(std::vector<fuchsia::ui::pointer::MouseEvent>)> |
| mouse_responder_; |
| |
| // The set of mouse devices that are currently interacting with the UI. |
| // A mouse is considered flutter::PointerData::Change::kDown if any button is |
| // pressed. This set is used to correctly set the phase in |
| // flutter::PointerData.change, with this high-level algorithm: |
| // if !mouse_down[id] && !button then: change = kHover |
| // if !mouse_down[id] && button then: change = kDown; mouse_down.add(id) |
| // if mouse_down[id] && button then: change = kMove |
| // if mouse_down[id] && !button then: change = kUp; mouse_down.remove(id) |
| std::unordered_set</*mouse device ID*/ uint32_t> mouse_down_; |
| |
| // For each mouse device, its device-specific information, such as mouse |
| // button priority order. |
| std::unordered_map</*mouse device ID*/ uint32_t, |
| fuchsia::ui::pointer::MouseDeviceInfo> |
| mouse_device_info_; |
| |
| // The fuchsia.ui.pointer.MouseSource 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> mouse_view_parameters_; |
| }; |
| |
| } // namespace flutter_runner |
| #endif // FLUTTER_SHELL_PLATFORM_FUCHSIA_POINTER_DELEGATE_H_ |