[AT-SPI] Fixing reseting of delay for CoalescableMessages
[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/public-api/adaptor-framework/timer.h>
29
30 using namespace Dali::Accessibility;
31
32 static Dali::Timer tickTimer;
33
34 BridgeBase::BridgeBase()
35 {
36 }
37
38 BridgeBase::~BridgeBase()
39 {
40   mApplication.mChildren.clear();
41 }
42
43 void BridgeBase::AddCoalescableMessage(CoalescableMessages kind, Dali::Accessibility::Accessible* obj, float delay, std::function<void()> functor)
44 {
45   if(delay < 0)
46   {
47     delay = 0;
48   }
49   auto countdownBase = static_cast<unsigned int>(delay * 10);
50
51   auto it = mCoalescableMessages.insert({{kind, obj}, {countdownBase, countdownBase, {}}});
52   if(it.second)
53   {
54     functor();
55   }
56   else
57   {
58     std::get<1>(it.first->second) = countdownBase;
59     std::get<2>(it.first->second) = std::move(functor);
60   }
61
62   if(!tickTimer)
63   {
64     tickTimer = Dali::Timer::New(100);
65     tickTimer.TickSignal().Connect(this, &BridgeBase::TickCoalescableMessages);
66   }
67
68   if(!tickTimer.IsRunning())
69   {
70     tickTimer.Start();
71   }
72 }
73
74 bool BridgeBase::TickCoalescableMessages()
75 {
76   for(auto it = mCoalescableMessages.begin(); it != mCoalescableMessages.end();)
77   {
78     auto& countdown     = std::get<0>(it->second);
79     auto  countdownBase = std::get<1>(it->second);
80     auto& functor       = std::get<2>(it->second);
81     if(countdown)
82     {
83       --countdown;
84     }
85     else
86     {
87       if(functor)
88       {
89         functor();
90         functor = {};
91         countdown = countdownBase;
92       }
93       else
94       {
95         it = mCoalescableMessages.erase(it);
96         continue;
97       }
98     }
99     ++it;
100   }
101   return !mCoalescableMessages.empty();
102 }
103
104 void BridgeBase::UpdateRegisteredEvents()
105 {
106   using ReturnType = std::vector<std::tuple<std::string, std::string>>;
107   mRegistry.method<DBus::ValueOrError<ReturnType>()>("GetRegisteredEvents").asyncCall([this](DBus::ValueOrError<ReturnType> msg) {
108     if(!msg)
109     {
110       LOG() << "Get registered events failed";
111       return;
112     }
113
114     IsBoundsChangedEventAllowed = false;
115
116     ReturnType values = std::get<ReturnType>(msg.getValues());
117     for(long unsigned int i = 0; i < values.size(); i++)
118     {
119       if(!std::get<1>(values[i]).compare("Object:BoundsChanged"))
120       {
121         IsBoundsChangedEventAllowed = true;
122       }
123     }
124   });
125 }
126
127 BridgeBase::ForceUpResult BridgeBase::ForceUp()
128 {
129   //TODO: checking mBusName is enough? or a new variable to check bridge state?
130   if(Bridge::ForceUp() == ForceUpResult::ALREADY_UP && !GetBusName().empty())
131   {
132     return ForceUpResult::ALREADY_UP;
133   }
134   auto proxy = DBus::DBusClient{dbusLocators::atspi::BUS, dbusLocators::atspi::OBJ_PATH, dbusLocators::atspi::BUS_INTERFACE, DBus::ConnectionType::SESSION};
135   auto addr  = proxy.method<std::string()>(dbusLocators::atspi::GET_ADDRESS).call();
136
137   if(!addr)
138   {
139     DALI_LOG_ERROR("failed at call '%s': %s\n", dbusLocators::atspi::GET_ADDRESS, addr.getError().message.c_str());
140     return ForceUpResult::FAILED;
141   }
142
143   mConnectionPtr  = DBusWrapper::Installed()->eldbus_address_connection_get_impl(std::get<0>(addr));
144   mData->mBusName = DBus::getConnectionName(mConnectionPtr);
145   mDbusServer     = {mConnectionPtr};
146
147   {
148     DBus::DBusInterfaceDescription desc{Accessible::GetInterfaceName(AtspiInterface::CACHE)};
149     AddFunctionToInterface(desc, "GetItems", &BridgeBase::GetItems);
150     mDbusServer.addInterface(AtspiDbusPathCache, desc);
151   }
152   {
153     DBus::DBusInterfaceDescription desc{Accessible::GetInterfaceName(AtspiInterface::APPLICATION)};
154     AddGetSetPropertyToInterface(desc, "Id", &BridgeBase::GetId, &BridgeBase::SetId);
155     mDbusServer.addInterface(AtspiPath, desc);
156   }
157
158   mRegistry = {AtspiDbusNameRegistry, AtspiDbusPathRegistry, Accessible::GetInterfaceName(AtspiInterface::REGISTRY), mConnectionPtr};
159
160   UpdateRegisteredEvents();
161
162   mRegistry.addSignal<void(void)>("EventListenerRegistered", [this](void) {
163     UpdateRegisteredEvents();
164   });
165
166   mRegistry.addSignal<void(void)>("EventListenerDeregistered", [this](void) {
167     UpdateRegisteredEvents();
168   });
169
170   return ForceUpResult::JUST_STARTED;
171 }
172
173 void BridgeBase::ForceDown()
174 {
175   Bridge::ForceDown();
176   mRegistry      = {};
177   mDbusServer    = {};
178   mConnectionPtr = {};
179 }
180
181 const std::string& BridgeBase::GetBusName() const
182 {
183   static std::string empty;
184   return mData ? mData->mBusName : empty;
185 }
186
187 Accessible* BridgeBase::FindByPath(const std::string& name) const
188 {
189   try
190   {
191     return Find(name);
192   }
193   catch(std::domain_error&)
194   {
195     return nullptr;
196   }
197 }
198
199 void BridgeBase::AddTopLevelWindow(Accessible* windowAccessible)
200 {
201   if(windowAccessible->GetInternalActor() == nullptr)
202   {
203     return;
204   }
205
206   // Prevent adding the default window twice.
207   if(!mApplication.mChildren.empty() &&
208      mApplication.mChildren[0]->GetInternalActor() == windowAccessible->GetInternalActor())
209   {
210     return;
211   }
212
213   // Adds Window to a list of Windows.
214   mApplication.mChildren.push_back(windowAccessible);
215   SetIsOnRootLevel(windowAccessible);
216
217   RegisterDefaultLabel(windowAccessible);
218 }
219
220 void BridgeBase::RemoveTopLevelWindow(Accessible* windowAccessible)
221 {
222   UnregisterDefaultLabel(windowAccessible);
223
224   for(auto i = 0u; i < mApplication.mChildren.size(); ++i)
225   {
226     if(mApplication.mChildren[i] == windowAccessible)
227     {
228       mApplication.mChildren.erase(mApplication.mChildren.begin() + i);
229       break;
230     }
231   }
232 }
233
234 void BridgeBase::RegisterDefaultLabel(Accessible* object)
235 {
236   if(std::find(mDefaultLabels.begin(), mDefaultLabels.end(), object) == mDefaultLabels.end())
237   {
238     mDefaultLabels.push_back(object);
239   }
240 }
241
242 void BridgeBase::UnregisterDefaultLabel(Accessible* object)
243 {
244   auto it = std::find(mDefaultLabels.begin(), mDefaultLabels.end(), object);
245   if(it != mDefaultLabels.end())
246   {
247     mDefaultLabels.erase(it);
248   }
249 }
250
251 std::string BridgeBase::StripPrefix(const std::string& path)
252 {
253   auto size = strlen(AtspiPath);
254   return path.substr(size + 1);
255 }
256
257 Accessible* BridgeBase::Find(const std::string& path) const
258 {
259   if(path == "root")
260   {
261     return &mApplication;
262   }
263
264   void*              accessible;
265   std::istringstream tmp{path};
266   if(!(tmp >> accessible))
267   {
268     throw std::domain_error{"invalid path '" + path + "'"};
269   }
270
271   auto it = mData->mKnownObjects.find(static_cast<Accessible*>(accessible));
272   if(it == mData->mKnownObjects.end() || (*it)->IsHidden())
273   {
274     throw std::domain_error{"unknown object '" + path + "'"};
275   }
276
277   return static_cast<Accessible*>(accessible);
278 }
279
280 Accessible* BridgeBase::Find(const Address& ptr) const
281 {
282   assert(ptr.GetBus() == mData->mBusName);
283   return Find(ptr.GetPath());
284 }
285
286 Accessible* BridgeBase::FindCurrentObject() const
287 {
288   auto path = DBus::DBusServer::getCurrentObjectPath();
289   auto size = strlen(AtspiPath);
290   if(path.size() <= size)
291   {
292     throw std::domain_error{"invalid path '" + path + "'"};
293   }
294   if(path.substr(0, size) != AtspiPath)
295   {
296     throw std::domain_error{"invalid path '" + path + "'"};
297   }
298   if(path[size] != '/')
299   {
300     throw std::domain_error{"invalid path '" + path + "'"};
301   }
302   return Find(StripPrefix(path));
303 }
304
305 void BridgeBase::SetId(int id)
306 {
307   this->mId = id;
308 }
309
310 int BridgeBase::GetId()
311 {
312   return this->mId;
313 }
314
315 auto BridgeBase::GetItems() -> DBus::ValueOrError<std::vector<CacheElementType>>
316 {
317   auto root = &mApplication;
318
319   std::vector<CacheElementType> res;
320
321   std::function<void(Accessible*)> proc =
322     [&](Accessible* item) {
323       res.emplace_back(std::move(CreateCacheElement(root)));
324       for(auto i = 0u; i < item->GetChildCount(); ++i)
325       {
326         proc(item->GetChildAtIndex(i));
327       }
328     };
329
330   return res;
331 }
332
333 auto BridgeBase::CreateCacheElement(Accessible* item) -> CacheElementType
334 {
335   if(!item)
336   {
337     return {};
338   }
339
340   auto root   = &mApplication;
341   auto parent = item->GetParent();
342
343   std::vector<Address> children;
344   for(auto i = 0u; i < item->GetChildCount(); ++i)
345   {
346     children.emplace_back(item->GetChildAtIndex(i)->GetAddress());
347   }
348
349   return std::make_tuple(
350     item->GetAddress(),
351     root->GetAddress(),
352     parent ? parent->GetAddress() : Address{},
353     children,
354     item->GetInterfacesAsStrings(),
355     item->GetName(),
356     item->GetRole(),
357     item->GetDescription(),
358     item->GetStates().GetRawData());
359 }