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;
47 * @brief The BridgeImpl class is to implement some Bridge functions.
49 class BridgeImpl : public virtual BridgeBase,
50 public BridgeAccessible,
52 public BridgeComponent,
53 public BridgeCollection,
57 public BridgeEditableText,
58 public BridgeSelection,
59 public BridgeApplication
61 DBus::DBusClient mAccessibilityStatusClient;
62 DBus::DBusClient mRegistryClient;
63 DBus::DBusClient mDirectReadingClient;
64 bool mIsScreenReaderEnabled = false;
65 bool mIsEnabled = false;
66 bool mIsShown = false;
67 std::unordered_map<int32_t, std::function<void(std::string)>> mDirectReadingCallbacks;
68 Dali::Actor mHighlightedActor;
69 std::function<void(Dali::Actor)> mHighlightClearAction;
70 Dali::CallbackBase* mIdleCallback = NULL;
78 * @copydoc Dali::Accessibility::Bridge::Emit()
80 Consumed Emit(KeyEventType type, unsigned int keyCode, const std::string& keyName, unsigned int timeStamp, bool isText) override
87 unsigned int keyType = 0;
91 case KeyEventType::KEY_PRESSED:
96 case KeyEventType::KEY_RELEASED:
107 auto methodObject = mRegistryClient.method<bool(std::tuple<uint32_t, int32_t, int32_t, int32_t, int32_t, std::string, bool>)>("NotifyListenersSync");
108 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});
111 LOG() << result.getError().message;
114 return std::get<0>(result) ? Consumed::YES : Consumed::NO;
118 * @copydoc Dali::Accessibility::Bridge::Pause()
120 void Pause() override
127 mDirectReadingClient.method<DBus::ValueOrError<void>(bool)>("PauseResume").asyncCall([](DBus::ValueOrError<void> msg) {
130 LOG() << "Direct reading command failed (" << msg.getError().message << ")";
137 * @copydoc Dali::Accessibility::Bridge::Resume()
139 void Resume() override
146 mDirectReadingClient.method<DBus::ValueOrError<void>(bool)>("PauseResume").asyncCall([](DBus::ValueOrError<void> msg) {
149 LOG() << "Direct reading command failed (" << msg.getError().message << ")";
156 * @copydoc Dali::Accessibility::Bridge::StopReading()
158 void StopReading(bool alsoNonDiscardable) override
165 mDirectReadingClient.method<DBus::ValueOrError<void>(bool)>("StopReading").asyncCall([](DBus::ValueOrError<void> msg) {
168 LOG() << "Direct reading command failed (" << msg.getError().message << ")";
175 * @copydoc Dali::Accessibility::Bridge::Say()
177 void Say(const std::string& text, bool discardable, std::function<void(std::string)> callback) override
184 mDirectReadingClient.method<DBus::ValueOrError<std::string, bool, int32_t>(std::string, bool)>("ReadCommand").asyncCall([=](DBus::ValueOrError<std::string, bool, int32_t> msg) {
187 LOG() << "Direct reading command failed (" << msg.getError().message << ")";
191 mDirectReadingCallbacks.emplace(std::get<2>(msg), callback);
199 * @copydoc Dali::Accessibility::Bridge::ForceDown()
201 void ForceDown() override
205 if(mData->mCurrentlyHighlightedActor && mData->mHighlightActor)
207 mData->mCurrentlyHighlightedActor.Remove(mData->mHighlightActor);
209 mData->mCurrentlyHighlightedActor = {};
210 mData->mHighlightActor = {};
212 mDisabledSignal.Emit();
214 mHighlightedActor = {};
215 mHighlightClearAction = {};
216 BridgeAccessible::ForceDown();
217 mRegistryClient = {};
218 mDirectReadingClient = {};
219 mDirectReadingCallbacks.clear();
223 * @copydoc Dali::Accessibility::Bridge::Terminate()
225 void Terminate() override
229 mData->mCurrentlyHighlightedActor = {};
230 mData->mHighlightActor = {};
233 if((NULL != mIdleCallback) && Dali::Adaptor::IsAvailable())
235 Dali::Adaptor::Get().RemoveIdle(mIdleCallback);
237 mAccessibilityStatusClient = {};
243 * @copydoc Dali::Accessibility::Bridge::ForceUp()
245 ForceUpResult ForceUp() override
247 if(BridgeAccessible::ForceUp() == ForceUpResult::ALREADY_UP)
249 return ForceUpResult::ALREADY_UP;
252 BridgeObject::RegisterInterfaces();
253 BridgeAccessible::RegisterInterfaces();
254 BridgeComponent::RegisterInterfaces();
255 BridgeCollection::RegisterInterfaces();
256 BridgeAction::RegisterInterfaces();
257 BridgeValue::RegisterInterfaces();
258 BridgeText::RegisterInterfaces();
259 BridgeEditableText::RegisterInterfaces();
260 BridgeSelection::RegisterInterfaces();
261 BridgeApplication::RegisterInterfaces();
263 RegisterOnBridge(&mApplication);
265 mRegistryClient = {AtspiDbusNameRegistry, AtspiDbusPathDec, AtspiDbusInterfaceDec, mConnectionPtr};
266 mDirectReadingClient = DBus::DBusClient{DirectReadingDBusName, DirectReadingDBusPath, DirectReadingDBusInterface, mConnectionPtr};
268 mDirectReadingClient.addSignal<void(int32_t, std::string)>("ReadingStateChanged", [=](int32_t id, std::string readingState) {
269 auto it = mDirectReadingCallbacks.find(id);
270 if(it != mDirectReadingCallbacks.end())
272 it->second(readingState);
273 if(readingState != "ReadingPaused" && readingState != "ReadingResumed" && readingState != "ReadingStarted")
275 mDirectReadingCallbacks.erase(it);
280 auto proxy = DBus::DBusClient{AtspiDbusNameRegistry, AtspiDbusPathRoot, AtspiDbusInterfaceSocket, mConnectionPtr};
281 Address root{"", "root"};
282 auto res = proxy.method<Address(Address)>("Embed").call(root);
285 LOG() << "Call to Embed failed: " << res.getError().message;
289 mApplication.mParent.SetAddress(std::move(std::get<0>(res)));
295 mEnabledSignal.Emit();
297 return ForceUpResult::JUST_STARTED;
301 * @brief Sends a signal to dbus that the default window is activated.
303 * TODO : This is subject to change if/when we implement multi-window support.
304 * @see BridgeObject::Emit()
308 auto win = mApplication.GetActiveWindow();
311 win->Emit(WindowEvent::ACTIVATE, 0);
316 * @brief Sends a signal to dbus that the default window is deactivated.
318 * TODO : This is subject to change if/when we implement multi-window support.
319 * @see BridgeObject::Emit()
321 void EmitDeactivate()
323 auto win = mApplication.GetActiveWindow();
326 win->Emit(WindowEvent::DEACTIVATE, 0);
331 * @copydoc Dali::Accessibility::Bridge::WindowHidden()
333 void WindowHidden() override
335 if(mIsShown && IsUp())
343 * @copydoc Dali::Accessibility::Bridge::WindowShown()
345 void WindowShown() override
347 if(!mIsShown && IsUp())
354 void ReadAndListenProperty()
357 auto enabled = mAccessibilityStatusClient.property<bool>("ScreenReaderEnabled").get();
360 mIsScreenReaderEnabled = std::get<0>(enabled);
363 enabled = mAccessibilityStatusClient.property<bool>("IsEnabled").get();
366 mIsEnabled = std::get<0>(enabled);
369 if(mIsScreenReaderEnabled || mIsEnabled)
374 // listen property change
375 mAccessibilityStatusClient.addPropertyChangedEvent<bool>("ScreenReaderEnabled", [this](bool res) {
376 mIsScreenReaderEnabled = res;
377 if(mIsScreenReaderEnabled || mIsEnabled)
387 mAccessibilityStatusClient.addPropertyChangedEvent<bool>("IsEnabled", [this](bool res) {
389 if(mIsScreenReaderEnabled || mIsEnabled)
400 bool InitializeAccessibilityStatusClient()
402 mAccessibilityStatusClient = DBus::DBusClient{A11yDbusName, A11yDbusPath, A11yDbusStatusInterface, DBus::ConnectionType::SESSION};
404 if (!mAccessibilityStatusClient)
406 DALI_LOG_ERROR("Accessibility Status DbusClient is not ready\n");
415 if ( InitializeAccessibilityStatusClient() )
417 ReadAndListenProperty();
418 mIdleCallback = NULL;
426 * @copydoc Dali::Accessibility::Bridge::Initialize()
428 void Initialize() override
430 if ( InitializeAccessibilityStatusClient() )
432 ReadAndListenProperty();
436 // Initialize failed. Try it again on Idle
437 if( Dali::Adaptor::IsAvailable() )
439 Dali::Adaptor& adaptor = Dali::Adaptor::Get();
440 if( NULL == mIdleCallback )
442 mIdleCallback = MakeCallback( this, &BridgeImpl::OnIdleSignal );
443 adaptor.AddIdle( mIdleCallback, true );
449 * @copydoc Dali::Accessibility::Bridge::GetScreenReaderEnabled()
451 bool GetScreenReaderEnabled() override
453 return mIsScreenReaderEnabled;
457 * @copydoc Dali::Accessibility::Bridge::IsEnabled()
459 bool IsEnabled() override
465 namespace // unnamed namespace
468 bool INITIALIZED_BRIDGE = false;
471 * @brief Creates BridgeImpl instance.
473 * @return The BridgeImpl instance
474 * @note This method is to check environment variable first. If ATSPI is disable using env, it returns dummy bridge instance.
476 Bridge* CreateBridge()
478 INITIALIZED_BRIDGE = true;
482 /* check environment variable first */
483 const char* envAtspiDisabled = Dali::EnvironmentVariable::GetEnvironmentVariable(DALI_ENV_DISABLE_ATSPI);
484 if(envAtspiDisabled && std::atoi(envAtspiDisabled) != 0)
486 return Dali::Accessibility::DummyBridge::GetInstance();
489 return new BridgeImpl;
491 catch(const std::exception&)
493 DALI_LOG_ERROR("Failed to initialize AT-SPI bridge");
494 return Dali::Accessibility::DummyBridge::GetInstance();
498 } // unnamed namespace
500 // Dali::Accessibility::Bridge class implementation
502 Bridge* Bridge::GetCurrentBridge()
504 static Bridge* bridge;
510 else if(mAutoInitState == AutoInitState::ENABLED)
512 bridge = CreateBridge();
514 /* check environment variable for suppressing screen-reader */
515 const char* envSuppressScreenReader = Dali::EnvironmentVariable::GetEnvironmentVariable(DALI_ENV_SUPPRESS_SCREEN_READER);
516 if(envSuppressScreenReader && std::atoi(envSuppressScreenReader) != 0)
518 bridge->SuppressScreenReader(true);
524 return Dali::Accessibility::DummyBridge::GetInstance();
527 void Bridge::DisableAutoInit()
529 if(INITIALIZED_BRIDGE)
531 DALI_LOG_ERROR("Bridge::DisableAutoInit() called after bridge auto-initialization");
534 mAutoInitState = AutoInitState::DISABLED;
537 void Bridge::EnableAutoInit()
539 mAutoInitState = AutoInitState::ENABLED;
541 if(INITIALIZED_BRIDGE)
546 auto rootLayer = Dali::Stage::GetCurrent().GetRootLayer();
547 auto window = Dali::DevelWindow::Get(rootLayer);
548 auto applicationName = Dali::Internal::Adaptor::Adaptor::GetApplicationPackageName();
550 auto* bridge = Bridge::GetCurrentBridge();
551 bridge->AddTopLevelWindow(Dali::Accessibility::Accessible::Get(rootLayer, true));
552 bridge->SetApplicationName(applicationName);
553 bridge->Initialize();
555 if(window && window.IsVisible())
557 bridge->WindowShown();