2 * Copyright (c) 2020 Samsung Electronics Co., Ltd.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
18 #import <Foundation/Foundation.h>
19 #include "extern-definitions.h"
21 #include "callback-manager-mac.h"
23 #include <unordered_map>
28 NSString *EventName = @"Dali::Internal::Adaptor::CallbackManager";
31 using Dali::Internal::Adaptor::CocoaCallbackManager;
33 // This is the observer that processes callback events
34 @interface CallbackObserver : NSObject
35 - (CallbackObserver *) init;
36 - (void) ReceiveCallback:(NSNotification *) aNotification;
39 // DaliCallback is the Objective-C object holding the information to execute the callback
40 @interface DaliCallback : NSObject
41 - (DaliCallback *) initWithImpl:(CocoaCallbackManager::Impl*) impl
42 withCallback:(Dali::CallbackBase *) callback;
43 - (void) ExecuteCallback;
46 namespace Dali::Internal::Adaptor
51 // Helper class to implement the callbacks containers
52 // The boolean value corresponds to the hasReturnValue parameter
53 struct CallbackContainer final : public std::unordered_map<CallbackBase*, bool>
55 using Parent = std::unordered_map<CallbackBase*, bool>;
56 using iterator = Parent::iterator;
57 using const_iterator = Parent::const_iterator;
58 using value_type = Parent::value_type;
59 using key_type = Parent::key_type;
60 using size_type = Parent::size_type;
62 ~CallbackContainer() { Clear(); }
64 void RemoveCallback(const_iterator item)
70 bool RemoveCallback(CallbackBase *callback)
72 if (auto it(find(callback)); it != end())
83 for (auto [cb, dummy]: *this)
91 // Execute the callback if it is present. The first item in the
92 // return value tells either the callback was executed or not.
93 // The second item gives the return value of the callback itself,
95 std::pair<const_iterator, std::optional<bool>>
96 Execute(CallbackBase *callback) const
98 std::optional<bool> retValue;
100 auto it(find(callback));
103 retValue = Execute(it);
106 return std::make_pair(it, retValue);
109 std::optional<bool> Execute(const_iterator it) const
111 auto [callback, hasReturnValue] = *it;
114 return CallbackBase::ExecuteReturn<bool>(*callback);
118 CallbackBase::Execute(*callback);
121 return std::optional<bool>();
126 // Internal implementation of the CallbackManager
127 struct CocoaCallbackManager::Impl final
129 CFRunLoopObserverContext mObserverContext;
131 Detail::CallbackContainer mCallbacks, mIdleEntererCallbacks;
132 CFRef<CFRunLoopObserverRef> mIdleObserver;
133 CallbackObserver *mObserver;
138 Impl(const Impl &) = delete;
139 Impl &operator=(const Impl&) = delete;
140 Impl(const Impl &&) = delete;
141 Impl &operator=(const Impl&&) = delete;
144 void EnqueueNotification(DaliCallback *callback) const;
145 inline bool AddIdleEntererCallback(CallbackBase *callback);
146 bool AddIdleCallback(CallbackBase *callback, bool hasReturnValue);
149 static void IdleEnterObserverCallback(
150 CFRunLoopObserverRef observer,
151 CFRunLoopActivity activity,
156 CocoaCallbackManager::Impl::Impl()
157 : mObserverContext{0, this, nullptr, nullptr, 0}
158 // mIdleObserver is configured to receive a notification
159 // when to run loop is about to sleep
160 , mIdleObserver(MakeRef(CFRunLoopObserverCreate(
162 kCFRunLoopBeforeWaiting,
165 IdleEnterObserverCallback,
168 CFRunLoopAddObserver(CFRunLoopGetMain(), mIdleObserver.get(), kCFRunLoopCommonModes);
169 mObserver = [[CallbackObserver alloc] init];
172 CocoaCallbackManager::Impl::~Impl()
174 CFRunLoopRemoveObserver(CFRunLoopGetMain(), mIdleObserver.get(), kCFRunLoopCommonModes);
175 auto *center = [NSNotificationCenter defaultCenter];
176 [center removeObserver:mObserver name:EventName object:nil];
179 bool CocoaCallbackManager::Impl::ProcessIdle()
181 auto ret = !mCallbacks.empty();
182 for (auto it(cbegin(mCallbacks)), e(cend(mCallbacks)); it != e; ++it)
184 if (!mCallbacks.Execute(it).value_or(false))
186 mCallbacks.RemoveCallback(it);
193 void CocoaCallbackManager::Impl::EnqueueNotification(DaliCallback *callback) const
195 auto *notification = [NSNotification notificationWithName:EventName object:callback];
196 auto *queue = [NSNotificationQueue defaultQueue];
197 [queue enqueueNotification:notification postingStyle:NSPostWhenIdle coalesceMask:0 forModes:nil];
200 bool CocoaCallbackManager::Impl::AddIdleEntererCallback(CallbackBase *callback)
202 return mIdleEntererCallbacks.emplace(callback, true).second;
205 void CocoaCallbackManager::Impl::IdleEnterObserverCallback(
206 CFRunLoopObserverRef observer,
207 CFRunLoopActivity activity,
211 auto *pImpl = reinterpret_cast<Impl*>(info);
213 for (auto it(cbegin(pImpl->mIdleEntererCallbacks)),
214 e(cend(pImpl->mIdleEntererCallbacks)); it != e; ++it)
216 if (!pImpl->mIdleEntererCallbacks.Execute(it).value_or(false))
218 pImpl->mIdleEntererCallbacks.RemoveCallback(it);
223 bool CocoaCallbackManager::Impl::AddIdleCallback(
224 CallbackBase *callback, bool hasReturnValue)
226 if (mCallbacks.emplace(callback, hasReturnValue).second)
228 auto *daliCallback = [[DaliCallback alloc] initWithImpl:this
229 withCallback:callback];
230 EnqueueNotification(daliCallback);
237 // Creates a concrete interface for CallbackManager
238 CallbackManager* CallbackManager::New()
240 return new CocoaCallbackManager;
243 CocoaCallbackManager::CocoaCallbackManager()
244 : mImpl(std::make_unique<CocoaCallbackManager::Impl>())
249 bool CocoaCallbackManager::AddIdleCallback(CallbackBase *callback, bool hasReturnValue)
251 return mRunning && mImpl->AddIdleCallback(callback, hasReturnValue);
254 void CocoaCallbackManager::RemoveIdleCallback(CallbackBase *callback)
256 mImpl->mCallbacks.RemoveCallback(callback);
259 bool CocoaCallbackManager::ProcessIdle()
261 return mImpl->ProcessIdle();
264 void CocoaCallbackManager::ClearIdleCallbacks()
266 mImpl->mCallbacks.Clear();
269 bool CocoaCallbackManager::AddIdleEntererCallback(CallbackBase* callback)
271 return mRunning && mImpl->AddIdleEntererCallback(callback);;
274 void CocoaCallbackManager::RemoveIdleEntererCallback(CallbackBase* callback)
276 mImpl->mIdleEntererCallbacks.RemoveCallback(callback);
279 void CocoaCallbackManager::Start()
281 DALI_ASSERT_DEBUG( mRunning == false );
285 void CocoaCallbackManager::Stop()
287 DALI_ASSERT_DEBUG( mRunning == true );
293 @implementation DaliCallback
295 CocoaCallbackManager::Impl *mImpl;
296 Dali::CallbackBase *mCallback;
299 - (DaliCallback *) initWithImpl:(CocoaCallbackManager::Impl *) impl
300 withCallback:(Dali::CallbackBase *) callback
306 mCallback = callback;
311 - (void) ExecuteCallback
313 // Look for the callback inside the list.
314 // If it is not there, then it was either called by ProcessIdle
315 // or was removed by RemoveCallback.
316 if (auto [iter, shouldKeep] = mImpl->mCallbacks.Execute(mCallback);
317 iter != mImpl->mCallbacks.end())
319 if (!shouldKeep.value_or(false))
321 mImpl->mCallbacks.RemoveCallback(iter);
325 mImpl->EnqueueNotification(self);
331 @implementation CallbackObserver
332 - (CallbackObserver *) init
337 auto *center = [NSNotificationCenter defaultCenter];
338 [center addObserver:self
339 selector:@selector(ReceiveCallback:)
346 - (void) ReceiveCallback:(NSNotification *)aNotification
348 DaliCallback *callback = [aNotification object];
349 [callback ExecuteCallback];