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