[ATSPI] Apply multi-window to ATSPI
[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   mApplication.mWindows.clear();
42 }
43
44 void BridgeBase::AddFilteredEvent(FilteredEvents kind, Dali::Accessibility::Accessible* obj, float delay, std::function<void()> functor)
45 {
46   if(delay < 0)
47   {
48     delay = 0;
49   }
50
51   auto it = mFilteredEvents.insert({{kind, obj}, {static_cast<unsigned int>(delay * 10), {}}});
52   if(it.second)
53   {
54     functor();
55   }
56   else
57   {
58     it.first->second.second = std::move(functor);
59   }
60
61   if(!tickTimer)
62   {
63     tickTimer = Dali::Timer::New(100);
64     tickTimer.TickSignal().Connect(this, &BridgeBase::TickFilteredEvents);
65   }
66 }
67
68 bool BridgeBase::TickFilteredEvents()
69 {
70   for(auto it = mFilteredEvents.begin(); it != mFilteredEvents.end();)
71   {
72     if(it->second.first)
73     {
74       --it->second.first;
75     }
76     else
77     {
78       if(it->second.second)
79       {
80         it->second.second();
81         it->second.second = {};
82       }
83       else
84       {
85         it = mFilteredEvents.erase(it);
86         continue;
87       }
88     }
89     ++it;
90   }
91   return !mFilteredEvents.empty();
92 }
93
94 void BridgeBase::UpdateRegisteredEvents()
95 {
96   using ReturnType = std::vector<std::tuple<std::string, std::string>>;
97   mRegistry.method<DBus::ValueOrError<ReturnType>()>("GetRegisteredEvents").asyncCall([this](DBus::ValueOrError<ReturnType> msg) {
98     if(!msg)
99     {
100       LOG() << "Get registered events failed";
101       return;
102     }
103
104     IsBoundsChangedEventAllowed = false;
105
106     ReturnType values = std::get<ReturnType>(msg.getValues());
107     for(long unsigned int i = 0; i < values.size(); i++)
108     {
109       if(!std::get<1>(values[i]).compare("Object:BoundsChanged"))
110       {
111         IsBoundsChangedEventAllowed = true;
112       }
113     }
114   });
115 }
116
117 BridgeBase::ForceUpResult BridgeBase::ForceUp()
118 {
119   if(Bridge::ForceUp() == ForceUpResult::ALREADY_UP)
120   {
121     return ForceUpResult::ALREADY_UP;
122   }
123   auto proxy = DBus::DBusClient{dbusLocators::atspi::BUS, dbusLocators::atspi::OBJ_PATH, dbusLocators::atspi::BUS_INTERFACE, DBus::ConnectionType::SESSION};
124   auto addr  = proxy.method<std::string()>(dbusLocators::atspi::GET_ADDRESS).call();
125
126   if(!addr)
127   {
128     throw std::domain_error{std::string("failed at call '") + dbusLocators::atspi::GET_ADDRESS + "': " + addr.getError().message};
129   }
130
131   mConnectionPtr  = DBusWrapper::Installed()->eldbus_address_connection_get_impl(std::get<0>(addr));
132   mData->mBusName = DBus::getConnectionName(mConnectionPtr);
133   mDbusServer     = {mConnectionPtr};
134
135   {
136     DBus::DBusInterfaceDescription desc{AtspiDbusInterfaceCache};
137     AddFunctionToInterface(desc, "GetItems", &BridgeBase::GetItems);
138     mDbusServer.addInterface(AtspiDbusPathCache, desc);
139   }
140   {
141     DBus::DBusInterfaceDescription desc{AtspiDbusInterfaceApplication};
142     AddGetSetPropertyToInterface(desc, "Id", &BridgeBase::GetId, &BridgeBase::SetId);
143     mDbusServer.addInterface(AtspiPath, desc);
144   }
145
146   mRegistry = {AtspiDbusNameRegistry, AtspiDbusPathRegistry, AtspiDbusInterfaceRegistry, mConnectionPtr};
147
148   UpdateRegisteredEvents();
149
150   mRegistry.addSignal<void(void)>("EventListenerRegistered", [this](void) {
151     UpdateRegisteredEvents();
152   });
153
154   mRegistry.addSignal<void(void)>("EventListenerDeregistered", [this](void) {
155     UpdateRegisteredEvents();
156   });
157
158   return ForceUpResult::JUST_STARTED;
159 }
160
161 void BridgeBase::ForceDown()
162 {
163   Bridge::ForceDown();
164   mRegistry      = {};
165   mDbusServer    = {};
166   mConnectionPtr = {};
167 }
168
169 const std::string& BridgeBase::GetBusName() const
170 {
171   static std::string empty;
172   return mData ? mData->mBusName : empty;
173 }
174
175 Accessible* BridgeBase::FindByPath(const std::string& name) const
176 {
177   try
178   {
179     return Find(name);
180   }
181   catch(std::domain_error&)
182   {
183     return nullptr;
184   }
185 }
186
187 void BridgeBase::AddPopup(Accessible* object)
188 {
189   if(std::find(mPopups.begin(), mPopups.end(), object) != mPopups.end())
190   {
191     return;
192   }
193   mPopups.push_back(object);
194   if(IsUp())
195   {
196     object->Emit(WindowEvent::ACTIVATE, 0);
197   }
198 }
199
200 void BridgeBase::RemovePopup(Accessible* object)
201 {
202   auto it = std::find(mPopups.begin(), mPopups.end(), object);
203   if(it == mPopups.end())
204   {
205     return;
206   }
207   mPopups.erase(it);
208
209   if(IsUp())
210   {
211     object->Emit(WindowEvent::DEACTIVATE, 0);
212     if(mPopups.empty())
213     {
214       mApplication.mChildren.back()->Emit(WindowEvent::ACTIVATE, 0);
215     }
216     else
217     {
218       mPopups.back()->Emit(WindowEvent::ACTIVATE, 0);
219     }
220   }
221 }
222
223 void BridgeBase::OnWindowVisibilityChanged(Dali::Window window, bool visible)
224 {
225   if(visible)
226   {
227     // TODO : Should we check 'out of screen' here? -> Then, we need an actor of this change.
228     Dali::Accessibility::Bridge::GetCurrentBridge()->WindowShown(window); // Called when Window is shown.
229   }
230   else
231   {
232     Dali::Accessibility::Bridge::GetCurrentBridge()->WindowHidden(window); // Called when Window is hidden and iconified.
233   }
234
235 }
236
237 void BridgeBase::OnWindowFocusChanged(Dali::Window window, bool focusIn)
238 {
239   if(focusIn)
240   {
241     Dali::Accessibility::Bridge::GetCurrentBridge()->WindowFocused(window); // Called when Window is focused.
242   }
243   else
244   {
245     Dali::Accessibility::Bridge::GetCurrentBridge()->WindowUnfocused(window); // Called when Window is out of focus.
246   }
247 }
248
249 void BridgeBase::AddTopLevelWindow(Accessible* windowAccessible)
250 {
251   if(windowAccessible->GetInternalActor() == nullptr)
252   {
253     return;
254   }
255
256   // Prevent adding the default window twice.
257   if(!mApplication.mChildren.empty() &&
258      mApplication.mChildren[0]->GetInternalActor() == windowAccessible->GetInternalActor())
259   {
260     return;
261   }
262
263   // Adds Window to a list of Windows.
264   mApplication.mChildren.push_back(windowAccessible);
265   SetIsOnRootLevel(windowAccessible);
266
267   Dali::Window window = Dali::DevelWindow::Get(windowAccessible->GetInternalActor());
268   if(window)
269   {
270     mApplication.mWindows.push_back(window);
271     Dali::DevelWindow::VisibilityChangedSignal(window).Connect(this, &BridgeBase::OnWindowVisibilityChanged);
272     window.FocusChangeSignal().Connect(this, &BridgeBase::OnWindowFocusChanged);
273   }
274 }
275
276 void BridgeBase::RemoveTopLevelWindow(Accessible* windowAccessible)
277 {
278   for(auto i = 0u; i < mApplication.mWindows.size(); ++i)
279   {
280     if(windowAccessible->GetInternalActor() == mApplication.mWindows[i].GetRootLayer())
281     {
282       Dali::Accessibility::Bridge::GetCurrentBridge()->WindowHidden(mApplication.mWindows[i]);
283       Dali::DevelWindow::VisibilityChangedSignal(mApplication.mWindows[i]).Disconnect(this, &BridgeBase::OnWindowVisibilityChanged);
284       mApplication.mWindows[i].FocusChangeSignal().Disconnect(this, &BridgeBase::OnWindowFocusChanged);
285       mApplication.mWindows.erase(mApplication.mWindows.begin() + i);
286       break;
287     }
288   }
289
290   for(auto i = 0u; i < mApplication.mChildren.size(); ++i)
291   {
292     if(mApplication.mChildren[i] == windowAccessible)
293     {
294       mApplication.mChildren.erase(mApplication.mChildren.begin() + i);
295       break;
296     }
297   }
298 }
299
300 std::string BridgeBase::StripPrefix(const std::string& path)
301 {
302   auto size = strlen(AtspiPath);
303   return path.substr(size + 1);
304 }
305
306 Accessible* BridgeBase::Find(const std::string& path) const
307 {
308   if(path == "root")
309   {
310     return &mApplication;
311   }
312
313   void* accessible;
314   std::istringstream tmp{path};
315   if(!(tmp >> accessible))
316   {
317     throw std::domain_error{"invalid path '" + path + "'"};
318   }
319
320   auto it = mData->mKnownObjects.find(static_cast<Accessible*>(accessible));
321   if(it == mData->mKnownObjects.end())
322   {
323     throw std::domain_error{"unknown object '" + path + "'"};
324   }
325
326   return static_cast<Accessible*>(accessible);
327 }
328
329 Accessible* BridgeBase::Find(const Address& ptr) const
330 {
331   assert(ptr.GetBus() == mData->mBusName);
332   return Find(ptr.GetPath());
333 }
334
335 Accessible* BridgeBase::FindSelf() const
336 {
337   auto path  = DBus::DBusServer::getCurrentObjectPath();
338   auto size = strlen(AtspiPath);
339   if(path.size() <= size)
340   {
341     throw std::domain_error{"invalid path '" + path + "'"};
342   }
343   if(path.substr(0, size) != AtspiPath)
344   {
345     throw std::domain_error{"invalid path '" + path + "'"};
346   }
347   if(path[size] != '/')
348   {
349     throw std::domain_error{"invalid path '" + path + "'"};
350   }
351   return Find(StripPrefix(path));
352 }
353
354 void BridgeBase::SetId(int id)
355 {
356   this->mId = id;
357 }
358
359 int BridgeBase::GetId()
360 {
361   return this->mId;
362 }
363
364 auto BridgeBase::GetItems() -> DBus::ValueOrError<std::vector<CacheElementType>>
365 {
366   auto root = &mApplication;
367
368   std::vector<CacheElementType> res;
369
370   std::function<void(Accessible*)> proc =
371     [&](Accessible* item) {
372       res.emplace_back(std::move(CreateCacheElement(root)));
373       for(auto i = 0u; i < item->GetChildCount(); ++i)
374       {
375         proc(item->GetChildAtIndex(i));
376       }
377     };
378
379   return res;
380 }
381
382 auto BridgeBase::CreateCacheElement(Accessible* item) -> CacheElementType
383 {
384   if(!item)
385   {
386     return {};
387   }
388
389   auto root   = &mApplication;
390   auto parent = item->GetParent();
391
392   std::vector<Address> children;
393   for(auto i = 0u; i < item->GetChildCount(); ++i)
394   {
395     children.emplace_back(item->GetChildAtIndex(i)->GetAddress());
396   }
397
398   return std::make_tuple(
399     item->GetAddress(),
400     root->GetAddress(),
401     parent ? parent->GetAddress() : Address{},
402     children,
403     item->GetInterfaces(),
404     item->GetName(),
405     item->GetRole(),
406     item->GetDescription(),
407     item->GetStates().GetRawData());
408 }