Merge "[AT-SPI] Rework intercepting key events" into 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   mRegistry      = {};
178   mDbusServer    = {};
179   mConnectionPtr = {};
180 }
181
182 const std::string& BridgeBase::GetBusName() const
183 {
184   static std::string empty;
185   return mData ? mData->mBusName : empty;
186 }
187
188 Accessible* BridgeBase::FindByPath(const std::string& name) const
189 {
190   try
191   {
192     return Find(name);
193   }
194   catch(std::domain_error&)
195   {
196     return nullptr;
197   }
198 }
199
200 void BridgeBase::AddTopLevelWindow(Accessible* windowAccessible)
201 {
202   if(windowAccessible->GetInternalActor() == nullptr)
203   {
204     return;
205   }
206
207   // Prevent adding the default window twice.
208   if(!mApplication.mChildren.empty() &&
209      mApplication.mChildren[0]->GetInternalActor() == windowAccessible->GetInternalActor())
210   {
211     return;
212   }
213
214   // Adds Window to a list of Windows.
215   mApplication.mChildren.push_back(windowAccessible);
216   SetIsOnRootLevel(windowAccessible);
217 }
218
219 void BridgeBase::RemoveTopLevelWindow(Accessible* windowAccessible)
220 {
221   for(auto i = 0u; i < mApplication.mChildren.size(); ++i)
222   {
223     if(mApplication.mChildren[i] == windowAccessible)
224     {
225       mApplication.mChildren.erase(mApplication.mChildren.begin() + i);
226       break;
227     }
228   }
229 }
230
231 void BridgeBase::CompressDefaultLabels()
232 {
233   // Remove entries for objects which no longer exist
234   mDefaultLabels.remove_if([](const DefaultLabelType& label) {
235     return !label.first.GetBaseHandle(); // Check window's weak handle
236     // TODO: Once Accessible becomes a handle type, check its weak handle here as well
237   });
238 }
239
240 void BridgeBase::RegisterDefaultLabel(Accessible* object)
241 {
242   CompressDefaultLabels();
243
244   Dali::WeakHandle<Dali::Window> window = GetWindow(object);
245   if(!window.GetBaseHandle()) // true also if `object` is null
246   {
247     DALI_LOG_ERROR("Cannot register default label: object does not belong to any window");
248     return;
249   }
250
251   auto it = std::find_if(mDefaultLabels.begin(), mDefaultLabels.end(), [object](const DefaultLabelType& label) {
252     return object == label.second;
253   });
254
255   if(it == mDefaultLabels.end())
256   {
257     mDefaultLabels.push_back({window, object});
258   }
259   else if(it->first != window)
260   {
261     // TODO: Tentative implementation. It is yet to be specified what should happen
262     // when the same object is re-registered as a default label for another window.
263     *it = {window, object};
264   }
265   else // it->first == window && it->second == object
266   {
267     // Nothing to do
268   }
269 }
270
271 void BridgeBase::UnregisterDefaultLabel(Accessible* object)
272 {
273   CompressDefaultLabels();
274
275   mDefaultLabels.remove_if([object](const DefaultLabelType& label) {
276     return object == label.second;
277   });
278 }
279
280 Accessible* BridgeBase::GetDefaultLabel(Accessible* root) const
281 {
282   Dali::WeakHandle<Dali::Window> window = GetWindow(root);
283   if(!window.GetBaseHandle())
284   {
285     return root;
286   }
287
288   auto it = std::find_if(mDefaultLabels.rbegin(), mDefaultLabels.rend(), [&window](const DefaultLabelType& label) {
289     return window == label.first;
290   });
291
292   return (it == mDefaultLabels.rend()) ? root : it->second;
293 }
294
295 std::string BridgeBase::StripPrefix(const std::string& path)
296 {
297   auto size = strlen(AtspiPath);
298   return path.substr(size + 1);
299 }
300
301 Accessible* BridgeBase::Find(const std::string& path) const
302 {
303   if(path == "root")
304   {
305     return &mApplication;
306   }
307
308   void*              accessible;
309   std::istringstream tmp{path};
310   if(!(tmp >> accessible))
311   {
312     throw std::domain_error{"invalid path '" + path + "'"};
313   }
314
315   auto it = mData->mKnownObjects.find(static_cast<Accessible*>(accessible));
316   if(it == mData->mKnownObjects.end() || (*it)->IsHidden())
317   {
318     throw std::domain_error{"unknown object '" + path + "'"};
319   }
320
321   return static_cast<Accessible*>(accessible);
322 }
323
324 Accessible* BridgeBase::Find(const Address& ptr) const
325 {
326   assert(ptr.GetBus() == mData->mBusName);
327   return Find(ptr.GetPath());
328 }
329
330 Accessible* BridgeBase::FindCurrentObject() const
331 {
332   auto path = DBus::DBusServer::getCurrentObjectPath();
333   auto size = strlen(AtspiPath);
334   if(path.size() <= size)
335   {
336     throw std::domain_error{"invalid path '" + path + "'"};
337   }
338   if(path.substr(0, size) != AtspiPath)
339   {
340     throw std::domain_error{"invalid path '" + path + "'"};
341   }
342   if(path[size] != '/')
343   {
344     throw std::domain_error{"invalid path '" + path + "'"};
345   }
346   return Find(StripPrefix(path));
347 }
348
349 void BridgeBase::SetId(int id)
350 {
351   this->mId = id;
352 }
353
354 int BridgeBase::GetId()
355 {
356   return this->mId;
357 }
358
359 auto BridgeBase::GetItems() -> DBus::ValueOrError<std::vector<CacheElementType>>
360 {
361   auto root = &mApplication;
362
363   std::vector<CacheElementType> res;
364
365   std::function<void(Accessible*)> proc =
366     [&](Accessible* item) {
367       res.emplace_back(std::move(CreateCacheElement(root)));
368       for(auto i = 0u; i < item->GetChildCount(); ++i)
369       {
370         proc(item->GetChildAtIndex(i));
371       }
372     };
373
374   return res;
375 }
376
377 auto BridgeBase::CreateCacheElement(Accessible* item) -> CacheElementType
378 {
379   if(!item)
380   {
381     return {};
382   }
383
384   auto root   = &mApplication;
385   auto parent = item->GetParent();
386
387   std::vector<Address> children;
388   for(auto i = 0u; i < item->GetChildCount(); ++i)
389   {
390     children.emplace_back(item->GetChildAtIndex(i)->GetAddress());
391   }
392
393   return std::make_tuple(
394     item->GetAddress(),
395     root->GetAddress(),
396     parent ? parent->GetAddress() : Address{},
397     children,
398     item->GetInterfacesAsStrings(),
399     item->GetName(),
400     item->GetRole(),
401     item->GetDescription(),
402     item->GetStates().GetRawData());
403 }
404
405 Dali::WeakHandle<Dali::Window> BridgeBase::GetWindow(Dali::Accessibility::Accessible* accessible)
406 {
407   Dali::WeakHandle<Dali::Window> windowHandle;
408   Dali::Actor                    actor = accessible ? accessible->GetInternalActor() : Dali::Actor();
409
410   if(actor)
411   {
412     Dali::Window window = Dali::DevelWindow::Get(actor);
413     windowHandle        = {window};
414   }
415
416   return windowHandle;
417 }