[dali_2.3.29] Merge branch 'devel/master'
[platform/core/uifw/dali-adaptor.git] / dali / internal / accessibility / bridge / bridge-base.cpp
index a3b9ec4..ba4e154 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2019 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2024 Samsung Electronics Co., Ltd.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
 #include <dali/internal/accessibility/bridge/bridge-base.h>
 
 // EXTERNAL INCLUDES
+#include <dali/devel-api/common/stage.h>
 #include <atomic>
 #include <cstdlib>
 #include <memory>
 
 // INTERNAL INCLUDES
+#include <dali/devel-api/adaptor-framework/window-devel.h>
 #include <dali/public-api/adaptor-framework/timer.h>
 
 using namespace Dali::Accessibility;
 
 static Dali::Timer tickTimer;
 
-BridgeBase::~BridgeBase()
+BridgeBase::BridgeBase()
 {
 }
 
-BridgeBase::BridgeBase()
+BridgeBase::~BridgeBase()
 {
+  mApplication.mChildren.clear();
 }
 
-void BridgeBase::addFilteredEvent( FilteredEvents kind, Dali::Accessibility::Accessible* obj, float delay, std::function<void()> functor )
+void BridgeBase::AddCoalescableMessage(CoalescableMessages kind, Dali::Accessibility::Accessible* obj, float delay, std::function<void()> functor)
 {
-  if( delay < 0 )
+  if(delay < 0)
   {
     delay = 0;
   }
+  auto countdownBase = static_cast<unsigned int>(delay * 10);
 
-  auto it = filteredEvents.insert({ { kind, obj }, { static_cast<unsigned int>(delay * 10), {} } });
-  if (it.second)
+  auto it = mCoalescableMessages.insert({{kind, obj}, {countdownBase, countdownBase, {}}});
+  if(it.second)
   {
     functor();
   }
   else
   {
-    it.first->second.second = std::move(functor);
+    std::get<1>(it.first->second) = countdownBase;
+    std::get<2>(it.first->second) = std::move(functor);
   }
 
-  if (!tickTimer)
+  if(!tickTimer)
   {
     tickTimer = Dali::Timer::New(100);
-    tickTimer.TickSignal().Connect(this, &BridgeBase::tickFilteredEvents);
+    tickTimer.TickSignal().Connect(this, &BridgeBase::TickCoalescableMessages);
+  }
+
+  if(!tickTimer.IsRunning())
+  {
+    tickTimer.Start();
   }
 }
 
