[Tizen][AT-SPI] Add Table and TableCell interfaces
[platform/core/uifw/dali-adaptor.git] / dali / internal / accessibility / bridge / bridge-impl.cpp
index 0d48895..2036741 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2021 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2022 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.
@@ -27,6 +27,7 @@
 // INTERNAL INCLUDES
 #include <dali/devel-api/adaptor-framework/environment-variable.h>
 #include <dali/devel-api/adaptor-framework/window-devel.h>
+#include <dali/internal/accessibility/bridge/accessibility-common.h>
 #include <dali/internal/accessibility/bridge/bridge-accessible.h>
 #include <dali/internal/accessibility/bridge/bridge-action.h>
 #include <dali/internal/accessibility/bridge/bridge-collection.h>
 #include <dali/internal/accessibility/bridge/bridge-hyperlink.h>
 #include <dali/internal/accessibility/bridge/bridge-object.h>
 #include <dali/internal/accessibility/bridge/bridge-selection.h>
+#include <dali/internal/accessibility/bridge/bridge-socket.h>
+#include <dali/internal/accessibility/bridge/bridge-table.h>
+#include <dali/internal/accessibility/bridge/bridge-table-cell.h>
 #include <dali/internal/accessibility/bridge/bridge-text.h>
 #include <dali/internal/accessibility/bridge/bridge-value.h>
 #include <dali/internal/accessibility/bridge/bridge-application.h>
-#include <dali/internal/accessibility/bridge/dummy-atspi.h>
+#include <dali/internal/accessibility/bridge/dummy/dummy-atspi.h>
 #include <dali/internal/adaptor/common/adaptor-impl.h>
 #include <dali/internal/system/common/environment-variables.h>
+#include <unistd.h>
 
 using namespace Dali::Accessibility;
 
@@ -67,14 +72,16 @@ class BridgeImpl : public virtual BridgeBase,
                    public BridgeSelection,
                    public BridgeApplication,
                    public BridgeHypertext,
-                   public BridgeHyperlink
+                   public BridgeHyperlink,
+                   public BridgeSocket,
+                   public BridgeTable,
+                   public BridgeTableCell
 {
   DBus::DBusClient                                              mAccessibilityStatusClient;
   DBus::DBusClient                                              mRegistryClient;
   DBus::DBusClient                                              mDirectReadingClient;
   bool                                                          mIsScreenReaderEnabled = false;
   bool                                                          mIsEnabled             = false;
-  bool                                                          mIsShown               = false;
   std::unordered_map<int32_t, std::function<void(std::string)>> mDirectReadingCallbacks;
   Dali::Actor                                                   mHighlightedActor;
   std::function<void(Dali::Actor)>                              mHighlightClearAction;
@@ -83,6 +90,7 @@ class BridgeImpl : public virtual BridgeBase,
   Dali::Timer                                                   mReadIsEnabledTimer;
   Dali::Timer                                                   mReadScreenReaderEnabledTimer;
   Dali::Timer                                                   mForceUpTimer;
+  std::string                                                   mPreferredBusName;
 
 public:
   BridgeImpl()
@@ -119,14 +127,7 @@ public:
       }
     }
 
