(AT-SPI) Created sub-folders for some of the Bridge code (for SAM score)
[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    * @copydoc Dali::Accessibility::Bridge::WindowShown()
429    */
430   void WindowShown(Dali::Window window) override
431   {
432     if(IsUp())
433     {
434       EmitShown(window);
435     }
436   }
437
438   /**
439    * @copydoc Dali::Accessibility::Bridge::WindowHidden()
440    */
441   void WindowHidden(Dali::Window window) override
442   {
443     if(IsUp())
444     {
445       EmitHidden(window);
446     }
447   }
448
449   /**
450    * @copydoc Dali::Accessibility::Bridge::WindowFocused()
451    */
452   void WindowFocused(Dali::Window window) override
453   {
454     if(IsUp())
455     {
456       EmitActivate(window);
457     }
458   }
459
460   /**
461    * @copydoc Dali::Accessibility::Bridge::WindowUnfocused()
462    */
463   void WindowUnfocused(Dali::Window window) override
464   {
465     if(IsUp())
466     {
467       EmitDeactivate(window);
468     }
469   }
470
471   /**
472    * @copydoc Dali::Accessibility::Bridge::SuppressScreenReader()
473    */
474   void SuppressScreenReader(bool suppress) override
475   {
476     if(mIsScreenReaderSuppressed == suppress)
477     {
478       return;
479     }
480     mIsScreenReaderSuppressed = suppress;
481     ReadScreenReaderEnabledProperty();
482   }
483
484   void SwitchBridge()
485   {
486     if((!mIsScreenReaderSuppressed && mIsScreenReaderEnabled) || mIsEnabled)
487     {
488       ForceUp();
489     }
490     else
491     {
492       ForceDown();
493     }
494   }
495
496   bool ReadIsEnabledTimerCallback()
497   {
498     ReadIsEnabledProperty();
499     return false;
500   }
501
502   void ReadIsEnabledProperty()
503   {
504     mAccessibilityStatusClient.property<bool>("IsEnabled").asyncGet([this](DBus::ValueOrError<bool> msg) {
505       if(!msg)
506       {
507         DALI_LOG_ERROR("Get IsEnabled property error: %s\n", msg.getError().message.c_str());
508         if(msg.getError().errorType == DBus::ErrorType::INVALID_REPLY)
509         {
510           if(!mReadIsEnabledTimer)
511           {
512             mReadIsEnabledTimer = Dali::Timer::New(RETRY_INTERVAL);
513             mReadIsEnabledTimer.TickSignal().Connect(this, &BridgeImpl::ReadIsEnabledTimerCallback);
514           }
515           mReadIsEnabledTimer.Start();
516         }
517         return;
518       }
519
520       if(mReadIsEnabledTimer)
521       {
522         mReadIsEnabledTimer.Stop();
523         mReadIsEnabledTimer.Reset();
524       }
525
526       mIsEnabled = std::get<0>(msg);
527       SwitchBridge();
528     });
529   }
530
531   void ListenIsEnabledProperty()
532   {
533     mAccessibilityStatusClient.addPropertyChangedEvent<bool>("IsEnabled", [this](bool res) {
534       mIsEnabled = res;
535       SwitchBridge();
536     });
537   }
538
539   bool ReadScreenReaderEnabledTimerCallback()
540   {
541     ReadScreenReaderEnabledProperty();
542     return false;
543   }
544
545   void ReadScreenReaderEnabledProperty()
546   {
547     // can be true because of SuppressScreenReader before init
548     if (!mAccessibilityStatusClient)
549     {
550       return;
551     }
552
553     mAccessibilityStatusClient.property<bool>("ScreenReaderEnabled").asyncGet([this](DBus::ValueOrError<bool> msg) {
554       if(!msg)
555       {
556         DALI_LOG_ERROR("Get ScreenReaderEnabled property error: %s\n", msg.getError().message.c_str());
557         if(msg.getError().errorType == DBus::ErrorType::INVALID_REPLY)
558         {
559           if(!mReadScreenReaderEnabledTimer)
560           {
561             mReadScreenReaderEnabledTimer = Dali::Timer::New(RETRY_INTERVAL);
562             mReadScreenReaderEnabledTimer.TickSignal().Connect(this, &BridgeImpl::ReadScreenReaderEnabledTimerCallback);
563           }
564           mReadScreenReaderEnabledTimer.Start();
565         }
566         return;
567       }
568
569       if(mReadScreenReaderEnabledTimer)
570       {
571         mReadScreenReaderEnabledTimer.Stop();
572         mReadScreenReaderEnabledTimer.Reset();
573       }
574
575       mIsScreenReaderEnabled = std::get<0>(msg);
576       SwitchBridge();
577     });
578   }
579
580   void EmitScreenReaderEnabledSignal()
581   {
582     if (mIsScreenReaderEnabled)
583     {
584       mScreenReaderEnabledSignal.Emit();
585     }
586     else
587     {
588       mScreenReaderDisabledSignal.Emit();
589     }
590   }
591
592   void ListenScreenReaderEnabledProperty()
593   {
594     mAccessibilityStatusClient.addPropertyChangedEvent<bool>("ScreenReaderEnabled", [this](bool res) {
595       mIsScreenReaderEnabled = res;
596       EmitScreenReaderEnabledSignal();
597       SwitchBridge();
598     });
599   }
600
601   void ReadAndListenProperties()
602   {
603     ReadIsEnabledProperty();
604     ListenIsEnabledProperty();
605
606     ReadScreenReaderEnabledProperty();
607     ListenScreenReaderEnabledProperty();
608   }
609
610   bool InitializeAccessibilityStatusClient()
611   {
612     mAccessibilityStatusClient = DBus::DBusClient{A11yDbusName, A11yDbusPath, A11yDbusStatusInterface, DBus::ConnectionType::SESSION};
613
614     if (!mAccessibilityStatusClient)
615     {
616       DALI_LOG_ERROR("Accessibility Status DbusClient is not ready\n");
617       return false;
618     }
619
620     return true;
621   }
622
623   bool InitializeTimerCallback()
624   {
625     if ( InitializeAccessibilityStatusClient() )
626     {
627       ReadAndListenProperties();
628       return false;
629     }
630     return true;
631   }
632
633   bool OnIdleSignal()
634   {
635     if ( InitializeAccessibilityStatusClient() )
636     {
637       ReadAndListenProperties();
638       mIdleCallback = NULL;
639       return false;
640     }
641
642     if(!mInitializeTimer)
643     {
644       mInitializeTimer = Dali::Timer::New(RETRY_INTERVAL);
645       mInitializeTimer.TickSignal().Connect(this, &BridgeImpl::InitializeTimerCallback);
646     }
647     mInitializeTimer.Start();
648
649     mIdleCallback = NULL;
650     return false;
651   }
652
653   /**
654    * @copydoc Dali::Accessibility::Bridge::Initialize()
655    */
656   void Initialize() override
657   {
658     if ( InitializeAccessibilityStatusClient() )
659     {
660       ReadAndListenProperties();
661       return;
662     }
663
664     // Initialize failed. Try it again on Idle
665     if( Dali::Adaptor::IsAvailable() )
666     {
667       Dali::Adaptor& adaptor = Dali::Adaptor::Get();
668       if( NULL == mIdleCallback )
669       {
670         mIdleCallback = MakeCallback( this, &BridgeImpl::OnIdleSignal );
671         adaptor.AddIdle( mIdleCallback, true );
672       }
673     }
674   }
675
676   /**
677    * @copydoc Dali::Accessibility::Bridge::GetScreenReaderEnabled()
678    */
679   bool GetScreenReaderEnabled() override
680   {
681     return mIsScreenReaderEnabled;
682   }
683
684   /**
685    * @copydoc Dali::Accessibility::Bridge::IsEnabled()
686    */
687   bool IsEnabled() override
688   {
689     return mIsEnabled;
690   }
691
692   Address EmbedSocket(const Address& plug, const Address& socket) override
693   {
694     auto client = CreateSocketClient(socket);
695     auto reply  = client.method<Address(Address)>("Embed").call(plug);
696
697     if(!reply)
698     {
699       DALI_LOG_ERROR("Failed to embed socket %s: %s", socket.ToString().c_str(), reply.getError().message.c_str());
700       return {};
701     }
702
703     return std::get<0>(reply.getValues());
704   }
705
706   void EmbedAtkSocket(const Address& plug, const Address& socket) override
707   {
708     auto client = CreateSocketClient(socket);
709
710     client.method<void(std::string)>("Embedded").call(ATSPI_PREFIX_PATH + plug.GetPath());
711   }
712
713   void UnembedSocket(const Address& plug, const Address& socket) override
714   {
715     auto client = CreateSocketClient(socket);
716
717     client.method<void(Address)>("Unembed").call(plug);
718   }
719
720   void SetSocketOffset(ProxyAccessible* socket, std::int32_t x, std::int32_t y) override
721   {
722     AddCoalescableMessage(CoalescableMessages::SET_OFFSET, socket, 1.0f, [=]() {
723       auto client = CreateSocketClient(socket->GetAddress());
724
725       client.method<void(std::int32_t, std::int32_t)>("SetOffset").asyncCall([](DBus::ValueOrError<void>) {}, x, y);
726     });
727   }
728
729   void SetExtentsOffset(std::int32_t x, std::int32_t y) override
730   {
731     if(mData)
732     {
733       mData->mExtentsOffset = {x, y};
734     }
735   }
736
737   void SetPreferredBusName(std::string_view preferredBusName) override
738   {
739     if(preferredBusName == mPreferredBusName)
740     {
741       return;
742     }
743
744     std::string oldPreferredBusName = std::move(mPreferredBusName);
745     mPreferredBusName               = std::string{preferredBusName};
746
747     if(IsUp())
748     {
749       ReleaseBusName(oldPreferredBusName);
750       RequestBusName(mPreferredBusName);
751     }
752     // else: request/release will be handled by ForceUp/ForceDown, respectively
753   }
754
755 private:
756   DBus::DBusClient CreateSocketClient(const Address& socket)
757   {
758     return {socket.GetBus(), ATSPI_PREFIX_PATH + socket.GetPath(), Accessible::GetInterfaceName(AtspiInterface::SOCKET), mConnectionPtr};
759   }
760
761   void RequestBusName(const std::string& busName)
762   {
763     if(busName.empty())
764     {
765       return;
766     }
767
768     DBus::requestBusName(mConnectionPtr, busName);
769   }
770
771   void ReleaseBusName(const std::string& busName)
772   {
773     if(busName.empty())
774     {
775       return;
776     }
777
778     DBus::releaseBusName(mConnectionPtr, busName);
779   }
780 }; // BridgeImpl
781
782 namespace // unnamed namespace
783 {
784
785 bool INITIALIZED_BRIDGE = false;
786
787 /**
788  * @brief Creates BridgeImpl instance.
789  *
790  * @return The BridgeImpl instance
791  * @note This method is to check environment variable first. If ATSPI is disable using env, it returns dummy bridge instance.
792  */
793 std::shared_ptr<Bridge> CreateBridge()
794 {
795   INITIALIZED_BRIDGE = true;
796
797   try
798   {
799     /* check environment variable first */
800     const char* envAtspiDisabled = Dali::EnvironmentVariable::GetEnvironmentVariable(DALI_ENV_DISABLE_ATSPI);
801     if(envAtspiDisabled && std::atoi(envAtspiDisabled) != 0)
802     {
803       return Dali::Accessibility::DummyBridge::GetInstance();
804     }
805
806     return std::make_shared<BridgeImpl>();
807   }
808   catch(const std::exception&)
809   {
810     DALI_LOG_ERROR("Failed to initialize AT-SPI bridge");
811     return Dali::Accessibility::DummyBridge::GetInstance();
812   }
813 }
814
815 } // unnamed namespace
816
817 // Dali::Accessibility::Bridge class implementation
818
819 std::shared_ptr<Bridge> Bridge::GetCurrentBridge()
820 {
821   static std::shared_ptr<Bridge> bridge;
822
823   if(bridge)
824   {
825     return bridge;
826   }
827   else if(mAutoInitState == AutoInitState::ENABLED)
828   {
829     bridge = CreateBridge();
830
831     /* check environment variable for suppressing screen-reader */
832     const char* envSuppressScreenReader = Dali::EnvironmentVariable::GetEnvironmentVariable(DALI_ENV_SUPPRESS_SCREEN_READER);
833     if(envSuppressScreenReader && std::atoi(envSuppressScreenReader) != 0)
834     {
835       bridge->SuppressScreenReader(true);
836     }
837
838     return bridge;
839   }
840
841   return Dali::Accessibility::DummyBridge::GetInstance();
842 }
843
844 void Bridge::DisableAutoInit()
845 {
846   if(INITIALIZED_BRIDGE)
847   {
848     DALI_LOG_ERROR("Bridge::DisableAutoInit() called after bridge auto-initialization");
849   }
850
851   mAutoInitState = AutoInitState::DISABLED;
852 }
853
854 void Bridge::EnableAutoInit()
855 {
856   mAutoInitState = AutoInitState::ENABLED;
857
858   if(INITIALIZED_BRIDGE)
859   {
860     return;
861   }
862
863   auto rootLayer       = Dali::Stage::GetCurrent().GetRootLayer(); // A root layer of the default window.
864   auto window          = Dali::DevelWindow::Get(rootLayer);
865   auto applicationName = Dali::Internal::Adaptor::Adaptor::GetApplicationPackageName();
866
867   auto accessible = Accessibility::Accessible::Get(rootLayer);
868
869   auto bridge = Bridge::GetCurrentBridge();
870   bridge->AddTopLevelWindow(accessible);
871   bridge->SetApplicationName(applicationName);
872   bridge->Initialize();
873
874   if(window && window.IsVisible())
875   {
876     bridge->WindowShown(window);
877   }
878 }
879
880 std::string Bridge::MakeBusNameForWidget(std::string_view widgetInstanceId)
881 {
882   // The bus name should consist of dot-separated alphanumeric elements, e.g. "com.example.BusName123".
883   // Allowed characters in each element: "[A-Z][a-z][0-9]_", but no element may start with a digit.
884
885   static const char prefix[]   = "com.samsung.dali.widget_";
886   static const char underscore = '_';
887
888   std::stringstream tmp;
889
890   tmp << prefix;
891
892   for(char ch : widgetInstanceId)
893   {
894     tmp << (std::isalnum(ch) ? ch : underscore);
895   }
896
897   return tmp.str();
898 }