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.
21 #include <dali/devel-api/common/stage.h>
22 #include <dali/integration-api/debug.h>
23 #include <dali/public-api/actors/layer.h>
25 #include <unordered_map>
28 #include <dali/devel-api/adaptor-framework/environment-variable.h>
29 #include <dali/devel-api/adaptor-framework/window-devel.h>
30 #include <dali/internal/accessibility/bridge/bridge-accessible.h>
31 #include <dali/internal/accessibility/bridge/bridge-action.h>
32 #include <dali/internal/accessibility/bridge/bridge-collection.h>
33 #include <dali/internal/accessibility/bridge/bridge-component.h>
34 #include <dali/internal/accessibility/bridge/bridge-editable-text.h>
35 #include <dali/internal/accessibility/bridge/bridge-hypertext.h>
36 #include <dali/internal/accessibility/bridge/bridge-hyperlink.h>
37 #include <dali/internal/accessibility/bridge/bridge-object.h>
38 #include <dali/internal/accessibility/bridge/bridge-selection.h>
39 #include <dali/internal/accessibility/bridge/bridge-socket.h>
40 #include <dali/internal/accessibility/bridge/bridge-text.h>
41 #include <dali/internal/accessibility/bridge/bridge-value.h>
42 #include <dali/internal/accessibility/bridge/bridge-application.h>
43 #include <dali/internal/accessibility/bridge/dummy-atspi.h>
44 #include <dali/internal/adaptor/common/adaptor-impl.h>
45 #include <dali/internal/system/common/environment-variables.h>
47 using namespace Dali::Accessibility;
49 namespace // unnamed namespace
52 const int RETRY_INTERVAL = 1000;
54 } // unnamed namespace
57 * @brief The BridgeImpl class is to implement some Bridge functions.
59 class BridgeImpl : public virtual BridgeBase,
60 public BridgeAccessible,
62 public BridgeComponent,
63 public BridgeCollection,
67 public BridgeEditableText,
68 public BridgeSelection,
69 public BridgeApplication,
70 public BridgeHypertext,
71 public BridgeHyperlink,
74 DBus::DBusClient mAccessibilityStatusClient;
75 DBus::DBusClient mRegistryClient;
76 DBus::DBusClient mDirectReadingClient;
77 bool mIsScreenReaderEnabled = false;
78 bool mIsEnabled = false;
79 std::unordered_map<int32_t, std::function<void(std::string)>> mDirectReadingCallbacks;
80 Dali::Actor mHighlightedActor;
81 std::function<void(Dali::Actor)> mHighlightClearAction;
82 Dali::CallbackBase* mIdleCallback = NULL;
83 Dali::Timer mInitializeTimer;
84 Dali::Timer mReadIsEnabledTimer;
85 Dali::Timer mReadScreenReaderEnabledTimer;
86 Dali::Timer mForceUpTimer;
94 * @copydoc Dali::Accessibility::Bridge::Emit()
96 Consumed Emit(KeyEventType type, unsigned int keyCode, const std::string& keyName, unsigned int timeStamp, bool isText) override
103 unsigned int keyType = 0;
107 case KeyEventType::KEY_PRESSED:
112 case KeyEventType::KEY_RELEASED:
123 auto methodObject = mRegistryClient.method<bool(std::tuple<uint32_t, int32_t, int32_t, int32_t, int32_t, std::string, bool>)>("NotifyListenersSync");
124 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});
127 LOG() << result.getError().message;
130 return std::get<0>(result) ? Consumed::YES : Consumed::NO;
134 * @copydoc Dali::Accessibility::Bridge::Pause()
136 void Pause() override
143 mDirectReadingClient.method<DBus::ValueOrError<void>(bool)>("PauseResume").asyncCall([](DBus::ValueOrError<void> msg) {
146 LOG() << "Direct reading command failed (" << msg.getError().message << ")\n";
153 * @copydoc Dali::Accessibility::Bridge::Resume()
155 void Resume() override
162 mDirectReadingClient.method<DBus::ValueOrError<void>(bool)>("PauseResume").asyncCall([](DBus::ValueOrError<void> msg) {
165 LOG() << "Direct reading command failed (" << msg.getError().message << ")\n";
172 * @copydoc Dali::Accessibility::Bridge::StopReading()
174 void StopReading(bool alsoNonDiscardable) override
181 mDirectReadingClient.method<DBus::ValueOrError<void>(bool)>("StopReading").asyncCall([](DBus::ValueOrError<void> msg) {
184 LOG() << "Direct reading command failed (" << msg.getError().message << ")\n";
191 * @copydoc Dali::Accessibility::Bridge::Say()
193 void Say(const std::string& text, bool discardable, std::function<void(std::string)> callback) override
200 mDirectReadingClient.method<DBus::ValueOrError<std::string, bool, int32_t>(std::string, bool)>("ReadCommand").asyncCall([=](DBus::ValueOrError<std::string, bool, int32_t> msg) {
203 LOG() << "Direct reading command failed (" << msg.getError().message << ")\n";
207 mDirectReadingCallbacks.emplace(std::get<2>(msg), callback);
215 * @copydoc Dali::Accessibility::Bridge::ForceDown()
217 void ForceDown() override
221 if(mData->mCurrentlyHighlightedActor && mData->mHighlightActor)
223 mData->mCurrentlyHighlightedActor.Remove(mData->mHighlightActor);
225 mData->mCurrentlyHighlightedActor = {};
226 mData->mHighlightActor = {};
228 mDisabledSignal.Emit();
229 UnembedSocket(mApplication.GetAddress(), {AtspiDbusNameRegistry, "root"});
232 mHighlightedActor = {};
233 mHighlightClearAction = {};
234 BridgeAccessible::ForceDown();
235 mRegistryClient = {};
236 mDirectReadingClient = {};
237 mDirectReadingCallbacks.clear();
238 mApplication.mChildren.clear();
246 mInitializeTimer.Stop();
247 mInitializeTimer.Reset();
250 if(mReadIsEnabledTimer)
252 mReadIsEnabledTimer.Stop();
253 mReadIsEnabledTimer.Reset();
256 if(mReadScreenReaderEnabledTimer)
258 mReadScreenReaderEnabledTimer.Stop();
259 mReadScreenReaderEnabledTimer.Reset();
264 mForceUpTimer.Stop();
265 mForceUpTimer.Reset();
270 * @copydoc Dali::Accessibility::Bridge::Terminate()
272 void Terminate() override
276 // The ~Window() after this point cannot emit DESTROY, because Bridge is not available. So emit DESTROY here.
277 for(auto windowAccessible : mApplication.mChildren)
279 BridgeObject::Emit(windowAccessible, WindowEvent::DESTROY);
281 mData->mCurrentlyHighlightedActor = {};
282 mData->mHighlightActor = {};
285 if((NULL != mIdleCallback) && Dali::Adaptor::IsAvailable())
287 Dali::Adaptor::Get().RemoveIdle(mIdleCallback);
289 mAccessibilityStatusClient = {};
294 bool ForceUpTimerCallback()
296 if(ForceUp() != ForceUpResult::FAILED)
304 * @copydoc Dali::Accessibility::Bridge::ForceUp()
306 ForceUpResult ForceUp() override
308 auto forceUpResult = BridgeAccessible::ForceUp();
309 if(forceUpResult == ForceUpResult::ALREADY_UP)
311 return forceUpResult;
313 else if(forceUpResult == ForceUpResult::FAILED)
317 mForceUpTimer = Dali::Timer::New(RETRY_INTERVAL);
318 mForceUpTimer.TickSignal().Connect(this, &BridgeImpl::ForceUpTimerCallback);
319 mForceUpTimer.Start();
321 return forceUpResult;
324 BridgeObject::RegisterInterfaces();
325 BridgeAccessible::RegisterInterfaces();
326 BridgeComponent::RegisterInterfaces();
327 BridgeCollection::RegisterInterfaces();
328 BridgeAction::RegisterInterfaces();
329 BridgeValue::RegisterInterfaces();
330 BridgeText::RegisterInterfaces();
331 BridgeEditableText::RegisterInterfaces();
332 BridgeSelection::RegisterInterfaces();
333 BridgeApplication::RegisterInterfaces();
334 BridgeHypertext::RegisterInterfaces();
335 BridgeHyperlink::RegisterInterfaces();
336 BridgeSocket::RegisterInterfaces();
338 RegisterOnBridge(&mApplication);
340 mRegistryClient = {AtspiDbusNameRegistry, AtspiDbusPathDec, Accessible::GetInterfaceName(AtspiInterface::DEVICE_EVENT_CONTROLLER), mConnectionPtr};
341 mDirectReadingClient = DBus::DBusClient{DirectReadingDBusName, DirectReadingDBusPath, DirectReadingDBusInterface, mConnectionPtr};
343 mDirectReadingClient.addSignal<void(int32_t, std::string)>("ReadingStateChanged", [=](int32_t id, std::string readingState) {
344 auto it = mDirectReadingCallbacks.find(id);
345 if(it != mDirectReadingCallbacks.end())
347 it->second(readingState);
348 if(readingState != "ReadingPaused" && readingState != "ReadingResumed" && readingState != "ReadingStarted")
350 mDirectReadingCallbacks.erase(it);
355 auto parentAddress = EmbedSocket(mApplication.GetAddress(), {AtspiDbusNameRegistry, "root"});
356 mApplication.mParent.SetAddress(std::move(parentAddress));
357 mEnabledSignal.Emit();
359 return ForceUpResult::JUST_STARTED;
363 * @brief Sends a signal to dbus that the window is shown.
365 * @param[in] window The window to be shown
366 * @see Accessible::EmitShowing() and BridgeObject::EmitStateChanged()
368 void EmitShown(Dali::Window window)
370 auto windowAccessible = mApplication.GetWindowAccessible(window);
373 windowAccessible->EmitShowing(true);
378 * @brief Sends a signal to dbus that the window is hidden.
380 * @param[in] window The window to be hidden
381 * @see Accessible::EmitShowing() and BridgeObject::EmitStateChanged()
383 void EmitHidden(Dali::Window window)
385 auto windowAccessible = mApplication.GetWindowAccessible(window);
388 windowAccessible->EmitShowing(false);
393 * @brief Sends a signal to dbus that the window is activated.
395 * @param[in] window The window to be activated
396 * @see BridgeObject::Emit()
398 void EmitActivate(Dali::Window window)
400 auto windowAccessible = mApplication.GetWindowAccessible(window);
403 windowAccessible->Emit(WindowEvent::ACTIVATE, 0);
408 * @brief Sends a signal to dbus that the window is deactivated.
410 * @param[in] window The window to be deactivated
411 * @see BridgeObject::Emit()
413 void EmitDeactivate(Dali::Window window)
415 auto windowAccessible = mApplication.GetWindowAccessible(window);
418 windowAccessible->Emit(WindowEvent::DEACTIVATE, 0);
423 * @copydoc Dali::Accessibility::Bridge::WindowShown()
425 void WindowShown(Dali::Window window) override
434 * @copydoc Dali::Accessibility::Bridge::WindowHidden()
436 void WindowHidden(Dali::Window window) override
445 * @copydoc Dali::Accessibility::Bridge::WindowFocused()
447 void WindowFocused(Dali::Window window) override
451 EmitActivate(window);
456 * @copydoc Dali::Accessibility::Bridge::WindowUnfocused()
458 void WindowUnfocused(Dali::Window window) override
462 EmitDeactivate(window);
467 * @copydoc Dali::Accessibility::Bridge::SuppressScreenReader()
469 void SuppressScreenReader(bool suppress) override
471 if(mIsScreenReaderSuppressed == suppress)
475 mIsScreenReaderSuppressed = suppress;
476 ReadScreenReaderEnabledProperty();
481 if((!mIsScreenReaderSuppressed && mIsScreenReaderEnabled) || mIsEnabled)
491 bool ReadIsEnabledTimerCallback()
493 ReadIsEnabledProperty();
497 void ReadIsEnabledProperty()
499 mAccessibilityStatusClient.property<bool>("IsEnabled").asyncGet([this](DBus::ValueOrError<bool> msg) {
502 DALI_LOG_ERROR("Get IsEnabled property error: %s\n", msg.getError().message.c_str());
503 if(msg.getError().errorType == DBus::ErrorType::INVALID_REPLY)
505 if(!mReadIsEnabledTimer)
507 mReadIsEnabledTimer = Dali::Timer::New(RETRY_INTERVAL);
508 mReadIsEnabledTimer.TickSignal().Connect(this, &BridgeImpl::ReadIsEnabledTimerCallback);
510 mReadIsEnabledTimer.Start();
515 if(mReadIsEnabledTimer)
517 mReadIsEnabledTimer.Stop();
518 mReadIsEnabledTimer.Reset();
521 mIsEnabled = std::get<0>(msg);
526 void ListenIsEnabledProperty()
528 mAccessibilityStatusClient.addPropertyChangedEvent<bool>("IsEnabled", [this](bool res) {
534 bool ReadScreenReaderEnabledTimerCallback()
536 ReadScreenReaderEnabledProperty();
540 void ReadScreenReaderEnabledProperty()
542 // can be true because of SuppressScreenReader before init
543 if (!mAccessibilityStatusClient)
548 mAccessibilityStatusClient.property<bool>("ScreenReaderEnabled").asyncGet([this](DBus::ValueOrError<bool> msg) {
551 DALI_LOG_ERROR("Get ScreenReaderEnabled property error: %s\n", msg.getError().message.c_str());
552 if(msg.getError().errorType == DBus::ErrorType::INVALID_REPLY)
554 if(!mReadScreenReaderEnabledTimer)
556 mReadScreenReaderEnabledTimer = Dali::Timer::New(RETRY_INTERVAL);
557 mReadScreenReaderEnabledTimer.TickSignal().Connect(this, &BridgeImpl::ReadScreenReaderEnabledTimerCallback);
559 mReadScreenReaderEnabledTimer.Start();
564 if(mReadScreenReaderEnabledTimer)
566 mReadScreenReaderEnabledTimer.Stop();
567 mReadScreenReaderEnabledTimer.Reset();
570 mIsScreenReaderEnabled = std::get<0>(msg);
575 void EmitScreenReaderEnabledSignal()
577 if (mIsScreenReaderEnabled)
579 mScreenReaderEnabledSignal.Emit();
583 mScreenReaderDisabledSignal.Emit();
587 void ListenScreenReaderEnabledProperty()
589 mAccessibilityStatusClient.addPropertyChangedEvent<bool>("ScreenReaderEnabled", [this](bool res) {
590 mIsScreenReaderEnabled = res;
591 EmitScreenReaderEnabledSignal();
596 void ReadAndListenProperties()
598 ReadIsEnabledProperty();
599 ListenIsEnabledProperty();
601 ReadScreenReaderEnabledProperty();
602 ListenScreenReaderEnabledProperty();
605 bool InitializeAccessibilityStatusClient()
607 mAccessibilityStatusClient = DBus::DBusClient{A11yDbusName, A11yDbusPath, A11yDbusStatusInterface, DBus::ConnectionType::SESSION};
609 if (!mAccessibilityStatusClient)
611 DALI_LOG_ERROR("Accessibility Status DbusClient is not ready\n");
618 bool InitializeTimerCallback()
620 if ( InitializeAccessibilityStatusClient() )
622 ReadAndListenProperties();
630 if ( InitializeAccessibilityStatusClient() )
632 ReadAndListenProperties();
633 mIdleCallback = NULL;
637 if(!mInitializeTimer)
639 mInitializeTimer = Dali::Timer::New(RETRY_INTERVAL);
640 mInitializeTimer.TickSignal().Connect(this, &BridgeImpl::InitializeTimerCallback);
642 mInitializeTimer.Start();
644 mIdleCallback = NULL;
649 * @copydoc Dali::Accessibility::Bridge::Initialize()
651 void Initialize() override
653 if ( InitializeAccessibilityStatusClient() )
655 ReadAndListenProperties();
659 // Initialize failed. Try it again on Idle
660 if( Dali::Adaptor::IsAvailable() )
662 Dali::Adaptor& adaptor = Dali::Adaptor::Get();
663 if( NULL == mIdleCallback )
665 mIdleCallback = MakeCallback( this, &BridgeImpl::OnIdleSignal );
666 adaptor.AddIdle( mIdleCallback, true );
672 * @copydoc Dali::Accessibility::Bridge::GetScreenReaderEnabled()
674 bool GetScreenReaderEnabled() override
676 return mIsScreenReaderEnabled;
680 * @copydoc Dali::Accessibility::Bridge::IsEnabled()
682 bool IsEnabled() override
687 Address EmbedSocket(const Address& plug, const Address& socket) override
689 auto client = CreateSocketClient(socket);
690 auto reply = client.method<Address(Address)>("Embed").call(plug);
694 DALI_LOG_ERROR("Failed to embed socket %s: %s", socket.ToString().c_str(), reply.getError().message.c_str());
698 return std::get<0>(reply.getValues());
701 void EmbedAtkSocket(const Address& plug, const Address& socket) override
703 auto client = CreateSocketClient(socket);
705 client.method<void(std::string)>("Embedded").call(ATSPI_PREFIX_PATH + plug.GetPath());
708 void UnembedSocket(const Address& plug, const Address& socket) override
710 auto client = CreateSocketClient(socket);
712 client.method<void(Address)>("Unembed").call(plug);
716 DBus::DBusClient CreateSocketClient(const Address& socket)
718 return {socket.GetBus(), ATSPI_PREFIX_PATH + socket.GetPath(), Accessible::GetInterfaceName(AtspiInterface::SOCKET), mConnectionPtr};
722 namespace // unnamed namespace
725 bool INITIALIZED_BRIDGE = false;
728 * @brief Creates BridgeImpl instance.
730 * @return The BridgeImpl instance
731 * @note This method is to check environment variable first. If ATSPI is disable using env, it returns dummy bridge instance.
733 std::shared_ptr<Bridge> CreateBridge()
735 INITIALIZED_BRIDGE = true;
739 /* check environment variable first */
740 const char* envAtspiDisabled = Dali::EnvironmentVariable::GetEnvironmentVariable(DALI_ENV_DISABLE_ATSPI);
741 if(envAtspiDisabled && std::atoi(envAtspiDisabled) != 0)
743 return Dali::Accessibility::DummyBridge::GetInstance();
746 return std::make_shared<BridgeImpl>();
748 catch(const std::exception&)
750 DALI_LOG_ERROR("Failed to initialize AT-SPI bridge");
751 return Dali::Accessibility::DummyBridge::GetInstance();
755 } // unnamed namespace
757 // Dali::Accessibility::Bridge class implementation
759 std::shared_ptr<Bridge> Bridge::GetCurrentBridge()
761 static std::shared_ptr<Bridge> bridge;
767 else if(mAutoInitState == AutoInitState::ENABLED)
769 bridge = CreateBridge();
771 /* check environment variable for suppressing screen-reader */
772 const char* envSuppressScreenReader = Dali::EnvironmentVariable::GetEnvironmentVariable(DALI_ENV_SUPPRESS_SCREEN_READER);
773 if(envSuppressScreenReader && std::atoi(envSuppressScreenReader) != 0)
775 bridge->SuppressScreenReader(true);
781 return Dali::Accessibility::DummyBridge::GetInstance();
784 void Bridge::DisableAutoInit()
786 if(INITIALIZED_BRIDGE)
788 DALI_LOG_ERROR("Bridge::DisableAutoInit() called after bridge auto-initialization");
791 mAutoInitState = AutoInitState::DISABLED;
794 void Bridge::EnableAutoInit()
796 mAutoInitState = AutoInitState::ENABLED;
798 if(INITIALIZED_BRIDGE)
803 auto rootLayer = Dali::Stage::GetCurrent().GetRootLayer(); // A root layer of the default window.
804 auto window = Dali::DevelWindow::Get(rootLayer);
805 auto applicationName = Dali::Internal::Adaptor::Adaptor::GetApplicationPackageName();
807 auto accessible = Accessibility::Accessible::Get(rootLayer);
809 auto bridge = Bridge::GetCurrentBridge();
810 bridge->AddTopLevelWindow(accessible);
811 bridge->SetApplicationName(applicationName);
812 bridge->Initialize();
814 if(window && window.IsVisible())
816 bridge->WindowShown(window);