-    auto methodObject = mRegistryClient.method<bool(std::tuple<uint32_t, int32_t, int32_t, int32_t, int32_t, std::string, bool>)>("NotifyListenersSync");
-    auto result       = methodObject.call(std::tuple<uint32_t, int32_t, int32_t, int32_t, int32_t, std::string, bool>{keyType, 0, static_cast<int32_t>(keyCode), 0, static_cast<int32_t>(timeStamp), keyName, isText ? 1 : 0});
-    if(!result)
-    {
-      LOG() << result.getError().message;
-      return Consumed::NO;
-    }
-    return std::get<0>(result) ? Consumed::YES : Consumed::NO;
+    return Consumed::NO;
   }
 
   /**
@@ -142,7 +143,7 @@ public:
     mDirectReadingClient.method<DBus::ValueOrError<void>(bool)>("PauseResume").asyncCall([](DBus::ValueOrError<void> msg) {
       if(!msg)
       {
-        LOG() << "Direct reading command failed (" << msg.getError().message << ")";
+        LOG() << "Direct reading command failed (" << msg.getError().message << ")\n";
       }
     },
                                                                                         true);
@@ -161,7 +162,7 @@ public:
     mDirectReadingClient.method<DBus::ValueOrError<void>(bool)>("PauseResume").asyncCall([](DBus::ValueOrError<void> msg) {
       if(!msg)
       {
-        LOG() << "Direct reading command failed (" << msg.getError().message << ")";
+        LOG() << "Direct reading command failed (" << msg.getError().message << ")\n";
       }
     },
                                                                                         false);
@@ -180,7 +181,7 @@ public:
     mDirectReadingClient.method<DBus::ValueOrError<void>(bool)>("StopReading").asyncCall([](DBus::ValueOrError<void> msg) {
       if(!msg)
       {
-        LOG() << "Direct reading command failed (" << msg.getError().message << ")";
+        LOG() << "Direct reading command failed (" << msg.getError().message << ")\n";
       }
     },
                                                                                         alsoNonDiscardable);
@@ -199,7 +200,7 @@ public:
     mDirectReadingClient.method<DBus::ValueOrError<std::string, bool, int32_t>(std::string, bool)>("ReadCommand").asyncCall([=](DBus::ValueOrError<std::string, bool, int32_t> msg) {
       if(!msg)
       {
-        LOG() << "Direct reading command failed (" << msg.getError().message << ")";
+        LOG() << "Direct reading command failed (" << msg.getError().message << ")\n";
       }
       else if(callback)
       {
@@ -225,7 +226,10 @@ public:
       mData->mHighlightActor            = {};
 
       mDisabledSignal.Emit();
+      UnembedSocket(mApplication.GetAddress(), {AtspiDbusNameRegistry, "root"});
+      ReleaseBusName(mPreferredBusName);
     }
+
     mHighlightedActor     = {};
     mHighlightClearAction = {};
     BridgeAccessible::ForceDown();
@@ -233,7 +237,6 @@ public:
     mDirectReadingClient  = {};
     mDirectReadingCallbacks.clear();
     mApplication.mChildren.clear();
-    mApplication.mWindows.clear();
     ClearTimer();
   }
 
@@ -271,6 +274,11 @@ public:
   {
     if(mData)
     {
+      // The ~Window() after this point cannot emit DESTROY, because Bridge is not available. So emit DESTROY here.
+      for(auto windowAccessible : mApplication.mChildren)
+      {
+        BridgeObject::Emit(windowAccessible, WindowEvent::DESTROY);
+      }
       mData->mCurrentlyHighlightedActor = {};
       mData->mHighlightActor            = {};
     }
@@ -326,10 +334,13 @@ public:
     BridgeApplication::RegisterInterfaces();
     BridgeHypertext::RegisterInterfaces();
     BridgeHyperlink::RegisterInterfaces();
+    BridgeSocket::RegisterInterfaces();
+    BridgeTable::RegisterInterfaces();
+    BridgeTableCell::RegisterInterfaces();
 
     RegisterOnBridge(&mApplication);
 
-    mRegistryClient      = {AtspiDbusNameRegistry, AtspiDbusPathDec, AtspiDbusInterfaceDec, mConnectionPtr};
+    mRegistryClient      = {AtspiDbusNameRegistry, AtspiDbusPathDec, Accessible::GetInterfaceName(AtspiInterface::DEVICE_EVENT_CONTROLLER), mConnectionPtr};
     mDirectReadingClient = DBus::DBusClient{DirectReadingDBusName, DirectReadingDBusPath, DirectReadingDBusInterface, mConnectionPtr};
 
     mDirectReadingClient.addSignal<void(int32_t, std::string)>("ReadingStateChanged", [=](int32_t id, std::string readingState) {
@@ -344,26 +355,12 @@ public:
       }
     });
 
-    auto    proxy = DBus::DBusClient{AtspiDbusNameRegistry, AtspiDbusPathRoot, AtspiDbusInterfaceSocket, mConnectionPtr};
-    Address root{"", "root"};
-    auto    res = proxy.method<Address(Address)>("Embed").call(root);
-    if(!res)
-    {
-      LOG() << "Call to Embed failed: " << res.getError().message;
-    }
-    assert(res);
-
-    mApplication.mParent.SetAddress(std::move(std::get<0>(res)));
+    RequestBusName(mPreferredBusName);
 
+    auto parentAddress = EmbedSocket(mApplication.GetAddress(), {AtspiDbusNameRegistry, "root"});
+    mApplication.mParent.SetAddress(std::move(parentAddress));
     mEnabledSignal.Emit();
 
-    if(mIsShown)
-    {
-      auto rootLayer = Dali::Stage::GetCurrent().GetRootLayer();
-      auto window    = Dali::DevelWindow::Get(rootLayer);
-      EmitActivate(window); // Currently, sends a signal that the default window is activated here.
-    }
-
     return ForceUpResult::JUST_STARTED;
   }
 
@@ -432,11 +429,10 @@ public:
    */
   void WindowShown(Dali::Window window) override
   {
-    if(!mIsShown && IsUp())
+    if(IsUp())
     {
       EmitShown(window);
     }
-    mIsShown = true;
   }
 
   /**
@@ -444,11 +440,10 @@ public:
    */
   void WindowHidden(Dali::Window window) override
   {
-    if(mIsShown && IsUp())
+    if(IsUp())
     {
       EmitHidden(window);
     }
-    mIsShown = false;
   }
 
   /**
@@ -456,7 +451,7 @@ public:
    */
   void WindowFocused(Dali::Window window) override
   {
-    if(mIsShown && IsUp())
+    if(IsUp())
     {
       EmitActivate(window);
     }
@@ -467,7 +462,7 @@ public:
    */
   void WindowUnfocused(Dali::Window window) override
   {
-    if(mIsShown && IsUp())
+    if(IsUp())
     {
       EmitDeactivate(window);
     }
@@ -582,10 +577,23 @@ public:
     });
   }
 
