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