-bool BridgeBase::tickFilteredEvents()
+bool BridgeBase::TickCoalescableMessages()
 {
-  for(auto it = filteredEvents.begin(); it != filteredEvents.end(); )
+  for(auto it = mCoalescableMessages.begin(); it != mCoalescableMessages.end();)
   {
-    if (it->second.first)
+    auto& countdown     = std::get<0>(it->second);
+    auto  countdownBase = std::get<1>(it->second);
+    auto& functor       = std::get<2>(it->second);
+    if(countdown)
     {
-      --it->second.first;
+      --countdown;
     }
     else
     {
-      if (it->second.second)
+      if(functor)
       {
-        it->second.second();
-        it->second.second = {};
+        functor();
+        functor   = {};
+        countdown = countdownBase;
       }
       else
       {
-        it = filteredEvents.erase(it);
+        it = mCoalescableMessages.erase(it);
         continue;
       }
     }
     ++it;
   }
-  return !filteredEvents.empty();
+  return !mCoalescableMessages.empty();
+}
+
+void BridgeBase::UpdateRegisteredEvents()
+{
+  using ReturnType = std::vector<std::tuple<std::string, std::string>>;
+  mRegistry.method<DBus::ValueOrError<ReturnType>()>("GetRegisteredEvents").asyncCall([this](DBus::ValueOrError<ReturnType> msg) {
+    if(!msg)
+    {
+      LOG() << "Get registered events failed";
+      return;
+    }
+
+    IsBoundsChangedEventAllowed = false;
+
+    ReturnType values = std::get<ReturnType>(msg.getValues());
+    for(long unsigned int i = 0; i < values.size(); i++)
+    {
+      if(!std::get<1>(values[i]).compare("Object:BoundsChanged"))
+      {
+        IsBoundsChangedEventAllowed = true;
+      }
+    }
+  });
 }
 
 BridgeBase::ForceUpResult BridgeBase::ForceUp()
 {
-  if( Bridge::ForceUp() == ForceUpResult::ALREADY_UP )
+  //TODO: checking mBusName is enough? or a new variable to check bridge state?
+  if(Bridge::ForceUp() == ForceUpResult::ALREADY_UP && !GetBusName().empty())
   {
     return ForceUpResult::ALREADY_UP;
   }
   auto proxy = DBus::DBusClient{dbusLocators::atspi::BUS, dbusLocators::atspi::OBJ_PATH, dbusLocators::atspi::BUS_INTERFACE, DBus::ConnectionType::SESSION};
-  auto addr = proxy.method< std::string() >( dbusLocators::atspi::GET_ADDRESS ).call();
+  auto addr  = proxy.method<std::string()>(dbusLocators::atspi::GET_ADDRESS).call();
 
-  if( !addr )
+  if(!addr)
   {
-    throw std::domain_error{std::string( "failed at call '" ) + dbusLocators::atspi::GET_ADDRESS + "': " + addr.getError().message};
+    DALI_LOG_ERROR("failed at call '%s': %s\n", dbusLocators::atspi::GET_ADDRESS, addr.getError().message.c_str());
+    return ForceUpResult::FAILED;
   }
 
-  con = DBusWrapper::Installed()->eldbus_address_connection_get_impl( std::get< 0 >( addr ) );
-  data->busName = DBus::getConnectionName( con );
-  dbusServer = { con };
+  mConnectionPtr  = DBusWrapper::Installed()->eldbus_address_connection_get_impl(std::get<0>(addr));
+  mData->mBusName = DBus::getConnectionName(mConnectionPtr);
+  mDbusServer     = {mConnectionPtr};
 
   {
-    DBus::DBusInterfaceDescription desc{"org.a11y.atspi.Cache"};
-    AddFunctionToInterface( desc, "GetItems", &BridgeBase::GetItems );
-    dbusServer.addInterface( "/org/a11y/atspi/cache", desc );
+    DBus::DBusInterfaceDescription desc{Accessible::GetInterfaceName(AtspiInterface::CACHE)};
+    AddFunctionToInterface(desc, "GetItems", &BridgeBase::GetItems);
+    mDbusServer.addInterface(AtspiDbusPathCache, desc);
   }
   {
-    DBus::DBusInterfaceDescription desc{"org.a11y.atspi.Application"};
-    AddGetSetPropertyToInterface( desc, "Id", &BridgeBase::IdGet, &BridgeBase::IdSet );
-    dbusServer.addInterface( AtspiPath, desc );
+    DBus::DBusInterfaceDescription desc{Accessible::GetInterfaceName(AtspiInterface::APPLICATION)};
+    AddGetSetPropertyToInterface(desc, "Id", &BridgeBase::GetId, &BridgeBase::SetId);
+    mDbusServer.addInterface(AtspiPath, desc);
   }
 
+  mRegistry = {AtspiDbusNameRegistry, AtspiDbusPathRegistry, Accessible::GetInterfaceName(AtspiInterface::REGISTRY), mConnectionPtr};
+
+  UpdateRegisteredEvents();
+
+  mRegistry.addSignal<void(void)>("EventListenerRegistered", [this](void) {
+    UpdateRegisteredEvents();
+  });
+
+  mRegistry.addSignal<void(void)>("EventListenerDeregistered", [this](void) {
+    UpdateRegisteredEvents();
+  });
+
   return ForceUpResult::JUST_STARTED;
 }
 
 void BridgeBase::ForceDown()
 {
   Bridge::ForceDown();
-  dbusServer = {};
-  con = {};
+  tickTimer.Reset();
+  mCoalescableMessages.clear();
+  mRegistry      = {};
+  mDbusServer    = {};
+  mConnectionPtr = {};
 }
 
 const std::string& BridgeBase::GetBusName() const
 {
   static std::string empty;
-  return data ? data->busName : empty;
+  return mData ? mData->mBusName : empty;
 }
 
-Accessible* BridgeBase::FindByPath( const std::string& name ) const
+Accessible* BridgeBase::FindByPath(const std::string& name) const
 {
   try
   {
-    return Find( name );
+    return Find(name);
   }
-  catch( std::domain_error& )
+  catch(std::domain_error&)
   {
     return nullptr;
   }
 }
 
-void BridgeBase::AddPopup( Accessible* obj )
+void BridgeBase::AddTopLevelWindow(Accessible* windowAccessible)
 {
-  if( std::find( popups.begin(), popups.end(), obj ) != popups.end() )
+  if(windowAccessible->GetInternalActor() == nullptr)
   {
     return;
   }
-  popups.push_back( obj );
-  if (IsUp())
+
+  // Prevent adding the default window twice.
+  if(!mApplication.mChildren.empty() &&
+     mApplication.mChildren[0]->GetInternalActor() == windowAccessible->GetInternalActor())
   {
-    obj->Emit( WindowEvent::ACTIVATE, 0 );
+    return;
   }
+
+  // Adds Window to a list of Windows.
+  mApplication.mChildren.push_back(windowAccessible);
+  SetIsOnRootLevel(windowAccessible);
 }
 