+  void EmitScreenReaderEnabledSignal()
+  {
+    if (mIsScreenReaderEnabled)
+    {
+      mScreenReaderEnabledSignal.Emit();
+    }
+    else
+    {
+      mScreenReaderDisabledSignal.Emit();
+    }
+  }
+
   void ListenScreenReaderEnabledProperty()
   {
     mAccessibilityStatusClient.addPropertyChangedEvent<bool>("ScreenReaderEnabled", [this](bool res) {
       mIsScreenReaderEnabled = res;
+      EmitScreenReaderEnabledSignal();
       SwitchBridge();
     });
   }
@@ -680,6 +688,95 @@ public:
   {
     return mIsEnabled;
   }
+
+  Address EmbedSocket(const Address& plug, const Address& socket) override
+  {
+    auto client = CreateSocketClient(socket);
+    auto reply  = client.method<Address(Address)>("Embed").call(plug);
+
+    if(!reply)
+    {
+      DALI_LOG_ERROR("Failed to embed socket %s: %s", socket.ToString().c_str(), reply.getError().message.c_str());
+      return {};
+    }
+
+    return std::get<0>(reply.getValues());
+  }
+
+  void EmbedAtkSocket(const Address& plug, const Address& socket) override
+  {
+    auto client = CreateSocketClient(socket);
+
+    client.method<void(std::string)>("Embedded").asyncCall([](DBus::ValueOrError<void>) {}, ATSPI_PREFIX_PATH + plug.GetPath());
+  }
+
+  void UnembedSocket(const Address& plug, const Address& socket) override
+  {
+    auto client = CreateSocketClient(socket);
+
+    client.method<void(Address)>("Unembed").asyncCall([](DBus::ValueOrError<void>) {}, plug);
+  }
+
+  void SetSocketOffset(ProxyAccessible* socket, std::int32_t x, std::int32_t y) override
+  {
+    AddCoalescableMessage(CoalescableMessages::SET_OFFSET, socket, 1.0f, [=]() {
+      auto client = CreateSocketClient(socket->GetAddress());
+
+      client.method<void(std::int32_t, std::int32_t)>("SetOffset").asyncCall([](DBus::ValueOrError<void>) {}, x, y);
+    });
+  }
+
+  void SetExtentsOffset(std::int32_t x, std::int32_t y) override
+  {
+    if(mData)
+    {
+      mData->mExtentsOffset = {x, y};
+    }
+  }
+
+  void SetPreferredBusName(std::string_view preferredBusName) override
+  {
+    if(preferredBusName == mPreferredBusName)
+    {
+      return;
+    }
+
+    std::string oldPreferredBusName = std::move(mPreferredBusName);
+    mPreferredBusName               = std::string{preferredBusName};
+
+    if(IsUp())
+    {
+      ReleaseBusName(oldPreferredBusName);
+      RequestBusName(mPreferredBusName);
+    }
+    // else: request/release will be handled by ForceUp/ForceDown, respectively
+  }
+
+private:
+  DBus::DBusClient CreateSocketClient(const Address& socket)
+  {
+    return {socket.GetBus(), ATSPI_PREFIX_PATH + socket.GetPath(), Accessible::GetInterfaceName(AtspiInterface::SOCKET), mConnectionPtr};
+  }
+
+  void RequestBusName(const std::string& busName)
+  {
+    if(busName.empty())
+    {
+      return;
+    }
+
+    DBus::requestBusName(mConnectionPtr, busName);
+  }
+
+  void ReleaseBusName(const std::string& busName)
+  {
+    if(busName.empty())
+    {
+      return;
+    }
+
+    DBus::releaseBusName(mConnectionPtr, busName);
+  }
 }; // BridgeImpl
 
 namespace // unnamed namespace
