Merge "[ATSPI] Add ScrollStarted/ScrollFinished interface" into devel/master
[platform/core/uifw/dali-adaptor.git] / dali / internal / accessibility / bridge / bridge-impl.cpp
1 /*
2  * Copyright (c) 2022 Samsung Electronics Co., Ltd.
3  *
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
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
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.
15  *
16  */
17
18 // CLASS HEADER
19
20 // EXTERNAL INCLUDES
21 #include <dali/devel-api/common/stage.h>
22 #include <dali/integration-api/debug.h>
23 #include <dali/public-api/actors/layer.h>
24 #include <iostream>
25 #include <unordered_map>
26
27 // INTERNAL INCLUDES
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/accessibility-common.h>
31 #include <dali/internal/accessibility/bridge/bridge-accessible.h>
32 #include <dali/internal/accessibility/bridge/bridge-action.h>
33 #include <dali/internal/accessibility/bridge/bridge-collection.h>
34 #include <dali/internal/accessibility/bridge/bridge-component.h>
35 #include <dali/internal/accessibility/bridge/bridge-editable-text.h>
36 #include <dali/internal/accessibility/bridge/bridge-hypertext.h>
37 #include <dali/internal/accessibility/bridge/bridge-hyperlink.h>
38 #include <dali/internal/accessibility/bridge/bridge-object.h>
39 #include <dali/internal/accessibility/bridge/bridge-selection.h>
40 #include <dali/internal/accessibility/bridge/bridge-socket.h>
41 #include <dali/internal/accessibility/bridge/bridge-text.h>
42 #include <dali/internal/accessibility/bridge/bridge-value.h>
43 #include <dali/internal/accessibility/bridge/bridge-application.h>
44 #include <dali/internal/accessibility/bridge/dummy/dummy-atspi.h>
45 #include <dali/internal/adaptor/common/adaptor-impl.h>
46 #include <dali/internal/system/common/environment-variables.h>
47
48 using namespace Dali::Accessibility;
49
50 namespace // unnamed namespace
51 {
52
53 const int RETRY_INTERVAL = 1000;
54
55 } // unnamed namespace
56
57 /**
58  * @brief The BridgeImpl class is to implement some Bridge functions.
59  */
60 class BridgeImpl : public virtual BridgeBase,
61                    public BridgeAccessible,
62                    public BridgeObject,
63                    public BridgeComponent,
64                    public BridgeCollection,
65                    public BridgeAction,
66                    public BridgeValue,
67                    public BridgeText,
68                    public BridgeEditableText,
69                    public BridgeSelection,
70                    public BridgeApplication,
71                    public BridgeHypertext,
72                    public BridgeHyperlink,
73                    public BridgeSocket
74 {
75   DBus::DBusClient                                              mAccessibilityStatusClient;
76   DBus::DBusClient                                              mRegistryClient;
77   DBus::DBusClient                                              mDirectReadingClient;
78   bool                                                          mIsScreenReaderEnabled = false;
79   bool                                                          mIsEnabled             = 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;
88   std::string                                                   mPreferredBusName;
89
90 public:
91   BridgeImpl()
92   {
93   }
94
95   /**
96    * @copydoc Dali::Accessibility::Bridge::Emit()
97    */
98   Consumed Emit(KeyEventType type, unsigned int keyCode, const std::string& keyName, unsigned int timeStamp, bool isText) override
99   {
100     if(!IsUp())
101     {
102       return Consumed::NO;
103     }
104
105     unsigned int keyType = 0;
106
107     switch(type)
108     {
109       case KeyEventType::KEY_PRESSED:
110       {
111         keyType = 0;
112         break;
113       }
114       case KeyEventType::KEY_RELEASED:
115       {
116         keyType = 1;
117         break;
118       }
119       default:
120       {
121         return Consumed::NO;
122       }
123     }
124
125     auto methodObject = mRegistryClient.method<bool(std::tuple<uint32_t, int32_t, int32_t, int32_t, int32_t, std::string, bool>)>("NotifyListenersSync");
126     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     if(!result)
128     {
129       LOG() << result.getError().message;
130       return Consumed::NO;
131     }
132     return std::get<0>(result) ? Consumed::YES : Consumed::NO;
133   }
134
135   /**
136    * @copydoc Dali::Accessibility::Bridge::Pause()
137    */
138   void Pause() override
139   {
140     if(!IsUp())
141     {
142       return;
143     }
144
145     mDirectReadingClient.method<DBus::ValueOrError<void>(bool)>("PauseResume").asyncCall([](DBus::ValueOrError<void> msg) {
146       if(!msg)
147       {
148         LOG() << "Direct reading command failed (" << msg.getError().message << ")\n";
149       }
150     },
151                                                                                         true);
152   }
153
154   /**
155    * @copydoc Dali::Accessibility::Bridge::Resume()
156    */
157   void Resume() override
158   {
159     if(!IsUp())
160     {
161       return;
162     }
163
164     mDirectReadingClient.method<DBus::ValueOrError<void>(bool)>("PauseResume").asyncCall([](DBus::ValueOrError<void> msg) {
165       if(!msg)
166       {
167         LOG() << "Direct reading command failed (" << msg.getError().message << ")\n";
168       }
169     },
170                                                                                         false);
171   }
172
173   /**
174    * @copydoc Dali::Accessibility::Bridge::StopReading()
175    */
176   void StopReading(bool alsoNonDiscardable) override
177   {
178     if(!IsUp())
179     {
180       return;
181     }
182
183     mDirectReadingClient.method<DBus::ValueOrError<void>(bool)>("StopReading").asyncCall([](DBus::ValueOrError<void> msg) {
184       if(!msg)
185       {
186         LOG() << "Direct reading command failed (" << msg.getError().message << ")\n";
187       }
188     },
189                                                                                         alsoNonDiscardable);
190   }
191
192   /**
193    * @copydoc Dali::Accessibility::Bridge::Say()
194    */
195   void Say(const std::string& text, bool discardable, std::function<void(std::string)> callback) override
196   {
197     if(!IsUp())
198     {
199       return;
200     }
201
202     mDirectReadingClient.method<DBus::ValueOrError<std::string, bool, int32_t>(std::string, bool)>("ReadCommand").asyncCall([=](DBus::ValueOrError<std::string, bool, int32_t> msg) {
203       if(!msg)
204       {
205         LOG() << "Direct reading command failed (" << msg.getError().message << ")\n";
206       }
207       else if(callback)
208       {
209         mDirectReadingCallbacks.emplace(std::get<2>(msg), callback);
210       }
211     },
212                                                                                                                            text,
213                                                                                                                            discardable);
214   }
215
216   /**
217    * @copydoc Dali::Accessibility::Bridge::ForceDown()
218    */
219   void ForceDown() override
220   {
221     if(mData)
222     {
223       if(mData->mCurrentlyHighlightedActor && mData->mHighlightActor)
224       {
225         mData->mCurrentlyHighlightedActor.Remove(mData->mHighlightActor);
226       }
227       mData->mCurrentlyHighlightedActor = {};
228       mData->mHighlightActor            = {};
229
230       mDisabledSignal.Emit();
231       UnembedSocket(mApplication.GetAddress(), {AtspiDbusNameRegistry, "root"});
232       ReleaseBusName(mPreferredBusName);
233     }
234
235     mHighlightedActor     = {};
236     mHighlightClearAction = {};
237     BridgeAccessible::ForceDown();
238     mRegistryClient       = {};
239     mDirectReadingClient  = {};
240     mDirectReadingCallbacks.clear();
241     mApplication.mChildren.clear();
242     ClearTimer();
243   }
244
245   void ClearTimer()
246   {
247     if(mInitializeTimer)
248     {
249       mInitializeTimer.Stop();
250       mInitializeTimer.Reset();
251     }
252
253     if(mReadIsEnabledTimer)
254     {
255       mReadIsEnabledTimer.Stop();
256       mReadIsEnabledTimer.Reset();
257     }
258
259     if(mReadScreenReaderEnabledTimer)
260     {
261       mReadScreenReaderEnabledTimer.Stop();
262       mReadScreenReaderEnabledTimer.Reset();
263     }
264
265     if(mForceUpTimer)
266     {
267       mForceUpTimer.Stop();
268       mForceUpTimer.Reset();
269     }
270   }
271
272   /**
273    * @copydoc Dali::Accessibility::Bridge::Terminate()
274    */
275   void Terminate() override
276   {
277     if(mData)
278     {
279       // The ~Window() after this point cannot emit DESTROY, because Bridge is not available. So emit DESTROY here.
280       for(auto windowAccessible : mApplication.mChildren)
281       {
282         BridgeObject::Emit(windowAccessible, WindowEvent::DESTROY);
283       }
284       mData->mCurrentlyHighlightedActor = {};
285       mData->mHighlightActor            = {};
286     }
287     ForceDown();
288     if((NULL != mIdleCallback) && Dali::Adaptor::IsAvailable())
289     {
290       Dali::Adaptor::Get().RemoveIdle(mIdleCallback);
291     }
292     mAccessibilityStatusClient        = {};
293     mDbusServer                       = {};
294     mConnectionPtr                    = {};
295   }
296
297   bool ForceUpTimerCallback()
298   {
299     if(ForceUp() != ForceUpResult::FAILED)
300     {
301       return false;
302     }
303     return true;
304   }
305
306   /**
307    * @copydoc Dali::Accessibility::Bridge::ForceUp()
308    */
309   ForceUpResult ForceUp() override
310   {
311     auto forceUpResult = BridgeAccessible::ForceUp();
312     if(forceUpResult == ForceUpResult::ALREADY_UP)
313     {
314       return forceUpResult;
315     }
316     else if(forceUpResult == ForceUpResult::FAILED)
317     {
318       if(!mForceUpTimer)
319       {
320         mForceUpTimer = Dali::Timer::New(RETRY_INTERVAL);
321         mForceUpTimer.TickSignal().Connect(this, &BridgeImpl::ForceUpTimerCallback);
322         mForceUpTimer.Start();
323       }
324       return forceUpResult;
325     }
326
327     BridgeObject::RegisterInterfaces();
328     BridgeAccessible::RegisterInterfaces();
329     BridgeComponent::RegisterInterfaces();
330     BridgeCollection::RegisterInterfaces();
331     BridgeAction::RegisterInterfaces();
332     BridgeValue::RegisterInterfaces();
333     BridgeText::RegisterInterfaces();
334     BridgeEditableText::RegisterInterfaces();
335     BridgeSelection::RegisterInterfaces();
336     BridgeApplication::RegisterInterfaces();
337     BridgeHypertext::RegisterInterfaces();
338     BridgeHyperlink::RegisterInterfaces();
339     BridgeSocket::RegisterInterfaces();
340
341     RegisterOnBridge(&mApplication);
342
343     mRegistryClient      = {AtspiDbusNameRegistry, AtspiDbusPathDec, Accessible::GetInterfaceName(AtspiInterface::DEVICE_EVENT_CONTROLLER), mConnectionPtr};
344     mDirectReadingClient = DBus::DBusClient{DirectReadingDBusName, DirectReadingDBusPath, DirectReadingDBusInterface, mConnectionPtr};
345
346     mDirectReadingClient.addSignal<void(int32_t, std::string)>("ReadingStateChanged", [=](int32_t id, std::string readingState) {
347       auto it = mDirectReadingCallbacks.find(id);
348       if(it != mDirectReadingCallbacks.end())
349       {
350         it->second(readingState);
351         if(readingState != "ReadingPaused" && readingState != "ReadingResumed" && readingState != "ReadingStarted")
352         {
353           mDirectReadingCallbacks.erase(it);
354         }
355       }
356     });
357
358     RequestBusName(mPreferredBusName);
359
360     auto parentAddress = EmbedSocket(mApplication.GetAddress(), {AtspiDbusNameRegistry, "root"});
361     mApplication.mParent.SetAddress(std::move(parentAddress));
362     mEnabledSignal.Emit();
363
364     return ForceUpResult::JUST_STARTED;
365   }
366
367   /**
368    * @brief Sends a signal to dbus that the window is shown.
369    *
370    * @param[in] window The window to be shown
371    * @see Accessible::EmitShowing() and BridgeObject::EmitStateChanged()
372    */
373   void EmitShown(Dali::Window window)
374   {
375     auto windowAccessible = mApplication.GetWindowAccessible(window);
376     if(windowAccessible)
377     {
378       windowAccessible->EmitShowing(true);
379     }
380   }
381
382   /**
383    * @brief Sends a signal to dbus that the window is hidden.
384    *
385    * @param[in] window The window to be hidden
386    * @see Accessible::EmitShowing() and BridgeObject::EmitStateChanged()
387    */
388   void EmitHidden(Dali::Window window)
389   {
390     auto windowAccessible = mApplication.GetWindowAccessible(window);
391     if(windowAccessible)
392     {
393       windowAccessible->EmitShowing(false);
394     }
395   }
396
397   /**
398    * @brief Sends a signal to dbus that the window is activated.
399    *
400    * @param[in] window The window to be activated
401    * @see BridgeObject::Emit()
402    */
403   void EmitActivate(Dali::Window window)
404   {
405     auto windowAccessible = mApplication.GetWindowAccessible(window);
406     if(windowAccessible)
407     {
408       windowAccessible->Emit(WindowEvent::ACTIVATE, 0);
409     }
410   }
411
412   /**
413    * @brief Sends a signal to dbus that the window is deactivated.
414    *
415    * @param[in] window The window to be deactivated
416    * @see BridgeObject::Emit()
417    */
418   void EmitDeactivate(Dali::Window window)
419   {
420     auto windowAccessible = mApplication.GetWindowAccessible(window);
421     if(windowAccessible)
422     {
423       windowAccessible->Emit(WindowEvent::DEACTIVATE, 0);
424     }
425   }
426
427   /**
428    * @brief Sends a signal to dbus that the window is minimized.
429    *
430    * @param[in] window The window to be minimized
431    * @see BridgeObject::Emit()
432    */
433   void EmitMinimize(Dali::Window window)
434   {
435     auto windowAccessible = mApplication.GetWindowAccessible(window);
436     if(windowAccessible)
437     {
438       windowAccessible->Emit(WindowEvent::MINIMIZE, 0);
439     }
440   }
441
442   /**
443    * @brief Sends a signal to dbus that the window is restored.
444    *
445    * @param[in] window The window to be restored
446    * @param[in] detail Restored window state
447    * @see BridgeObject::Emit()
448    */
449   void EmitRestore(Dali::Window window, Dali::Accessibility::WindowRestoreType detail)
450   {
451     auto windowAccessible = mApplication.GetWindowAccessible(window);
452     if(windowAccessible)
453     {
454       windowAccessible->Emit(WindowEvent::RESTORE, static_cast<unsigned int>(detail));
455     }
456   }
457
458   /**
459    * @brief Sends a signal to dbus that the window is maximized.
460    *
461    * @param[in] window The window to be maximized
462    * @see BridgeObject::Emit()
463    */
464   void EmitMaximize(Dali::Window window)
465   {
466     auto windowAccessible = mApplication.GetWindowAccessible(window);
467     if(windowAccessible)
468     {
469       windowAccessible->Emit(WindowEvent::MAXIMIZE, 0);
470     }
471   }
472
473   /**
474    * @copydoc Dali::Accessibility::Bridge::WindowShown()
475    */
476   void WindowShown(Dali::Window window) override
477   {
478     if(IsUp())
479     {
480       EmitShown(window);
481     }
482   }
483
484   /**
485    * @copydoc Dali::Accessibility::Bridge::WindowHidden()
486    */
487   void WindowHidden(Dali::Window window) override
488   {
489     if(IsUp())
490     {
491       EmitHidden(window);
492     }
493   }
494
495   /**
496    * @copydoc Dali::Accessibility::Bridge::WindowFocused()
497    */
498   void WindowFocused(Dali::Window window) override
499   {
500     if(IsUp())
501     {
502       EmitActivate(window);
503     }
504   }
505
506   /**
507    * @copydoc Dali::Accessibility::Bridge::WindowUnfocused()
508    */
509   void WindowUnfocused(Dali::Window window) override
510   {
511     if(IsUp())
512     {
513       EmitDeactivate(window);
514     }
515   }
516
517   /**
518    * @copydoc Dali::Accessibility::Bridge::WindowMinimized()
519    */
520   void WindowMinimized(Dali::Window window) override
521   {
522     if(IsUp())
523     {
524       EmitMinimize(window);
525     }
526   }
527
528   /**
529    * @copydoc Dali::Accessibility::Bridge::WindowRestored()
530    */
531   void WindowRestored(Dali::Window window, WindowRestoreType detail) override
532   {
533     if(IsUp())
534     {
535       EmitRestore(window, detail);
536     }
537   }
538
539   /**
540    * @copydoc Dali::Accessibility::Bridge::WindowMaximized()
541    */
542   void WindowMaximized(Dali::Window window) override
543   {
544     if(IsUp())
545     {
546       EmitMaximize(window);
547     }
548   }
549
550   /**
551    * @copydoc Dali::Accessibility::Bridge::SuppressScreenReader()
552    */
553   void SuppressScreenReader(bool suppress) override
554   {
555     if(mIsScreenReaderSuppressed == suppress)
556     {
557       return;
558     }
559     mIsScreenReaderSuppressed = suppress;
560     ReadScreenReaderEnabledProperty();
561   }
562
563   void SwitchBridge()
564   {
565     if((!mIsScreenReaderSuppressed && mIsScreenReaderEnabled) || mIsEnabled)
566     {
567       ForceUp();
568     }
569     else
570     {
571       ForceDown();
572     }
573   }
574
575   bool ReadIsEnabledTimerCallback()
576   {
577     ReadIsEnabledProperty();
578     return false;
579   }
580
581   void ReadIsEnabledProperty()
582   {
583     mAccessibilityStatusClient.property<bool>("IsEnabled").asyncGet([this](DBus::ValueOrError<bool> msg) {
584       if(!msg)
585       {
586         DALI_LOG_ERROR("Get IsEnabled property error: %s\n", msg.getError().message.c_str());
587         if(msg.getError().errorType == DBus::ErrorType::INVALID_REPLY)
588         {
589           if(!mReadIsEnabledTimer)
590           {
591             mReadIsEnabledTimer = Dali::Timer::New(RETRY_INTERVAL);
592             mReadIsEnabledTimer.TickSignal().Connect(this, &BridgeImpl::ReadIsEnabledTimerCallback);
593           }
594           mReadIsEnabledTimer.Start();
595         }
596         return;
597       }
598
599       if(mReadIsEnabledTimer)
600       {
601         mReadIsEnabledTimer.Stop();
602         mReadIsEnabledTimer.Reset();
603       }
604
605       mIsEnabled = std::get<0>(msg);
606       SwitchBridge();
607     });
608   }
609
610   void ListenIsEnabledProperty()
611   {
612     mAccessibilityStatusClient.addPropertyChangedEvent<bool>("IsEnabled", [this](bool res) {
613       mIsEnabled = res;
614       SwitchBridge();
615     });
616   }
617
618   bool ReadScreenReaderEnabledTimerCallback()
619   {
620     ReadScreenReaderEnabledProperty();
621     return false;
622   }
623
624   void ReadScreenReaderEnabledProperty()
625   {
626     // can be true because of SuppressScreenReader before init
627     if (!mAccessibilityStatusClient)
628     {
629       return;
630     }
631
632     mAccessibilityStatusClient.property<bool>("ScreenReaderEnabled").asyncGet([this](DBus::ValueOrError<bool> msg) {
633       if(!msg)
634       {
635         DALI_LOG_ERROR("Get ScreenReaderEnabled property error: %s\n", msg.getError().message.c_str());
636         if(msg.getError().errorType == DBus::ErrorType::INVALID_REPLY)
637         {
638           if(!mReadScreenReaderEnabledTimer)
639           {
640             mReadScreenReaderEnabledTimer = Dali::Timer::New(RETRY_INTERVAL);
641             mReadScreenReaderEnabledTimer.TickSignal().Connect(this, &BridgeImpl::ReadScreenReaderEnabledTimerCallback);
642           }
643           mReadScreenReaderEnabledTimer.Start();
644         }
645         return;
646       }
647
648       if(mReadScreenReaderEnabledTimer)
649       {
650         mReadScreenReaderEnabledTimer.Stop();
651         mReadScreenReaderEnabledTimer.Reset();
652       }
653
654       mIsScreenReaderEnabled = std::get<0>(msg);
655       SwitchBridge();
656     });
657   }
658
659   void EmitScreenReaderEnabledSignal()
660   {
661     if (mIsScreenReaderEnabled)
662     {
663       mScreenReaderEnabledSignal.Emit();
664     }
665     else
666     {
667       mScreenReaderDisabledSignal.Emit();
668     }
669   }
670
671   void ListenScreenReaderEnabledProperty()
672   {
673     mAccessibilityStatusClient.addPropertyChangedEvent<bool>("ScreenReaderEnabled", [this](bool res) {
674       mIsScreenReaderEnabled = res;
675       EmitScreenReaderEnabledSignal();
676       SwitchBridge();
677     });
678   }
679
680   void ReadAndListenProperties()
681   {
682     ReadIsEnabledProperty();
683     ListenIsEnabledProperty();
684
685     ReadScreenReaderEnabledProperty();
686     ListenScreenReaderEnabledProperty();
687   }
688
689   bool InitializeAccessibilityStatusClient()
690   {
691     mAccessibilityStatusClient = DBus::DBusClient{A11yDbusName, A11yDbusPath, A11yDbusStatusInterface, DBus::ConnectionType::SESSION};
692
693     if (!mAccessibilityStatusClient)
694     {
695       DALI_LOG_ERROR("Accessibility Status DbusClient is not ready\n");
696       return false;
697     }
698
699     return true;
700   }
701
702   bool InitializeTimerCallback()
703   {
704     if ( InitializeAccessibilityStatusClient() )
705     {
706       ReadAndListenProperties();
707       return false;
708     }
709     return true;
710   }
711
712   bool OnIdleSignal()
713   {
714     if ( InitializeAccessibilityStatusClient() )
715     {
716       ReadAndListenProperties();
717       mIdleCallback = NULL;
718       return false;
719     }
720
721     if(!mInitializeTimer)
722     {
723       mInitializeTimer = Dali::Timer::New(RETRY_INTERVAL);
724       mInitializeTimer.TickSignal().Connect(this, &BridgeImpl::InitializeTimerCallback);
725     }
726     mInitializeTimer.Start();
727
728     mIdleCallback = NULL;
729     return false;
730   }
731
732   /**
733    * @copydoc Dali::Accessibility::Bridge::Initialize()
734    */
735   void Initialize() override
736   {
737     if ( InitializeAccessibilityStatusClient() )
738     {
739       ReadAndListenProperties();
740       return;
741     }
742
743     // Initialize failed. Try it again on Idle
744     if( Dali::Adaptor::IsAvailable() )
745     {
746       Dali::Adaptor& adaptor = Dali::Adaptor::Get();
747       if( NULL == mIdleCallback )
748       {
749         mIdleCallback = MakeCallback( this, &BridgeImpl::OnIdleSignal );
750         adaptor.AddIdle( mIdleCallback, true );
751       }
752     }
753   }
754
755   /**
756    * @copydoc Dali::Accessibility::Bridge::GetScreenReaderEnabled()
757    */
758   bool GetScreenReaderEnabled() override
759   {
760     return mIsScreenReaderEnabled;
761   }
762
763   /**
764    * @copydoc Dali::Accessibility::Bridge::IsEnabled()
765    */
766   bool IsEnabled() override
767   {
768     return mIsEnabled;
769   }
770
771   Address EmbedSocket(const Address& plug, const Address& socket) override
772   {
773     auto client = CreateSocketClient(socket);
774     auto reply  = client.method<Address(Address)>("Embed").call(plug);
775
776     if(!reply)
777     {
778       DALI_LOG_ERROR("Failed to embed socket %s: %s", socket.ToString().c_str(), reply.getError().message.c_str());
779       return {};
780     }
781
782     return std::get<0>(reply.getValues());
783   }
784
785   void EmbedAtkSocket(const Address& plug, const Address& socket) override
786   {
787     auto client = CreateSocketClient(socket);
788
789     client.method<void(std::string)>("Embedded").call(ATSPI_PREFIX_PATH + plug.GetPath());
790   }
791
792   void UnembedSocket(const Address& plug, const Address& socket) override
793   {
794     auto client = CreateSocketClient(socket);
795
796     client.method<void(Address)>("Unembed").call(plug);
797   }
798
799   void SetSocketOffset(ProxyAccessible* socket, std::int32_t x, std::int32_t y) override
800   {
801     AddCoalescableMessage(CoalescableMessages::SET_OFFSET, socket, 1.0f, [=]() {
802       auto client = CreateSocketClient(socket->GetAddress());
803
804       client.method<void(std::int32_t, std::int32_t)>("SetOffset").asyncCall([](DBus::ValueOrError<void>) {}, x, y);
805     });
806   }
807
808   void SetExtentsOffset(std::int32_t x, std::int32_t y) override
809   {
810     if(mData)
811     {
812       mData->mExtentsOffset = {x, y};
813     }
814   }
815
816   void SetPreferredBusName(std::string_view preferredBusName) override
817   {
818     if(preferredBusName == mPreferredBusName)
819     {
820       return;
821     }
822
823     std::string oldPreferredBusName = std::move(mPreferredBusName);
824     mPreferredBusName               = std::string{preferredBusName};
825
826     if(IsUp())
827     {
828       ReleaseBusName(oldPreferredBusName);
829       RequestBusName(mPreferredBusName);
830     }
831     // else: request/release will be handled by ForceUp/ForceDown, respectively
832   }
833
834 private:
835   DBus::DBusClient CreateSocketClient(const Address& socket)
836   {
837     return {socket.GetBus(), ATSPI_PREFIX_PATH + socket.GetPath(), Accessible::GetInterfaceName(AtspiInterface::SOCKET), mConnectionPtr};
838   }
839
840   void RequestBusName(const std::string& busName)
841   {
842     if(busName.empty())
843     {
844       return;
845     }
846
847     DBus::requestBusName(mConnectionPtr, busName);
848   }
849
850   void ReleaseBusName(const std::string& busName)
851   {
852     if(busName.empty())
853     {
854       return;
855     }
856
857     DBus::releaseBusName(mConnectionPtr, busName);
858   }
859 }; // BridgeImpl
860
861 namespace // unnamed namespace
862 {
863
864 bool INITIALIZED_BRIDGE = false;
865
866 /**
867  * @brief Creates BridgeImpl instance.
868  *
869  * @return The BridgeImpl instance
870  * @note This method is to check environment variable first. If ATSPI is disable using env, it returns dummy bridge instance.
871  */
872 std::shared_ptr<Bridge> CreateBridge()
873 {
874   INITIALIZED_BRIDGE = true;
875
876   try
877   {
878     /* check environment variable first */
879     const char* envAtspiDisabled = Dali::EnvironmentVariable::GetEnvironmentVariable(DALI_ENV_DISABLE_ATSPI);
880     if(envAtspiDisabled && std::atoi(envAtspiDisabled) != 0)
881     {
882       return Dali::Accessibility::DummyBridge::GetInstance();
883     }
884
885     return std::make_shared<BridgeImpl>();
886   }
887   catch(const std::exception&)
888   {
889     DALI_LOG_ERROR("Failed to initialize AT-SPI bridge");
890     return Dali::Accessibility::DummyBridge::GetInstance();
891   }
892 }
893
894 } // unnamed namespace
895
896 // Dali::Accessibility::Bridge class implementation
897
898 std::shared_ptr<Bridge> Bridge::GetCurrentBridge()
899 {
900   static std::shared_ptr<Bridge> bridge;
901
902   if(bridge)
903   {
904     return bridge;
905   }
906   else if(mAutoInitState == AutoInitState::ENABLED)
907   {
908     bridge = CreateBridge();
909
910     /* check environment variable for suppressing screen-reader */
911     const char* envSuppressScreenReader = Dali::EnvironmentVariable::GetEnvironmentVariable(DALI_ENV_SUPPRESS_SCREEN_READER);
912     if(envSuppressScreenReader && std::atoi(envSuppressScreenReader) != 0)
913     {
914       bridge->SuppressScreenReader(true);
915     }
916
917     return bridge;
918   }
919
920   return Dali::Accessibility::DummyBridge::GetInstance();
921 }
922
923 void Bridge::DisableAutoInit()
924 {
925   if(INITIALIZED_BRIDGE)
926   {
927     DALI_LOG_ERROR("Bridge::DisableAutoInit() called after bridge auto-initialization");
928   }
929
930   mAutoInitState = AutoInitState::DISABLED;
931 }
932
933 void Bridge::EnableAutoInit()
934 {
935   mAutoInitState = AutoInitState::ENABLED;
936
937   if(INITIALIZED_BRIDGE)
938   {
939     return;
940   }
941
942   auto rootLayer       = Dali::Stage::GetCurrent().GetRootLayer(); // A root layer of the default window.
943   auto window          = Dali::DevelWindow::Get(rootLayer);
944   auto applicationName = Dali::Internal::Adaptor::Adaptor::GetApplicationPackageName();
945
946   auto accessible = Accessibility::Accessible::Get(rootLayer);
947
948   auto bridge = Bridge::GetCurrentBridge();
949   bridge->AddTopLevelWindow(accessible);
950   bridge->SetApplicationName(applicationName);
951   bridge->Initialize();
952
953   if(window && window.IsVisible())
954   {
955     bridge->WindowShown(window);
956   }
957 }
958
959 std::string Bridge::MakeBusNameForWidget(std::string_view widgetInstanceId)
960 {
961   // The bus name should consist of dot-separated alphanumeric elements, e.g. "com.example.BusName123".
962   // Allowed characters in each element: "[A-Z][a-z][0-9]_", but no element may start with a digit.
963
964   static const char prefix[]   = "com.samsung.dali.widget_";
965   static const char underscore = '_';
966
967   std::stringstream tmp;
968
969   tmp << prefix;
970
971   for(char ch : widgetInstanceId)
972   {
973     tmp << (std::isalnum(ch) ? ch : underscore);
974   }
975
976   return tmp.str();
977 }