2 * Copyright (c) 2024 Samsung Electronics Co., Ltd.
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
21 #include <dali/devel-api/common/stage.h>
22 #include <dali/integration-api/debug.h>
23 #include <dali/public-api/actors/layer.h>
25 #include <unordered_map>
28 #include <dali/devel-api/adaptor-framework/environment-variable.h>
29 #include <dali/devel-api/adaptor-framework/window-devel.h>
30 #include <dali/internal/accessibility/bridge/accessibility-common.h>
31 #include <dali/internal/accessibility/bridge/bridge-accessible.h>
32 #include <dali/internal/accessibility/bridge/bridge-action.h>
33 #include <dali/internal/accessibility/bridge/bridge-application.h>
34 #include <dali/internal/accessibility/bridge/bridge-collection.h>
35 #include <dali/internal/accessibility/bridge/bridge-component.h>
36 #include <dali/internal/accessibility/bridge/bridge-editable-text.h>
37 #include <dali/internal/accessibility/bridge/bridge-hyperlink.h>
38 #include <dali/internal/accessibility/bridge/bridge-hypertext.h>
39 #include <dali/internal/accessibility/bridge/bridge-object.h>
40 #include <dali/internal/accessibility/bridge/bridge-selection.h>
41 #include <dali/internal/accessibility/bridge/bridge-socket.h>
42 #include <dali/internal/accessibility/bridge/bridge-table-cell.h>
43 #include <dali/internal/accessibility/bridge/bridge-table.h>
44 #include <dali/internal/accessibility/bridge/bridge-text.h>
45 #include <dali/internal/accessibility/bridge/bridge-value.h>
46 #include <dali/internal/accessibility/bridge/dummy/dummy-atspi.h>
47 #include <dali/internal/adaptor/common/adaptor-impl.h>
48 #include <dali/internal/system/common/environment-variables.h>
50 using namespace Dali::Accessibility;
52 namespace // unnamed namespace
54 const int RETRY_INTERVAL = 1000;
56 } // unnamed namespace
59 * @brief The BridgeImpl class is to implement some Bridge functions.
61 class BridgeImpl : public virtual BridgeBase,
62 public BridgeAccessible,
64 public BridgeComponent,
65 public BridgeCollection,
69 public BridgeEditableText,
70 public BridgeSelection,
71 public BridgeApplication,
72 public BridgeHypertext,
73 public BridgeHyperlink,
76 public BridgeTableCell
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;
94 BridgeImpl() = default;
97 * @copydoc Dali::Accessibility::Bridge::Emit()
99 Consumed Emit(KeyEventType type, unsigned int keyCode, const std::string& keyName, unsigned int timeStamp, bool isText) override
106 unsigned int keyType = 0;
110 case KeyEventType::KEY_PRESSED:
115 case KeyEventType::KEY_RELEASED:
126 auto methodObject = mRegistryClient.method<bool(std::tuple<uint32_t, int32_t, int32_t, int32_t, int32_t, std::string, bool>)>("NotifyListenersSync");
127 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});
130 LOG() << result.getError().message;
133 return std::get<0>(result) ? Consumed::YES : Consumed::NO;
137 * @copydoc Dali::Accessibility::Bridge::Pause()
139 void Pause() override
146 mDirectReadingClient.method<DBus::ValueOrError<void>(bool)>("PauseResume").asyncCall([](DBus::ValueOrError<void> msg) {
149 LOG() << "Direct reading command failed (" << msg.getError().message << ")\n";
156 * @copydoc Dali::Accessibility::Bridge::Resume()
158 void Resume() override
165 mDirectReadingClient.method<DBus::ValueOrError<void>(bool)>("PauseResume").asyncCall([](DBus::ValueOrError<void> msg) {
168 LOG() << "Direct reading command failed (" << msg.getError().message << ")\n";
175 * @copydoc Dali::Accessibility::Bridge::StopReading()
177 void StopReading(bool alsoNonDiscardable) override
184 mDirectReadingClient.method<DBus::ValueOrError<void>(bool)>("StopReading").asyncCall([](DBus::ValueOrError<void> msg) {
187 LOG() << "Direct reading command failed (" << msg.getError().message << ")\n";
194 * @copydoc Dali::Accessibility::Bridge::Say()
196 void Say(const std::string& text, bool discardable, std::function<void(std::string)> callback) override
203 mDirectReadingClient.method<DBus::ValueOrError<std::string, bool, int32_t>(std::string, bool)>("ReadCommand").asyncCall([=](DBus::ValueOrError<std::string, bool, int32_t> msg) {
206 LOG() << "Direct reading command failed (" << msg.getError().message << ")\n";
210 mDirectReadingCallbacks.emplace(std::get<2>(msg), callback);
218 * @copydoc Dali::Accessibility::Bridge::ForceDown()
220 void ForceDown() override
224 if(mData->mCurrentlyHighlightedActor && mData->mHighlightActor)
226 mData->mCurrentlyHighlightedActor.Remove(mData->mHighlightActor);
228 mData->mCurrentlyHighlightedActor = {};
229 mData->mHighlightActor = {};
231 mDisabledSignal.Emit();
232 UnembedSocket(mApplication.GetAddress(), {AtspiDbusNameRegistry, "root"});
233 ReleaseBusName(mPreferredBusName);
236 mHighlightedActor = {};
237 mHighlightClearAction = {};
238 BridgeAccessible::ForceDown();
239 mRegistryClient = {};
240 mDirectReadingClient = {};
241 mDirectReadingCallbacks.clear();
242 mApplication.mChildren.clear();
250 mInitializeTimer.Stop();
251 mInitializeTimer.Reset();
254 if(mReadIsEnabledTimer)
256 mReadIsEnabledTimer.Stop();
257 mReadIsEnabledTimer.Reset();
260 if(mReadScreenReaderEnabledTimer)
262 mReadScreenReaderEnabledTimer.Stop();
263 mReadScreenReaderEnabledTimer.Reset();
268 mForceUpTimer.Stop();
269 mForceUpTimer.Reset();
274 * @copydoc Dali::Accessibility::Bridge::Terminate()
276 void Terminate() override
280 // The ~Window() after this point cannot emit DESTROY, because Bridge is not available. So emit DESTROY here.
281 for(auto windowAccessible : mApplication.mChildren)
283 BridgeObject::Emit(windowAccessible, WindowEvent::DESTROY);
285 mData->mCurrentlyHighlightedActor = {};
286 mData->mHighlightActor = {};
289 if((NULL != mIdleCallback) && Dali::Adaptor::IsAvailable())
291 Dali::Adaptor::Get().RemoveIdle(mIdleCallback);
293 mAccessibilityStatusClient = {};
298 bool ForceUpTimerCallback()
300 if(ForceUp() != ForceUpResult::FAILED)
308 * @copydoc Dali::Accessibility::Bridge::ForceUp()
310 ForceUpResult ForceUp() override
312 auto forceUpResult = BridgeAccessible::ForceUp();
313 if(forceUpResult == ForceUpResult::ALREADY_UP)
315 return forceUpResult;
317 else if(forceUpResult == ForceUpResult::FAILED)
321 mForceUpTimer = Dali::Timer::New(RETRY_INTERVAL);
322 mForceUpTimer.TickSignal().Connect(this, &BridgeImpl::ForceUpTimerCallback);
323 mForceUpTimer.Start();
325 return forceUpResult;
328 BridgeObject::RegisterInterfaces();
329 BridgeAccessible::RegisterInterfaces();
330 BridgeComponent::RegisterInterfaces();
331 BridgeCollection::RegisterInterfaces();
332 BridgeAction::RegisterInterfaces();
333 BridgeValue::RegisterInterfaces();
334 BridgeText::RegisterInterfaces();
335 BridgeEditableText::RegisterInterfaces();
336 BridgeSelection::RegisterInterfaces();
337 BridgeApplication::RegisterInterfaces();
338 BridgeHypertext::RegisterInterfaces();
339 BridgeHyperlink::RegisterInterfaces();
340 BridgeSocket::RegisterInterfaces();
341 BridgeTable::RegisterInterfaces();
342 BridgeTableCell::RegisterInterfaces();
344 RegisterOnBridge(&mApplication);
346 mRegistryClient = {AtspiDbusNameRegistry, AtspiDbusPathDec, Accessible::GetInterfaceName(AtspiInterface::DEVICE_EVENT_CONTROLLER), mConnectionPtr};
347 mDirectReadingClient = DBus::DBusClient{DirectReadingDBusName, DirectReadingDBusPath, DirectReadingDBusInterface, mConnectionPtr};
349 mDirectReadingClient.addSignal<void(int32_t, std::string)>("ReadingStateChanged", [=](int32_t id, std::string readingState) {
350 auto it = mDirectReadingCallbacks.find(id);
351 if(it != mDirectReadingCallbacks.end())
353 it->second(readingState);
354 if(readingState != "ReadingPaused" && readingState != "ReadingResumed" && readingState != "ReadingStarted")
356 mDirectReadingCallbacks.erase(it);
361 RequestBusName(mPreferredBusName);
363 auto parentAddress = EmbedSocket(mApplication.GetAddress(), {AtspiDbusNameRegistry, "root"});
364 mApplication.mParent.SetAddress(std::move(parentAddress));
365 mEnabledSignal.Emit();
367 return ForceUpResult::JUST_STARTED;
371 * @brief Sends a signal to dbus that the window is created.
373 * @param[in] window The window to be created
374 * @see BridgeObject::Emit()
376 void EmitCreated(Dali::Window window)
378 auto windowAccessible = mApplication.GetWindowAccessible(window);
381 windowAccessible->Emit(WindowEvent::CREATE, 0);
386 * @brief Sends a signal to dbus that the window is shown.
388 * @param[in] window The window to be shown
389 * @see Accessible::EmitShowing() and BridgeObject::EmitStateChanged()
391 void EmitShown(Dali::Window window)
393 auto windowAccessible = mApplication.GetWindowAccessible(window);
396 windowAccessible->EmitShowing(true);
401 * @brief Sends a signal to dbus that the window is hidden.
403 * @param[in] window The window to be hidden
404 * @see Accessible::EmitShowing() and BridgeObject::EmitStateChanged()
406 void EmitHidden(Dali::Window window)
408 auto windowAccessible = mApplication.GetWindowAccessible(window);
411 windowAccessible->EmitShowing(false);
416 * @brief Sends a signal to dbus that the window is activated.
418 * @param[in] window The window to be activated
419 * @see BridgeObject::Emit()
421 void EmitActivate(Dali::Window window)
423 auto windowAccessible = mApplication.GetWindowAccessible(window);
426 windowAccessible->Emit(WindowEvent::ACTIVATE, 0);
431 * @brief Sends a signal to dbus that the window is deactivated.
433 * @param[in] window The window to be deactivated
434 * @see BridgeObject::Emit()
436 void EmitDeactivate(Dali::Window window)
438 auto windowAccessible = mApplication.GetWindowAccessible(window);
441 windowAccessible->Emit(WindowEvent::DEACTIVATE, 0);
446 * @brief Sends a signal to dbus that the window is minimized.
448 * @param[in] window The window to be minimized
449 * @see BridgeObject::Emit()
451 void EmitMinimize(Dali::Window window)
453 auto windowAccessible = mApplication.GetWindowAccessible(window);
456 windowAccessible->Emit(WindowEvent::MINIMIZE, 0);
461 * @brief Sends a signal to dbus that the window is restored.
463 * @param[in] window The window to be restored
464 * @param[in] detail Restored window state
465 * @see BridgeObject::Emit()
467 void EmitRestore(Dali::Window window, Dali::Accessibility::WindowRestoreType detail)
469 auto windowAccessible = mApplication.GetWindowAccessible(window);
472 windowAccessible->Emit(WindowEvent::RESTORE, static_cast<unsigned int>(detail));
477 * @brief Sends a signal to dbus that the window is maximized.
479 * @param[in] window The window to be maximized
480 * @see BridgeObject::Emit()
482 void EmitMaximize(Dali::Window window)
484 auto windowAccessible = mApplication.GetWindowAccessible(window);
487 windowAccessible->Emit(WindowEvent::MAXIMIZE, 0);
492 * @copydoc Dali::Accessibility::Bridge::WindowCreated()
494 void WindowCreated(Dali::Window window) override
503 * @copydoc Dali::Accessibility::Bridge::WindowShown()
505 void WindowShown(Dali::Window window) override
514 * @copydoc Dali::Accessibility::Bridge::WindowHidden()
516 void WindowHidden(Dali::Window window) override
525 * @copydoc Dali::Accessibility::Bridge::WindowFocused()
527 void WindowFocused(Dali::Window window) override
531 EmitActivate(window);
536 * @copydoc Dali::Accessibility::Bridge::WindowUnfocused()
538 void WindowUnfocused(Dali::Window window) override
542 EmitDeactivate(window);
547 * @copydoc Dali::Accessibility::Bridge::WindowMinimized()
549 void WindowMinimized(Dali::Window window) override
553 EmitMinimize(window);
558 * @copydoc Dali::Accessibility::Bridge::WindowRestored()
560 void WindowRestored(Dali::Window window, WindowRestoreType detail) override
564 EmitRestore(window, detail);
569 * @copydoc Dali::Accessibility::Bridge::WindowMaximized()
571 void WindowMaximized(Dali::Window window) override
575 EmitMaximize(window);
580 * @copydoc Dali::Accessibility::Bridge::SuppressScreenReader()
582 void SuppressScreenReader(bool suppress) override
584 if(mIsScreenReaderSuppressed == suppress)
588 mIsScreenReaderSuppressed = suppress;
589 ReadScreenReaderEnabledProperty();
594 if((!mIsScreenReaderSuppressed && mIsScreenReaderEnabled) || mIsEnabled)
604 bool ReadIsEnabledTimerCallback()
606 ReadIsEnabledProperty();
610 void ReadIsEnabledProperty()
612 mAccessibilityStatusClient.property<bool>("IsEnabled").asyncGet([this](DBus::ValueOrError<bool> msg) {
615 DALI_LOG_ERROR("Get IsEnabled property error: %s\n", msg.getError().message.c_str());
616 if(msg.getError().errorType == DBus::ErrorType::INVALID_REPLY)
618 if(!mReadIsEnabledTimer)
620 mReadIsEnabledTimer = Dali::Timer::New(RETRY_INTERVAL);
621 mReadIsEnabledTimer.TickSignal().Connect(this, &BridgeImpl::ReadIsEnabledTimerCallback);
623 mReadIsEnabledTimer.Start();
628 if(mReadIsEnabledTimer)
630 mReadIsEnabledTimer.Stop();
631 mReadIsEnabledTimer.Reset();
634 mIsEnabled = std::get<0>(msg);
639 void ListenIsEnabledProperty()
641 mAccessibilityStatusClient.addPropertyChangedEvent<bool>("IsEnabled", [this](bool res) {
647 bool ReadScreenReaderEnabledTimerCallback()
649 ReadScreenReaderEnabledProperty();
653 void ReadScreenReaderEnabledProperty()
655 // can be true because of SuppressScreenReader before init
656 if(!mAccessibilityStatusClient)
661 mAccessibilityStatusClient.property<bool>("ScreenReaderEnabled").asyncGet([this](DBus::ValueOrError<bool> msg) {
664 DALI_LOG_ERROR("Get ScreenReaderEnabled property error: %s\n", msg.getError().message.c_str());
665 if(msg.getError().errorType == DBus::ErrorType::INVALID_REPLY)
667 if(!mReadScreenReaderEnabledTimer)
669 mReadScreenReaderEnabledTimer = Dali::Timer::New(RETRY_INTERVAL);
670 mReadScreenReaderEnabledTimer.TickSignal().Connect(this, &BridgeImpl::ReadScreenReaderEnabledTimerCallback);
672 mReadScreenReaderEnabledTimer.Start();
677 if(mReadScreenReaderEnabledTimer)
679 mReadScreenReaderEnabledTimer.Stop();
680 mReadScreenReaderEnabledTimer.Reset();
683 mIsScreenReaderEnabled = std::get<0>(msg);
688 void EmitScreenReaderEnabledSignal()
690 if(mIsScreenReaderEnabled)
692 mScreenReaderEnabledSignal.Emit();
696 mScreenReaderDisabledSignal.Emit();
700 void ListenScreenReaderEnabledProperty()
702 mAccessibilityStatusClient.addPropertyChangedEvent<bool>("ScreenReaderEnabled", [this](bool res) {
703 mIsScreenReaderEnabled = res;
704 EmitScreenReaderEnabledSignal();
709 void ReadAndListenProperties()
711 ReadIsEnabledProperty();
712 ListenIsEnabledProperty();
714 ReadScreenReaderEnabledProperty();
715 ListenScreenReaderEnabledProperty();
718 bool InitializeAccessibilityStatusClient()
720 mAccessibilityStatusClient = DBus::DBusClient{A11yDbusName, A11yDbusPath, A11yDbusStatusInterface, DBus::ConnectionType::SESSION};
722 if(!mAccessibilityStatusClient)
724 DALI_LOG_ERROR("Accessibility Status DbusClient is not ready\n");
731 bool InitializeTimerCallback()
733 if(InitializeAccessibilityStatusClient())
735 ReadAndListenProperties();
743 if(InitializeAccessibilityStatusClient())
745 ReadAndListenProperties();
746 mIdleCallback = NULL;
750 if(!mInitializeTimer)
752 mInitializeTimer = Dali::Timer::New(RETRY_INTERVAL);
753 mInitializeTimer.TickSignal().Connect(this, &BridgeImpl::InitializeTimerCallback);
755 mInitializeTimer.Start();
757 mIdleCallback = NULL;
762 * @copydoc Dali::Accessibility::Bridge::Initialize()
764 void Initialize() override
766 if(InitializeAccessibilityStatusClient())
768 ReadAndListenProperties();
772 // Initialize failed. Try it again on Idle
773 if(Dali::Adaptor::IsAvailable())
775 Dali::Adaptor& adaptor = Dali::Adaptor::Get();
776 if(NULL == mIdleCallback)
778 mIdleCallback = MakeCallback(this, &BridgeImpl::OnIdleSignal);
779 if(DALI_UNLIKELY(!adaptor.AddIdle(mIdleCallback, true)))
781 DALI_LOG_ERROR("Fail to add idle callback for bridge initialize. Call it synchronously.\n");
789 * @copydoc Dali::Accessibility::Bridge::GetScreenReaderEnabled()
791 bool GetScreenReaderEnabled() override
793 return mIsScreenReaderEnabled;
797 * @copydoc Dali::Accessibility::Bridge::IsEnabled()
799 bool IsEnabled() override
804 Address EmbedSocket(const Address& plug, const Address& socket) override
806 auto client = CreateSocketClient(socket);
807 auto reply = client.method<Address(Address)>("Embed").call(plug);
811 DALI_LOG_ERROR("Failed to embed socket %s: %s", socket.ToString().c_str(), reply.getError().message.c_str());
815 return std::get<0>(reply.getValues());
818 void EmbedAtkSocket(const Address& plug, const Address& socket) override
820 auto client = CreateSocketClient(socket);
822 client.method<void(std::string)>("Embedded").asyncCall([](DBus::ValueOrError<void>) {}, ATSPI_PREFIX_PATH + plug.GetPath());
825 void UnembedSocket(const Address& plug, const Address& socket) override
827 auto client = CreateSocketClient(socket);
829 client.method<void(Address)>("Unembed").asyncCall([](DBus::ValueOrError<void>) {}, plug);
832 void SetSocketOffset(ProxyAccessible* socket, std::int32_t x, std::int32_t y) override
834 AddCoalescableMessage(CoalescableMessages::SET_OFFSET, socket, 1.0f, [=]() {
835 auto client = CreateSocketClient(socket->GetAddress());
837 client.method<void(std::int32_t, std::int32_t)>("SetOffset").asyncCall([](DBus::ValueOrError<void>) {}, x, y);
841 void SetExtentsOffset(std::int32_t x, std::int32_t y) override
845 mData->mExtentsOffset = {x, y};
849 void SetPreferredBusName(std::string_view preferredBusName) override
851 if(preferredBusName == mPreferredBusName)
856 std::string oldPreferredBusName = std::move(mPreferredBusName);
857 mPreferredBusName = std::string{preferredBusName};
861 ReleaseBusName(oldPreferredBusName);
862 RequestBusName(mPreferredBusName);
864 // else: request/release will be handled by ForceUp/ForceDown, respectively
868 DBus::DBusClient CreateSocketClient(const Address& socket)
870 return {socket.GetBus(), ATSPI_PREFIX_PATH + socket.GetPath(), Accessible::GetInterfaceName(AtspiInterface::SOCKET), mConnectionPtr};
873 void RequestBusName(const std::string& busName)
880 DBus::requestBusName(mConnectionPtr, busName);
883 void ReleaseBusName(const std::string& busName)
890 DBus::releaseBusName(mConnectionPtr, busName);
894 namespace // unnamed namespace
896 bool INITIALIZED_BRIDGE = false;
899 * @brief Creates BridgeImpl instance.
901 * @return The BridgeImpl instance
902 * @note This method is to check environment variable first. If ATSPI is disable using env, it returns dummy bridge instance.
904 std::shared_ptr<Bridge> CreateBridge()
906 INITIALIZED_BRIDGE = true;
910 /* check environment variable first */
911 const char* envAtspiDisabled = Dali::EnvironmentVariable::GetEnvironmentVariable(DALI_ENV_DISABLE_ATSPI);
912 if(envAtspiDisabled && std::atoi(envAtspiDisabled) != 0)
914 return Dali::Accessibility::DummyBridge::GetInstance();
917 return std::make_shared<BridgeImpl>();
919 catch(const std::exception&)
921 DALI_LOG_ERROR("Failed to initialize AT-SPI bridge");
922 return Dali::Accessibility::DummyBridge::GetInstance();
926 } // unnamed namespace
928 // Dali::Accessibility::Bridge class implementation
930 std::shared_ptr<Bridge> Bridge::GetCurrentBridge()
932 static std::shared_ptr<Bridge> bridge;
938 else if(mAutoInitState == AutoInitState::ENABLED)
940 bridge = CreateBridge();
942 /* check environment variable for suppressing screen-reader */
943 const char* envSuppressScreenReader = Dali::EnvironmentVariable::GetEnvironmentVariable(DALI_ENV_SUPPRESS_SCREEN_READER);
944 if(envSuppressScreenReader && std::atoi(envSuppressScreenReader) != 0)
946 bridge->SuppressScreenReader(true);
952 return Dali::Accessibility::DummyBridge::GetInstance();
955 void Bridge::DisableAutoInit()
957 if(INITIALIZED_BRIDGE)
959 DALI_LOG_ERROR("Bridge::DisableAutoInit() called after bridge auto-initialization");
962 mAutoInitState = AutoInitState::DISABLED;
965 void Bridge::EnableAutoInit()
967 mAutoInitState = AutoInitState::ENABLED;
969 if(INITIALIZED_BRIDGE)
974 auto rootLayer = Dali::Stage::GetCurrent().GetRootLayer(); // A root layer of the default window.
975 auto window = Dali::DevelWindow::Get(rootLayer);
976 auto applicationName = Dali::Internal::Adaptor::Adaptor::GetApplicationPackageName();
978 auto accessible = Accessibility::Accessible::Get(rootLayer);
980 auto bridge = Bridge::GetCurrentBridge();
981 bridge->AddTopLevelWindow(accessible);
982 bridge->SetApplicationName(applicationName);
983 bridge->Initialize();
985 if(window && window.IsVisible())
987 bridge->WindowShown(window);
991 std::string Bridge::MakeBusNameForWidget(std::string_view widgetInstanceId)
993 // The bus name should consist of dot-separated alphanumeric elements, e.g. "com.example.BusName123".
994 // Allowed characters in each element: "[A-Z][a-z][0-9]_", but no element may start with a digit.
996 static const char prefix[] = "com.samsung.dali.widget_";
997 static const char underscore = '_';
999 std::stringstream tmp;
1003 for(char ch : widgetInstanceId)
1005 tmp << (std::isalnum(ch) ? ch : underscore);