Skip to content

Commit

Permalink
Add event listeners to Scheduler (microsoft#1481)
Browse files Browse the repository at this point in the history
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 <ncor@fb.com>
  • Loading branch information
2 people authored and Shawn Dempsey committed Mar 10, 2023
1 parent 4ffb7dd commit e5025f4
Show file tree
Hide file tree
Showing 6 changed files with 139 additions and 0 deletions.
21 changes: 21 additions & 0 deletions ReactCommon/react/renderer/core/EventDispatcher.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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));
}

Expand All @@ -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));
}

Expand All @@ -62,5 +70,18 @@ const EventQueue &EventDispatcher::getEventQueue(EventPriority priority) const {
}
}

void EventDispatcher::addListener(
const std::shared_ptr<EventListener const> &listener) const {
eventListeners_.addListener(listener);
}

/*
* Removes provided event listener to the event dispatcher.
*/
void EventDispatcher::removeListener(
const std::shared_ptr<EventListener const> &listener) const {
eventListeners_.removeListener(listener);
}

} // namespace react
} // namespace facebook
15 changes: 15 additions & 0 deletions ReactCommon/react/renderer/core/EventDispatcher.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@

#include <react/renderer/core/BatchedEventQueue.h>
#include <react/renderer/core/EventBeat.h>
#include <react/renderer/core/EventListener.h>
#include <react/renderer/core/EventPriority.h>
#include <react/renderer/core/EventQueueProcessor.h>
#include <react/renderer/core/StateUpdate.h>
Expand Down Expand Up @@ -52,13 +53,27 @@ 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<EventListener const> &listener) const;

/*
* Removes provided event listener to the event dispatcher.
*/
void removeListener(
const std::shared_ptr<EventListener const> &listener) const;

private:
EventQueue const &getEventQueue(EventPriority priority) const;

std::unique_ptr<UnbatchedEventQueue> synchronousUnbatchedQueue_;
std::unique_ptr<BatchedEventQueue> synchronousBatchedQueue_;
std::unique_ptr<UnbatchedEventQueue> asynchronousUnbatchedQueue_;
std::unique_ptr<BatchedEventQueue> asynchronousBatchedQueue_;

mutable EventListenerContainer eventListeners_;
};

} // namespace react
Expand Down
39 changes: 39 additions & 0 deletions ReactCommon/react/renderer/core/EventListener.cpp
Original file line number Diff line number Diff line change
@@ -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<butter::shared_mutex> lock(mutex_);

bool handled = false;
for (auto const &listener : eventListeners_) {
handled = handled || listener->operator()(event);
}
return handled;
}

void EventListenerContainer::addListener(
const std::shared_ptr<EventListener const> &listener) {
std::unique_lock<butter::shared_mutex> lock(mutex_);

eventListeners_.push_back(listener);
}

void EventListenerContainer::removeListener(
const std::shared_ptr<EventListener const> &listener) {
std::unique_lock<butter::shared_mutex> lock(mutex_);

auto it = std::find(eventListeners_.begin(), eventListeners_.end(), listener);
if (it != eventListeners_.end()) {
eventListeners_.erase(it);
}
}

} // namespace facebook::react
44 changes: 44 additions & 0 deletions ReactCommon/react/renderer/core/EventListener.h
Original file line number Diff line number Diff line change
@@ -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 <string>

#include <react/renderer/core/RawEvent.h>

#include <butter/mutex.h>

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<bool(const RawEvent &event)>;

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<EventListener const> &listener);
void removeListener(const std::shared_ptr<EventListener const> &listener);

private:
butter::shared_mutex mutex_;
std::vector<std::shared_ptr<EventListener const>> eventListeners_;
};

} // namespace react
} // namespace facebook
14 changes: 14 additions & 0 deletions ReactCommon/react/renderer/scheduler/Scheduler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -350,5 +350,19 @@ ContextContainer::Shared Scheduler::getContextContainer() const {
return contextContainer_;
}

void Scheduler::addEventListener(
const std::shared_ptr<EventListener const> &listener) {
if (eventDispatcher_->has_value()) {
eventDispatcher_->value().addListener(listener);
}
}

void Scheduler::removeEventListener(
const std::shared_ptr<EventListener const> &listener) {
if (eventDispatcher_->has_value()) {
eventDispatcher_->value().removeListener(listener);
}
}

} // namespace react
} // namespace facebook
6 changes: 6 additions & 0 deletions ReactCommon/react/renderer/scheduler/Scheduler.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
#include <react/renderer/components/root/RootComponentDescriptor.h>
#include <react/renderer/core/ComponentDescriptor.h>
#include <react/renderer/core/EventEmitter.h>
#include <react/renderer/core/EventListener.h>
#include <react/renderer/core/LayoutConstraints.h>
#include <react/renderer/mounting/MountingOverrideDelegate.h>
#include <react/renderer/scheduler/InspectorData.h>
Expand Down Expand Up @@ -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<EventListener const> &listener);
void removeEventListener(
const std::shared_ptr<EventListener const> &listener);

private:
friend class SurfaceHandler;

Expand Down

0 comments on commit e5025f4

Please sign in to comment.