[Tizen] Calculrate screen position with RenderTask
[platform/core/uifw/dali-core.git] / dali / public-api / signals / base-signal.cpp
1 /*
2  * Copyright (c) 2022 Samsung Electronics Co., Ltd.
3  *
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
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
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.
15  *
16  */
17
18 // CLASS HEADER
19 #include <dali/public-api/signals/base-signal.h>
20
21 // EXTERNAL INCLUDES
22 #include <unordered_map>
23
24 // INTERNAL INCLUDES
25 #include <dali/integration-api/debug.h>
26
27 namespace
28 {
29 struct CallbackBasePtrHash
30 {
31   std::size_t operator()(const Dali::CallbackBase* callback) const noexcept
32   {
33     std::size_t functionHash = reinterpret_cast<std::size_t>(reinterpret_cast<void*>(callback->mFunction));
34     std::size_t objectHash   = reinterpret_cast<std::size_t>(reinterpret_cast<void*>(callback->mImpl.mObjectPointer));
35     return functionHash ^ objectHash;
36   }
37 };
38 struct CallbackBasePtrEqual
39 {
40   bool operator()(const Dali::CallbackBase* lhs, const Dali::CallbackBase* rhs) const noexcept
41   {
42     return (*lhs) == (*rhs);
43   }
44 };
45 } // unnamed namespace
46
47 namespace Dali
48 {
49 /**
50  * @brief Extra struct for callback base cache.
51  */
52 struct BaseSignal::Impl
53 {
54   Impl()  = default;
55   ~Impl() = default;
56
57   /**
58    * @brief Get the iterator of connections list by the callback base pointer.
59    * Note that we should compare the 'value' of callback, not pointer.
60    * So, we need to define custom hash & compare functor of callback base pointer.
61    */
62   std::unordered_map<const CallbackBase*, std::list<SignalConnection>::iterator, CallbackBasePtrHash, CallbackBasePtrEqual> mCallbackCache;
63 };
64
65 BaseSignal::BaseSignal()
66 : mCacheImpl(new BaseSignal::Impl()),
67   mEmittingFlag(false)
68 {
69 }
70
71 BaseSignal::~BaseSignal()
72 {
73   // We can't assert in a destructor
74   if(mEmittingFlag)
75   {
76     DALI_LOG_ERROR("Invalid destruction of Signal during Emit()\n");
77
78     // Set the signal deletion flag as well if set
79     if(mSignalDeleted)
80     {
81       *mSignalDeleted = true;
82     }
83   }
84
85   // The signal is being destroyed. We have to inform any slots
86   // that are connected, that the signal is dead.
87   for(auto iter = mSignalConnections.begin(), iterEnd = mSignalConnections.end(); iter != iterEnd; ++iter)
88   {
89     auto& connection = *iter;
90
91     // Note that values are set to NULL in DeleteConnection
92     if(connection)
93     {
94       connection.Disconnect(this);
95     }
96   }
97
98   delete mCacheImpl;
99 }
100
101 void BaseSignal::OnConnect(CallbackBase* callback)
102 {
103   DALI_ASSERT_ALWAYS(nullptr != callback && "Invalid member function pointer passed to Connect()");
104
105   auto iter = FindCallback(callback);
106
107   // Don't double-connect the same callback
108   if(iter == mSignalConnections.end())
109   {
110     auto newIter = mSignalConnections.insert(mSignalConnections.end(), SignalConnection(callback));
111
112     // Store inserted iterator for this callback
113     mCacheImpl->mCallbackCache[callback] = newIter;
114   }
115   else
116   {
117     // clean-up required
118     delete callback;
119   }
120 }
121
122 void BaseSignal::OnDisconnect(CallbackBase* callback)
123 {
124   DALI_ASSERT_ALWAYS(nullptr != callback && "Invalid member function pointer passed to Disconnect()");
125
126   auto iter = FindCallback(callback);
127
128   if(iter != mSignalConnections.end())
129   {
130     DeleteConnection(iter);
131   }
132
133   // call back is a temporary created to find which slot should be disconnected.
134   delete callback;
135 }
136
137 void BaseSignal::OnConnect(ConnectionTrackerInterface* tracker, CallbackBase* callback)
138 {
139   DALI_ASSERT_ALWAYS(nullptr != tracker && "Invalid ConnectionTrackerInterface pointer passed to Connect()");
140   DALI_ASSERT_ALWAYS(nullptr != callback && "Invalid member function pointer passed to Connect()");
141
142   auto iter = FindCallback(callback);
143
144   // Don't double-connect the same callback
145   if(iter == mSignalConnections.end())
146   {
147     auto newIter = mSignalConnections.insert(mSignalConnections.end(), {tracker, callback});
148
149     // Store inserted iterator for this callback
150     mCacheImpl->mCallbackCache[callback] = newIter;
151
152     // Let the connection tracker know that a connection between a signal and a slot has been made.
153     tracker->SignalConnected(this, callback);
154   }
155   else
156   {
157     // clean-up required
158     delete callback;
159   }
160 }
161
162 void BaseSignal::OnDisconnect(ConnectionTrackerInterface* tracker, CallbackBase* callback)
163 {
164   DALI_ASSERT_ALWAYS(nullptr != tracker && "Invalid ConnectionTrackerInterface pointer passed to Disconnect()");
165   DALI_ASSERT_ALWAYS(nullptr != callback && "Invalid member function pointer passed to Disconnect()");
166
167   auto iter = FindCallback(callback);
168
169   if(iter != mSignalConnections.end())
170   {
171     // temporary pointer to disconnected callback
172     // Note that (*iter).GetCallback() != callback is possible.
173     CallbackBase* disconnectedCallback = (*iter).GetCallback();
174
175     // close the signal side connection first.
176     DeleteConnection(iter);
177
178     // close the slot side connection
179     tracker->SignalDisconnected(this, disconnectedCallback);
180   }
181
182   // call back is a temporary created to find which slot should be disconnected.
183   delete callback;
184 }
185
186 // for SlotObserver::SlotDisconnected
187 void BaseSignal::SlotDisconnected(CallbackBase* callback)
188 {
189   DALI_ASSERT_ALWAYS(nullptr != callback && "Invalid callback function passed to SlotObserver::SlotDisconnected()");
190
191   auto iter = FindCallback(callback);
192   if(DALI_LIKELY(iter != mSignalConnections.end()))
193   {
194     DeleteConnection(iter);
195     return;
196   }
197
198   DALI_ABORT("Callback lost in SlotDisconnected()");
199 }
200
201 std::list<SignalConnection>::iterator BaseSignal::FindCallback(CallbackBase* callback) noexcept
202 {
203   const auto& convertorIter = mCacheImpl->mCallbackCache.find(callback);
204
205   if(convertorIter != mCacheImpl->mCallbackCache.end())
206   {
207     const auto& iter = convertorIter->second; // std::list<SignalConnection>::iterator
208
209     if(*iter) // the value of iterator can be null.
210     {
211       if(*(iter->GetCallback()) == *callback)
212       {
213         return iter;
214       }
215     }
216   }
217   return mSignalConnections.end();
218 }
219
220 void BaseSignal::DeleteConnection(std::list<SignalConnection>::iterator iter)
221 {
222   // Erase cache first.
223   mCacheImpl->mCallbackCache.erase(iter->GetCallback());
224
225   if(mEmittingFlag)
226   {
227     // IMPORTANT - do not remove from items from mSignalConnections, reset instead.
228     // Signal Emit() methods require that connection count is not reduced while iterating
229     // i.e. DeleteConnection can be called from within callbacks, while iterating through mSignalConnections.
230     (*iter) = {nullptr};
231     ++mNullConnections;
232   }
233   else
234   {
235     // If application connects and disconnects without the signal never emitting,
236     // the mSignalConnections vector keeps growing and growing as CleanupConnections() is done from Emit.
237     mSignalConnections.erase(iter);
238   }
239 }
240
241 void BaseSignal::CleanupConnections()
242 {
243   if(!mSignalConnections.empty())
244   {
245     //Remove Signals that are already markeed nullptr.
246     mSignalConnections.remove_if([](auto& elem) { return (elem) ? false : true; });
247   }
248   mNullConnections = 0;
249 }
250
251 // BaseSignal::EmitGuard
252
253 BaseSignal::EmitGuard::EmitGuard(bool& flag)
254 : mFlag(nullptr)
255 {
256   if(!flag)
257   {
258     mFlag = &flag;
259     flag  = true;
260   }
261   else
262   {
263     // mFlag is NULL when Emit() is called during Emit()
264     DALI_LOG_ERROR("Cannot call Emit() from inside Emit()\n");
265   }
266 }
267
268 BaseSignal::EmitGuard::~EmitGuard()
269 {
270   if(mFlag)
271   {
272     *mFlag = false;
273   }
274 }
275
276 bool BaseSignal::EmitGuard::ErrorOccurred()
277 {
278   // mFlag is NULL when Emit() is called during Emit()
279   return (nullptr == mFlag);
280 }
281
282 } // namespace Dali