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