@@ -706,6 +803,12 @@ std::shared_ptr<Bridge> CreateBridge()
       return Dali::Accessibility::DummyBridge::GetInstance();
     }
 
+    // check if debug mode
+    if(access("/etc/debug", F_OK) != 0)
+    {
+      return Dali::Accessibility::DummyBridge::GetInstance();
+    }
+
     return std::make_shared<BridgeImpl>();
   }
   catch(const std::exception&)
@@ -767,7 +870,7 @@ void Bridge::EnableAutoInit()
   auto window          = Dali::DevelWindow::Get(rootLayer);
   auto applicationName = Dali::Internal::Adaptor::Adaptor::GetApplicationPackageName();
 
-  auto accessible = Accessibility::Accessible::Get(rootLayer, true);
+  auto accessible = Accessibility::Accessible::Get(rootLayer);
 
   auto bridge = Bridge::GetCurrentBridge();
   bridge->AddTopLevelWindow(accessible);
@@ -779,3 +882,23 @@ void Bridge::EnableAutoInit()
     bridge->WindowShown(window);
   }
 }
+
+std::string Bridge::MakeBusNameForWidget(std::string_view widgetInstanceId)
+{
+  // The bus name should consist of dot-separated alphanumeric elements, e.g. "com.example.BusName123".
+  // Allowed characters in each element: "[A-Z][a-z][0-9]_", but no element may start with a digit.
+
+  static const char prefix[]   = "com.samsung.dali.widget_";
+  static const char underscore = '_';
+
+  std::stringstream tmp;
+
+  tmp << prefix;
+
+  for(char ch : widgetInstanceId)
+  {
+    tmp << (std::isalnum(ch) ? ch : underscore);
+  }
+
+  return tmp.str();
+}