[dali_2.3.24] Merge branch 'devel/master'
[platform/core/uifw/dali-adaptor.git] / dali / internal / accessibility / bridge / bridge-base.cpp
1 /*
2  * Copyright (c) 2021 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/internal/accessibility/bridge/bridge-base.h>
20
21 // EXTERNAL INCLUDES
22 #include <dali/devel-api/common/stage.h>
23 #include <atomic>
24 #include <cstdlib>
25 #include <memory>
26
27 // INTERNAL INCLUDES
28 #include <dali/devel-api/adaptor-framework/window-devel.h>
29 #include <dali/public-api/adaptor-framework/timer.h>
30
31 using namespace Dali::Accessibility;
32
33 static Dali::Timer tickTimer;
34
35 BridgeBase::BridgeBase()
36 {
37 }
38
39 BridgeBase::~BridgeBase()
40 {
41   mApplication.mChildren.clear();
42 }
43
44 void BridgeBase::AddCoalescableMessage(CoalescableMessages kind, Dali::Accessibility::Accessible* obj, float delay, std::function<void()> functor)
45 {
46   if(delay < 0)
47   {
48     delay = 0;
49   }
50   auto countdownBase = static_cast<unsigned int>(delay * 10);
51
52   auto it = mCoalescableMessages.insert({{kind, obj}, {countdownBase, countdownBase, {}}});
53   if(it.second)
54   {
55     functor();
56   }
57   else
58   {
59     std::get<1>(it.first->second) = countdownBase;
60     std::get<2>(it.first->second) = std::move(functor);
61   }
62
63   if(!tickTimer)
64   {
65     tickTimer = Dali::Timer::New(100);
66     tickTimer.TickSignal().Connect(this, &BridgeBase::TickCoalescableMessages);
67   }
68
69   if(!tickTimer.IsRunning())
70   {
71     tickTimer.Start();
72   }
73 }
74
75 bool BridgeBase::TickCoalescableMessages()
76 {
77   for(auto it = mCoalescableMessages.begin(); it != mCoalescableMessages.end();)
78   {
79     auto& countdown     = std::get<0>(it->second);
80     auto  countdownBase = std::get<1>(it->second);
81     auto& functor       = std::get<2>(it->second);
82     if(countdown)
83     {
84       --countdown;
85     }
86     else
87     {
88       if(functor)
89       {
90         functor();
91         functor = {};
92         countdown = countdownBase;
93       }
94       else
95       {
96         it = mCoalescableMessages.erase(it);
97         continue;
98       }
99     }
100     ++it;
101   }
102   return !mCoalescableMessages.empty();
103 }
104
105 void BridgeBase::UpdateRegisteredEvents()
106 {
107   using ReturnType = std::vector<std::tuple<std::string, std::string>>;
108   mRegistry.method<DBus::ValueOrError<ReturnType>()>("GetRegisteredEvents").asyncCall([this](DBus::ValueOrError<ReturnType> msg) {
109     if(!msg)
110     {
111       LOG() << "Get registered events failed";
112       return;
113     }
114
115     IsBoundsChangedEventAllowed = false;
116
117     ReturnType values = std::get<ReturnType>(msg.getValues());
118     for(long unsigned int i = 0; i < values.size(); i++)
119     {
120       if(!std::get<1>(values[i]).compare("Object:BoundsChanged"))
121       {
122         IsBoundsChangedEventAllowed = true;
123       }
124     }
125   });
126 }
127
128 BridgeBase::ForceUpResult BridgeBase::ForceUp()
129 {
130   //TODO: checking mBusName is enough? or a new variable to check bridge state?
131   if(Bridge::ForceUp() == ForceUpResult::ALREADY_UP && !GetBusName().empty())
132   {
133     return ForceUpResult::ALREADY_UP;
134   }
135   auto proxy = DBus::DBusClient{dbusLocators::atspi::BUS, dbusLocators::atspi::OBJ_PATH, dbusLocators::atspi::BUS_INTERFACE, DBus::ConnectionType::SESSION};
136   auto addr  = proxy.method<std::string()>(dbusLocators::atspi::GET_ADDRESS).call();
137
138   if(!addr)
139   {
140     DALI_LOG_ERROR("failed at call '%s': %s\n", dbusLocators::atspi::GET_ADDRESS, addr.getError().message.c_str());
141     return ForceUpResult::FAILED;
142   }
143
144   mConnectionPtr  = DBusWrapper::Installed()->eldbus_address_connection_get_impl(std::get<0>(addr));
145   mData->mBusName = DBus::getConnectionName(mConnectionPtr);
146   mDbusServer     = {mConnectionPtr};
147
148   {
149     DBus::DBusInterfaceDescription desc{Accessible::GetInterfaceName(AtspiInterface::CACHE)};
150     AddFunctionToInterface(desc, "GetItems", &BridgeBase::GetItems);
151     mDbusServer.addInterface(AtspiDbusPathCache, desc);
152   }
153   {
154     DBus::DBusInterfaceDescription desc{Accessible::GetInterfaceName(AtspiInterface::APPLICATION)};
155     AddGetSetPropertyToInterface(desc, "Id", &BridgeBase::GetId, &BridgeBase::SetId);
156     mDbusServer.addInterface(AtspiPath, desc);
157   }
158
159   mRegistry = {AtspiDbusNameRegistry, AtspiDbusPathRegistry, Accessible::GetInterfaceName(AtspiInterface::REGISTRY), mConnectionPtr};
160
161   UpdateRegisteredEvents();
162
163   mRegistry.addSignal<void(void)>("EventListenerRegistered", [this](void) {
164     UpdateRegisteredEvents();
165   });
166
167   mRegistry.addSignal<void(void)>("EventListenerDeregistered", [this](void) {
168     UpdateRegisteredEvents();
169   });
170
171   return ForceUpResult::JUST_STARTED;
172 }
173
174 void BridgeBase::ForceDown()
175 {
176   Bridge::ForceDown();
177   tickTimer.Reset();
178   mCoalescableMessages.clear();
179   mRegistry      = {};
180   mDbusServer    = {};
181   mConnectionPtr = {};
182 }
183
184 const std::string& BridgeBase::GetBusName() const
185 {
186   static std::string empty;
187   return mData ? mData->mBusName : empty;
188 }
189
190 Accessible* BridgeBase::FindByPath(const std::string& name) const
191 {
192   try
193   {
194     return Find(name);
195   }
196   catch(std::domain_error&)
197   {
198     return nullptr;
199   }
200 }
201
202 void BridgeBase::AddTopLevelWindow(Accessible* windowAccessible)
203 {
204   if(windowAccessible->GetInternalActor() == nullptr)
205   {
206     return;
207   }
208
209   // Prevent adding the default window twice.
210   if(!mApplication.mChildren.empty() &&
211      mApplication.mChildren[0]->GetInternalActor() == windowAccessible->GetInternalActor())
212   {
213     return;
214   }
215
216   // Adds Window to a list of Windows.
217   mApplication.mChildren.push_back(windowAccessible);
218   SetIsOnRootLevel(windowAccessible);
219 }
220
221 void BridgeBase::RemoveTopLevelWindow(Accessible* windowAccessible)
222 {
223   for(auto i = 0u; i < mApplication.mChildren.size(); ++i)
224   {
225     if(mApplication.mChildren[i] == windowAccessible)
226     {
227       mApplication.mChildren.erase(mApplication.mChildren.begin() + i);
228       break;
229     }
230   }
231 }
232
233 void BridgeBase::CompressDefaultLabels()
234 {
235   // Remove entries for objects which no longer exist
236   mDefaultLabels.remove_if([](const DefaultLabelType& label) {
237     return !label.first.GetBaseHandle(); // Check window's weak handle
238     // TODO: Once Accessible becomes a handle type, check its weak handle here as well
239   });
240 }
241
242 void BridgeBase::RegisterDefaultLabel(Accessible* object)
243 {
244   CompressDefaultLabels();
245
246   Dali::WeakHandle<Dali::Window> window = GetWindow(object);
247   if(!window.GetBaseHandle()) // true also if `object` is null
248   {
249     DALI_LOG_ERROR("Cannot register default label: object does not belong to any window");
250     return;
251   }
252
253   auto it = std::find_if(mDefaultLabels.begin(), mDefaultLabels.end(), [object](const DefaultLabelType& label) {
254     return object == label.second;
255   });
256
257   if(it == mDefaultLabels.end())
258   {
259     mDefaultLabels.push_back({window, object});
260   }
261   else if(it->first != window)
262   {
263     // TODO: Tentative implementation. It is yet to be specified what should happen
264     // when the same object is re-registered as a default label for another window.
265     *it = {window, object};
266   }
267   else // it->first == window && it->second == object
268   {
269     // Nothing to do
270   }
271 }
272
273 void BridgeBase::UnregisterDefaultLabel(Accessible* object)
274 {
275   CompressDefaultLabels();
276
277   mDefaultLabels.remove_if([object](const DefaultLabelType& label) {
278     return object == label.second;
279   });
280 }
281
282 Accessible* BridgeBase::GetDefaultLabel(Accessible* root) const
283 {
284   Dali::WeakHandle<Dali::Window> window = GetWindow(root);
285   if(!window.GetBaseHandle())
286   {
287     return root;
288   }
289
290   auto it = std::find_if(mDefaultLabels.rbegin(), mDefaultLabels.rend(), [&window](const DefaultLabelType& label) {
291     return window == label.first;
292   });
293
294   return (it == mDefaultLabels.rend()) ? root : it->second;
295 }
296
297 std::string BridgeBase::StripPrefix(const std::string& path)
298 {
299   auto size = strlen(AtspiPath);
300   return path.substr(size + 1);
301 }
302
303 Accessible* BridgeBase::Find(const std::string& path) const
304 {
305   if(path == "root")
306   {
307     return &mApplication;
308   }
309
310   void*              accessible;
311   std::istringstream tmp{path};
312   if(!(tmp >> accessible))
313   {
314     throw std::domain_error{"invalid path '" + path + "'"};
315   }
316
317   auto it = mData->mKnownObjects.find(static_cast<Accessible*>(accessible));
318   if(it == mData->mKnownObjects.end() || (*it)->IsHidden())
319   {
320     throw std::domain_error{"unknown object '" + path + "'"};
321   }
322
323   return static_cast<Accessible*>(accessible);
324 }
325
326 Accessible* BridgeBase::Find(const Address& ptr) const
327 {
328   assert(ptr.GetBus() == mData->mBusName);
329   return Find(ptr.GetPath());
330 }
331
332 Accessible* BridgeBase::FindCurrentObject() const
333 {
334   auto path = DBus::DBusServer::getCurrentObjectPath();
335   auto size = strlen(AtspiPath);
336   if(path.size() <= size)
337   {
338     throw std::domain_error{"invalid path '" + path + "'"};
339   }
340   if(path.substr(0, size) != AtspiPath)
341   {
342     throw std::domain_error{"invalid path '" + path + "'"};
343   }
344   if(path[size] != '/')
345   {
346     throw std::domain_error{"invalid path '" + path + "'"};
347   }
348   return Find(StripPrefix(path));
349 }
350
351 void BridgeBase::SetId(int id)
352 {
353   this->mId = id;
354 }
355
356 int BridgeBase::GetId()
357 {
358   return this->mId;
359 }
360
361 auto BridgeBase::GetItems() -> DBus::ValueOrError<std::vector<CacheElementType>>
362 {
363   auto root = &mApplication;
364
365   std::vector<CacheElementType> res;
366
367   std::function<void(Accessible*)> proc =
368     [&](Accessible* item) {
369       res.emplace_back(std::move(CreateCacheElement(root)));
370       for(auto i = 0u; i < item->GetChildCount(); ++i)
371       {
372         proc(item->GetChildAtIndex(i));
373       }
374     };
375
376   return res;
377 }
378
379 auto BridgeBase::CreateCacheElement(Accessible* item) -> CacheElementType
380 {
381   if(!item)
382   {
383     return {};
384   }
385
386   auto root   = &mApplication;
387   auto parent = item->GetParent();
388
389   std::vector<Address> children;
390   for(auto i = 0u; i < item->GetChildCount(); ++i)
391   {
392     children.emplace_back(item->GetChildAtIndex(i)->GetAddress());
393   }
394
395   return std::make_tuple(
396     item->GetAddress(),
397     root->GetAddress(),
398     parent ? parent->GetAddress() : Address{},
399     children,
400     item->GetInterfacesAsStrings(),
401     item->GetName(),
402     item->GetRole(),
403     item->GetDescription(),
404     item->GetStates().GetRawData());
405 }
406
407 Dali::WeakHandle<Dali::Window> BridgeBase::GetWindow(Dali::Accessibility::Accessible* accessible)
408 {
409   Dali::WeakHandle<Dali::Window> windowHandle;
410   Dali::Actor                    actor = accessible ? accessible->GetInternalActor() : Dali::Actor();
411
412   if(actor)
413   {
414     Dali::Window window = Dali::DevelWindow::Get(actor);
415     windowHandle        = {window};
416   }
417
418   return windowHandle;
419 }