From e5025f4a98f4ed8bf6e2bbe003299f33d9b42a9d Mon Sep 17 00:00:00 2001 From: Christoph Purrer Date: Fri, 11 Nov 2022 22:20:41 +0100 Subject: [PATCH] Add event listeners to Scheduler (#1481) Summary: Minimal set of changes to intercept events in external modules. Current intended use-case is for Reanimated to handle events for the Animated properties. Changelog: [Added] Add listeners to allow intercepting events in C++ core. Reviewed By: cipolleschi Differential Revision: D35312534 fbshipit-source-id: ec924b57fd0c0dabf7be7b886dbef23bf3170d6c # Conflicts: # packages/rn-tester/Podfile.lock Co-authored-by: Nicola Corti --- .../react/renderer/core/EventDispatcher.cpp | 21 +++++++++ .../react/renderer/core/EventDispatcher.h | 15 +++++++ .../react/renderer/core/EventListener.cpp | 39 ++++++++++++++++ .../react/renderer/core/EventListener.h | 44 +++++++++++++++++++ .../react/renderer/scheduler/Scheduler.cpp | 14 ++++++ .../react/renderer/scheduler/Scheduler.h | 6 +++ 6 files changed, 139 insertions(+) create mode 100644 ReactCommon/react/renderer/core/EventListener.cpp create mode 100644 ReactCommon/react/renderer/core/EventListener.h diff --git a/ReactCommon/react/renderer/core/EventDispatcher.cpp b/ReactCommon/react/renderer/core/EventDispatcher.cpp index 615390b04d6a0c..34f68f4bf3b88f 100644 --- a/ReactCommon/react/renderer/core/EventDispatcher.cpp +++ b/ReactCommon/react/renderer/core/EventDispatcher.cpp @@ -36,6 +36,10 @@ EventDispatcher::EventDispatcher( void EventDispatcher::dispatchEvent(RawEvent &&rawEvent, EventPriority priority) const { + // Allows the event listener to interrupt default event dispatch + if (eventListeners_.willDispatchEvent(rawEvent)) { + return; + } getEventQueue(priority).enqueueEvent(std::move(rawEvent)); } @@ -46,6 +50,10 @@ void EventDispatcher::dispatchStateUpdate( } void EventDispatcher::dispatchUniqueEvent(RawEvent &&rawEvent) const { + // Allows the event listener to interrupt default event dispatch + if (eventListeners_.willDispatchEvent(rawEvent)) { + return; + } asynchronousBatchedQueue_->enqueueUniqueEvent(std::move(rawEvent)); } @@ -62,5 +70,18 @@ const EventQueue &EventDispatcher::getEventQueue(EventPriority priority) const { } } +void EventDispatcher::addListener( + const std::shared_ptr &listener) const { + eventListeners_.addListener(listener); +} + +/* + * Removes provided event listener to the event dispatcher. + */ +void EventDispatcher::removeListener( + const std::shared_ptr &listener) const { + eventListeners_.removeListener(listener); +} + } // namespace react } // namespace facebook diff --git a/ReactCommon/react/renderer/core/EventDispatcher.h b/ReactCommon/react/renderer/core/EventDispatcher.h index 9b630b3bf2a9be..19f1fabb7e7b70 100644 --- a/ReactCommon/react/renderer/core/EventDispatcher.h +++ b/ReactCommon/react/renderer/core/EventDispatcher.h @@ -9,6 +9,7 @@ #include #include +#include #include #include #include @@ -52,6 +53,18 @@ class EventDispatcher { void dispatchStateUpdate(StateUpdate &&stateUpdate, EventPriority priority) const; +#pragma mark - Event listeners + /* + * Adds provided event listener to the event dispatcher. + */ + void addListener(const std::shared_ptr &listener) const; + + /* + * Removes provided event listener to the event dispatcher. + */ + void removeListener( + const std::shared_ptr &listener) const; + private: EventQueue const &getEventQueue(EventPriority priority) const; @@ -59,6 +72,8 @@ class EventDispatcher { std::unique_ptr synchronousBatchedQueue_; std::unique_ptr asynchronousUnbatchedQueue_; std::unique_ptr asynchronousBatchedQueue_; + + mutable EventListenerContainer eventListeners_; }; } // namespace react diff --git a/ReactCommon/react/renderer/core/EventListener.cpp b/ReactCommon/react/renderer/core/EventListener.cpp new file mode 100644 index 00000000000000..48f5793292dc7b --- /dev/null +++ b/ReactCommon/react/renderer/core/EventListener.cpp @@ -0,0 +1,39 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#include "EventListener.h" + +namespace facebook::react { + +bool EventListenerContainer::willDispatchEvent(const RawEvent &event) { + std::shared_lock lock(mutex_); + + bool handled = false; + for (auto const &listener : eventListeners_) { + handled = handled || listener->operator()(event); + } + return handled; +} + +void EventListenerContainer::addListener( + const std::shared_ptr &listener) { + std::unique_lock lock(mutex_); + + eventListeners_.push_back(listener); +} + +void EventListenerContainer::removeListener( + const std::shared_ptr &listener) { + std::unique_lock lock(mutex_); + + auto it = std::find(eventListeners_.begin(), eventListeners_.end(), listener); + if (it != eventListeners_.end()) { + eventListeners_.erase(it); + } +} + +} // namespace facebook::react diff --git a/ReactCommon/react/renderer/core/EventListener.h b/ReactCommon/react/renderer/core/EventListener.h new file mode 100644 index 00000000000000..f9176b514cbfa0 --- /dev/null +++ b/ReactCommon/react/renderer/core/EventListener.h @@ -0,0 +1,44 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#pragma once + +#include + +#include + +#include + +namespace facebook { +namespace react { + +/** + * Listener for events dispatched to JS runtime. + * Return `true` to interrupt default dispatch to JS event emitter, `false` to + * pass through to default handlers. + */ +using EventListener = std::function; + +class EventListenerContainer { + public: + /* + * Invoke listeners in this container with the event. + * Returns true if event was handled by the listener, false to continue + * default dispatch. + */ + bool willDispatchEvent(const RawEvent &event); + + void addListener(const std::shared_ptr &listener); + void removeListener(const std::shared_ptr &listener); + + private: + butter::shared_mutex mutex_; + std::vector> eventListeners_; +}; + +} // namespace react +} // namespace facebook diff --git a/ReactCommon/react/renderer/scheduler/Scheduler.cpp b/ReactCommon/react/renderer/scheduler/Scheduler.cpp index 25a9f82655c1c3..219afaf1bdeb76 100644 --- a/ReactCommon/react/renderer/scheduler/Scheduler.cpp +++ b/ReactCommon/react/renderer/scheduler/Scheduler.cpp @@ -350,5 +350,19 @@ ContextContainer::Shared Scheduler::getContextContainer() const { return contextContainer_; } +void Scheduler::addEventListener( + const std::shared_ptr &listener) { + if (eventDispatcher_->has_value()) { + eventDispatcher_->value().addListener(listener); + } +} + +void Scheduler::removeEventListener( + const std::shared_ptr &listener) { + if (eventDispatcher_->has_value()) { + eventDispatcher_->value().removeListener(listener); + } +} + } // namespace react } // namespace facebook diff --git a/ReactCommon/react/renderer/scheduler/Scheduler.h b/ReactCommon/react/renderer/scheduler/Scheduler.h index 30dad1f75adb04..f06adbaadf14ca 100644 --- a/ReactCommon/react/renderer/scheduler/Scheduler.h +++ b/ReactCommon/react/renderer/scheduler/Scheduler.h @@ -16,6 +16,7 @@ #include #include #include +#include #include #include #include @@ -107,6 +108,11 @@ class Scheduler final : public UIManagerDelegate { #pragma mark - ContextContainer ContextContainer::Shared getContextContainer() const; +#pragma mark - Event listeners + void addEventListener(const std::shared_ptr &listener); + void removeEventListener( + const std::shared_ptr &listener); + private: friend class SurfaceHandler;