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 bool mIsShown = false;
80 std::unordered_map<int32_t, std::function<void(std::string)>> mDirectReadingCallbacks;
81 Dali::Actor mHighlightedActor;
82 std::function<void(Dali::Actor)> mHighlightClearAction;
83 Dali::CallbackBase* mIdleCallback = NULL;
84 Dali::Timer mInitializeTimer;
85 Dali::Timer mReadIsEnabledTimer;
86 Dali::Timer mReadScreenReaderEnabledTimer;
87 Dali::Timer mForceUpTimer;
95 * @copydoc Dali::Accessibility::Bridge::Emit()
97 Consumed Emit(KeyEventType type, unsigned int keyCode, const std::string& keyName, unsigned int timeStamp, bool isText) override
104 unsigned int keyType = 0;
108 case KeyEventType::KEY_PRESSED:
113 case KeyEventType::KEY_RELEASED:
124 auto methodObject = mRegistryClient.method<bool(std::tuple<uint32_t, int32_t, int32_t, int32_t, int32_t, std::string, bool>)>("NotifyListenersSync");
125 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});
128 LOG() << result.getError().message;
131 return std::get<0>(result) ? Consumed::YES : Consumed::NO;
135 * @copydoc Dali::Accessibility::Bridge::Pause()
137 void Pause() override
144 mDirectReadingClient.method<DBus::ValueOrError<void>(bool)>("PauseResume").asyncCall([](DBus::ValueOrError<void> msg) {
147 LOG() << "Direct reading command failed (" << msg.getError().message << ")\n";
154 * @copydoc Dali::Accessibility::Bridge::Resume()
156 void Resume() override
163 mDirectReadingClient.method<DBus::ValueOrError<void>(bool)>("PauseResume").asyncCall([](DBus::ValueOrError<void> msg) {
166 LOG() << "Direct reading command failed (" << msg.getError().message << ")\n";
173 * @copydoc Dali::Accessibility::Bridge::StopReading()
175 void StopReading(bool alsoNonDiscardable) override
182 mDirectReadingClient.method<DBus::ValueOrError<void>(bool)>("StopReading").asyncCall([](DBus::ValueOrError<void> msg) {
185 LOG() << "Direct reading command failed (" << msg.getError().message << ")\n";
192 * @copydoc Dali::Accessibility::Bridge::Say()
194 void Say(const std::string& text, bool discardable, std::function<void(std::string)> callback) override
201 mDirectReadingClient.method<DBus::ValueOrError<std::string, bool, int32_t>(std::string, bool)>("ReadCommand").asyncCall([=](DBus::ValueOrError<std::string, bool, int32_t> msg) {
204 LOG() << "Direct reading command failed (" << msg.getError().message << ")\n";
208 mDirectReadingCallbacks.emplace(std::get<2>(msg), callback);
216 * @copydoc Dali::Accessibility::Bridge::ForceDown()
218 void ForceDown() override
222 if(mData->mCurrentlyHighlightedActor && mData->mHighlightActor)
224 mData->mCurrentlyHighlightedActor.Remove(mData->mHighlightActor);
226 mData->mCurrentlyHighlightedActor = {};
227 mData->mHighlightActor = {};
229 mDisabledSignal.Emit();
231 mHighlightedActor = {};
232 mHighlightClearAction = {};
233 BridgeAccessible::ForceDown();
234 mRegistryClient = {};
235 mDirectReadingClient = {};
236 mDirectReadingCallbacks.clear();
237 mApplication.mChildren.clear();
245 mInitializeTimer.Stop();
246 mInitializeTimer.Reset();
249 if(mReadIsEnabledTimer)
251 mReadIsEnabledTimer.Stop();
252 mReadIsEnabledTimer.Reset();
255 if(mReadScreenReaderEnabledTimer)
257 mReadScreenReaderEnabledTimer.Stop();
258 mReadScreenReaderEnabledTimer.Reset();
263 mForceUpTimer.Stop();
264 mForceUpTimer.Reset();
269 * @copydoc Dali::Accessibility::Bridge::Terminate()
271 void Terminate() override
275 mData->mCurrentlyHighlightedActor = {};
276 mData->mHighlightActor = {};
279 if((NULL != mIdleCallback) && Dali::Adaptor::IsAvailable())
281 Dali::Adaptor::Get().RemoveIdle(mIdleCallback);
283 mAccessibilityStatusClient = {};
288 bool ForceUpTimerCallback()
290 if(ForceUp() != ForceUpResult::FAILED)
298 * @copydoc Dali::Accessibility::Bridge::ForceUp()
300 ForceUpResult ForceUp() override
302 auto forceUpResult = BridgeAccessible::ForceUp();
303 if(forceUpResult == ForceUpResult::ALREADY_UP)
305 return forceUpResult;
307 else if(forceUpResult == ForceUpResult::FAILED)
311 mForceUpTimer = Dali::Timer::New(RETRY_INTERVAL);
312 mForceUpTimer.TickSignal().Connect(this, &BridgeImpl::ForceUpTimerCallback);
313 mForceUpTimer.Start();
315 return forceUpResult;
318 BridgeObject::RegisterInterfaces();
319 BridgeAccessible::RegisterInterfaces();
320 BridgeComponent::RegisterInterfaces();
321 BridgeCollection::RegisterInterfaces();
322 BridgeAction::RegisterInterfaces();
323 BridgeValue::RegisterInterfaces();
324 BridgeText::RegisterInterfaces();
325 BridgeEditableText::RegisterInterfaces();
326 BridgeSelection::RegisterInterfaces();
327 BridgeApplication::RegisterInterfaces();
328 BridgeHypertext::RegisterInterfaces();
329 BridgeHyperlink::RegisterInterfaces();
330 BridgeSocket::RegisterInterfaces();
332 RegisterOnBridge(&mApplication);
334 mRegistryClient = {AtspiDbusNameRegistry, AtspiDbusPathDec, Accessible::GetInterfaceName(AtspiInterface::DEVICE_EVENT_CONTROLLER), mConnectionPtr};
335 mDirectReadingClient = DBus::DBusClient{DirectReadingDBusName, DirectReadingDBusPath, DirectReadingDBusInterface, mConnectionPtr};
337 mDirectReadingClient.addSignal<void(int32_t, std::string)>("ReadingStateChanged", [=](int32_t id, std::string readingState) {
338 auto it = mDirectReadingCallbacks.find(id);
339 if(it != mDirectReadingCallbacks.end())
341 it->second(readingState);
342 if(readingState != "ReadingPaused" && readingState != "ReadingResumed" && readingState != "ReadingStarted")
344 mDirectReadingCallbacks.erase(it);
349 auto proxy = DBus::DBusClient{AtspiDbusNameRegistry, AtspiDbusPathRoot, Accessible::GetInterfaceName(AtspiInterface::SOCKET), mConnectionPtr};
350 Address root{"", "root"};
351 auto res = proxy.method<Address(Address)>("Embed").call(root);
354 LOG() << "Call to Embed failed: " << res.getError().message;
358 mApplication.mParent.SetAddress(std::move(std::get<0>(res)));
360 mEnabledSignal.Emit();
362 return ForceUpResult::JUST_STARTED;
366 * @brief Sends a signal to dbus that the window is shown.
368 * @param[in] window The window to be shown
369 * @see Accessible::EmitShowing() and BridgeObject::EmitStateChanged()
371 void EmitShown(Dali::Window window)
373 auto windowAccessible = mApplication.GetWindowAccessible(window);
376 windowAccessible->EmitShowing(true);
381 * @brief Sends a signal to dbus that the window is hidden.
383 * @param[in] window The window to be hidden
384 * @see Accessible::EmitShowing() and BridgeObject::EmitStateChanged()
386 void EmitHidden(Dali::Window window)
388 auto windowAccessible = mApplication.GetWindowAccessible(window);
391 windowAccessible->EmitShowing(false);
396 * @brief Sends a signal to dbus that the window is activated.
398 * @param[in] window The window to be activated
399 * @see BridgeObject::Emit()
401 void EmitActivate(Dali::Window window)
403 auto windowAccessible = mApplication.GetWindowAccessible(window);
406 windowAccessible->Emit(WindowEvent::ACTIVATE, 0);
411 * @brief Sends a signal to dbus that the window is deactivated.
413 * @param[in] window The window to be deactivated
414 * @see BridgeObject::Emit()
416 void EmitDeactivate(Dali::Window window)
418 auto windowAccessible = mApplication.GetWindowAccessible(window);
421 windowAccessible->Emit(WindowEvent::DEACTIVATE, 0);
426 * @copydoc Dali::Accessibility::Bridge::WindowShown()
428 void WindowShown(Dali::Window window) override
430 if(!mIsShown && IsUp())
438 * @copydoc Dali::Accessibility::Bridge::WindowHidden()
440 void WindowHidden(Dali::Window window) override
442 if(mIsShown && IsUp())
450 * @copydoc Dali::Accessibility::Bridge::WindowFocused()
452 void WindowFocused(Dali::Window window) override
454 if(mIsShown && IsUp())
456 EmitActivate(window);
461 * @copydoc Dali::Accessibility::Bridge::WindowUnfocused()
463 void WindowUnfocused(Dali::Window window) override
465 if(mIsShown && IsUp())
467 EmitDeactivate(window);
472 * @copydoc Dali::Accessibility::Bridge::SuppressScreenReader()
474 void SuppressScreenReader(bool suppress) override
476 if(mIsScreenReaderSuppressed == suppress)
480 mIsScreenReaderSuppressed = suppress;
481 ReadScreenReaderEnabledProperty();
486 if((!mIsScreenReaderSuppressed && mIsScreenReaderEnabled) || mIsEnabled)
496 bool ReadIsEnabledTimerCallback()
498 ReadIsEnabledProperty();
502 void ReadIsEnabledProperty()
504 mAccessibilityStatusClient.property<bool>("IsEnabled").asyncGet([this](DBus::ValueOrError<bool> msg) {
507 DALI_LOG_ERROR("Get IsEnabled property error: %s\n", msg.getError().message.c_str());
508 if(msg.getError().errorType == DBus::ErrorType::INVALID_REPLY)
510 if(!mReadIsEnabledTimer)
512 mReadIsEnabledTimer = Dali::Timer::New(RETRY_INTERVAL);
513 mReadIsEnabledTimer.TickSignal().Connect(this, &BridgeImpl::ReadIsEnabledTimerCallback);
515 mReadIsEnabledTimer.Start();
520 if(mReadIsEnabledTimer)
522 mReadIsEnabledTimer.Stop();
523 mReadIsEnabledTimer.Reset();
526 mIsEnabled = std::get<0>(msg);
531 void ListenIsEnabledProperty()
533 mAccessibilityStatusClient.addPropertyChangedEvent<bool>("IsEnabled", [this](bool res) {
539 bool ReadScreenReaderEnabledTimerCallback()
541 ReadScreenReaderEnabledProperty();
545 void ReadScreenReaderEnabledProperty()
547 // can be true because of SuppressScreenReader before init
548 if (!mAccessibilityStatusClient)
553 mAccessibilityStatusClient.property<bool>("ScreenReaderEnabled").asyncGet([this](DBus::ValueOrError<bool> msg) {
556 DALI_LOG_ERROR("Get ScreenReaderEnabled property error: %s\n", msg.getError().message.c_str());
557 if(msg.getError().errorType == DBus::ErrorType::INVALID_REPLY)
559 if(!mReadScreenReaderEnabledTimer)
561 mReadScreenReaderEnabledTimer = Dali::Timer::New(RETRY_INTERVAL);
562 mReadScreenReaderEnabledTimer.TickSignal().Connect(this, &BridgeImpl::ReadScreenReaderEnabledTimerCallback);
564 mReadScreenReaderEnabledTimer.Start();
569 if(mReadScreenReaderEnabledTimer)
571 mReadScreenReaderEnabledTimer.Stop();
572 mReadScreenReaderEnabledTimer.Reset();
575 mIsScreenReaderEnabled = std::get<0>(msg);
580 void EmitScreenReaderEnabledSignal()
582 if (mIsScreenReaderEnabled)
584 mScreenReaderEnabledSignal.Emit();
588 mScreenReaderDisabledSignal.Emit();
592 void ListenScreenReaderEnabledProperty()
594 mAccessibilityStatusClient.addPropertyChangedEvent<bool>("ScreenReaderEnabled", [this](bool res) {
595 mIsScreenReaderEnabled = res;
596 EmitScreenReaderEnabledSignal();
601 void ReadAndListenProperties()
603 ReadIsEnabledProperty();
604 ListenIsEnabledProperty();
606 ReadScreenReaderEnabledProperty();
607 ListenScreenReaderEnabledProperty();
610 bool InitializeAccessibilityStatusClient()
612 mAccessibilityStatusClient = DBus::DBusClient{A11yDbusName, A11yDbusPath, A11yDbusStatusInterface, DBus::ConnectionType::SESSION};
614 if (!mAccessibilityStatusClient)
616 DALI_LOG_ERROR("Accessibility Status DbusClient is not ready\n");
623 bool InitializeTimerCallback()
625 if ( InitializeAccessibilityStatusClient() )
627 ReadAndListenProperties();
635 if ( InitializeAccessibilityStatusClient() )
637 ReadAndListenProperties();
638 mIdleCallback = NULL;
642 if(!mInitializeTimer)
644 mInitializeTimer = Dali::Timer::New(RETRY_INTERVAL);
645 mInitializeTimer.TickSignal().Connect(this, &BridgeImpl::InitializeTimerCallback);
647 mInitializeTimer.Start();
649 mIdleCallback = NULL;
654 * @copydoc Dali::Accessibility::Bridge::Initialize()
656 void Initialize() override
658 if ( InitializeAccessibilityStatusClient() )
660 ReadAndListenProperties();
664 // Initialize failed. Try it again on Idle
665 if( Dali::Adaptor::IsAvailable() )
667 Dali::Adaptor& adaptor = Dali::Adaptor::Get();
668 if( NULL == mIdleCallback )
670 mIdleCallback = MakeCallback( this, &BridgeImpl::OnIdleSignal );
671 adaptor.AddIdle( mIdleCallback, true );
677 * @copydoc Dali::Accessibility::Bridge::GetScreenReaderEnabled()
679 bool GetScreenReaderEnabled() override
681 return mIsScreenReaderEnabled;
685 * @copydoc Dali::Accessibility::Bridge::IsEnabled()
687 bool IsEnabled() override
693 namespace // unnamed namespace
696 bool INITIALIZED_BRIDGE = false;
699 * @brief Creates BridgeImpl instance.
701 * @return The BridgeImpl instance
702 * @note This method is to check environment variable first. If ATSPI is disable using env, it returns dummy bridge instance.
704 std::shared_ptr<Bridge> CreateBridge()
706 INITIALIZED_BRIDGE = true;
710 /* check environment variable first */
711 const char* envAtspiDisabled = Dali::EnvironmentVariable::GetEnvironmentVariable(DALI_ENV_DISABLE_ATSPI);
712 if(envAtspiDisabled && std::atoi(envAtspiDisabled) != 0)
714 return Dali::Accessibility::DummyBridge::GetInstance();
717 return std::make_shared<BridgeImpl>();
719 catch(const std::exception&)
721 DALI_LOG_ERROR("Failed to initialize AT-SPI bridge");
722 return Dali::Accessibility::DummyBridge::GetInstance();
726 } // unnamed namespace
728 // Dali::Accessibility::Bridge class implementation
730 std::shared_ptr<Bridge> Bridge::GetCurrentBridge()
732 static std::shared_ptr<Bridge> bridge;
738 else if(mAutoInitState == AutoInitState::ENABLED)
740 bridge = CreateBridge();
742 /* check environment variable for suppressing screen-reader */
743 const char* envSuppressScreenReader = Dali::EnvironmentVariable::GetEnvironmentVariable(DALI_ENV_SUPPRESS_SCREEN_READER);
744 if(envSuppressScreenReader && std::atoi(envSuppressScreenReader) != 0)
746 bridge->SuppressScreenReader(true);
752 return Dali::Accessibility::DummyBridge::GetInstance();
755 void Bridge::DisableAutoInit()
757 if(INITIALIZED_BRIDGE)
759 DALI_LOG_ERROR("Bridge::DisableAutoInit() called after bridge auto-initialization");
762 mAutoInitState = AutoInitState::DISABLED;
765 void Bridge::EnableAutoInit()
767 mAutoInitState = AutoInitState::ENABLED;
769 if(INITIALIZED_BRIDGE)
774 auto rootLayer = Dali::Stage::GetCurrent().GetRootLayer(); // A root layer of the default window.
775 auto window = Dali::DevelWindow::Get(rootLayer);
776 auto applicationName = Dali::Internal::Adaptor::Adaptor::GetApplicationPackageName();
778 auto accessible = Accessibility::Accessible::Get(rootLayer, true);
780 auto bridge = Bridge::GetCurrentBridge();
781 bridge->AddTopLevelWindow(accessible);
782 bridge->SetApplicationName(applicationName);
783 bridge->Initialize();
785 if(window && window.IsVisible())
787 bridge->WindowShown(window);