2 * Copyright (c) 2021 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/public-api/adaptor-framework/timer.h>
30 using namespace Dali::Accessibility;
32 static Dali::Timer tickTimer;
34 BridgeBase::BridgeBase()
38 BridgeBase::~BridgeBase()
40 mApplication.mChildren.clear();
43 void BridgeBase::AddCoalescableMessage(CoalescableMessages kind, Dali::Accessibility::Accessible* obj, float delay, std::function<void()> functor)
49 auto countdownBase = static_cast<unsigned int>(delay * 10);
51 auto it = mCoalescableMessages.insert({{kind, obj}, {countdownBase, countdownBase, {}}});
58 std::get<1>(it.first->second) = countdownBase;
59 std::get<2>(it.first->second) = std::move(functor);
64 tickTimer = Dali::Timer::New(100);
65 tickTimer.TickSignal().Connect(this, &BridgeBase::TickCoalescableMessages);
68 if(!tickTimer.IsRunning())
74 bool BridgeBase::TickCoalescableMessages()
76 for(auto it = mCoalescableMessages.begin(); it != mCoalescableMessages.end();)
78 auto& countdown = std::get<0>(it->second);
79 auto countdownBase = std::get<1>(it->second);
80 auto& functor = std::get<2>(it->second);
91 countdown = countdownBase;
95 it = mCoalescableMessages.erase(it);
101 return !mCoalescableMessages.empty();
104 void BridgeBase::UpdateRegisteredEvents()
106 using ReturnType = std::vector<std::tuple<std::string, std::string>>;
107 mRegistry.method<DBus::ValueOrError<ReturnType>()>("GetRegisteredEvents").asyncCall([this](DBus::ValueOrError<ReturnType> msg) {
110 LOG() << "Get registered events failed";
114 IsBoundsChangedEventAllowed = false;
116 ReturnType values = std::get<ReturnType>(msg.getValues());
117 for(long unsigned int i = 0; i < values.size(); i++)
119 if(!std::get<1>(values[i]).compare("Object:BoundsChanged"))
121 IsBoundsChangedEventAllowed = true;
127 BridgeBase::ForceUpResult BridgeBase::ForceUp()
129 //TODO: checking mBusName is enough? or a new variable to check bridge state?
130 if(Bridge::ForceUp() == ForceUpResult::ALREADY_UP && !GetBusName().empty())
132 return ForceUpResult::ALREADY_UP;
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();
139 DALI_LOG_ERROR("failed at call '%s': %s\n", dbusLocators::atspi::GET_ADDRESS, addr.getError().message.c_str());
140 return ForceUpResult::FAILED;
143 mConnectionPtr = DBusWrapper::Installed()->eldbus_address_connection_get_impl(std::get<0>(addr));
144 mData->mBusName = DBus::getConnectionName(mConnectionPtr);
145 mDbusServer = {mConnectionPtr};
148 DBus::DBusInterfaceDescription desc{Accessible::GetInterfaceName(AtspiInterface::CACHE)};
149 AddFunctionToInterface(desc, "GetItems", &BridgeBase::GetItems);
150 mDbusServer.addInterface(AtspiDbusPathCache, desc);
153 DBus::DBusInterfaceDescription desc{Accessible::GetInterfaceName(AtspiInterface::APPLICATION)};
154 AddGetSetPropertyToInterface(desc, "Id", &BridgeBase::GetId, &BridgeBase::SetId);
155 mDbusServer.addInterface(AtspiPath, desc);
158 mRegistry = {AtspiDbusNameRegistry, AtspiDbusPathRegistry, Accessible::GetInterfaceName(AtspiInterface::REGISTRY), mConnectionPtr};
160 UpdateRegisteredEvents();
162 mRegistry.addSignal<void(void)>("EventListenerRegistered", [this](void) {
163 UpdateRegisteredEvents();
166 mRegistry.addSignal<void(void)>("EventListenerDeregistered", [this](void) {
167 UpdateRegisteredEvents();
170 return ForceUpResult::JUST_STARTED;
173 void BridgeBase::ForceDown()
181 const std::string& BridgeBase::GetBusName() const
183 static std::string empty;
184 return mData ? mData->mBusName : empty;
187 Accessible* BridgeBase::FindByPath(const std::string& name) const
193 catch(std::domain_error&)
199 void BridgeBase::AddTopLevelWindow(Accessible* windowAccessible)
201 if(windowAccessible->GetInternalActor() == nullptr)
206 // Prevent adding the default window twice.
207 if(!mApplication.mChildren.empty() &&
208 mApplication.mChildren[0]->GetInternalActor() == windowAccessible->GetInternalActor())
213 // Adds Window to a list of Windows.
214 mApplication.mChildren.push_back(windowAccessible);
215 SetIsOnRootLevel(windowAccessible);
218 void BridgeBase::RemoveTopLevelWindow(Accessible* windowAccessible)
220 for(auto i = 0u; i < mApplication.mChildren.size(); ++i)
222 if(mApplication.mChildren[i] == windowAccessible)
224 mApplication.mChildren.erase(mApplication.mChildren.begin() + i);
230 void BridgeBase::RegisterDefaultLabel(Accessible* object)
232 if(std::find(mDefaultLabels.begin(), mDefaultLabels.end(), object) == mDefaultLabels.end())
234 mDefaultLabels.push_back(object);
238 void BridgeBase::UnregisterDefaultLabel(Accessible* object)
240 auto it = std::find(mDefaultLabels.begin(), mDefaultLabels.end(), object);
241 if(it != mDefaultLabels.end())
243 mDefaultLabels.erase(it);
247 Accessible* BridgeBase::GetDefaultLabel(Accessible* root) const
249 // TODO (multi-window support): Change mDefaultLabels to a collection of vectors
250 // (one per window) and select the right one based on the window that 'root' belongs to.
251 return mDefaultLabels.empty() ? root : mDefaultLabels.back();
254 std::string BridgeBase::StripPrefix(const std::string& path)
256 auto size = strlen(AtspiPath);
257 return path.substr(size + 1);
260 Accessible* BridgeBase::Find(const std::string& path) const
264 return &mApplication;
268 std::istringstream tmp{path};
269 if(!(tmp >> accessible))
271 throw std::domain_error{"invalid path '" + path + "'"};
274 auto it = mData->mKnownObjects.find(static_cast<Accessible*>(accessible));
275 if(it == mData->mKnownObjects.end() || (*it)->IsHidden())
277 throw std::domain_error{"unknown object '" + path + "'"};
280 return static_cast<Accessible*>(accessible);
283 Accessible* BridgeBase::Find(const Address& ptr) const
285 assert(ptr.GetBus() == mData->mBusName);
286 return Find(ptr.GetPath());
289 Accessible* BridgeBase::FindCurrentObject() const
291 auto path = DBus::DBusServer::getCurrentObjectPath();
292 auto size = strlen(AtspiPath);
293 if(path.size() <= size)
295 throw std::domain_error{"invalid path '" + path + "'"};
297 if(path.substr(0, size) != AtspiPath)
299 throw std::domain_error{"invalid path '" + path + "'"};
301 if(path[size] != '/')
303 throw std::domain_error{"invalid path '" + path + "'"};
305 return Find(StripPrefix(path));
308 void BridgeBase::SetId(int id)
313 int BridgeBase::GetId()
318 auto BridgeBase::GetItems() -> DBus::ValueOrError<std::vector<CacheElementType>>
320 auto root = &mApplication;
322 std::vector<CacheElementType> res;
324 std::function<void(Accessible*)> proc =
325 [&](Accessible* item) {
326 res.emplace_back(std::move(CreateCacheElement(root)));
327 for(auto i = 0u; i < item->GetChildCount(); ++i)
329 proc(item->GetChildAtIndex(i));
336 auto BridgeBase::CreateCacheElement(Accessible* item) -> CacheElementType
343 auto root = &mApplication;
344 auto parent = item->GetParent();
346 std::vector<Address> children;
347 for(auto i = 0u; i < item->GetChildCount(); ++i)
349 children.emplace_back(item->GetChildAtIndex(i)->GetAddress());
352 return std::make_tuple(
355 parent ? parent->GetAddress() : Address{},
357 item->GetInterfacesAsStrings(),
360 item->GetDescription(),
361 item->GetStates().GetRawData());