-void BridgeBase::RemovePopup( Accessible* obj )
+void BridgeBase::RemoveTopLevelWindow(Accessible* windowAccessible)
 {
-  auto it = std::find( popups.begin(), popups.end(), obj );
-  if( it == popups.end() )
+  for(auto i = 0u; i < mApplication.mChildren.size(); ++i)
   {
+    if(mApplication.mChildren[i] == windowAccessible)
+    {
+      mApplication.mChildren.erase(mApplication.mChildren.begin() + i);
+      break;
+    }
+  }
+}
+
+void BridgeBase::CompressDefaultLabels()
+{
+  // Remove entries for objects which no longer exist
+  mDefaultLabels.remove_if([](const DefaultLabelType& label) {
+    // Check 1) window's weak handle; 2) accessible's ref object
+    return !label.first.GetBaseHandle() || label.second.expired();
+  });
+}
+
+void BridgeBase::RegisterDefaultLabel(std::shared_ptr<Accessible> object)
+{
+  CompressDefaultLabels();
+
+  Dali::WeakHandle<Dali::Window> window = GetWindow(object.get());
+  if(!window.GetBaseHandle()) // true also if `object` is null
+  {
+    DALI_LOG_ERROR("Cannot register default label: object does not belong to any window");
     return;
   }
-  popups.erase( it );
-  if (IsUp())
+
+  auto it = std::find_if(mDefaultLabels.begin(), mDefaultLabels.end(), [&object](const DefaultLabelType& label) {
+    auto labelPtr = label.second.lock();
+    return labelPtr && object == labelPtr;
+  });
+
+  if(it == mDefaultLabels.end())
   {
-    obj->Emit( WindowEvent::DEACTIVATE, 0 );
-    if( popups.empty() )
-    {
-      application.children.back()->Emit( WindowEvent::ACTIVATE, 0 );
-    }
-    else
-    {
-      popups.back()->Emit( WindowEvent::ACTIVATE, 0 );
-    }
+    mDefaultLabels.push_back({window, object});
+  }
+  else if(it->first != window)
+  {
+    // TODO: Tentative implementation. It is yet to be specified what should happen
+    // when the same object is re-registered as a default label for another window.
+    *it = {window, object};
+  }
+  else // it->first == window && it->second == object
+  {
+    // Nothing to do
   }
 }
 
-void BridgeBase::AddTopLevelWindow( Accessible* root )
+void BridgeBase::UnregisterDefaultLabel(std::shared_ptr<Accessible> object)
 {
-  application.children.push_back( root );
-  SetIsOnRootLevel( root );
+  CompressDefaultLabels();
+
+  mDefaultLabels.remove_if([&object](const DefaultLabelType& label) {
+    auto labelPtr = label.second.lock();
+    return labelPtr && object == labelPtr;
+  });
 }
 
-void BridgeBase::RemoveTopLevelWindow( Accessible* root )
+Accessible* BridgeBase::GetDefaultLabel(Accessible* root)
 {
-  for(auto i = 0u; i < application.children.size(); ++i)
+  CompressDefaultLabels();
+
+  Dali::WeakHandle<Dali::Window> window = GetWindow(root);
+  if(!window.GetBaseHandle())
+  {
+    return root;
+  }
+
+  auto it = std::find_if(mDefaultLabels.rbegin(), mDefaultLabels.rend(), [&window](const DefaultLabelType& label) {
+    return window == label.first;
+  });
+
+  Accessible* rawPtr = root;
+  if(it != mDefaultLabels.rend())
   {
-    if( application.children[i] == root )
+    if(auto labelPtr = it->second.lock())
     {
-      application.children.erase(application.children.begin() + i);
-      break;
+      rawPtr = labelPtr.get();
     }
   }
+
+  return rawPtr;
 }
 
-std::string BridgeBase::StripPrefix( const std::string& path )
+std::string BridgeBase::StripPrefix(const std::string& path)
 {
-  auto size = strlen( AtspiPath );
-  return path.substr( size + 1 );
+  auto size = strlen(AtspiPath);
+  return path.substr(size + 1);
 }
 
-Accessible* BridgeBase::Find( const std::string& path ) const
+Accessible* BridgeBase::Find(const std::string& path) const
 {
-  if( path == "root" )
+  if(path == "root")
   {
-    return &application;
+    return &mApplication;
   }
-  void* p;
-  std::istringstream tmp{ path };
-  if (! ( tmp >> p) )
+
+  void*              accessible;
+  std::istringstream tmp{path};
+  if(!(tmp >> accessible))
   {
     throw std::domain_error{"invalid path '" + path + "'"};
   }
-  auto it = data->knownObjects.find( static_cast<Accessible*>( p ) );
-  if( it == data->knownObjects.end() || (*it)->GetStates()[State::DEFUNCT] )
+
+  auto it = mData->mKnownObjects.find(static_cast<Accessible*>(accessible));
+  if(it == mData->mKnownObjects.end() || (*it)->IsHidden())
   {
     throw std::domain_error{"unknown object '" + path + "'"};
   }
-  return static_cast<Accessible*>( p );
+
+  return static_cast<Accessible*>(accessible);
 }
 
