[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-table.h>
43 #include <dali/internal/accessibility/bridge/bridge-table-cell.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         adaptor.AddIdle(mIdleCallback, true);
773       }
774     }
775   }
776
777   /**
778    * @copydoc Dali::Accessibility::Bridge::GetScreenReaderEnabled()
779    */
780   bool GetScreenReaderEnabled() override
781   {
782     return mIsScreenReaderEnabled;
783   }
784
785   /**
786    * @copydoc Dali::Accessibility::Bridge::IsEnabled()
787    */
788   bool IsEnabled() override
789   {
790     return mIsEnabled;
791   }
792
793   Address EmbedSocket(const Address& plug, const Address& socket) override
794   {
795     auto client = CreateSocketClient(socket);
796     auto reply  = client.method<Address(Address)>("Embed").call(plug);
797
798     if(!reply)
799     {
800       DALI_LOG_ERROR("Failed to embed socket %s: %s", socket.ToString().c_str(), reply.getError().message.c_str());
801       return {};
802     }
803
804     return std::get<0>(reply.getValues());
805   }
806
807   void EmbedAtkSocket(const Address& plug, const Address& socket) override
808   {
809     auto client = CreateSocketClient(socket);
810
811     client.method<void(std::string)>("Embedded").asyncCall([](DBus::ValueOrError<void>) {}, ATSPI_PREFIX_PATH + plug.GetPath());
812   }
813
814   void UnembedSocket(const Address& plug, const Address& socket) override
815   {
816     auto client = CreateSocketClient(socket);
817
818     client.method<void(Address)>("Unembed").asyncCall([](DBus::ValueOrError<void>) {}, plug);
819   }
820
821   void SetSocketOffset(ProxyAccessible* socket, std::int32_t x, std::int32_t y) override
822   {
823     AddCoalescableMessage(CoalescableMessages::SET_OFFSET, socket, 1.0f, [=]() {
824       auto client = CreateSocketClient(socket->GetAddress());
825
826       client.method<void(std::int32_t, std::int32_t)>("SetOffset").asyncCall([](DBus::ValueOrError<void>) {}, x, y);
827     });
828   }
829
830   void SetExtentsOffset(std::int32_t x, std::int32_t y) override
831   {
832     if(mData)
833     {
834       mData->mExtentsOffset = {x, y};
835     }
836   }
837
838   void SetPreferredBusName(std::string_view preferredBusName) override
839   {
840     if(preferredBusName == mPreferredBusName)
841     {
842       return;
843     }
844
845     std::string oldPreferredBusName = std::move(mPreferredBusName);
846     mPreferredBusName               = std::string{preferredBusName};
847
848     if(IsUp())
849     {
850       ReleaseBusName(oldPreferredBusName);
851       RequestBusName(mPreferredBusName);
852     }
853     // else: request/release will be handled by ForceUp/ForceDown, respectively
854   }
855
856 private:
857   DBus::DBusClient CreateSocketClient(const Address& socket)
858   {
859     return {socket.GetBus(), ATSPI_PREFIX_PATH + socket.GetPath(), Accessible::GetInterfaceName(AtspiInterface::SOCKET), mConnectionPtr};
860   }
861
862   void RequestBusName(const std::string& busName)
863   {
864     if(busName.empty())
865     {
866       return;
867     }
868
869     DBus::requestBusName(mConnectionPtr, busName);
870   }
871
872   void ReleaseBusName(const std::string& busName)
873   {
874     if(busName.empty())
875     {
876       return;
877     }
878
879     DBus::releaseBusName(mConnectionPtr, busName);
880   }
881 }; // BridgeImpl
882
883 namespace // unnamed namespace
884 {
885 bool INITIALIZED_BRIDGE = false;
886
887 /**
888  * @brief Creates BridgeImpl instance.
889  *
890  * @return The BridgeImpl instance
891  * @note This method is to check environment variable first. If ATSPI is disable using env, it returns dummy bridge instance.
892  */
893 std::shared_ptr<Bridge> CreateBridge()
894 {
895   INITIALIZED_BRIDGE = true;
896
897   try
898   {
899     /* check environment variable first */
900     const char* envAtspiDisabled = Dali::EnvironmentVariable::GetEnvironmentVariable(DALI_ENV_DISABLE_ATSPI);
901     if(envAtspiDisabled && std::atoi(envAtspiDisabled) != 0)
902     {
903       return Dali::Accessibility::DummyBridge::GetInstance();
904     }
905
906     return std::make_shared<BridgeImpl>();
907   }
908   catch(const std::exception&)
909   {
910     DALI_LOG_ERROR("Failed to initialize AT-SPI bridge");
911     return Dali::Accessibility::DummyBridge::GetInstance();
912   }
913 }
914
915 } // unnamed namespace
916
917 // Dali::Accessibility::Bridge class implementation
918
919 std::shared_ptr<Bridge> Bridge::GetCurrentBridge()
920 {
921   static std::shared_ptr<Bridge> bridge;
922
923   if(bridge)
924   {
925     return bridge;
926   }
927   else if(mAutoInitState == AutoInitState::ENABLED)
928   {
929     bridge = CreateBridge();
930
931     /* check environment variable for suppressing screen-reader */
932     const char* envSuppressScreenReader = Dali::EnvironmentVariable::GetEnvironmentVariable(DALI_ENV_SUPPRESS_SCREEN_READER);
933     if(envSuppressScreenReader && std::atoi(envSuppressScreenReader) != 0)
934     {
935       bridge->SuppressScreenReader(true);
936     }
937
938     return bridge;
939   }
940
941   return Dali::Accessibility::DummyBridge::GetInstance();
942 }
943
944 void Bridge::DisableAutoInit()
945 {
946   if(INITIALIZED_BRIDGE)
947   {
948     DALI_LOG_ERROR("Bridge::DisableAutoInit() called after bridge auto-initialization");
949   }
950
951   mAutoInitState = AutoInitState::DISABLED;
952 }
953
954 void Bridge::EnableAutoInit()
955 {
956   mAutoInitState = AutoInitState::ENABLED;
957
958   if(INITIALIZED_BRIDGE)
959   {
960     return;
961   }
962
963   auto rootLayer       = Dali::Stage::GetCurrent().GetRootLayer(); // A root layer of the default window.
964   auto window          = Dali::DevelWindow::Get(rootLayer);
965   auto applicationName = Dali::Internal::Adaptor::Adaptor::GetApplicationPackageName();
966
967   auto accessible = Accessibility::Accessible::Get(rootLayer);
968
969   auto bridge = Bridge::GetCurrentBridge();
970   bridge->AddTopLevelWindow(accessible);
971   bridge->SetApplicationName(applicationName);
972   bridge->Initialize();
973
974   if(window && window.IsVisible())
975   {
976     bridge->WindowShown(window);
977   }
978 }
979
980 std::string Bridge::MakeBusNameForWidget(std::string_view widgetInstanceId)
981 {
982   // The bus name should consist of dot-separated alphanumeric elements, e.g. "com.example.BusName123".
983   // Allowed characters in each element: "[A-Z][a-z][0-9]_", but no element may start with a digit.
984
985   static const char prefix[]   = "com.samsung.dali.widget_";
986   static const char underscore = '_';
987
988   std::stringstream tmp;
989
990   tmp << prefix;
991
992   for(char ch : widgetInstanceId)
993   {
994     tmp << (std::isalnum(ch) ? ch : underscore);
995   }
996
997   return tmp.str();
998 }