Revert "[Tizen] Revert "[Tizen] Disable ATSPI when release or perf image""
[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 #include <unistd.h>
50
51 using namespace Dali::Accessibility;
52
53 namespace // unnamed namespace
54 {
55 const int RETRY_INTERVAL = 1000;
56
57 } // unnamed namespace
58
59 /**
60  * @brief The BridgeImpl class is to implement some Bridge functions.
61  */
62 class BridgeImpl : public virtual BridgeBase,
63                    public BridgeAccessible,
64                    public BridgeObject,
65                    public BridgeComponent,
66                    public BridgeCollection,
67                    public BridgeAction,
68                    public BridgeValue,
69                    public BridgeText,
70                    public BridgeEditableText,
71                    public BridgeSelection,
72                    public BridgeApplication,
73                    public BridgeHypertext,
74                    public BridgeHyperlink,
75                    public BridgeSocket,
76                    public BridgeTable,
77                    public BridgeTableCell
78 {
79   DBus::DBusClient                                              mAccessibilityStatusClient{};
80   DBus::DBusClient                                              mRegistryClient{};
81   DBus::DBusClient                                              mDirectReadingClient{};
82   bool                                                          mIsScreenReaderEnabled{false};
83   bool                                                          mIsEnabled{false};
84   std::unordered_map<int32_t, std::function<void(std::string)>> mDirectReadingCallbacks{};
85   Dali::Actor                                                   mHighlightedActor;
86   std::function<void(Dali::Actor)>                              mHighlightClearAction{nullptr};
87   Dali::CallbackBase*                                           mIdleCallback{};
88   Dali::Timer                                                   mInitializeTimer;
89   Dali::Timer                                                   mReadIsEnabledTimer;
90   Dali::Timer                                                   mReadScreenReaderEnabledTimer;
91   Dali::Timer                                                   mForceUpTimer;
92   std::string                                                   mPreferredBusName;
93
94 public:
95   BridgeImpl() = default;
96
97   /**
98    * @copydoc Dali::Accessibility::Bridge::Emit()
99    */
100   Consumed Emit(KeyEventType type, unsigned int keyCode, const std::string& keyName, unsigned int timeStamp, bool isText) override
101   {
102     if(!IsUp())
103     {
104       return Consumed::NO;
105     }
106
107     unsigned int keyType = 0;
108
109     switch(type)
110     {
111       case KeyEventType::KEY_PRESSED:
112       {
113         keyType = 0;
114         break;
115       }
116       case KeyEventType::KEY_RELEASED:
117       {
118         keyType = 1;
119         break;
120       }
121       default:
122       {
123         return Consumed::NO;
124       }
125     }
126
127     return Consumed::NO;
128   }
129
130   /**
131    * @copydoc Dali::Accessibility::Bridge::Pause()
132    */
133   void Pause() override
134   {
135     if(!IsUp())
136     {
137       return;
138     }
139
140     mDirectReadingClient.method<DBus::ValueOrError<void>(bool)>("PauseResume").asyncCall([](DBus::ValueOrError<void> msg) {
141       if(!msg)
142       {
143         LOG() << "Direct reading command failed (" << msg.getError().message << ")\n";
144       }
145     },
146                                                                                          true);
147   }
148
149   /**
150    * @copydoc Dali::Accessibility::Bridge::Resume()
151    */
152   void Resume() override
153   {
154     if(!IsUp())
155     {
156       return;
157     }
158
159     mDirectReadingClient.method<DBus::ValueOrError<void>(bool)>("PauseResume").asyncCall([](DBus::ValueOrError<void> msg) {
160       if(!msg)
161       {
162         LOG() << "Direct reading command failed (" << msg.getError().message << ")\n";
163       }
164     },
165                                                                                          false);
166   }
167
168   /**
169    * @copydoc Dali::Accessibility::Bridge::StopReading()
170    */
171   void StopReading(bool alsoNonDiscardable) override
172   {
173     if(!IsUp())
174     {
175       return;
176     }
177
178     mDirectReadingClient.method<DBus::ValueOrError<void>(bool)>("StopReading").asyncCall([](DBus::ValueOrError<void> msg) {
179       if(!msg)
180       {
181         LOG() << "Direct reading command failed (" << msg.getError().message << ")\n";
182       }
183     },
184                                                                                          alsoNonDiscardable);
185   }
186
187   /**
188    * @copydoc Dali::Accessibility::Bridge::Say()
189    */
190   void Say(const std::string& text, bool discardable, std::function<void(std::string)> callback) override
191   {
192     if(!IsUp())
193     {
194       return;
195     }
196
197     mDirectReadingClient.method<DBus::ValueOrError<std::string, bool, int32_t>(std::string, bool)>("ReadCommand").asyncCall([=](DBus::ValueOrError<std::string, bool, int32_t> msg) {
198       if(!msg)
199       {
200         LOG() << "Direct reading command failed (" << msg.getError().message << ")\n";
201       }
202       else if(callback)
203       {
204         mDirectReadingCallbacks.emplace(std::get<2>(msg), callback);
205       }
206     },
207                                                                                                                             text,
208                                                                                                                             discardable);
209   }
210
211   /**
212    * @copydoc Dali::Accessibility::Bridge::ForceDown()
213    */
214   void ForceDown() override
215   {
216     if(mData)
217     {
218       if(mData->mCurrentlyHighlightedActor && mData->mHighlightActor)
219       {
220         mData->mCurrentlyHighlightedActor.Remove(mData->mHighlightActor);
221       }
222       mData->mCurrentlyHighlightedActor = {};
223       mData->mHighlightActor            = {};
224
225       mDisabledSignal.Emit();
226       UnembedSocket(mApplication.GetAddress(), {AtspiDbusNameRegistry, "root"});
227       ReleaseBusName(mPreferredBusName);
228     }
229
230     mHighlightedActor     = {};
231     mHighlightClearAction = {};
232     BridgeAccessible::ForceDown();
233     mRegistryClient      = {};
234     mDirectReadingClient = {};
235     mDirectReadingCallbacks.clear();
236     mApplication.mChildren.clear();
237     ClearTimer();
238   }
239
240   void ClearTimer()
241   {
242     if(mInitializeTimer)
243     {
244       mInitializeTimer.Stop();
245       mInitializeTimer.Reset();
246     }
247
248     if(mReadIsEnabledTimer)
249     {
250       mReadIsEnabledTimer.Stop();
251       mReadIsEnabledTimer.Reset();
252     }
253
254     if(mReadScreenReaderEnabledTimer)
255     {
256       mReadScreenReaderEnabledTimer.Stop();
257       mReadScreenReaderEnabledTimer.Reset();
258     }
259
260     if(mForceUpTimer)
261     {
262       mForceUpTimer.Stop();
263       mForceUpTimer.Reset();
264     }
265   }
266
267   /**
268    * @copydoc Dali::Accessibility::Bridge::Terminate()
269    */
270   void Terminate() override
271   {
272     if(mData)
273     {
274       // The ~Window() after this point cannot emit DESTROY, because Bridge is not available. So emit DESTROY here.
275       for(auto windowAccessible : mApplication.mChildren)
276       {
277         BridgeObject::Emit(windowAccessible, WindowEvent::DESTROY);
278       }
279       mData->mCurrentlyHighlightedActor = {};
280       mData->mHighlightActor            = {};
281     }
282     ForceDown();
283     if((NULL != mIdleCallback) && Dali::Adaptor::IsAvailable())
284     {
285       Dali::Adaptor::Get().RemoveIdle(mIdleCallback);
286     }
287     mAccessibilityStatusClient = {};
288     mDbusServer                = {};
289     mConnectionPtr             = {};
290   }
291
292   bool ForceUpTimerCallback()
293   {
294     if(ForceUp() != ForceUpResult::FAILED)
295     {
296       return false;
297     }
298     return true;
299   }
300
301   /**
302    * @copydoc Dali::Accessibility::Bridge::ForceUp()
303    */
304   ForceUpResult ForceUp() override
305   {
306     auto forceUpResult = BridgeAccessible::ForceUp();
307     if(forceUpResult == ForceUpResult::ALREADY_UP)
308     {
309       return forceUpResult;
310     }
311     else if(forceUpResult == ForceUpResult::FAILED)
312     {
313       if(!mForceUpTimer)
314       {
315         mForceUpTimer = Dali::Timer::New(RETRY_INTERVAL);
316         mForceUpTimer.TickSignal().Connect(this, &BridgeImpl::ForceUpTimerCallback);
317         mForceUpTimer.Start();
318       }
319       return forceUpResult;
320     }
321
322     BridgeObject::RegisterInterfaces();
323     BridgeAccessible::RegisterInterfaces();
324     BridgeComponent::RegisterInterfaces();
325     BridgeCollection::RegisterInterfaces();
326     BridgeAction::RegisterInterfaces();
327     BridgeValue::RegisterInterfaces();
328     BridgeText::RegisterInterfaces();
329     BridgeEditableText::RegisterInterfaces();
330     BridgeSelection::RegisterInterfaces();
331     BridgeApplication::RegisterInterfaces();
332     BridgeHypertext::RegisterInterfaces();
333     BridgeHyperlink::RegisterInterfaces();
334     BridgeSocket::RegisterInterfaces();
335     BridgeTable::RegisterInterfaces();
336     BridgeTableCell::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     // Check if the image is either release or perf mode
882     if((access("/etc/release", F_OK) == 0) || (access("/etc/perf", F_OK) == 0))
883     {
884       return Dali::Accessibility::DummyBridge::GetInstance();
885     }
886
887     return std::make_shared<BridgeImpl>();
888   }
889   catch(const std::exception&)
890   {
891     DALI_LOG_ERROR("Failed to initialize AT-SPI bridge");
892     return Dali::Accessibility::DummyBridge::GetInstance();
893   }
894 }
895
896 } // unnamed namespace
897
898 // Dali::Accessibility::Bridge class implementation
899
900 std::shared_ptr<Bridge> Bridge::GetCurrentBridge()
901 {
902   static std::shared_ptr<Bridge> bridge;
903
904   if(bridge)
905   {
906     return bridge;
907   }
908   else if(mAutoInitState == AutoInitState::ENABLED)
909   {
910     bridge = CreateBridge();
911
912     /* check environment variable for suppressing screen-reader */
913     const char* envSuppressScreenReader = Dali::EnvironmentVariable::GetEnvironmentVariable(DALI_ENV_SUPPRESS_SCREEN_READER);
914     if(envSuppressScreenReader && std::atoi(envSuppressScreenReader) != 0)
915     {
916       bridge->SuppressScreenReader(true);
917     }
918
919     return bridge;
920   }
921
922   return Dali::Accessibility::DummyBridge::GetInstance();
923 }
924
925 void Bridge::DisableAutoInit()
926 {
927   if(INITIALIZED_BRIDGE)
928   {
929     DALI_LOG_ERROR("Bridge::DisableAutoInit() called after bridge auto-initialization");
930   }
931
932   mAutoInitState = AutoInitState::DISABLED;
933 }
934
935 void Bridge::EnableAutoInit()
936 {
937   mAutoInitState = AutoInitState::ENABLED;
938
939   if(INITIALIZED_BRIDGE)
940   {
941     return;
942   }
943
944   auto rootLayer       = Dali::Stage::GetCurrent().GetRootLayer(); // A root layer of the default window.
945   auto window          = Dali::DevelWindow::Get(rootLayer);
946   auto applicationName = Dali::Internal::Adaptor::Adaptor::GetApplicationPackageName();
947
948   auto accessible = Accessibility::Accessible::Get(rootLayer);
949
950   auto bridge = Bridge::GetCurrentBridge();
951   bridge->AddTopLevelWindow(accessible);
952   bridge->SetApplicationName(applicationName);
953   bridge->Initialize();
954
955   if(window && window.IsVisible())
956   {
957     bridge->WindowShown(window);
958   }
959 }
960
961 std::string Bridge::MakeBusNameForWidget(std::string_view widgetInstanceId)
962 {
963   // The bus name should consist of dot-separated alphanumeric elements, e.g. "com.example.BusName123".
964   // Allowed characters in each element: "[A-Z][a-z][0-9]_", but no element may start with a digit.
965
966   static const char prefix[]   = "com.samsung.dali.widget_";
967   static const char underscore = '_';
968
969   std::stringstream tmp;
970
971   tmp << prefix;
972
973   for(char ch : widgetInstanceId)
974   {
975     tmp << (std::isalnum(ch) ? ch : underscore);
976   }
977
978   return tmp.str();
979 }