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-object.h>
36 #include <dali/internal/accessibility/bridge/bridge-selection.h>
37 #include <dali/internal/accessibility/bridge/bridge-text.h>
38 #include <dali/internal/accessibility/bridge/bridge-value.h>
39 #include <dali/internal/accessibility/bridge/bridge-application.h>
40 #include <dali/internal/accessibility/bridge/dummy-atspi.h>
41 #include <dali/internal/adaptor/common/adaptor-impl.h>
42 #include <dali/internal/system/common/environment-variables.h>
44 using namespace Dali::Accessibility;
46 namespace // unnamed namespace
49 const int RETRY_INTERVAL = 1000;
51 } // unnamed namespace
54 * @brief The BridgeImpl class is to implement some Bridge functions.
56 class BridgeImpl : public virtual BridgeBase,
57 public BridgeAccessible,
59 public BridgeComponent,
60 public BridgeCollection,
64 public BridgeEditableText,
65 public BridgeSelection,
66 public BridgeApplication
68 DBus::DBusClient mAccessibilityStatusClient;
69 DBus::DBusClient mRegistryClient;
70 DBus::DBusClient mDirectReadingClient;
71 bool mIsScreenReaderEnabled = false;
72 bool mIsEnabled = false;
73 bool mIsShown = false;
74 std::unordered_map<int32_t, std::function<void(std::string)>> mDirectReadingCallbacks;
75 Dali::Actor mHighlightedActor;
76 std::function<void(Dali::Actor)> mHighlightClearAction;
77 Dali::CallbackBase* mIdleCallback = NULL;
78 Dali::Timer mInitializeTimer;
79 Dali::Timer mReadIsEnabledTimer;
80 Dali::Timer mReadScreenReaderEnabledTimer;
88 * @copydoc Dali::Accessibility::Bridge::Emit()
90 Consumed Emit(KeyEventType type, unsigned int keyCode, const std::string& keyName, unsigned int timeStamp, bool isText) override
97 unsigned int keyType = 0;
101 case KeyEventType::KEY_PRESSED:
106 case KeyEventType::KEY_RELEASED:
117 auto methodObject = mRegistryClient.method<bool(std::tuple<uint32_t, int32_t, int32_t, int32_t, int32_t, std::string, bool>)>("NotifyListenersSync");
118 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});
121 LOG() << result.getError().message;
124 return std::get<0>(result) ? Consumed::YES : Consumed::NO;
128 * @copydoc Dali::Accessibility::Bridge::Pause()
130 void Pause() override
137 mDirectReadingClient.method<DBus::ValueOrError<void>(bool)>("PauseResume").asyncCall([](DBus::ValueOrError<void> msg) {
140 LOG() << "Direct reading command failed (" << msg.getError().message << ")";
147 * @copydoc Dali::Accessibility::Bridge::Resume()
149 void Resume() override
156 mDirectReadingClient.method<DBus::ValueOrError<void>(bool)>("PauseResume").asyncCall([](DBus::ValueOrError<void> msg) {
159 LOG() << "Direct reading command failed (" << msg.getError().message << ")";
166 * @copydoc Dali::Accessibility::Bridge::StopReading()
168 void StopReading(bool alsoNonDiscardable) override
175 mDirectReadingClient.method<DBus::ValueOrError<void>(bool)>("StopReading").asyncCall([](DBus::ValueOrError<void> msg) {
178 LOG() << "Direct reading command failed (" << msg.getError().message << ")";
185 * @copydoc Dali::Accessibility::Bridge::Say()
187 void Say(const std::string& text, bool discardable, std::function<void(std::string)> callback) override
194 mDirectReadingClient.method<DBus::ValueOrError<std::string, bool, int32_t>(std::string, bool)>("ReadCommand").asyncCall([=](DBus::ValueOrError<std::string, bool, int32_t> msg) {
197 LOG() << "Direct reading command failed (" << msg.getError().message << ")";
201 mDirectReadingCallbacks.emplace(std::get<2>(msg), callback);
209 * @copydoc Dali::Accessibility::Bridge::ForceDown()
211 void ForceDown() override
215 if(mData->mCurrentlyHighlightedActor && mData->mHighlightActor)
217 mData->mCurrentlyHighlightedActor.Remove(mData->mHighlightActor);
219 mData->mCurrentlyHighlightedActor = {};
220 mData->mHighlightActor = {};
222 mDisabledSignal.Emit();
224 mHighlightedActor = {};
225 mHighlightClearAction = {};
226 BridgeAccessible::ForceDown();
227 mRegistryClient = {};
228 mDirectReadingClient = {};
229 mDirectReadingCallbacks.clear();
230 mApplication.mChildren.clear();
231 mApplication.mWindows.clear();
238 mInitializeTimer.Stop();
241 if(mReadIsEnabledTimer)
243 mReadIsEnabledTimer.Stop();
246 if(mReadScreenReaderEnabledTimer)
248 mReadScreenReaderEnabledTimer.Stop();
253 * @copydoc Dali::Accessibility::Bridge::Terminate()
255 void Terminate() override
259 mData->mCurrentlyHighlightedActor = {};
260 mData->mHighlightActor = {};
264 if((NULL != mIdleCallback) && Dali::Adaptor::IsAvailable())
266 Dali::Adaptor::Get().RemoveIdle(mIdleCallback);
268 mAccessibilityStatusClient = {};
274 * @copydoc Dali::Accessibility::Bridge::ForceUp()
276 ForceUpResult ForceUp() override
278 if(BridgeAccessible::ForceUp() == ForceUpResult::ALREADY_UP)
280 return ForceUpResult::ALREADY_UP;
283 BridgeObject::RegisterInterfaces();
284 BridgeAccessible::RegisterInterfaces();
285 BridgeComponent::RegisterInterfaces();
286 BridgeCollection::RegisterInterfaces();
287 BridgeAction::RegisterInterfaces();
288 BridgeValue::RegisterInterfaces();
289 BridgeText::RegisterInterfaces();
290 BridgeEditableText::RegisterInterfaces();
291 BridgeSelection::RegisterInterfaces();
292 BridgeApplication::RegisterInterfaces();
294 RegisterOnBridge(&mApplication);
296 mRegistryClient = {AtspiDbusNameRegistry, AtspiDbusPathDec, AtspiDbusInterfaceDec, mConnectionPtr};
297 mDirectReadingClient = DBus::DBusClient{DirectReadingDBusName, DirectReadingDBusPath, DirectReadingDBusInterface, mConnectionPtr};
299 mDirectReadingClient.addSignal<void(int32_t, std::string)>("ReadingStateChanged", [=](int32_t id, std::string readingState) {
300 auto it = mDirectReadingCallbacks.find(id);
301 if(it != mDirectReadingCallbacks.end())
303 it->second(readingState);
304 if(readingState != "ReadingPaused" && readingState != "ReadingResumed" && readingState != "ReadingStarted")
306 mDirectReadingCallbacks.erase(it);
311 auto proxy = DBus::DBusClient{AtspiDbusNameRegistry, AtspiDbusPathRoot, AtspiDbusInterfaceSocket, mConnectionPtr};
312 Address root{"", "root"};
313 auto res = proxy.method<Address(Address)>("Embed").call(root);
316 LOG() << "Call to Embed failed: " << res.getError().message;
320 mApplication.mParent.SetAddress(std::move(std::get<0>(res)));
324 auto rootLayer = Dali::Stage::GetCurrent().GetRootLayer();
325 auto window = Dali::DevelWindow::Get(rootLayer);
326 EmitActivate(window); // Currently, sends a signal that the default window is activated here.
329 mEnabledSignal.Emit();
331 return ForceUpResult::JUST_STARTED;
335 * @brief Sends a signal to dbus that the window is shown.
337 * @param[in] window The window to be shown
338 * @see Accessible::EmitShowing() and BridgeObject::EmitStateChanged()
340 void EmitShown(Dali::Window window)
342 auto windowAccessible = mApplication.GetWindowAccessible(window);
345 windowAccessible->EmitShowing(true);
350 * @brief Sends a signal to dbus that the window is hidden.
352 * @param[in] window The window to be hidden
353 * @see Accessible::EmitShowing() and BridgeObject::EmitStateChanged()
355 void EmitHidden(Dali::Window window)
357 auto windowAccessible = mApplication.GetWindowAccessible(window);
360 windowAccessible->EmitShowing(false);
365 * @brief Sends a signal to dbus that the window is activated.
367 * @param[in] window The window to be activated
368 * @see BridgeObject::Emit()
370 void EmitActivate(Dali::Window window)
372 auto windowAccessible = mApplication.GetWindowAccessible(window);
375 windowAccessible->Emit(WindowEvent::ACTIVATE, 0);
380 * @brief Sends a signal to dbus that the window is deactivated.
382 * @param[in] window The window to be deactivated
383 * @see BridgeObject::Emit()
385 void EmitDeactivate(Dali::Window window)
387 auto windowAccessible = mApplication.GetWindowAccessible(window);
390 windowAccessible->Emit(WindowEvent::DEACTIVATE, 0);
395 * @copydoc Dali::Accessibility::Bridge::WindowShown()
397 void WindowShown(Dali::Window window) override
399 if(!mIsShown && IsUp())
407 * @copydoc Dali::Accessibility::Bridge::WindowHidden()
409 void WindowHidden(Dali::Window window) override
411 if(mIsShown && IsUp())
419 * @copydoc Dali::Accessibility::Bridge::WindowFocused()
421 void WindowFocused(Dali::Window window) override
423 if(mIsShown && IsUp())
425 EmitActivate(window);
430 * @copydoc Dali::Accessibility::Bridge::WindowUnfocused()
432 void WindowUnfocused(Dali::Window window) override
434 if(mIsShown && IsUp())
436 EmitDeactivate(window);
441 * @copydoc Dali::Accessibility::Bridge::SuppressScreenReader()
443 void SuppressScreenReader(bool suppress) override
445 if(mIsScreenReaderSuppressed == suppress)
449 mIsScreenReaderSuppressed = suppress;
450 ReadScreenReaderEnabledProperty();
453 bool ReadIsEnabledTimerCallback()
455 ReadIsEnabledProperty();
459 void ReadIsEnabledProperty()
461 mAccessibilityStatusClient.property<bool>("IsEnabled").asyncGet([this](DBus::ValueOrError<bool> msg) {
464 DALI_LOG_ERROR("Get IsEnabled property error: %s\n", msg.getError().message.c_str());
465 if(msg.getError().errorType == DBus::ErrorType::INVALID_REPLY)
467 if(!mReadIsEnabledTimer)
469 mReadIsEnabledTimer = Dali::Timer::New(RETRY_INTERVAL);
470 mReadIsEnabledTimer.TickSignal().Connect(this, &BridgeImpl::ReadIsEnabledTimerCallback);
472 mReadIsEnabledTimer.Start();
476 mIsEnabled = std::get<0>(msg);
477 if((!mIsScreenReaderSuppressed && mIsScreenReaderEnabled) || mIsEnabled)
488 void ListenIsEnabledProperty()
490 mAccessibilityStatusClient.addPropertyChangedEvent<bool>("IsEnabled", [this](bool res) {
492 if(mIsScreenReaderEnabled || mIsEnabled)
503 bool ReadScreenReaderEnabledTimerCallback()
505 ReadScreenReaderEnabledProperty();
509 void ReadScreenReaderEnabledProperty()
511 // can be true because of SuppressScreenReader before init
512 if (!mAccessibilityStatusClient)
517 mAccessibilityStatusClient.property<bool>("ScreenReaderEnabled").asyncGet([this](DBus::ValueOrError<bool> msg) {
520 DALI_LOG_ERROR("Get ScreenReaderEnabled property error: %s\n", msg.getError().message.c_str());
521 if(msg.getError().errorType == DBus::ErrorType::INVALID_REPLY)
523 if(!mReadScreenReaderEnabledTimer)
525 mReadScreenReaderEnabledTimer = Dali::Timer::New(RETRY_INTERVAL);
526 mReadScreenReaderEnabledTimer.TickSignal().Connect(this, &BridgeImpl::ReadScreenReaderEnabledTimerCallback);
528 mReadScreenReaderEnabledTimer.Start();
532 mIsScreenReaderEnabled = std::get<0>(msg);
533 if((!mIsScreenReaderSuppressed && mIsScreenReaderEnabled) || mIsEnabled)
544 void ListenScreenReaderEnabledProperty()
546 mAccessibilityStatusClient.addPropertyChangedEvent<bool>("ScreenReaderEnabled", [this](bool res) {
547 mIsScreenReaderEnabled = res;
548 if((!mIsScreenReaderSuppressed && mIsScreenReaderEnabled) || mIsEnabled)
559 void ReadAndListenProperties()
561 ReadIsEnabledProperty();
562 ListenIsEnabledProperty();
564 ReadScreenReaderEnabledProperty();
565 ListenScreenReaderEnabledProperty();
568 bool InitializeAccessibilityStatusClient()
570 mAccessibilityStatusClient = DBus::DBusClient{A11yDbusName, A11yDbusPath, A11yDbusStatusInterface, DBus::ConnectionType::SESSION};
572 if (!mAccessibilityStatusClient)
574 DALI_LOG_ERROR("Accessibility Status DbusClient is not ready\n");
581 bool InitializeTimerCallback()
583 if ( InitializeAccessibilityStatusClient() )
585 ReadAndListenProperties();
593 if ( InitializeAccessibilityStatusClient() )
595 ReadAndListenProperties();
596 mIdleCallback = NULL;
600 if(!mInitializeTimer)
602 mInitializeTimer = Dali::Timer::New(RETRY_INTERVAL);
603 mInitializeTimer.TickSignal().Connect(this, &BridgeImpl::InitializeTimerCallback);
605 mInitializeTimer.Start();
607 mIdleCallback = NULL;
612 * @copydoc Dali::Accessibility::Bridge::Initialize()
614 void Initialize() override
616 if ( InitializeAccessibilityStatusClient() )
618 ReadAndListenProperties();
622 // Initialize failed. Try it again on Idle
623 if( Dali::Adaptor::IsAvailable() )
625 Dali::Adaptor& adaptor = Dali::Adaptor::Get();
626 if( NULL == mIdleCallback )
628 mIdleCallback = MakeCallback( this, &BridgeImpl::OnIdleSignal );
629 adaptor.AddIdle( mIdleCallback, true );
635 * @copydoc Dali::Accessibility::Bridge::GetScreenReaderEnabled()
637 bool GetScreenReaderEnabled() override
639 return mIsScreenReaderEnabled;
643 * @copydoc Dali::Accessibility::Bridge::IsEnabled()
645 bool IsEnabled() override
651 namespace // unnamed namespace
654 bool INITIALIZED_BRIDGE = false;
657 * @brief Creates BridgeImpl instance.
659 * @return The BridgeImpl instance
660 * @note This method is to check environment variable first. If ATSPI is disable using env, it returns dummy bridge instance.
662 Bridge* CreateBridge()
664 INITIALIZED_BRIDGE = true;
668 /* check environment variable first */
669 const char* envAtspiDisabled = Dali::EnvironmentVariable::GetEnvironmentVariable(DALI_ENV_DISABLE_ATSPI);
670 if(envAtspiDisabled && std::atoi(envAtspiDisabled) != 0)
672 return Dali::Accessibility::DummyBridge::GetInstance();
675 return new BridgeImpl;
677 catch(const std::exception&)
679 DALI_LOG_ERROR("Failed to initialize AT-SPI bridge");
680 return Dali::Accessibility::DummyBridge::GetInstance();
684 } // unnamed namespace
686 // Dali::Accessibility::Bridge class implementation
688 Bridge* Bridge::GetCurrentBridge()
690 static Bridge* bridge;
696 else if(mAutoInitState == AutoInitState::ENABLED)
698 bridge = CreateBridge();
700 /* check environment variable for suppressing screen-reader */
701 const char* envSuppressScreenReader = Dali::EnvironmentVariable::GetEnvironmentVariable(DALI_ENV_SUPPRESS_SCREEN_READER);
702 if(envSuppressScreenReader && std::atoi(envSuppressScreenReader) != 0)
704 bridge->SuppressScreenReader(true);
710 return Dali::Accessibility::DummyBridge::GetInstance();
713 void Bridge::DisableAutoInit()
715 if(INITIALIZED_BRIDGE)
717 DALI_LOG_ERROR("Bridge::DisableAutoInit() called after bridge auto-initialization");
720 mAutoInitState = AutoInitState::DISABLED;
723 void Bridge::EnableAutoInit()
725 mAutoInitState = AutoInitState::ENABLED;
727 if(INITIALIZED_BRIDGE)
732 auto rootLayer = Dali::Stage::GetCurrent().GetRootLayer(); // A root layer of the default window.
733 auto window = Dali::DevelWindow::Get(rootLayer);
734 auto applicationName = Dali::Internal::Adaptor::Adaptor::GetApplicationPackageName();
736 auto accessible = Accessibility::Accessible::Get(rootLayer, true);
738 auto* bridge = Bridge::GetCurrentBridge();
739 bridge->AddTopLevelWindow(accessible);
740 bridge->SetApplicationName(applicationName);
741 bridge->Initialize();
743 if(window && window.IsVisible())
745 bridge->WindowShown(window);