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-text.h>
40 #include <dali/internal/accessibility/bridge/bridge-value.h>
41 #include <dali/internal/accessibility/bridge/bridge-application.h>
42 #include <dali/internal/accessibility/bridge/dummy-atspi.h>
43 #include <dali/internal/adaptor/common/adaptor-impl.h>
44 #include <dali/internal/system/common/environment-variables.h>
46 using namespace Dali::Accessibility;
49 * @brief The BridgeImpl class is to implement some Bridge functions.
51 class BridgeImpl : public virtual BridgeBase,
52 public BridgeAccessible,
54 public BridgeComponent,
55 public BridgeCollection,
59 public BridgeEditableText,
60 public BridgeSelection,
61 public BridgeApplication,
62 public BridgeHypertext,
63 public BridgeHyperlink
65 DBus::DBusClient mAccessibilityStatusClient;
66 DBus::DBusClient mRegistryClient;
67 DBus::DBusClient mDirectReadingClient;
68 bool mIsScreenReaderEnabled = false;
69 bool mIsEnabled = false;
70 bool mIsShown = false;
71 std::unordered_map<int32_t, std::function<void(std::string)>> mDirectReadingCallbacks;
72 Dali::Actor mHighlightedActor;
73 std::function<void(Dali::Actor)> mHighlightClearAction;
74 Dali::CallbackBase* mIdleCallback = NULL;
82 * @copydoc Dali::Accessibility::Bridge::Emit()
84 Consumed Emit(KeyEventType type, unsigned int keyCode, const std::string& keyName, unsigned int timeStamp, bool isText) override
91 unsigned int keyType = 0;
95 case KeyEventType::KEY_PRESSED:
100 case KeyEventType::KEY_RELEASED:
111 auto methodObject = mRegistryClient.method<bool(std::tuple<uint32_t, int32_t, int32_t, int32_t, int32_t, std::string, bool>)>("NotifyListenersSync");
112 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});
115 LOG() << result.getError().message;
118 return std::get<0>(result) ? Consumed::YES : Consumed::NO;
122 * @copydoc Dali::Accessibility::Bridge::Pause()
124 void Pause() override
131 mDirectReadingClient.method<DBus::ValueOrError<void>(bool)>("PauseResume").asyncCall([](DBus::ValueOrError<void> msg) {
134 LOG() << "Direct reading command failed (" << msg.getError().message << ")";
141 * @copydoc Dali::Accessibility::Bridge::Resume()
143 void Resume() override
150 mDirectReadingClient.method<DBus::ValueOrError<void>(bool)>("PauseResume").asyncCall([](DBus::ValueOrError<void> msg) {
153 LOG() << "Direct reading command failed (" << msg.getError().message << ")";
160 * @copydoc Dali::Accessibility::Bridge::StopReading()
162 void StopReading(bool alsoNonDiscardable) override
169 mDirectReadingClient.method<DBus::ValueOrError<void>(bool)>("StopReading").asyncCall([](DBus::ValueOrError<void> msg) {
172 LOG() << "Direct reading command failed (" << msg.getError().message << ")";
179 * @copydoc Dali::Accessibility::Bridge::Say()
181 void Say(const std::string& text, bool discardable, std::function<void(std::string)> callback) override
188 mDirectReadingClient.method<DBus::ValueOrError<std::string, bool, int32_t>(std::string, bool)>("ReadCommand").asyncCall([=](DBus::ValueOrError<std::string, bool, int32_t> msg) {
191 LOG() << "Direct reading command failed (" << msg.getError().message << ")";
195 mDirectReadingCallbacks.emplace(std::get<2>(msg), callback);
203 * @copydoc Dali::Accessibility::Bridge::ForceDown()
205 void ForceDown() override
209 if(mData->mCurrentlyHighlightedActor && mData->mHighlightActor)
211 mData->mCurrentlyHighlightedActor.Remove(mData->mHighlightActor);
213 mData->mCurrentlyHighlightedActor = {};
214 mData->mHighlightActor = {};
216 mDisabledSignal.Emit();
218 mHighlightedActor = {};
219 mHighlightClearAction = {};
220 BridgeAccessible::ForceDown();
221 mRegistryClient = {};
222 mDirectReadingClient = {};
223 mDirectReadingCallbacks.clear();
224 mApplication.mChildren.clear();
225 mApplication.mWindows.clear();
229 * @copydoc Dali::Accessibility::Bridge::Terminate()
231 void Terminate() override
235 mData->mCurrentlyHighlightedActor = {};
236 mData->mHighlightActor = {};
239 if((NULL != mIdleCallback) && Dali::Adaptor::IsAvailable())
241 Dali::Adaptor::Get().RemoveIdle(mIdleCallback);
243 mAccessibilityStatusClient = {};
249 * @copydoc Dali::Accessibility::Bridge::ForceUp()
251 ForceUpResult ForceUp() override
253 if(BridgeAccessible::ForceUp() == ForceUpResult::ALREADY_UP)
255 return ForceUpResult::ALREADY_UP;
258 BridgeObject::RegisterInterfaces();
259 BridgeAccessible::RegisterInterfaces();
260 BridgeComponent::RegisterInterfaces();
261 BridgeCollection::RegisterInterfaces();
262 BridgeAction::RegisterInterfaces();
263 BridgeValue::RegisterInterfaces();
264 BridgeText::RegisterInterfaces();
265 BridgeEditableText::RegisterInterfaces();
266 BridgeSelection::RegisterInterfaces();
267 BridgeApplication::RegisterInterfaces();
268 BridgeHypertext::RegisterInterfaces();
269 BridgeHyperlink::RegisterInterfaces();
271 RegisterOnBridge(&mApplication);
273 mRegistryClient = {AtspiDbusNameRegistry, AtspiDbusPathDec, AtspiDbusInterfaceDec, mConnectionPtr};
274 mDirectReadingClient = DBus::DBusClient{DirectReadingDBusName, DirectReadingDBusPath, DirectReadingDBusInterface, mConnectionPtr};
276 mDirectReadingClient.addSignal<void(int32_t, std::string)>("ReadingStateChanged", [=](int32_t id, std::string readingState) {
277 auto it = mDirectReadingCallbacks.find(id);
278 if(it != mDirectReadingCallbacks.end())
280 it->second(readingState);
281 if(readingState != "ReadingPaused" && readingState != "ReadingResumed" && readingState != "ReadingStarted")
283 mDirectReadingCallbacks.erase(it);
288 auto proxy = DBus::DBusClient{AtspiDbusNameRegistry, AtspiDbusPathRoot, AtspiDbusInterfaceSocket, mConnectionPtr};
289 Address root{"", "root"};
290 auto res = proxy.method<Address(Address)>("Embed").call(root);
293 LOG() << "Call to Embed failed: " << res.getError().message;
297 mApplication.mParent.SetAddress(std::move(std::get<0>(res)));
299 mEnabledSignal.Emit();
303 auto rootLayer = Dali::Stage::GetCurrent().GetRootLayer();
304 auto window = Dali::DevelWindow::Get(rootLayer);
305 EmitActivate(window); // Currently, sends a signal that the default window is activated here.
308 return ForceUpResult::JUST_STARTED;
312 * @brief Sends a signal to dbus that the window is shown.
314 * @param[in] window The window to be shown
315 * @see Accessible::EmitShowing() and BridgeObject::EmitStateChanged()
317 void EmitShown(Dali::Window window)
319 auto windowAccessible = mApplication.GetWindowAccessible(window);
322 windowAccessible->EmitShowing(true);
327 * @brief Sends a signal to dbus that the window is hidden.
329 * @param[in] window The window to be hidden
330 * @see Accessible::EmitShowing() and BridgeObject::EmitStateChanged()
332 void EmitHidden(Dali::Window window)
334 auto windowAccessible = mApplication.GetWindowAccessible(window);
337 windowAccessible->EmitShowing(false);
342 * @brief Sends a signal to dbus that the window is activated.
344 * @param[in] window The window to be activated
345 * @see BridgeObject::Emit()
347 void EmitActivate(Dali::Window window)
349 auto windowAccessible = mApplication.GetWindowAccessible(window);
352 windowAccessible->Emit(WindowEvent::ACTIVATE, 0);
357 * @brief Sends a signal to dbus that the window is deactivated.
359 * @param[in] window The window to be deactivated
360 * @see BridgeObject::Emit()
362 void EmitDeactivate(Dali::Window window)
364 auto windowAccessible = mApplication.GetWindowAccessible(window);
367 windowAccessible->Emit(WindowEvent::DEACTIVATE, 0);
372 * @copydoc Dali::Accessibility::Bridge::WindowShown()
374 void WindowShown(Dali::Window window) override
376 if(!mIsShown && IsUp())
384 * @copydoc Dali::Accessibility::Bridge::WindowHidden()
386 void WindowHidden(Dali::Window window) override
388 if(mIsShown && IsUp())
396 * @copydoc Dali::Accessibility::Bridge::WindowFocused()
398 void WindowFocused(Dali::Window window) override
400 if(mIsShown && IsUp())
402 EmitActivate(window);
407 * @copydoc Dali::Accessibility::Bridge::WindowUnfocused()
409 void WindowUnfocused(Dali::Window window) override
411 if(mIsShown && IsUp())
413 EmitDeactivate(window);
417 void ReadIsEnabledProperty()
419 mAccessibilityStatusClient.property<bool>("IsEnabled").asyncGet([this](DBus::ValueOrError<bool> msg) {
422 DALI_LOG_ERROR("Get IsEnabled property error: %s\n", msg.getError().message.c_str());
423 if(msg.getError().errorType == DBus::ErrorType::INVALID_REPLY)
425 ReadIsEnabledProperty();
429 mIsEnabled = std::get<0>(msg);
437 void ListenIsEnabledProperty()
439 mAccessibilityStatusClient.addPropertyChangedEvent<bool>("IsEnabled", [this](bool res) {
441 if(mIsScreenReaderEnabled || mIsEnabled)
452 void ReadScreenReaderEnabledProperty()
454 mAccessibilityStatusClient.property<bool>("ScreenReaderEnabled").asyncGet([this](DBus::ValueOrError<bool> msg) {
457 DALI_LOG_ERROR("Get ScreenReaderEnabled property error: %s\n", msg.getError().message.c_str());
458 if(msg.getError().errorType == DBus::ErrorType::INVALID_REPLY)
460 ReadScreenReaderEnabledProperty();
464 mIsScreenReaderEnabled = std::get<0>(msg);
465 if(mIsScreenReaderEnabled)
472 void ListenScreenReaderEnabledProperty()
474 mAccessibilityStatusClient.addPropertyChangedEvent<bool>("ScreenReaderEnabled", [this](bool res) {
475 mIsScreenReaderEnabled = res;
476 if(mIsScreenReaderEnabled || mIsEnabled)
487 void ReadAndListenProperties()
489 ReadIsEnabledProperty();
490 ListenIsEnabledProperty();
492 ReadScreenReaderEnabledProperty();
493 ListenScreenReaderEnabledProperty();
496 bool InitializeAccessibilityStatusClient()
498 mAccessibilityStatusClient = DBus::DBusClient{A11yDbusName, A11yDbusPath, A11yDbusStatusInterface, DBus::ConnectionType::SESSION};
500 if (!mAccessibilityStatusClient)
502 DALI_LOG_ERROR("Accessibility Status DbusClient is not ready\n");
511 if ( InitializeAccessibilityStatusClient() )
513 ReadAndListenProperties();
514 mIdleCallback = NULL;
522 * @copydoc Dali::Accessibility::Bridge::Initialize()
524 void Initialize() override
526 if ( InitializeAccessibilityStatusClient() )
528 ReadAndListenProperties();
532 // Initialize failed. Try it again on Idle
533 if( Dali::Adaptor::IsAvailable() )
535 Dali::Adaptor& adaptor = Dali::Adaptor::Get();
536 if( NULL == mIdleCallback )
538 mIdleCallback = MakeCallback( this, &BridgeImpl::OnIdleSignal );
539 adaptor.AddIdle( mIdleCallback, true );
545 * @copydoc Dali::Accessibility::Bridge::GetScreenReaderEnabled()
547 bool GetScreenReaderEnabled() override
549 return mIsScreenReaderEnabled;
553 * @copydoc Dali::Accessibility::Bridge::IsEnabled()
555 bool IsEnabled() override
561 namespace // unnamed namespace
564 bool INITIALIZED_BRIDGE = false;
567 * @brief Creates BridgeImpl instance.
569 * @return The BridgeImpl instance
570 * @note This method is to check environment variable first. If ATSPI is disable using env, it returns dummy bridge instance.
572 Bridge* CreateBridge()
574 INITIALIZED_BRIDGE = true;
578 /* check environment variable first */
579 const char* envAtspiDisabled = Dali::EnvironmentVariable::GetEnvironmentVariable(DALI_ENV_DISABLE_ATSPI);
580 if(envAtspiDisabled && std::atoi(envAtspiDisabled) != 0)
582 return Dali::Accessibility::DummyBridge::GetInstance();
585 return new BridgeImpl;
587 catch(const std::exception&)
589 DALI_LOG_ERROR("Failed to initialize AT-SPI bridge");
590 return Dali::Accessibility::DummyBridge::GetInstance();
594 } // unnamed namespace
596 // Dali::Accessibility::Bridge class implementation
598 Bridge* Bridge::GetCurrentBridge()
600 static Bridge* bridge;
606 else if(mAutoInitState == AutoInitState::ENABLED)
608 bridge = CreateBridge();
610 /* check environment variable for suppressing screen-reader */
611 const char* envSuppressScreenReader = Dali::EnvironmentVariable::GetEnvironmentVariable(DALI_ENV_SUPPRESS_SCREEN_READER);
612 if(envSuppressScreenReader && std::atoi(envSuppressScreenReader) != 0)
614 bridge->SuppressScreenReader(true);
620 return Dali::Accessibility::DummyBridge::GetInstance();
623 void Bridge::DisableAutoInit()
625 if(INITIALIZED_BRIDGE)
627 DALI_LOG_ERROR("Bridge::DisableAutoInit() called after bridge auto-initialization");
630 mAutoInitState = AutoInitState::DISABLED;
633 void Bridge::EnableAutoInit()
635 mAutoInitState = AutoInitState::ENABLED;
637 if(INITIALIZED_BRIDGE)
642 auto rootLayer = Dali::Stage::GetCurrent().GetRootLayer(); // A root layer of the default window.
643 auto window = Dali::DevelWindow::Get(rootLayer);
644 auto applicationName = Dali::Internal::Adaptor::Adaptor::GetApplicationPackageName();
646 auto accessible = Accessibility::Accessible::Get(rootLayer, true);
648 auto* bridge = Bridge::GetCurrentBridge();
649 bridge->AddTopLevelWindow(accessible);
650 bridge->SetApplicationName(applicationName);
651 bridge->Initialize();
653 if(window && window.IsVisible())
655 bridge->WindowShown(window);