X-Git-Url: http://review.tizen.org/git/?a=blobdiff_plain;f=dali%2Finternal%2Faccessibility%2Fbridge%2Fbridge-impl.cpp;h=f76a0eae3bb57a260a9dfa8574fc54d7a9a35588;hb=1646a271a6cd0854eec41c68e37b39478b83fb96;hp=ea95081a6f00f8c18ff203bf4adb6a64b779565e;hpb=0ed267a55279935b6c85e8938b9f5016c6286a50;p=platform%2Fcore%2Fuifw%2Fdali-adaptor.git diff --git a/dali/internal/accessibility/bridge/bridge-impl.cpp b/dali/internal/accessibility/bridge/bridge-impl.cpp index ea95081..f76a0ea 100644 --- a/dali/internal/accessibility/bridge/bridge-impl.cpp +++ b/dali/internal/accessibility/bridge/bridge-impl.cpp @@ -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. @@ -18,25 +18,46 @@ // CLASS HEADER // EXTERNAL INCLUDES +#include #include +#include #include #include // INTERNAL INCLUDES +#include +#include +#include #include #include +#include #include #include #include +#include +#include #include +#include +#include +#include +#include #include #include -#include +#include +#include #include -#include using namespace Dali::Accessibility; +namespace // unnamed namespace +{ +const int RETRY_INTERVAL = 1000; + +} // unnamed namespace + +/** + * @brief The BridgeImpl class is to implement some Bridge functions. + */ class BridgeImpl : public virtual BridgeBase, public BridgeAccessible, public BridgeObject, @@ -45,47 +66,36 @@ class BridgeImpl : public virtual BridgeBase, public BridgeAction, public BridgeValue, public BridgeText, - public BridgeEditableText + public BridgeEditableText, + public BridgeSelection, + public BridgeApplication, + public BridgeHypertext, + public BridgeHyperlink, + public BridgeSocket, + public BridgeTable, + public BridgeTableCell { - DBus::DBusClient listenOnAtspiEnabledSignalClient; - DBus::DBusClient registryClient, directReadingClient; - bool screenReaderEnabled = false; - bool isEnabled = false; - bool isShown = false; - std::unordered_map> directReadingCallbacks; - Dali::Actor highlightedActor; - std::function highlightClearAction; + DBus::DBusClient mAccessibilityStatusClient{}; + DBus::DBusClient mRegistryClient{}; + DBus::DBusClient mDirectReadingClient{}; + bool mIsScreenReaderEnabled{false}; + bool mIsEnabled{false}; + std::unordered_map> mDirectReadingCallbacks{}; + Dali::Actor mHighlightedActor; + std::function mHighlightClearAction{nullptr}; + Dali::CallbackBase* mIdleCallback{}; + Dali::Timer mInitializeTimer; + Dali::Timer mReadIsEnabledTimer; + Dali::Timer mReadScreenReaderEnabledTimer; + Dali::Timer mForceUpTimer; + std::string mPreferredBusName; public: - BridgeImpl() - { - listenOnAtspiEnabledSignalClient = DBus::DBusClient{A11yDbusName, A11yDbusPath, A11yDbusStatusInterface, DBus::ConnectionType::SESSION}; - - listenOnAtspiEnabledSignalClient.addPropertyChangedEvent("ScreenReaderEnabled", [this](bool res) { - screenReaderEnabled = res; - if(screenReaderEnabled || isEnabled) - { - ForceUp(); - } - else - { - ForceDown(); - } - }); - - listenOnAtspiEnabledSignalClient.addPropertyChangedEvent("IsEnabled", [this](bool res) { - isEnabled = res; - if(screenReaderEnabled || isEnabled) - { - ForceUp(); - } - else - { - ForceDown(); - } - }); - } + BridgeImpl() = default; + /** + * @copydoc Dali::Accessibility::Bridge::Emit() + */ Consumed Emit(KeyEventType type, unsigned int keyCode, const std::string& keyName, unsigned int timeStamp, bool isText) override { if(!IsUp()) @@ -93,18 +103,18 @@ public: return Consumed::NO; } - unsigned int evType = 0; + unsigned int keyType = 0; switch(type) { case KeyEventType::KEY_PRESSED: { - evType = 0; + keyType = 0; break; } case KeyEventType::KEY_RELEASED: { - evType = 1; + keyType = 1; break; } default: @@ -112,8 +122,9 @@ public: return Consumed::NO; } } - auto m = registryClient.method)>("NotifyListenersSync"); - auto result = m.call(std::tuple{evType, 0, static_cast(keyCode), 0, static_cast(timeStamp), keyName, isText ? 1 : 0}); + + auto methodObject = mRegistryClient.method)>("NotifyListenersSync"); + auto result = methodObject.call(std::tuple{keyType, 0, static_cast(keyCode), 0, static_cast(timeStamp), keyName, isText ? 1 : 0}); if(!result) { LOG() << result.getError().message; @@ -122,6 +133,9 @@ public: return std::get<0>(result) ? Consumed::YES : Consumed::NO; } + /** + * @copydoc Dali::Accessibility::Bridge::Pause() + */ void Pause() override { if(!IsUp()) @@ -129,15 +143,18 @@ public: return; } - directReadingClient.method(bool)>("PauseResume").asyncCall([](DBus::ValueOrError msg) { + mDirectReadingClient.method(bool)>("PauseResume").asyncCall([](DBus::ValueOrError msg) { if(!msg) { - LOG() << "Direct reading command failed (" << msg.getError().message << ")"; + LOG() << "Direct reading command failed (" << msg.getError().message << ")\n"; } }, - true); + true); } + /** + * @copydoc Dali::Accessibility::Bridge::Resume() + */ void Resume() override { if(!IsUp()) @@ -145,15 +162,18 @@ public: return; } - directReadingClient.method(bool)>("PauseResume").asyncCall([](DBus::ValueOrError msg) { + mDirectReadingClient.method(bool)>("PauseResume").asyncCall([](DBus::ValueOrError msg) { if(!msg) { - LOG() << "Direct reading command failed (" << msg.getError().message << ")"; + LOG() << "Direct reading command failed (" << msg.getError().message << ")\n"; } }, - false); + false); } + /** + * @copydoc Dali::Accessibility::Bridge::StopReading() + */ void StopReading(bool alsoNonDiscardable) override { if(!IsUp()) @@ -161,15 +181,18 @@ public: return; } - directReadingClient.method(bool)>("StopReading").asyncCall([](DBus::ValueOrError msg) { + mDirectReadingClient.method(bool)>("StopReading").asyncCall([](DBus::ValueOrError msg) { if(!msg) { - LOG() << "Direct reading command failed (" << msg.getError().message << ")"; + LOG() << "Direct reading command failed (" << msg.getError().message << ")\n"; } }, - alsoNonDiscardable); + alsoNonDiscardable); } + /** + * @copydoc Dali::Accessibility::Bridge::Say() + */ void Say(const std::string& text, bool discardable, std::function callback) override { if(!IsUp()) @@ -177,57 +200,129 @@ public: return; } - directReadingClient.method(std::string, bool)>("ReadCommand").asyncCall([=](DBus::ValueOrError msg) { + mDirectReadingClient.method(std::string, bool)>("ReadCommand").asyncCall([=](DBus::ValueOrError msg) { if(!msg) { - LOG() << "Direct reading command failed (" << msg.getError().message << ")"; + LOG() << "Direct reading command failed (" << msg.getError().message << ")\n"; } else if(callback) { - directReadingCallbacks.emplace(std::get<2>(msg), callback); + mDirectReadingCallbacks.emplace(std::get<2>(msg), callback); } }, - text, - discardable); + text, + discardable); } + /** + * @copydoc Dali::Accessibility::Bridge::ForceDown() + */ void ForceDown() override { - if(data) + if(mData) { - if(data->currentlyHighlightedActor && data->highlightActor) + if(mData->mCurrentlyHighlightedActor && mData->mHighlightActor) { - data->currentlyHighlightedActor.Remove(data->highlightActor); + mData->mCurrentlyHighlightedActor.Remove(mData->mHighlightActor); } - data->currentlyHighlightedActor = {}; - data->highlightActor = {}; + mData->mCurrentlyHighlightedActor = {}; + mData->mHighlightActor = {}; + + mDisabledSignal.Emit(); + UnembedSocket(mApplication.GetAddress(), {AtspiDbusNameRegistry, "root"}); + ReleaseBusName(mPreferredBusName); } - highlightedActor = {}; - highlightClearAction = {}; + + mHighlightedActor = {}; + mHighlightClearAction = {}; BridgeAccessible::ForceDown(); - registryClient = {}; - directReadingClient = {}; - directReadingCallbacks.clear(); + mRegistryClient = {}; + mDirectReadingClient = {}; + mDirectReadingCallbacks.clear(); + mApplication.mChildren.clear(); + ClearTimer(); + } + + void ClearTimer() + { + if(mInitializeTimer) + { + mInitializeTimer.Stop(); + mInitializeTimer.Reset(); + } + + if(mReadIsEnabledTimer) + { + mReadIsEnabledTimer.Stop(); + mReadIsEnabledTimer.Reset(); + } + + if(mReadScreenReaderEnabledTimer) + { + mReadScreenReaderEnabledTimer.Stop(); + mReadScreenReaderEnabledTimer.Reset(); + } + + if(mForceUpTimer) + { + mForceUpTimer.Stop(); + mForceUpTimer.Reset(); + } } + /** + * @copydoc Dali::Accessibility::Bridge::Terminate() + */ void Terminate() override { - if(data) + if(mData) { - data->currentlyHighlightedActor = {}; - data->highlightActor = {}; + // 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 = {}; } ForceDown(); - listenOnAtspiEnabledSignalClient = {}; - dbusServer = {}; - con = {}; + if((NULL != mIdleCallback) && Dali::Adaptor::IsAvailable()) + { + Dali::Adaptor::Get().RemoveIdle(mIdleCallback); + } + mAccessibilityStatusClient = {}; + mDbusServer = {}; + mConnectionPtr = {}; } + bool ForceUpTimerCallback() + { + if(ForceUp() != ForceUpResult::FAILED) + { + return false; + } + return true; + } + + /** + * @copydoc Dali::Accessibility::Bridge::ForceUp() + */ ForceUpResult ForceUp() override { - if(BridgeAccessible::ForceUp() == ForceUpResult::ALREADY_UP) + auto forceUpResult = BridgeAccessible::ForceUp(); + if(forceUpResult == ForceUpResult::ALREADY_UP) { - return ForceUpResult::ALREADY_UP; + return forceUpResult; + } + else if(forceUpResult == ForceUpResult::FAILED) + { + if(!mForceUpTimer) + { + mForceUpTimer = Dali::Timer::New(RETRY_INTERVAL); + mForceUpTimer.TickSignal().Connect(this, &BridgeImpl::ForceUpTimerCallback); + mForceUpTimer.Start(); + } + return forceUpResult; } BridgeObject::RegisterInterfaces(); @@ -238,142 +333,585 @@ public: BridgeValue::RegisterInterfaces(); BridgeText::RegisterInterfaces(); BridgeEditableText::RegisterInterfaces(); - - RegisterOnBridge(&application); - - registryClient = {AtspiDbusNameRegistry, AtspiDbusPathDec, AtspiDbusInterfaceDec, con}; - directReadingClient = DBus::DBusClient{DirectReadingDBusName, DirectReadingDBusPath, DirectReadingDBusInterface, con}; - directReadingClient.addSignal("ReadingStateChanged", [=](int32_t id, std::string readingState) { - auto it = directReadingCallbacks.find(id); - if(it != directReadingCallbacks.end()) + BridgeSelection::RegisterInterfaces(); + BridgeApplication::RegisterInterfaces(); + BridgeHypertext::RegisterInterfaces(); + BridgeHyperlink::RegisterInterfaces(); + BridgeSocket::RegisterInterfaces(); + BridgeTable::RegisterInterfaces(); + BridgeTableCell::RegisterInterfaces(); + + RegisterOnBridge(&mApplication); + + mRegistryClient = {AtspiDbusNameRegistry, AtspiDbusPathDec, Accessible::GetInterfaceName(AtspiInterface::DEVICE_EVENT_CONTROLLER), mConnectionPtr}; + mDirectReadingClient = DBus::DBusClient{DirectReadingDBusName, DirectReadingDBusPath, DirectReadingDBusInterface, mConnectionPtr}; + + mDirectReadingClient.addSignal("ReadingStateChanged", [=](int32_t id, std::string readingState) { + auto it = mDirectReadingCallbacks.find(id); + if(it != mDirectReadingCallbacks.end()) { it->second(readingState); if(readingState != "ReadingPaused" && readingState != "ReadingResumed" && readingState != "ReadingStarted") - directReadingCallbacks.erase(it); + { + mDirectReadingCallbacks.erase(it); + } } }); - auto proxy = DBus::DBusClient{AtspiDbusNameRegistry, AtspiDbusPathRoot, AtspiDbusInterfaceSocket, con}; - Address root{"", "root"}; - auto res = proxy.method("Embed").call(root); - if(!res) + RequestBusName(mPreferredBusName); + + auto parentAddress = EmbedSocket(mApplication.GetAddress(), {AtspiDbusNameRegistry, "root"}); + mApplication.mParent.SetAddress(std::move(parentAddress)); + mEnabledSignal.Emit(); + + return ForceUpResult::JUST_STARTED; + } + + /** + * @brief Sends a signal to dbus that the window is shown. + * + * @param[in] window The window to be shown + * @see Accessible::EmitShowing() and BridgeObject::EmitStateChanged() + */ + void EmitShown(Dali::Window window) + { + auto windowAccessible = mApplication.GetWindowAccessible(window); + if(windowAccessible) { - LOG() << "Call to Embed failed: " << res.getError().message; + windowAccessible->EmitShowing(true); } - assert(res); - application.parent.SetAddress(std::move(std::get<0>(res))); - if(isShown) + } + + /** + * @brief Sends a signal to dbus that the window is hidden. + * + * @param[in] window The window to be hidden + * @see Accessible::EmitShowing() and BridgeObject::EmitStateChanged() + */ + void EmitHidden(Dali::Window window) + { + auto windowAccessible = mApplication.GetWindowAccessible(window); + if(windowAccessible) { - EmitActivate(); + windowAccessible->EmitShowing(false); } - return ForceUpResult::JUST_STARTED; } - void EmitActivate() + /** + * @brief Sends a signal to dbus that the window is activated. + * + * @param[in] window The window to be activated + * @see BridgeObject::Emit() + */ + void EmitActivate(Dali::Window window) { - auto win = application.getActiveWindow(); - if(win) + auto windowAccessible = mApplication.GetWindowAccessible(window); + if(windowAccessible) { - win->Emit(WindowEvent::ACTIVATE, 0); + windowAccessible->Emit(WindowEvent::ACTIVATE, 0); } } - void EmitDeactivate() + /** + * @brief Sends a signal to dbus that the window is deactivated. + * + * @param[in] window The window to be deactivated + * @see BridgeObject::Emit() + */ + void EmitDeactivate(Dali::Window window) { - auto win = application.getActiveWindow(); - if(win) + auto windowAccessible = mApplication.GetWindowAccessible(window); + if(windowAccessible) { - win->Emit(WindowEvent::DEACTIVATE, 0); + windowAccessible->Emit(WindowEvent::DEACTIVATE, 0); } } - void ApplicationHidden() override + /** + * @brief Sends a signal to dbus that the window is minimized. + * + * @param[in] window The window to be minimized + * @see BridgeObject::Emit() + */ + void EmitMinimize(Dali::Window window) { - if(isShown && IsUp()) + auto windowAccessible = mApplication.GetWindowAccessible(window); + if(windowAccessible) { - EmitDeactivate(); + windowAccessible->Emit(WindowEvent::MINIMIZE, 0); } - isShown = false; } - void ApplicationShown() override + /** + * @brief Sends a signal to dbus that the window is restored. + * + * @param[in] window The window to be restored + * @param[in] detail Restored window state + * @see BridgeObject::Emit() + */ + void EmitRestore(Dali::Window window, Dali::Accessibility::WindowRestoreType detail) { - if(!isShown && IsUp()) + auto windowAccessible = mApplication.GetWindowAccessible(window); + if(windowAccessible) { - EmitActivate(); + windowAccessible->Emit(WindowEvent::RESTORE, static_cast(detail)); } - isShown = true; } - void Initialize() override + /** + * @brief Sends a signal to dbus that the window is maximized. + * + * @param[in] window The window to be maximized + * @see BridgeObject::Emit() + */ + void EmitMaximize(Dali::Window window) + { + auto windowAccessible = mApplication.GetWindowAccessible(window); + if(windowAccessible) + { + windowAccessible->Emit(WindowEvent::MAXIMIZE, 0); + } + } + + /** + * @copydoc Dali::Accessibility::Bridge::WindowShown() + */ + void WindowShown(Dali::Window window) override + { + if(IsUp()) + { + EmitShown(window); + } + } + + /** + * @copydoc Dali::Accessibility::Bridge::WindowHidden() + */ + void WindowHidden(Dali::Window window) override + { + if(IsUp()) + { + EmitHidden(window); + } + } + + /** + * @copydoc Dali::Accessibility::Bridge::WindowFocused() + */ + void WindowFocused(Dali::Window window) override + { + if(IsUp()) + { + EmitActivate(window); + } + } + + /** + * @copydoc Dali::Accessibility::Bridge::WindowUnfocused() + */ + void WindowUnfocused(Dali::Window window) override { - auto req = DBus::DBusClient{A11yDbusName, A11yDbusPath, A11yDbusStatusInterface, DBus::ConnectionType::SESSION}; - auto p = req.property("ScreenReaderEnabled").get(); - if(p) + if(IsUp()) { - screenReaderEnabled = std::get<0>(p); + EmitDeactivate(window); } - p = req.property("IsEnabled").get(); - if(p) + } + + /** + * @copydoc Dali::Accessibility::Bridge::WindowMinimized() + */ + void WindowMinimized(Dali::Window window) override + { + if(IsUp()) + { + EmitMinimize(window); + } + } + + /** + * @copydoc Dali::Accessibility::Bridge::WindowRestored() + */ + void WindowRestored(Dali::Window window, WindowRestoreType detail) override + { + if(IsUp()) + { + EmitRestore(window, detail); + } + } + + /** + * @copydoc Dali::Accessibility::Bridge::WindowMaximized() + */ + void WindowMaximized(Dali::Window window) override + { + if(IsUp()) + { + EmitMaximize(window); + } + } + + /** + * @copydoc Dali::Accessibility::Bridge::SuppressScreenReader() + */ + void SuppressScreenReader(bool suppress) override + { + if(mIsScreenReaderSuppressed == suppress) { - isEnabled = std::get<0>(p); + return; } - if(screenReaderEnabled || isEnabled) + mIsScreenReaderSuppressed = suppress; + ReadScreenReaderEnabledProperty(); + } + + void SwitchBridge() + { + if((!mIsScreenReaderSuppressed && mIsScreenReaderEnabled) || mIsEnabled) { ForceUp(); } + else + { + ForceDown(); + } } - bool GetScreenReaderEnabled() + bool ReadIsEnabledTimerCallback() { - return screenReaderEnabled; + ReadIsEnabledProperty(); + return false; } - bool GetIsEnabled() + void ReadIsEnabledProperty() { - return isEnabled; + mAccessibilityStatusClient.property("IsEnabled").asyncGet([this](DBus::ValueOrError msg) { + if(!msg) + { + DALI_LOG_ERROR("Get IsEnabled property error: %s\n", msg.getError().message.c_str()); + if(msg.getError().errorType == DBus::ErrorType::INVALID_REPLY) + { + if(!mReadIsEnabledTimer) + { + mReadIsEnabledTimer = Dali::Timer::New(RETRY_INTERVAL); + mReadIsEnabledTimer.TickSignal().Connect(this, &BridgeImpl::ReadIsEnabledTimerCallback); + } + mReadIsEnabledTimer.Start(); + } + return; + } + + if(mReadIsEnabledTimer) + { + mReadIsEnabledTimer.Stop(); + mReadIsEnabledTimer.Reset(); + } + + mIsEnabled = std::get<0>(msg); + SwitchBridge(); + }); } -}; -static bool bridgeInitialized; + void ListenIsEnabledProperty() + { + mAccessibilityStatusClient.addPropertyChangedEvent("IsEnabled", [this](bool res) { + mIsEnabled = res; + SwitchBridge(); + }); + } + + bool ReadScreenReaderEnabledTimerCallback() + { + ReadScreenReaderEnabledProperty(); + return false; + } + + void ReadScreenReaderEnabledProperty() + { + // can be true because of SuppressScreenReader before init + if(!mAccessibilityStatusClient) + { + return; + } + + mAccessibilityStatusClient.property("ScreenReaderEnabled").asyncGet([this](DBus::ValueOrError msg) { + if(!msg) + { + DALI_LOG_ERROR("Get ScreenReaderEnabled property error: %s\n", msg.getError().message.c_str()); + if(msg.getError().errorType == DBus::ErrorType::INVALID_REPLY) + { + if(!mReadScreenReaderEnabledTimer) + { + mReadScreenReaderEnabledTimer = Dali::Timer::New(RETRY_INTERVAL); + mReadScreenReaderEnabledTimer.TickSignal().Connect(this, &BridgeImpl::ReadScreenReaderEnabledTimerCallback); + } + mReadScreenReaderEnabledTimer.Start(); + } + return; + } -static Bridge* CreateBridge() + if(mReadScreenReaderEnabledTimer) + { + mReadScreenReaderEnabledTimer.Stop(); + mReadScreenReaderEnabledTimer.Reset(); + } + + mIsScreenReaderEnabled = std::get<0>(msg); + SwitchBridge(); + }); + } + + void EmitScreenReaderEnabledSignal() + { + if(mIsScreenReaderEnabled) + { + mScreenReaderEnabledSignal.Emit(); + } + else + { + mScreenReaderDisabledSignal.Emit(); + } + } + + void ListenScreenReaderEnabledProperty() + { + mAccessibilityStatusClient.addPropertyChangedEvent("ScreenReaderEnabled", [this](bool res) { + mIsScreenReaderEnabled = res; + EmitScreenReaderEnabledSignal(); + SwitchBridge(); + }); + } + + void ReadAndListenProperties() + { + ReadIsEnabledProperty(); + ListenIsEnabledProperty(); + + ReadScreenReaderEnabledProperty(); + ListenScreenReaderEnabledProperty(); + } + + bool InitializeAccessibilityStatusClient() + { + mAccessibilityStatusClient = DBus::DBusClient{A11yDbusName, A11yDbusPath, A11yDbusStatusInterface, DBus::ConnectionType::SESSION}; + + if(!mAccessibilityStatusClient) + { + DALI_LOG_ERROR("Accessibility Status DbusClient is not ready\n"); + return false; + } + + return true; + } + + bool InitializeTimerCallback() + { + if(InitializeAccessibilityStatusClient()) + { + ReadAndListenProperties(); + return false; + } + return true; + } + + bool OnIdleSignal() + { + if(InitializeAccessibilityStatusClient()) + { + ReadAndListenProperties(); + mIdleCallback = NULL; + return false; + } + + if(!mInitializeTimer) + { + mInitializeTimer = Dali::Timer::New(RETRY_INTERVAL); + mInitializeTimer.TickSignal().Connect(this, &BridgeImpl::InitializeTimerCallback); + } + mInitializeTimer.Start(); + + mIdleCallback = NULL; + return false; + } + + /** + * @copydoc Dali::Accessibility::Bridge::Initialize() + */ + void Initialize() override + { + if(InitializeAccessibilityStatusClient()) + { + ReadAndListenProperties(); + return; + } + + // Initialize failed. Try it again on Idle + if(Dali::Adaptor::IsAvailable()) + { + Dali::Adaptor& adaptor = Dali::Adaptor::Get(); + if(NULL == mIdleCallback) + { + mIdleCallback = MakeCallback(this, &BridgeImpl::OnIdleSignal); + adaptor.AddIdle(mIdleCallback, true); + } + } + } + + /** + * @copydoc Dali::Accessibility::Bridge::GetScreenReaderEnabled() + */ + bool GetScreenReaderEnabled() override + { + return mIsScreenReaderEnabled; + } + + /** + * @copydoc Dali::Accessibility::Bridge::IsEnabled() + */ + bool IsEnabled() override + { + return mIsEnabled; + } + + Address EmbedSocket(const Address& plug, const Address& socket) override + { + auto client = CreateSocketClient(socket); + auto reply = client.method("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("Embedded").asyncCall([](DBus::ValueOrError) {}, ATSPI_PREFIX_PATH + plug.GetPath()); + } + + void UnembedSocket(const Address& plug, const Address& socket) override + { + auto client = CreateSocketClient(socket); + + client.method("Unembed").asyncCall([](DBus::ValueOrError) {}, 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("SetOffset").asyncCall([](DBus::ValueOrError) {}, 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 +{ +bool INITIALIZED_BRIDGE = false; + +/** + * @brief Creates BridgeImpl instance. + * + * @return The BridgeImpl instance + * @note This method is to check environment variable first. If ATSPI is disable using env, it returns dummy bridge instance. + */ +std::shared_ptr CreateBridge() { - bridgeInitialized = true; + INITIALIZED_BRIDGE = true; try { /* check environment variable first */ - const char *envAtspiDisabled = Dali::EnvironmentVariable::GetEnvironmentVariable(DALI_ENV_DISABLE_ATSPI); - if (envAtspiDisabled && std::atoi(envAtspiDisabled) != 0) + const char* envAtspiDisabled = Dali::EnvironmentVariable::GetEnvironmentVariable(DALI_ENV_DISABLE_ATSPI); + if(envAtspiDisabled && std::atoi(envAtspiDisabled) != 0) { return Dali::Accessibility::DummyBridge::GetInstance(); } - return new BridgeImpl; + return std::make_shared(); } - catch (const std::exception&) + catch(const std::exception&) { DALI_LOG_ERROR("Failed to initialize AT-SPI bridge"); return Dali::Accessibility::DummyBridge::GetInstance(); } } -Bridge* Bridge::GetCurrentBridge() +} // unnamed namespace + +// Dali::Accessibility::Bridge class implementation + +std::shared_ptr Bridge::GetCurrentBridge() { - static Bridge* bridge; + static std::shared_ptr bridge; - if (bridge) + if(bridge) { return bridge; } - else if (autoInitState == AutoInitState::ENABLED) + else if(mAutoInitState == AutoInitState::ENABLED) { bridge = CreateBridge(); /* check environment variable for suppressing screen-reader */ - const char *envSuppressScreenReader = Dali::EnvironmentVariable::GetEnvironmentVariable(DALI_ENV_SUPPRESS_SCREEN_READER); - if (envSuppressScreenReader && std::atoi(envSuppressScreenReader) != 0) + const char* envSuppressScreenReader = Dali::EnvironmentVariable::GetEnvironmentVariable(DALI_ENV_SUPPRESS_SCREEN_READER); + if(envSuppressScreenReader && std::atoi(envSuppressScreenReader) != 0) { bridge->SuppressScreenReader(true); } @@ -386,25 +924,56 @@ Bridge* Bridge::GetCurrentBridge() void Bridge::DisableAutoInit() { - if (bridgeInitialized) + if(INITIALIZED_BRIDGE) { DALI_LOG_ERROR("Bridge::DisableAutoInit() called after bridge auto-initialization"); } - autoInitState = AutoInitState::DISABLED; + mAutoInitState = AutoInitState::DISABLED; } -void Bridge::EnableAutoInit(Accessible* topLevelWindow, const std::string& applicationName) +void Bridge::EnableAutoInit() { - autoInitState = AutoInitState::ENABLED; + mAutoInitState = AutoInitState::ENABLED; - if (bridgeInitialized) + if(INITIALIZED_BRIDGE) { return; } - auto* bridge = Bridge::GetCurrentBridge(); - bridge->AddTopLevelWindow(topLevelWindow); + auto rootLayer = Dali::Stage::GetCurrent().GetRootLayer(); // A root layer of the default window. + auto window = Dali::DevelWindow::Get(rootLayer); + auto applicationName = Dali::Internal::Adaptor::Adaptor::GetApplicationPackageName(); + + auto accessible = Accessibility::Accessible::Get(rootLayer); + + auto bridge = Bridge::GetCurrentBridge(); + bridge->AddTopLevelWindow(accessible); bridge->SetApplicationName(applicationName); bridge->Initialize(); + + if(window && window.IsVisible()) + { + 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(); }