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