2 * Copyright (c) 2024 Samsung Electronics Co., Ltd.
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
19 #include <dali/internal/accessibility/bridge/bridge-base.h>
22 #include <dali/devel-api/common/stage.h>
28 #include <dali/devel-api/adaptor-framework/window-devel.h>
29 #include <dali/public-api/adaptor-framework/timer.h>
31 using namespace Dali::Accessibility;
33 static Dali::Timer tickTimer;
35 BridgeBase::BridgeBase()
39 BridgeBase::~BridgeBase()
41 mApplication.mChildren.clear();
44 void BridgeBase::AddCoalescableMessage(CoalescableMessages kind, Dali::Accessibility::Accessible* obj, float delay, std::function<void()> functor)
50 auto countdownBase = static_cast<unsigned int>(delay * 10);
52 auto it = mCoalescableMessages.insert({{kind, obj}, {countdownBase, countdownBase, {}}});
59 std::get<1>(it.first->second) = countdownBase;
60 std::get<2>(it.first->second) = std::move(functor);
65 tickTimer = Dali::Timer::New(100);
66 tickTimer.TickSignal().Connect(this, &BridgeBase::TickCoalescableMessages);
69 if(!tickTimer.IsRunning())
75 bool BridgeBase::TickCoalescableMessages()
77 for(auto it = mCoalescableMessages.begin(); it != mCoalescableMessages.end();)
79 auto& countdown = std::get<0>(it->second);
80 auto countdownBase = std::get<1>(it->second);
81 auto& functor = std::get<2>(it->second);
92 countdown = countdownBase;
96 it = mCoalescableMessages.erase(it);
102 return !mCoalescableMessages.empty();
105 void BridgeBase::UpdateRegisteredEvents()
107 using ReturnType = std::vector<std::tuple<std::string, std::string>>;
108 mRegistry.method<DBus::ValueOrError<ReturnType>()>("GetRegisteredEvents").asyncCall([this](DBus::ValueOrError<ReturnType> msg) {
111 LOG() << "Get registered events failed";
115 IsBoundsChangedEventAllowed = false;
117 ReturnType values = std::get<ReturnType>(msg.getValues());
118 for(long unsigned int i = 0; i < values.size(); i++)
120 if(!std::get<1>(values[i]).compare("Object:BoundsChanged"))
122 IsBoundsChangedEventAllowed = true;
128 BridgeBase::ForceUpResult BridgeBase::ForceUp()
130 //TODO: checking mBusName is enough? or a new variable to check bridge state?
131 if(Bridge::ForceUp() == ForceUpResult::ALREADY_UP && !GetBusName().empty())
133 return ForceUpResult::ALREADY_UP;
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();
140 DALI_LOG_ERROR("failed at call '%s': %s\n", dbusLocators::atspi::GET_ADDRESS, addr.getError().message.c_str());
141 return ForceUpResult::FAILED;
144 mConnectionPtr = DBusWrapper::Installed()->eldbus_address_connection_get_impl(std::get<0>(addr));
145 mData->mBusName = DBus::getConnectionName(mConnectionPtr);
146 mDbusServer = {mConnectionPtr};
149 DBus::DBusInterfaceDescription desc{Accessible::GetInterfaceName(AtspiInterface::CACHE)};
150 AddFunctionToInterface(desc, "GetItems", &BridgeBase::GetItems);
151 mDbusServer.addInterface(AtspiDbusPathCache, desc);
154 DBus::DBusInterfaceDescription desc{Accessible::GetInterfaceName(AtspiInterface::APPLICATION)};
155 AddGetSetPropertyToInterface(desc, "Id", &BridgeBase::GetId, &BridgeBase::SetId);
156 mDbusServer.addInterface(AtspiPath, desc);
159 mRegistry = {AtspiDbusNameRegistry, AtspiDbusPathRegistry, Accessible::GetInterfaceName(AtspiInterface::REGISTRY), mConnectionPtr};
161 UpdateRegisteredEvents();
163 mRegistry.addSignal<void(void)>("EventListenerRegistered", [this](void) {
164 UpdateRegisteredEvents();
167 mRegistry.addSignal<void(void)>("EventListenerDeregistered", [this](void) {
168 UpdateRegisteredEvents();
171 return ForceUpResult::JUST_STARTED;
174 void BridgeBase::ForceDown()
178 mCoalescableMessages.clear();
184 const std::string& BridgeBase::GetBusName() const
186 static std::string empty;
187 return mData ? mData->mBusName : empty;
190 Accessible* BridgeBase::FindByPath(const std::string& name) const
196 catch(std::domain_error&)
202 void BridgeBase::AddTopLevelWindow(Accessible* windowAccessible)
204 if(windowAccessible->GetInternalActor() == nullptr)
209 // Prevent adding the default window twice.
210 if(!mApplication.mChildren.empty() &&
211 mApplication.mChildren[0]->GetInternalActor() == windowAccessible->GetInternalActor())
216 // Adds Window to a list of Windows.
217 mApplication.mChildren.push_back(windowAccessible);
218 SetIsOnRootLevel(windowAccessible);
221 void BridgeBase::RemoveTopLevelWindow(Accessible* windowAccessible)
223 for(auto i = 0u; i < mApplication.mChildren.size(); ++i)
225 if(mApplication.mChildren[i] == windowAccessible)
227 mApplication.mChildren.erase(mApplication.mChildren.begin() + i);
233 void BridgeBase::CompressDefaultLabels()
235 // Remove entries for objects which no longer exist
236 mDefaultLabels.remove_if([](const DefaultLabelType& label) {
237 // Check 1) window's weak handle; 2) accessible's ref object
238 return !label.first.GetBaseHandle() || label.second.expired();
242 void BridgeBase::RegisterDefaultLabel(std::shared_ptr<Accessible> object)
244 CompressDefaultLabels();
246 Dali::WeakHandle<Dali::Window> window = GetWindow(object.get());
247 if(!window.GetBaseHandle()) // true also if `object` is null
249 DALI_LOG_ERROR("Cannot register default label: object does not belong to any window");
253 auto it = std::find_if(mDefaultLabels.begin(), mDefaultLabels.end(), [&object](const DefaultLabelType& label) {
254 auto labelPtr = label.second.lock();
255 return labelPtr && object == labelPtr;
258 if(it == mDefaultLabels.end())
260 mDefaultLabels.push_back({window, object});
262 else if(it->first != window)
264 // TODO: Tentative implementation. It is yet to be specified what should happen
265 // when the same object is re-registered as a default label for another window.
266 *it = {window, object};
268 else // it->first == window && it->second == object
274 void BridgeBase::UnregisterDefaultLabel(std::shared_ptr<Accessible> object)
276 CompressDefaultLabels();
278 mDefaultLabels.remove_if([&object](const DefaultLabelType& label) {
279 auto labelPtr = label.second.lock();
280 return labelPtr && object == labelPtr;
284 Accessible* BridgeBase::GetDefaultLabel(Accessible* root)
286 CompressDefaultLabels();
288 Dali::WeakHandle<Dali::Window> window = GetWindow(root);
289 if(!window.GetBaseHandle())
294 auto it = std::find_if(mDefaultLabels.rbegin(), mDefaultLabels.rend(), [&window](const DefaultLabelType& label) {
295 return window == label.first;
298 Accessible* rawPtr = root;
299 if(it != mDefaultLabels.rend())
301 if(auto labelPtr = it->second.lock())
303 rawPtr = labelPtr.get();
310 std::string BridgeBase::StripPrefix(const std::string& path)
312 auto size = strlen(AtspiPath);
313 return path.substr(size + 1);
316 Accessible* BridgeBase::Find(const std::string& path) const
320 return &mApplication;
324 std::istringstream tmp{path};
325 if(!(tmp >> accessible))
327 throw std::domain_error{"invalid path '" + path + "'"};
330 auto it = mData->mKnownObjects.find(static_cast<Accessible*>(accessible));
331 if(it == mData->mKnownObjects.end() || (*it)->IsHidden())
333 throw std::domain_error{"unknown object '" + path + "'"};
336 return static_cast<Accessible*>(accessible);
339 Accessible* BridgeBase::Find(const Address& ptr) const
341 assert(ptr.GetBus() == mData->mBusName);
342 return Find(ptr.GetPath());
345 Accessible* BridgeBase::FindCurrentObject() const
347 auto path = DBus::DBusServer::getCurrentObjectPath();
348 auto size = strlen(AtspiPath);
349 if(path.size() <= size)
351 throw std::domain_error{"invalid path '" + path + "'"};
353 if(path.substr(0, size) != AtspiPath)
355 throw std::domain_error{"invalid path '" + path + "'"};
357 if(path[size] != '/')
359 throw std::domain_error{"invalid path '" + path + "'"};
361 return Find(StripPrefix(path));
364 void BridgeBase::SetId(int id)
369 int BridgeBase::GetId()
374 auto BridgeBase::GetItems() -> DBus::ValueOrError<std::vector<CacheElementType>>
376 auto root = &mApplication;
378 std::vector<CacheElementType> res;
380 std::function<void(Accessible*)> proc =
381 [&](Accessible* item) {
382 res.emplace_back(std::move(CreateCacheElement(root)));
383 for(auto i = 0u; i < item->GetChildCount(); ++i)
385 proc(item->GetChildAtIndex(i));
392 auto BridgeBase::CreateCacheElement(Accessible* item) -> CacheElementType
399 auto root = &mApplication;
400 auto parent = item->GetParent();
402 std::vector<Address> children;
403 for(auto i = 0u; i < item->GetChildCount(); ++i)
405 children.emplace_back(item->GetChildAtIndex(i)->GetAddress());
408 return std::make_tuple(
411 parent ? parent->GetAddress() : Address{},
413 item->GetInterfacesAsStrings(),
416 item->GetDescription(),
417 item->GetStates().GetRawData());
420 Dali::WeakHandle<Dali::Window> BridgeBase::GetWindow(Dali::Accessibility::Accessible* accessible)
422 Dali::WeakHandle<Dali::Window> windowHandle;
423 Dali::Actor actor = accessible ? accessible->GetInternalActor() : Dali::Actor();
427 Dali::Window window = Dali::DevelWindow::Get(actor);
428 windowHandle = {window};