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