-Accessible* BridgeBase::Find( const Address& ptr ) const
+Accessible* BridgeBase::Find(const Address& ptr) const
 {
-  assert( ptr.GetBus() == data->busName );
-  return Find( ptr.GetPath() );
+  assert(ptr.GetBus() == mData->mBusName);
+  return Find(ptr.GetPath());
 }
 
-Accessible* BridgeBase::FindSelf() const
+Accessible* BridgeBase::FindCurrentObject() const
 {
-  auto pth = DBus::DBusServer::getCurrentObjectPath();
-  auto size = strlen( AtspiPath );
-  if( pth.size() <= size )
+  auto path = DBus::DBusServer::getCurrentObjectPath();
+  auto size = strlen(AtspiPath);
+  if(path.size() <= size)
   {
-    throw std::domain_error{"invalid path '" + pth + "'"};
+    throw std::domain_error{"invalid path '" + path + "'"};
   }
-  if( pth.substr( 0, size ) != AtspiPath )
+  if(path.substr(0, size) != AtspiPath)
   {
-    throw std::domain_error{"invalid path '" + pth + "'"};
+    throw std::domain_error{"invalid path '" + path + "'"};
   }
-  if( pth[size] != '/' )
+  if(path[size] != '/')
   {
-    throw std::domain_error{"invalid path '" + pth + "'"};
+    throw std::domain_error{"invalid path '" + path + "'"};
   }
-  return Find( StripPrefix( pth ) );
+  return Find(StripPrefix(path));
 }
 
-void BridgeBase::IdSet( int id )
+void BridgeBase::SetId(int id)
 {
-  this->id = id;
+  this->mId = id;
 }
 
-int BridgeBase::IdGet()
+int BridgeBase::GetId()
 {
-  return this->id;
+  return this->mId;
 }
 
-auto BridgeBase::GetItems() -> DBus::ValueOrError< std::vector< CacheElementType > >
+auto BridgeBase::GetItems() -> DBus::ValueOrError<std::vector<CacheElementType>>
 {
-  auto root = &application;
+  auto root = &mApplication;
 
-  std::vector< CacheElementType > res;
+  std::vector<CacheElementType> res;
 
-  std::function< void(Accessible*) > proc =
-    [&]( Accessible* item )
-    {
-      res.emplace_back( std::move( CreateCacheElement( root ) ) );
-      for( auto i = 0u; i < item->GetChildCount(); ++i )
+  std::function<void(Accessible*)> proc =
+    [&](Accessible* item) {
+      res.emplace_back(std::move(CreateCacheElement(root)));
+      for(auto i = 0u; i < item->GetChildCount(); ++i)
       {
-        proc( item->GetChildAtIndex( i ) );
+        proc(item->GetChildAtIndex(i));
       }
     };
 
   return res;
 }
 
-auto BridgeBase::CreateCacheElement( Accessible* item ) -> CacheElementType
+auto BridgeBase::CreateCacheElement(Accessible* item) -> CacheElementType
 {
-  if( !item )
+  if(!item)
   {
     return {};
   }
 
-  auto root = &application;
+  auto root   = &mApplication;
   auto parent = item->GetParent();
 
-  std::vector< Address > children;
-  for( auto i = 0u; i < item->GetChildCount(); ++i )
+  std::vector<Address> children;
+  for(auto i = 0u; i < item->GetChildCount(); ++i)
   {
-    children.emplace_back( item->GetChildAtIndex( i )->GetAddress() );
+    children.emplace_back(item->GetChildAtIndex(i)->GetAddress());
   }
 
   return std::make_tuple(
@@ -299,11 +410,23 @@ auto BridgeBase::CreateCacheElement( Accessible* item ) -> CacheElementType
     root->GetAddress(),
     parent ? parent->GetAddress() : Address{},
     children,
-    item->GetInterfaces(),
+    item->GetInterfacesAsStrings(),
     item->GetName(),
     item->GetRole(),
     item->GetDescription(),
-    item->GetStates().GetRawData()
-  );
+    item->GetStates().GetRawData());
 }
 
+Dali::WeakHandle<Dali::Window> BridgeBase::GetWindow(Dali::Accessibility::Accessible* accessible)
+{
+  Dali::WeakHandle<Dali::Window> windowHandle;
+  Dali::Actor                    actor = accessible ? accessible->GetInternalActor() : Dali::Actor();
+
+  if(actor)
+  {
+    Dali::Window window = Dali::DevelWindow::Get(actor);
+    windowHandle        = {window};
+  }
+
+  return windowHandle;
+}