From 20abe349f228817dbaaa95609373896a3cabe921 Mon Sep 17 00:00:00 2001 From: Bartlomiej Grzelewski Date: Thu, 24 Sep 2020 12:34:32 +0200 Subject: [PATCH] [AT-SPI] Add TestDBusWrapper and bypass compilation problems Change-Id: I6780a0f888782df64ac04df2cf8f7d4d2d64192b --- .../src/dali-toolkit-internal/CMakeLists.txt | 6 + .../accessibility-test-utils.cpp | 6 +- .../dali-toolkit-test-utils/dbus-wrapper.cpp | 1085 +++++++ .../dali-toolkit-test-utils/dbus-wrapper.h | 3330 ++++++++++++++++++++ .../utc-Dali-Accessibility-Accessible.cpp | 3 + .../utc-Dali-Accessibility-Controls-BridgeUp.cpp | 3 + .../utc-Dali-Accessibility-Controls.cpp | 3 + .../utc-Dali-Accessibility-Text.cpp | 3 + .../utc-Dali-Accessibility-Value.cpp | 3 + .../utc-Dali-ColorConversion.cpp | 3 + .../utc-Dali-DebugRendering.cpp | 4 + .../utc-Dali-PropertyHelper.cpp | 3 + .../utc-Dali-TextSelectionPopup-internal.cpp | 3 + .../utc-Dali-TextureManager.cpp | 4 +- .../toolkit-test-application.h | 2 +- 15 files changed, 4458 insertions(+), 3 deletions(-) create mode 100644 automated-tests/src/dali-toolkit-internal/dali-toolkit-test-utils/dbus-wrapper.cpp create mode 100644 automated-tests/src/dali-toolkit-internal/dali-toolkit-test-utils/dbus-wrapper.h diff --git a/automated-tests/src/dali-toolkit-internal/CMakeLists.txt b/automated-tests/src/dali-toolkit-internal/CMakeLists.txt index da82ae6..7aea8eb 100755 --- a/automated-tests/src/dali-toolkit-internal/CMakeLists.txt +++ b/automated-tests/src/dali-toolkit-internal/CMakeLists.txt @@ -82,14 +82,20 @@ LIST(APPEND TC_SOURCES dali-toolkit-test-utils/dummy-visual.cpp dali-toolkit-test-utils/test-addon-manager.cpp dali-toolkit-test-utils/accessibility-test-utils.cpp + dali-toolkit-test-utils/dbus-wrapper.cpp ) PKG_CHECK_MODULES(${CAPI_LIB} REQUIRED dali2-core dali2-adaptor dali2-toolkit + eldbus + ecore-input + eina ) +MESSAGE("Libraries to link with:>${${CAPI_LIB}_LIBRARIES}") + ADD_COMPILE_OPTIONS( -O0 -ggdb --coverage -Wall -Werror -DDEBUG_ENABLED -fPIC ) ADD_COMPILE_OPTIONS( ${${CAPI_LIB}_CFLAGS_OTHER} ) diff --git a/automated-tests/src/dali-toolkit-internal/dali-toolkit-test-utils/accessibility-test-utils.cpp b/automated-tests/src/dali-toolkit-internal/dali-toolkit-test-utils/accessibility-test-utils.cpp index 39433b4..0f110bc 100644 --- a/automated-tests/src/dali-toolkit-internal/dali-toolkit-test-utils/accessibility-test-utils.cpp +++ b/automated-tests/src/dali-toolkit-internal/dali-toolkit-test-utils/accessibility-test-utils.cpp @@ -1,5 +1,8 @@ #include #include +#include +#include +#include "dbus-wrapper.h" namespace Dali { namespace Accessibility { @@ -14,7 +17,8 @@ namespace Dali { auto bridge = Accessibility::Bridge::GetCurrentBridge(); Dali::Stage stage = Dali::Stage::GetCurrent(); auto accessible = Accessibility::Accessible::Get( stage.GetRootLayer() ); - bridge->SetApplicationChild( accessible ); +// bridge->SetApplicationChild( accessible ); // BART + bridge->AddTopLevelWindow( accessible ); // BART bridge->SetApplicationName( "TestApp" ); bridge->Initialize(); diff --git a/automated-tests/src/dali-toolkit-internal/dali-toolkit-test-utils/dbus-wrapper.cpp b/automated-tests/src/dali-toolkit-internal/dali-toolkit-test-utils/dbus-wrapper.cpp new file mode 100644 index 0000000..d59df19 --- /dev/null +++ b/automated-tests/src/dali-toolkit-internal/dali-toolkit-test-utils/dbus-wrapper.cpp @@ -0,0 +1,1085 @@ +/* + * Copyright 2019 Samsung Electronics Co., Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// CLASS HEADER +#include "dbus-wrapper.h" + +//#include + +// EXTERNAL INCLUDES +#include +#include +#include + +#include +#include + +// INTERNAL INCLUDES +#include + +#define DBUS_INTERFACE_PROPERTIES "org.freedesktop.DBus.Properties" + +#undef EINA_FALSE +#undef EINA_TRUE +#define EINA_TRUE static_cast< Eina_Bool >( 1 ) +#define EINA_FALSE static_cast< Eina_Bool >( 0 ) + +//#define DBUS_DEBUG(...) do { DBus::debugPrint(__FILE__, __LINE__, __VA_ARGS__); } while (0) + +// BART QUICK HACK +#undef assert +#define assert(x) do{}while(0) + +std::atomic< unsigned int > DBus::detail::CallId::LastId{0}; +static std::function< void( const char*, size_t ) > debugPrinter; +static std::mutex debugLock; + +thread_local std::string DBus::DBusServer::currentObjectPath; +thread_local DBusWrapper::ConnectionPtr DBus::DBusServer::currentConnection; + +void DBus::setDebugPrinter( std::function< void( const char*, size_t ) > printer ) +{ + std::lock_guard< std::mutex > lock( debugLock ); + debugPrinter = std::move( printer ); +} + +void DBus::debugPrint( const char* file, size_t line, const char* format, ... ) +{ + std::function< void( const char*, size_t ) > debugPrintFunc; + { + std::lock_guard< std::mutex > lock( debugLock ); + if( !debugPrinter ) + return; + debugPrintFunc = debugPrinter; + } + std::vector< char > buf( 4096 ); + int offset; + while( true ) + { + offset = snprintf( buf.data(), buf.size(), "%s:%u: ", file, static_cast< unsigned int >( line ) ); + if( offset < 0 ) + return; + if( static_cast< size_t >( offset ) < buf.size() ) + break; + buf.resize( offset + 1 ); + } + + while( true ) + { + va_list args; + va_start( args, format ); + int z = vsnprintf( buf.data() + offset, buf.size() - offset, format, args ); + va_end( args ); + if( z < 0 ) + return; + bool done = static_cast< size_t >( z ) + static_cast< size_t >( offset ) < buf.size(); + buf.resize( static_cast< size_t >( z ) + static_cast< size_t >( offset ) ); + if( done ) + break; + } + debugPrintFunc( buf.data(), buf.size() ); +} + +DBusWrapper::ConnectionPtr DBus::getDBusConnectionByName( const std::string& name ) +{ + return DBUS_W->eldbus_address_connection_get_impl( name ); +} + +DBusWrapper::ConnectionPtr DBus::getDBusConnectionByType( ConnectionType connectionType ) +{ + return DBUS_W->eldbus_connection_get_impl(connectionType); +} + +DBus::DBusClient::DBusClient( std::string busName, std::string pathName, std::string interfaceName, ConnectionType tp ) : DBusClient( std::move( busName ), std::move( pathName ), std::move( interfaceName ), getDBusConnectionByType( tp ) ) +{ +} + +DBus::DBusClient::DBusClient( std::string busName, std::string pathName, std::string interfaceName, const DBusWrapper::ConnectionPtr &conn ) +{ + if( !conn ) + connectionState->connection = getDBusConnectionByType( ConnectionType::SESSION ); + else + connectionState->connection = conn; + + std::ostringstream o; + o << "bus = " << busName << " path = " << pathName << " connection = " << DBUS_W->eldbus_connection_unique_name_get_impl( connectionState->connection ); + info = o.str(); + + connectionState->object = DBUS_W->eldbus_object_get_impl( connectionState->connection, busName.c_str(), pathName.c_str() ); + if( connectionState->object ) + { + connectionState->proxy = DBUS_W->eldbus_proxy_get_impl( connectionState->object, interfaceName ); + if( interfaceName != DBUS_INTERFACE_PROPERTIES ) + { + connectionState->propertiesProxy = DBUS_W->eldbus_proxy_get_impl( connectionState->object, DBUS_INTERFACE_PROPERTIES ); + } + else + { + connectionState->propertiesProxy = DBUS_W->eldbus_proxy_copy_impl( connectionState->proxy ); + } + } + connectionInfo = std::make_shared< ConnectionInfo >(); + connectionInfo->busName = std::move( busName ); + connectionInfo->pathName = std::move( pathName ); + connectionInfo->interfaceName = std::move( interfaceName ); +} + +DBus::DBusServer::DBusServer( ConnectionType tp ) : DBus::DBusServer( DBus::getDBusConnectionByType( tp ) ) +{ +} + +DBus::DBusServer::DBusServer( const DBusWrapper::ConnectionPtr &conn ) +{ + if( !conn ) + connection = getDBusConnectionByType( ConnectionType::SESSION ); + else + connection = conn; +} + +DBus::DBusInterfaceDescription::DBusInterfaceDescription( std::string interfaceName ) : interfaceName( std::move( interfaceName ) ) +{ +} + +void DBus::DBusServer::addInterface( const std::string& pathName, DBusInterfaceDescription& dscr, bool fallback ) +{ + DBUS_W->add_interface_impl( fallback, pathName, connection, destructorObject->destructors, dscr.interfaceName, dscr.methods, dscr.properties, dscr.signals ); +} + +std::string DBus::DBusServer::getBusName() const +{ + return getConnectionName( connection ); +} + +std::string DBus::getConnectionName( const DBusWrapper::ConnectionPtr &c ) +{ + return DBUS_W->eldbus_connection_unique_name_get_impl( c ); +} + +bool DBus::DBusClient::getFromEinaValue(const _Eina_Value *v, void *dst) +{ + return eina_value_get(const_cast(v), dst); +} + +/// \cond +static std::unique_ptr InstalledWrapper; + +struct DefaultDBusWrapper : public DBusWrapper { + constexpr static int ELDBUS_CALL_TIMEOUT = 1000; + + DefaultDBusWrapper() { + } + ~DefaultDBusWrapper() { + } + #define DEFINE_GS(name, eldbus_name, unref_call) \ + static eldbus_name *get(const std::shared_ptr &a) { \ + return static_cast(a.get())->Value; \ + } \ + static eldbus_name *release(const std::shared_ptr &a) { \ + auto z = static_cast(a.get())->Value; \ + static_cast(a.get())->Value = nullptr; \ + return z; \ + } \ + template static std::shared_ptr create(const eldbus_name *v, ARGS && ... args) { \ + return std::make_shared(const_cast(v), std::forward(args)...); \ + } + + #define DEFINE_TYPE(name, eldbus_name, unref_call) \ + struct name ## Impl : public name { \ + eldbus_name *Value = nullptr; \ + bool EraseOnExit = false; \ + name ## Impl(eldbus_name *Value, bool EraseOnExit = false) : Value(Value), EraseOnExit(EraseOnExit) { } \ + ~name ## Impl() { \ + if (EraseOnExit && Value) { unref_call; } \ + } \ + }; \ + DEFINE_GS(name, eldbus_name, unref_call) + + struct ConnectionImpl : public Connection { + Eldbus_Connection *Value = nullptr; + bool EraseOnExit = false; + ConnectionImpl(Eldbus_Connection *Value, bool EraseOnExit = false) : Value(Value), EraseOnExit(EraseOnExit) { + ecore_event_init(); + eldbus_init(); + } + ~ConnectionImpl() { + if (EraseOnExit && Value) { + eldbus_connection_unref( Value ); + } + eldbus_shutdown(); + ecore_event_shutdown(); + } + }; + struct MessageIterImpl : public MessageIter { + Eldbus_Message_Iter *Value = nullptr, *Parent = nullptr; + bool EraseOnExit = false; + MessageIterImpl(Eldbus_Message_Iter *Value, Eldbus_Message_Iter *Parent, bool EraseOnExit = false) : Value(Value), Parent(Parent), EraseOnExit(EraseOnExit) { } + ~MessageIterImpl() { + if (EraseOnExit && Value && Parent) + eldbus_message_iter_container_close(Parent, Value); + } + }; + + DEFINE_GS(Connection, Eldbus_Connection, ) + DEFINE_GS(MessageIter, Eldbus_Message_Iter, ) + DEFINE_TYPE(Message, Eldbus_Message, eldbus_message_unref( Value )) + DEFINE_TYPE(Proxy, Eldbus_Proxy, ) + DEFINE_TYPE(Object, Eldbus_Object, eldbus_object_unref( Value )) + DEFINE_TYPE(Pending, Eldbus_Pending, ) + DEFINE_TYPE(EventPropertyChanged, Eldbus_Proxy_Event_Property_Changed, ) + #undef DEFINE_TYPE + + std::shared_ptr eldbus_address_connection_get_impl(const std::string &addr) override { + eldbus_init(); + auto p = eldbus_address_connection_get(addr.c_str()); + auto w = create(p, true); + eldbus_shutdown(); + return w; + } + +#define eldbus_message_iter_arguments_append_impl_basic(type, sig) \ + void eldbus_message_iter_arguments_append_impl(const MessageIterPtr &it, type src) override { \ + eldbus_message_iter_arguments_append( get(it), #sig, src ); \ + } \ + bool eldbus_message_iter_get_and_next_impl(const MessageIterPtr &it, type &dst) override { \ + return eldbus_message_iter_get_and_next( get(it), (#sig)[0], &dst ); \ + } + + eldbus_message_iter_arguments_append_impl_basic(uint8_t, y) + eldbus_message_iter_arguments_append_impl_basic(uint16_t, q) + eldbus_message_iter_arguments_append_impl_basic(uint32_t, u) + eldbus_message_iter_arguments_append_impl_basic(uint64_t, t) + eldbus_message_iter_arguments_append_impl_basic(int16_t, n) + eldbus_message_iter_arguments_append_impl_basic(int32_t, i) + eldbus_message_iter_arguments_append_impl_basic(int64_t, x) + eldbus_message_iter_arguments_append_impl_basic(double, d) + +#undef eldbus_message_iter_arguments_append_impl_basic + + void eldbus_message_iter_arguments_append_impl(const MessageIterPtr &it, bool src) override { + eldbus_message_iter_arguments_append( get(it), "b", src ? 1 : 0 ); + } + bool eldbus_message_iter_get_and_next_impl(const MessageIterPtr &it, bool &dst) override { + unsigned char q; + auto z = eldbus_message_iter_get_and_next( get(it), 'b', &q ); + dst = q != 0; + return z; + } + void eldbus_message_iter_arguments_append_impl(const MessageIterPtr &it, const std::string &src) override { + eldbus_message_iter_arguments_append( get(it), "s", src.c_str() ); + } + bool eldbus_message_iter_get_and_next_impl(const MessageIterPtr &it, std::string &dst) override { + auto iter = get(it); + const char* q; + if( !eldbus_message_iter_get_and_next( iter, 's', &q ) ) + { + if( !eldbus_message_iter_get_and_next( iter, 'o', &q ) ) + return false; + } + dst = q; + return true; + } + void eldbus_message_iter_arguments_append_impl(const MessageIterPtr &it, const ObjectPath &src) override { + eldbus_message_iter_arguments_append( get(it), "o", src.value.c_str() ); + } + bool eldbus_message_iter_get_and_next_impl(const MessageIterPtr &it, ObjectPath &dst) override { + const char* q; + if( !eldbus_message_iter_get_and_next( get(it), 'o', &q ) ) + return false; + dst.value = q; + return true; + } + + MessageIterPtr eldbus_message_iter_container_new_impl(const MessageIterPtr &it, int type, const std::string &sig) override { + auto z = eldbus_message_iter_container_new( get(it), type, !sig.empty() ? sig.c_str() : NULL ); + return create(z, get(it), true); + } + MessageIterPtr eldbus_message_iter_get_and_next_by_type_impl(const MessageIterPtr &it, int type) override { + Eldbus_Message_Iter* entry; + if (!eldbus_message_iter_get_and_next( get(it), type, &entry ) ) return {}; + return create(entry, get(it), false); + } + MessageIterPtr eldbus_message_iter_get_impl(const MessagePtr &msg, bool) override { + return create(eldbus_message_iter_get(get(msg)), nullptr, false); + } + MessagePtr eldbus_proxy_method_call_new_impl(const ProxyPtr &proxy, const std::string &funcName) { + return create(eldbus_proxy_method_call_new( get(proxy), funcName.c_str() ) ); + } + MessagePtr eldbus_proxy_send_and_block_impl(const ProxyPtr &proxy, const MessagePtr &msg) override { + return create(eldbus_proxy_send_and_block(get(proxy), release(msg), ELDBUS_CALL_TIMEOUT) ); + } + bool eldbus_message_error_get_impl(const MessagePtr &msg, std::string &name, std::string &text) override { + const char *errname, *errmsg; + if( eldbus_message_error_get( get(msg), &errname, &errmsg ) ) { + name = errname; + text = errmsg; + return true; + } + return false; + } + std::string eldbus_message_signature_get_impl(const MessagePtr &msg) override { + return eldbus_message_signature_get(get(msg)); + } + + static void callAsyncCb( void* data, const Eldbus_Message *msg, Eldbus_Pending *pending ) + { + auto d = static_cast< SendCallback* >( data ); + (*d)( create(msg, false) ); + } + static void pendingFreeCb( void* data, const void* ) + { + auto d = static_cast< SendCallback* >( data ); + delete d; + } + static void listenerCallbackFree( void* data, const void* ) + { + auto d = static_cast< std::function< void( const Eldbus_Message* msg ) >* >( data ); + delete d; + } + + PendingPtr eldbus_proxy_send_impl(const ProxyPtr &proxy, const MessagePtr &msg, const SendCallback &callback) override { + auto cb = new SendCallback{ callback }; + auto pending = eldbus_proxy_send( get(proxy), release(msg), callAsyncCb, cb, ELDBUS_CALL_TIMEOUT ); + if( pending ) + { + eldbus_pending_free_cb_add( pending, pendingFreeCb, cb ); + } + else + { + delete cb; + } + return create(pending, false); + } + std::string eldbus_proxy_interface_get_impl(const ProxyPtr &proxy) override { + return eldbus_proxy_interface_get( get(proxy) ); + } + static void listenerCallback( void* data, const Eldbus_Message* msg ) + { + auto p = static_cast< std::function< void( const Eldbus_Message* msg ) >* >( data ); + ( *p )( msg ); + } + void eldbus_proxy_signal_handler_add_impl(const ProxyPtr &proxy, const std::string &member, const std::function &cb) override { + auto tmp = new std::function< void( const Eldbus_Message* msg ) >{ + [cb](const Eldbus_Message* msg) { + cb(create(msg, false)); + } + }; + auto handler = eldbus_proxy_signal_handler_add( get(proxy), member.c_str(), listenerCallback, tmp ); + if( handler ) + eldbus_proxy_free_cb_add( get(proxy), listenerCallbackFree, tmp ); + else + delete tmp; + } + std::string eldbus_message_iter_signature_get_impl(const MessageIterPtr &iter) override { + return eldbus_message_iter_signature_get( get(iter) ); + } + MessagePtr eldbus_message_method_return_new_impl( const MessagePtr &msg) override { + return create(eldbus_message_method_return_new( get(msg) ) ); + } + MessagePtr eldbus_message_error_new_impl( const MessagePtr &msg, const std::string &err, const std::string &txt ) override { + return create(eldbus_message_error_new( get(msg), err.c_str(), txt.c_str() ) ); + } + PendingPtr eldbus_connection_send_impl(const ConnectionPtr &conn, const MessagePtr &msg) override { + return create(eldbus_connection_send( get(conn), get(msg), NULL, NULL, -1 ) ); + } + MessagePtr eldbus_message_signal_new_impl(const std::string &path, const std::string &iface, const std::string &name) override { + return create(eldbus_message_signal_new( path.c_str(), iface.c_str(), name.c_str() ) ); + } + MessagePtr eldbus_message_ref_impl(const MessagePtr &msg) override { + return create(eldbus_message_ref( get(msg) ), true ); + } + ConnectionPtr eldbus_connection_get_impl(ConnectionType type) override { + Eldbus_Connection_Type eldbusType = ELDBUS_CONNECTION_TYPE_SYSTEM; + + switch( type ) + { + case ConnectionType::SYSTEM: + { + eldbusType = ELDBUS_CONNECTION_TYPE_SYSTEM; + break; + } + case ConnectionType::SESSION: + { + eldbusType = ELDBUS_CONNECTION_TYPE_SESSION; + break; + } + } + + eldbus_init(); + auto p = eldbus_connection_get( eldbusType ); + auto w = create(p, true); + eldbus_shutdown(); + return w; + } + std::string eldbus_connection_unique_name_get_impl(const ConnectionPtr &conn) override { + return eldbus_connection_unique_name_get( get(conn) ); + } + ObjectPtr eldbus_object_get_impl( const ConnectionPtr &conn, const std::string &bus, const std::string &path ) override { + return create(eldbus_object_get(get(conn), bus.c_str(), path.c_str() ), true ); + } + ProxyPtr eldbus_proxy_get_impl( const ObjectPtr &obj, const std::string &interface ) override { + return create(eldbus_proxy_get(get(obj), interface.c_str() ), false ); + } + ProxyPtr eldbus_proxy_copy_impl( const ProxyPtr &ptr) override { + return create(get(ptr), false ); + } + + + + + + + struct Implementation + { + Eldbus_Service_Interface_Desc dsc; + std::vector< std::vector< Eldbus_Arg_Info > > argsInfos; + std::vector< Eldbus_Method > methods; + std::vector< Eldbus_Signal > signals; + std::vector< Eldbus_Property > properties; + + std::unordered_map< std::string, DBusWrapper::MethodInfo > methodsMap; + std::unordered_map< std::string, DBusWrapper::PropertyInfo > propertiesMap; + std::unordered_map< unsigned int, DBusWrapper::SignalInfo > signalsMap; + + DBusWrapper::ConnectionWeakPtr connection; + }; + + static std::unordered_map< const Eldbus_Service_Interface*, std::unique_ptr< Implementation > > globalEntries; + static std::mutex globalEntriesMutex; + +#undef EINA_FALSE +#undef EINA_TRUE +#define EINA_FALSE static_cast(0) +#define EINA_TRUE static_cast(1) + + static Eina_Bool property_get_callback( const Eldbus_Service_Interface* iface, const char* propertyName, Eldbus_Message_Iter* iter, + const Eldbus_Message* message, Eldbus_Message** error ) + { + Implementation* impl = nullptr; + { + std::lock_guard< std::mutex > lock( globalEntriesMutex ); + auto it = globalEntries.find( iface ); + if( it != globalEntries.end() ) + impl = it->second.get(); + } + if( !impl ) + return EINA_FALSE; + + auto it = impl->propertiesMap.find( propertyName ); + if( it == impl->propertiesMap.end() || !it->second.getCallback ) + return EINA_FALSE; + + auto connection = impl->connection.lock(); + if( !connection ) + return EINA_FALSE; + + DBus::DBusServer::CurrentObjectSetter currentObjectSetter( connection, eldbus_message_path_get( message ) ); + auto reply = it->second.getCallback( create(message, false), create(iter, nullptr, false) ); + if( !reply.empty() ) + { + if( error ) + *error = eldbus_message_error_new( message, "org.freedesktop.DBus.Error.Failed", reply.c_str() ); + return EINA_FALSE; + } + + return EINA_TRUE; + } + + static Eldbus_Message* property_set_callback( const Eldbus_Service_Interface* iface, const char* propertyName, Eldbus_Message_Iter* iter, + const Eldbus_Message* message ) + { + Implementation* impl = nullptr; + { + std::lock_guard< std::mutex > lock( globalEntriesMutex ); + auto it = globalEntries.find( iface ); + if( it != globalEntries.end() ) + impl = it->second.get(); + } + if( !impl ) + { + auto ret = eldbus_message_error_new( message, "org.freedesktop.DBus.Error.Failed", "Unknown interface" ); + return ret; + } + auto it = impl->propertiesMap.find( propertyName ); + if( it == impl->propertiesMap.end() || !it->second.setCallback ) + { + auto ret = eldbus_message_error_new( message, "org.freedesktop.DBus.Error.Failed", "Unknown setter" ); + return ret; + } + auto connection = impl->connection.lock(); + if( !connection ) + { + auto ret = eldbus_message_error_new( message, "org.freedesktop.DBus.Error.Failed", "Connection lost" ); + return ret; + } + + DBus::DBusServer::CurrentObjectSetter currentObjectSetter( connection, eldbus_message_path_get( message ) ); + auto reply = it->second.setCallback( create(message, false), create(iter, nullptr, false) ); + + Eldbus_Message* ret = nullptr; + if( !reply.empty() ) + { + ret = eldbus_message_error_new( message, "org.freedesktop.DBus.Error.Failed", reply.c_str() ); + } + else + { + ret = eldbus_message_method_return_new( message ); + } + return ret; + } + + static Eldbus_Message* method_callback( const Eldbus_Service_Interface* iface, const Eldbus_Message* message ) + { + Implementation* impl = nullptr; + { + std::lock_guard< std::mutex > lock( globalEntriesMutex ); + auto it = globalEntries.find( iface ); + if( it != globalEntries.end() ) + impl = it->second.get(); + } + if( !impl ) + { + auto ret = eldbus_message_error_new( message, "org.freedesktop.DBus.Error.Failed", "Unknown interface" ); + return ret; + } + std::string memberName = eldbus_message_member_get( message ); + auto it = impl->methodsMap.find( memberName ); + if( it == impl->methodsMap.end() ) + { + auto ret = eldbus_message_error_new( message, "org.freedesktop.DBus.Error.Failed", "Unknown method" ); + return ret; + } + auto connection = impl->connection.lock(); + if( !connection ) + { + auto ret = eldbus_message_error_new( message, "org.freedesktop.DBus.Error.Failed", "Connection lost" ); + return ret; + } + DBus::DBusServer::CurrentObjectSetter currentObjectSetter( connection, eldbus_message_path_get( message ) ); + auto reply = it->second.callback( create(message) ); + return release(reply); + } + + void add_interface_impl( bool fallback, const std::string& pathName, + const ConnectionPtr &connection, + std::vector> &destructors, + const std::string& interfaceName, + std::vector< MethodInfo >& dscrMethods, + std::vector< PropertyInfo >& dscrProperties, + std::vector< SignalInfo >& dscrSignals ) override + { + std::vector< Eldbus_Method > methods; + std::vector< Eldbus_Signal > signals; + std::vector< Eldbus_Property > properties; + std::vector< std::vector< Eldbus_Arg_Info > > argsInfos; + std::unordered_map< std::string, DBus::DBusInterfaceDescription::MethodInfo > methodsMap; + std::unordered_map< std::string, DBus::DBusInterfaceDescription::PropertyInfo > propertiesMap; + std::unordered_map< unsigned int, DBus::DBusInterfaceDescription::SignalInfo > signalsMap; + + DBUS_DEBUG( "interface %s path %s on bus %s", interfaceName.c_str(), pathName.c_str(), DBus::getConnectionName( connection ).c_str() ); + auto makeArgInfo = [&](const std::vector> &input) { + argsInfos.push_back({}); + auto &dst = argsInfos.back(); + for(auto &s : input) { + auto a = Strings.add(s.first); + auto b = Strings.add(s.second); + dst.push_back({ a, b }); + } + dst.push_back({ nullptr, nullptr }); + return dst.data(); + }; + for( auto& ee : dscrMethods ) + { + auto key = ee.memberName; + DBUS_DEBUG( "adding method %s", ee.memberName.c_str() ); + for( auto& r : ee.in ) + { + DBUS_DEBUG( "in %s '%s'", r.first.c_str(), r.second.c_str() ); + } + for( auto& r : ee.out ) + { + DBUS_DEBUG( "out %s '%s'", r.first.c_str(), r.second.c_str() ); + } + auto& e = ( methodsMap[key] = std::move( ee ) ); + methods.push_back( {} ); + auto& m = methods.back(); + m.member = e.memberName.c_str(); + m.in = makeArgInfo(e.in); + m.out = makeArgInfo(e.out); + m.cb = method_callback; + m.flags = 0; + } + for( auto& ee : dscrProperties ) + { + auto key = ee.memberName; + DBUS_DEBUG( "adding property %s", ee.memberName.c_str() ); + auto& e = ( propertiesMap[key] = std::move( ee ) ); + properties.push_back( {} ); + auto& m = properties.back(); + m.name = e.memberName.c_str(); + m.type = e.typeSignature.c_str(); + m.get_func = e.getCallback ? property_get_callback : nullptr; + m.set_func = e.setCallback ? property_set_callback : nullptr; + m.flags = 0; + } + dscrMethods.clear(); + dscrProperties.clear(); + dscrSignals.clear(); + + methods.push_back( {nullptr, nullptr, nullptr, nullptr, 0} ); + signals.push_back( {nullptr, nullptr, 0} ); + properties.push_back( {nullptr, nullptr, nullptr, nullptr, 0} ); + + auto impl = std::unique_ptr< Implementation >( new Implementation{ + {interfaceName.c_str(), + methods.data(), + signals.data(), + properties.data(), + nullptr, + nullptr}, + std::move( argsInfos ), + std::move( methods ), + std::move( signals ), + std::move( properties ), + std::move( methodsMap ), + std::move( propertiesMap ), + std::move( signalsMap ), + connection} ); + + { + std::lock_guard< std::mutex > lock( globalEntriesMutex ); + auto v = fallback ? eldbus_service_interface_fallback_register( get(connection), pathName.c_str(), &impl->dsc ) : eldbus_service_interface_register( get(connection), pathName.c_str(), &impl->dsc ); + assert( v ); + globalEntries[v] = std::move( impl ); + DBUS_DEBUG( "registering interface %p (%d)", v, fallback ? 1 : 0 ); + destructors.push_back([=]() { + DBUS_DEBUG( "unregistering interface %p", v ); + eldbus_service_interface_unregister( v ); + std::lock_guard< std::mutex > lock( globalEntriesMutex ); + globalEntries.erase( v ); + }); + } + } + static void listenerEventChangedCallback( void* data, Eldbus_Proxy* proxy EINA_UNUSED, void* event ) + { + auto p = static_cast< std::function< void( Eldbus_Proxy_Event_Property_Changed* ) >* >( data ); + ( *p )( static_cast< Eldbus_Proxy_Event_Property_Changed* >( event ) ); + } + static void ProxyEventCallbackDelCb( void* data, const void *obj ) + { + auto d = static_cast< std::function< void( Eldbus_Proxy_Event_Property_Changed* ) >* >( data ); + delete d; + } + void add_property_changed_event_listener_impl( const ProxyPtr &proxy, const std::string &interface, const std::string &name, std::function< void( const Eina_Value * ) > cb) override { + auto callbackLambdaPtr = new std::function< void( Eldbus_Proxy_Event_Property_Changed *epc ) >{ + [cb, name, interface]( Eldbus_Proxy_Event_Property_Changed *ev ) { + const char* ifc = eldbus_proxy_interface_get( ev->proxy ); + if( ev->name && ev->name == name && ifc && interface == ifc ) + { + cb(ev->value); + } + } }; + auto p = get(proxy); + eldbus_proxy_event_callback_add( p, ELDBUS_PROXY_EVENT_PROPERTY_CHANGED, + listenerEventChangedCallback, callbackLambdaPtr ); + eldbus_proxy_free_cb_add( p, ProxyEventCallbackDelCb, callbackLambdaPtr ); + } +}; + +std::unordered_map< const Eldbus_Service_Interface*, std::unique_ptr< DefaultDBusWrapper::Implementation > > DefaultDBusWrapper::globalEntries; +std::mutex DefaultDBusWrapper::globalEntriesMutex; + +DBusWrapper *DBusWrapper::Installed() +{ + if (!InstalledWrapper) + InstalledWrapper.reset(new DefaultDBusWrapper); + return InstalledWrapper.get(); +} + +void DBusWrapper::Install(std::unique_ptr w) +{ + InstalledWrapper = std::move(w); +} + + +struct TestDBusWrapper::ConnectionImpl : public TestDBusWrapper::Connection { +}; + +TestDBusWrapper::TestDBusWrapper() +{ + eina_init(); + connection = std::make_shared(); + testMethods[std::tuple{"/org/a11y/bus", "org.a11y.Status", "ScreenReaderEnabled", MethodType::Getter}] = + testMethods[std::tuple{"/org/a11y/bus", "org.a11y.Status", "IsEnabled", MethodType::Getter}] = + [this](const DBusWrapper::MessagePtr &m) -> MessagePtr { + auto reply = newReplyMessage(m); + Encode(reply, std::tuple>{ TestDBusWrapper::Variant{ false} }); + return reply; + }; +} +TestDBusWrapper::~TestDBusWrapper() +{ + eina_shutdown(); +} + +TestDBusWrapper::ConnectionPtr TestDBusWrapper::eldbus_address_connection_get_impl(const std::string &addr) +{ + return connection; +} + +struct TestDBusWrapper::MessageIterImpl : public TestDBusWrapper::MessageIter { + MessagePtr msg; + Element *elem; + bool write; + std::list::iterator it; + MessageIterImpl(MessagePtr msg, Element *elem, bool write) : msg(std::move(msg)), elem(elem), write(write) { + if (!write) it = elem->get().begin(); + } + MessageIterImpl(MessagePtr msg, Element *elem, bool write, std::list::iterator it) : msg(std::move(msg)), elem(elem), write(write), it(it) { } +}; + +struct TestDBusWrapper::MessageImpl : public TestDBusWrapper::Message { + std::string bus, path, interface, name; + Element data; + bool reply, error = false; + std::string error_a, error_b; + + MessageImpl(std::string bus, std::string path, std::string interface, std::string name, bool reply) : + bus(std::move(bus)), path(std::move(path)), interface(std::move(interface)), name(std::move(name)), data{ ElementList{} }, reply(reply) { } + MessageImpl(std::string bus, std::string path, std::string interface, std::string name, std::string error_a, std::string error_b) : + bus(std::move(bus)), path(std::move(path)), interface(std::move(interface)), name(std::move(name)), data{ ElementList{} }, + reply(true), error(true), error_a(std::move(error_a)), error_b(std::move(error_b)) { } +}; + +struct TestDBusWrapper::ProxyImpl : public TestDBusWrapper::Proxy { + std::string bus, path, interface; + ProxyImpl(std::string bus, std::string path, std::string interface) : bus(std::move(bus)), path(std::move(path)), interface(std::move(interface)) { } +}; +struct TestDBusWrapper::ObjectImpl : public TestDBusWrapper::Object { + std::string bus, path; + ObjectImpl(std::string bus, std::string path) : bus(std::move(bus)), path(std::move(path)) { } +}; +#define DEFINE_TYPE(name) TestDBusWrapper::name ## Impl *TestDBusWrapper::get(const name ## Ptr &v) { return static_cast(v.get()); } + DEFINE_TYPE(Connection) + DEFINE_TYPE(Proxy) + DEFINE_TYPE(Object) + DEFINE_TYPE(Message) + DEFINE_TYPE(MessageIter) +#undef DEFINE_TYPE + +template void TestDBusWrapperAppendBasic(const TestDBusWrapper::MessageIterPtr &it, T src) { + auto m = TestDBusWrapper::get(it); + if (!m->write) throw TestDBusWrapper::error{}; + if (!m->elem->isContainer()) throw TestDBusWrapper::error{}; + m->elem->get().push_back(TestDBusWrapper::Element{ src }); +} +template bool TestDBusWrapperGetBasicAndNext(const TestDBusWrapper::MessageIterPtr &it, T &src) { + auto m = TestDBusWrapper::get(it); + if (m->write) throw TestDBusWrapper::error{}; + if (!m->elem->isContainer()) return false; + auto &lst = m->elem->get(); + if (m->it == lst.end()) return false; + if (!m->it->is()) return false; + src = m->it->get(); + ++m->it; + return true; +} + +#define eldbus_message_iter_arguments_append_impl_basic_impl(type_set, type_get, sig) \ +void TestDBusWrapper::eldbus_message_iter_arguments_append_impl(const MessageIterPtr &it, type_set src) { \ + TestDBusWrapperAppendBasic(it, src); \ +} \ +bool TestDBusWrapper::eldbus_message_iter_get_and_next_impl(const MessageIterPtr &it, type_get &dst) { \ + return TestDBusWrapperGetBasicAndNext(it, dst); \ +} +#define eldbus_message_iter_arguments_append_impl_basic(type, sig) \ +eldbus_message_iter_arguments_append_impl_basic_impl(type, type, sig) + +eldbus_message_iter_arguments_append_impl_basic(uint8_t, y) +eldbus_message_iter_arguments_append_impl_basic(uint16_t, q) +eldbus_message_iter_arguments_append_impl_basic(uint32_t, u) +eldbus_message_iter_arguments_append_impl_basic(uint64_t, t) +eldbus_message_iter_arguments_append_impl_basic(int16_t, n) +eldbus_message_iter_arguments_append_impl_basic(int32_t, i) +eldbus_message_iter_arguments_append_impl_basic(int64_t, x) +eldbus_message_iter_arguments_append_impl_basic(double, d) +eldbus_message_iter_arguments_append_impl_basic(bool, b) +eldbus_message_iter_arguments_append_impl_basic_impl(const std::string &, std::string, s) +eldbus_message_iter_arguments_append_impl_basic_impl(const ObjectPath &, ObjectPath, o) + +#undef eldbus_message_iter_arguments_append_impl_basic +#undef eldbus_message_iter_arguments_append_impl_basic_impl + +TestDBusWrapper::MessageIterPtr TestDBusWrapper::eldbus_message_iter_container_new_impl(const MessageIterPtr &it, int type, const std::string &sig) { + auto m = get(it); + if (!m->write) throw error{}; + if (!m->elem->isContainer()) throw error{}; + auto &lst = m->elem->get(); + if (type == 'r' || type == 'e' || type == 'a' || type == 'v') { + lst.push_back({ ElementList{}, type }); + return std::make_shared(MessageIterImpl{ m->msg, &lst.back(), true }); + } + throw error{}; +} + +TestDBusWrapper::MessageIterPtr TestDBusWrapper::eldbus_message_iter_get_and_next_by_type_impl(const MessageIterPtr &it, int type) { + auto m = get(it); + if (m->write) throw error{}; + if (!m->elem->isContainer()) throw error{} << "not a container"; + auto &lst = m->elem->get(); + if (m->it == lst.end()) return {}; + if (m->it->signature() != type) return {}; + auto v = std::make_shared(MessageIterImpl{ m->msg, &*m->it, false }); + ++m->it; + return v; +} + +TestDBusWrapper::MessageIterPtr TestDBusWrapper::eldbus_message_iter_get_impl(const MessagePtr &it, bool write) { + auto m = get(it); + return std::make_shared(MessageIterImpl{ it, &m->data, write, m->data.get().begin() }); +} + +bool TestDBusWrapper::completed(const MessageIterPtr &iter) +{ + auto m = get(iter); + assert(!m->write); + return m->it == m->elem->get().end(); +} + +TestDBusWrapper::MessagePtr TestDBusWrapper::eldbus_proxy_method_call_new_impl(const ProxyPtr &proxy, const std::string &funcName) { + auto p = get(proxy); + return std::make_shared(MessageImpl{ + p->bus, p->path, p->interface, funcName, false + }); +} +TestDBusWrapper::MessagePtr TestDBusWrapper::call( + std::unordered_map, std::function> &mp, + const std::string &mpName, const MessagePtr &msg, MethodType type) { + auto m = get(msg); + auto findMethod = [&](std::string path, const std::string &iname, const std::string &name, MethodType type) { + if (path.empty()) throw error{}; + if (path[0] != '/') { + path = "/org/a11y/atspi/accessible/" + path; + m->path = path; + } + while(!path.empty()) { + auto it = mp.find(std::tuple { path, iname, name, type }); + if (it != mp.end()) return it; + auto index = path.rfind('/'); + if (index == std::string::npos) break; + path.resize(index); + } + return mp.end(); + }; + std::string iname, name; + if (type == MethodType::Method && m->interface == "org.freedesktop.DBus.Properties") { + type = m->name == "Get" ? MethodType::Getter : MethodType::Setter; + auto &lst = m->data.get(); + assert(lst.size() >= 2); + iname = lst.front().get(); + lst.pop_front(); + name = lst.front().get(); + lst.pop_front(); + } + else { + iname = m->interface; + name = m->name; + } + auto it = findMethod(m->path, iname, name, type); + if (it == mp.end()) { + const char *mt; + if (type == MethodType::Method) mt = "MethodType::Method"; + else if (type == MethodType::Getter) mt = "MethodType::Getter"; + else if (type == MethodType::Setter) mt = "MethodType::Setter"; + else assert(0); + throw error{} << "missing {\"" << m->path << "\", \"" << iname << "\", \"" << name << "\", " << mt << "} in " << mpName; + } + DBus::DBusServer::CurrentObjectSetter setter{ connection, m->path }; + return it->second(msg); +} +TestDBusWrapper::MessagePtr TestDBusWrapper::eldbus_proxy_send_and_block_impl(const ProxyPtr &proxy, const MessagePtr &msg) { +// BART +// auto p = get(proxy); +// auto m = get(msg); +// assert(p->bus == m->bus); +// assert(p->path == m->path); +// assert(p->interface == m->interface); + return call(testMethods, "testMethods", msg, MethodType::Method); +} + +bool TestDBusWrapper::eldbus_message_error_get_impl(const MessagePtr &msg, std::string &name, std::string &text) { + auto m = get(msg); + name = m->error_a; + text = m->error_b; + return m->error; +} +static void calculate_signature(std::ostringstream &ostr, TestDBusWrapper::Element &e) +{ + ostr << static_cast(e.signature()); + if (e.isContainer()) { + ostr << "("; + auto &lst = e.get(); + for(auto &q : lst) calculate_signature(ostr, q); + ostr << ")"; + } +} +std::string TestDBusWrapper::eldbus_message_signature_get_impl(const MessagePtr &msg) { + auto m = get(msg); + std::ostringstream ostr; + for(auto &q : m->data.get()) + calculate_signature(ostr, q); + return ostr.str(); +} +TestDBusWrapper::PendingPtr TestDBusWrapper::eldbus_proxy_send_impl(const ProxyPtr &proxy, const MessagePtr &msg, const SendCallback &callback) { +// BART +// auto p = get(proxy); +// auto m = get(msg); +// assert(p->bus == m->bus); +// assert(p->path == m->path); +// assert(p->interface == m->interface); + asyncCalls.push_back([this, msg, callback]() { + auto r = call(testMethods, "testMethods", msg, MethodType::Method); + callback(r); + }); + return {}; +} +std::string TestDBusWrapper::eldbus_proxy_interface_get_impl(const ProxyPtr &proxy) { + auto p = get(proxy); + return p->interface; +} +void TestDBusWrapper::eldbus_proxy_signal_handler_add_impl(const ProxyPtr &proxy, const std::string &member, const std::function &cb) { + auto p = get(proxy); + daliSignals[std::tuple { p->path, p->interface, member }] = cb; +} +std::string TestDBusWrapper::eldbus_message_iter_signature_get_impl(const MessageIterPtr &iter) { + auto m = get(iter); + if (m->write) throw error{}; + if (!m->elem->isContainer()) throw error{}; + std::ostringstream ostr; + auto &lst = m->elem->get(); + for(auto it = m->it; it != lst.end(); ++it) + calculate_signature(ostr, *it); + return ostr.str(); +} +TestDBusWrapper::MessagePtr TestDBusWrapper::eldbus_message_method_return_new_impl( const MessagePtr &msg) { + auto m = get(msg); + return std::make_shared(MessageImpl{ + m->bus, m->path, m->interface, m->name, true + }); +} +TestDBusWrapper::MessagePtr TestDBusWrapper::eldbus_message_error_new_impl( const MessagePtr &msg, const std::string &err, const std::string &txt ) { + auto m = get(msg); + return std::make_shared(MessageImpl{ + m->bus, m->path, m->interface, m->name, err, txt + }); +} +TestDBusWrapper::PendingPtr TestDBusWrapper::eldbus_connection_send_impl(const ConnectionPtr &conn, const MessagePtr &msg) { + call(testMethods, "testMethods", msg, MethodType::Method); + return {}; +} +std::shared_ptr TestDBusWrapper::createEinaValue(bool b) { + Eina_Value *value = eina_value_new(EINA_VALUE_TYPE_UCHAR); + eina_value_set(value, b); + return std::shared_ptr(value, [](Eina_Value *v) { + eina_value_free(v); + }); +} + +TestDBusWrapper::MessagePtr TestDBusWrapper::newMessage(const std::string &path, const std::string &interface, const std::string &name, bool reply) { + return std::make_shared(MessageImpl{ + "bus", path, interface, name, reply + }); +} + +TestDBusWrapper::MessagePtr TestDBusWrapper::newReplyMessage(const MessagePtr &msg) { + auto m = get(msg); + return newMessage(m->path, m->interface, m->name, true); +} + +TestDBusWrapper::MessagePtr TestDBusWrapper::eldbus_message_signal_new_impl(const std::string &path, const std::string &iface, const std::string &name) { + return std::make_shared(MessageImpl{ + "bus", path, iface, name, true + }); +} +TestDBusWrapper::MessagePtr TestDBusWrapper::eldbus_message_ref_impl(const MessagePtr &msg) { + return msg; +} +TestDBusWrapper::ConnectionPtr TestDBusWrapper::eldbus_connection_get_impl(ConnectionType type) { + return connection; +} +std::string TestDBusWrapper::eldbus_connection_unique_name_get_impl(const ConnectionPtr &conn) { + return "bus"; +} +TestDBusWrapper::ObjectPtr TestDBusWrapper::eldbus_object_get_impl( const ConnectionPtr &conn, const std::string &bus, const std::string &path ) { + return std::make_shared(ObjectImpl{ + bus, path + }); +} +TestDBusWrapper::ProxyPtr TestDBusWrapper::eldbus_proxy_get_impl( const ObjectPtr &obj, const std::string &interface ) { + auto o = get(obj); + return std::make_shared(ProxyImpl{ o->bus, o->path, interface }); +} +TestDBusWrapper::ProxyPtr TestDBusWrapper::eldbus_proxy_copy_impl( const ProxyPtr &ptr) { + return ptr; +} +void TestDBusWrapper::add_property_changed_event_listener_impl( const ProxyPtr &proxy, const std::string &interface, const std::string &name, std::function< void( const Eina_Value * ) > cb) { + auto p = get(proxy); + propertyChangeListeners[std::tuple{ p->path, interface, name }] = cb; +} +void TestDBusWrapper::add_interface_impl( bool fallback, const std::string& path_, + const ConnectionPtr &connection, + std::vector> &destructors, + const std::string& interface, + std::vector< MethodInfo >& dscrMethods, + std::vector< PropertyInfo >& dscrProperties, + std::vector< SignalInfo >& dscrSignals ) { + std::string bus = "bus"; + std::string path = path_; + while(!path.empty() && path.back() == '/') path.pop_back(); + for(auto &m : dscrMethods) { + auto key = std::make_tuple( "/org/a11y/atspi/accessible" + path, interface, m.memberName, MethodType::Method ); + daliMethods[key] = m.callback; + } + for(auto &m : dscrProperties) { + auto key = std::make_tuple( "/org/a11y/atspi/accessible" + path, interface, m.memberName, MethodType::Getter ); + daliMethods[key] = [=](const MessagePtr &msg) -> MessagePtr { + auto ret = std::make_shared(MessageImpl{ + "bus", path, interface, m.memberName, true + }); + auto v = m.getCallback(msg, std::make_shared(MessageIterImpl{ ret, &ret->data, true })); + ret->error = !v.empty(); + if (ret->error) { + ret->error_a = "call failed"; + ret->error_b = std::move(v); + } + return ret; + }; + std::get<3>(key) = MethodType::Setter; + daliMethods[key] = [=](const MessagePtr &msg) -> MessagePtr { + auto v = m.setCallback(msg, std::make_shared(MessageIterImpl{ msg, &get(msg)->data, false })); + return v.empty() ? std::make_shared(MessageImpl{ + "bus", path, interface, m.memberName, true, + }) : std::make_shared(MessageImpl{ + "bus", path, interface, m.memberName, "call failed", v + }); + // interface is probably wrong, due to retarded way properties are handlded by dbus + }; + } + for(auto &m : dscrSignals) { + daliSignalsMap[std::tuple{ "/org/a11y/atspi/accessible" + path, interface, m.uniqueId }] = m.memberName; + } +} + +/// \endcond diff --git a/automated-tests/src/dali-toolkit-internal/dali-toolkit-test-utils/dbus-wrapper.h b/automated-tests/src/dali-toolkit-internal/dali-toolkit-test-utils/dbus-wrapper.h new file mode 100644 index 0000000..e44aa56 --- /dev/null +++ b/automated-tests/src/dali-toolkit-internal/dali-toolkit-test-utils/dbus-wrapper.h @@ -0,0 +1,3330 @@ +#ifndef DALI_INTERNAL_ACCESSIBILITY_BRIDGE_DBUS_H +#define DALI_INTERNAL_ACCESSIBILITY_BRIDGE_DBUS_H + +/* + * Copyright 2019 Samsung Electronics Co., Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// EXTERNAL INCLUDES +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// INTERNAL INCLUDES +//#include +//#include + +#include + +#define ATSPI_PREFIX_PATH "/org/a11y/atspi/accessible/" +#define ATSPI_NULL_PATH "/org/a11y/atspi/null" + + +//// BART QUICK HACK +#define bart_assert(x) do{}while(0) + +struct _Eina_Value; + +/// \cond +struct ObjectPath +{ + std::string value; +}; + +struct DBusWrapper +{ + virtual ~DBusWrapper() = default; + + enum class ConnectionType + { + SYSTEM, + SESSION + }; + +#define DEFINE_TYPE(name, eldbus_name, unref_call) \ + struct name { virtual ~name() = default; }; \ + using name ## Ptr = std::shared_ptr; \ + using name ## WeakPtr = std::weak_ptr; \ + + DEFINE_TYPE(Connection, Eldbus_Connection, ) + DEFINE_TYPE(MessageIter, Eldbus_Message_Iter, eldbus_message_iter_container_close( Value )) + DEFINE_TYPE(Message, Eldbus_Message, eldbus_message_unref( Value )) + DEFINE_TYPE(Proxy, Eldbus_Proxy, eldbus_proxy_unref( Value )) + DEFINE_TYPE(Object, Eldbus_Object, eldbus_object_unref( Value )) + DEFINE_TYPE(Pending, Eldbus_Pending, ) + DEFINE_TYPE(EventPropertyChanged, Eldbus_Proxy_Event_Property_Changed, ) + +#undef DEFINE_TYPE + virtual ConnectionPtr eldbus_address_connection_get_impl(const std::string &addr) = 0; + +#define eldbus_message_iter_arguments_append_impl_basic_impl(type_set, type_get, sig) \ + virtual void eldbus_message_iter_arguments_append_impl(const MessageIterPtr &it, type_set src) = 0; \ + virtual bool eldbus_message_iter_get_and_next_impl(const MessageIterPtr &it, type_get &dst) = 0; +#define eldbus_message_iter_arguments_append_impl_basic(type, sig) \ + eldbus_message_iter_arguments_append_impl_basic_impl(type, type, sig) + + eldbus_message_iter_arguments_append_impl_basic(uint8_t, y) + eldbus_message_iter_arguments_append_impl_basic(uint16_t, q) + eldbus_message_iter_arguments_append_impl_basic(uint32_t, u) + eldbus_message_iter_arguments_append_impl_basic(uint64_t, t) + eldbus_message_iter_arguments_append_impl_basic(int16_t, n) + eldbus_message_iter_arguments_append_impl_basic(int32_t, i) + eldbus_message_iter_arguments_append_impl_basic(int64_t, x) + eldbus_message_iter_arguments_append_impl_basic(double, d) + eldbus_message_iter_arguments_append_impl_basic(bool, b) + eldbus_message_iter_arguments_append_impl_basic_impl(const std::string &, std::string, s) + eldbus_message_iter_arguments_append_impl_basic_impl(const ObjectPath &, ObjectPath, o) + +#undef eldbus_message_iter_arguments_append_impl_basic +#undef eldbus_message_iter_arguments_append_impl_basic_impl + + virtual MessageIterPtr eldbus_message_iter_container_new_impl(const MessageIterPtr &it, int type, const std::string &sig) = 0; + virtual MessageIterPtr eldbus_message_iter_get_and_next_by_type_impl(const MessageIterPtr &it, int type) = 0; + virtual MessageIterPtr eldbus_message_iter_get_impl(const MessagePtr &it, bool write) = 0; + virtual MessagePtr eldbus_proxy_method_call_new_impl(const ProxyPtr &proxy, const std::string &funcName) = 0; + virtual MessagePtr eldbus_proxy_send_and_block_impl(const ProxyPtr &proxy, const MessagePtr &msg) = 0; + virtual bool eldbus_message_error_get_impl(const MessagePtr &msg, std::string &name, std::string &text) = 0; + virtual std::string eldbus_message_signature_get_impl(const MessagePtr &msg) = 0; + + using SendCallback = std::function; + virtual PendingPtr eldbus_proxy_send_impl(const ProxyPtr &proxy, const MessagePtr &msg, const SendCallback &callback) = 0; + virtual std::string eldbus_proxy_interface_get_impl(const ProxyPtr &) = 0; + virtual void eldbus_proxy_signal_handler_add_impl(const ProxyPtr &proxy, const std::string &member, const std::function &cb) = 0; + virtual std::string eldbus_message_iter_signature_get_impl(const MessageIterPtr &iter) = 0; + virtual MessagePtr eldbus_message_method_return_new_impl( const MessagePtr &msg) = 0; + virtual MessagePtr eldbus_message_error_new_impl( const MessagePtr &msg, const std::string &err, const std::string &txt ) = 0; + virtual PendingPtr eldbus_connection_send_impl(const ConnectionPtr &conn, const MessagePtr &msg) = 0; + virtual MessagePtr eldbus_message_signal_new_impl(const std::string &path, const std::string &iface, const std::string &name) = 0; + virtual MessagePtr eldbus_message_ref_impl(const MessagePtr &msg) = 0; + virtual ConnectionPtr eldbus_connection_get_impl(ConnectionType type) = 0; + virtual std::string eldbus_connection_unique_name_get_impl(const ConnectionPtr &conn) = 0; + virtual ObjectPtr eldbus_object_get_impl( const ConnectionPtr &conn, const std::string &bus, const std::string &path ) = 0; + virtual ProxyPtr eldbus_proxy_get_impl( const ObjectPtr &obj, const std::string &interface ) = 0; + virtual ProxyPtr eldbus_proxy_copy_impl( const ProxyPtr &ptr) = 0; + + class StringStorage + { + public: + struct char_ptr_deleter + { + void operator()( char* p ) + { + free( p ); + } + }; + std::vector< std::unique_ptr< char, char_ptr_deleter > > storage; + + const char* add( const char* txt ) + { + auto ptr = strdup( txt ); + storage.push_back( std::unique_ptr< char, char_ptr_deleter >( ptr ) ); + return storage.back().get(); + } + const char* add( const std::string& txt ) + { + return add( txt.c_str() ); + } + }; + + struct CallId + { + static std::atomic< unsigned int > LastId; + unsigned int id = ++LastId; + }; + struct MethodInfo + { + CallId id; + std::string memberName; + std::vector< std::pair > in, out; // _Eldbus_Arg_Info + std::function< DBusWrapper::MessagePtr( const DBusWrapper::MessagePtr &msg ) > callback; + }; + struct SignalInfo + { + CallId id; + std::string memberName; + std::vector< std::pair > args; + unsigned int uniqueId; + }; + struct PropertyInfo + { + CallId setterId, getterId; + std::string memberName, typeSignature; + std::function< std::string( const DBusWrapper::MessagePtr &src, const DBusWrapper::MessageIterPtr &dst ) > getCallback, setCallback; + }; + struct SignalId + { + CallId id; + + SignalId() = default; + SignalId( CallId id ) : id( id ) {} + }; + virtual void add_interface_impl( bool fallback, const std::string& pathName, + const ConnectionPtr &connection, + std::vector> &destructors, + const std::string& interfaceName, + std::vector< MethodInfo >& dscrMethods, + std::vector< PropertyInfo >& dscrProperties, + std::vector< SignalInfo >& dscrSignals ) = 0; + virtual void add_property_changed_event_listener_impl( const ProxyPtr &proxy, const std::string &interface, const std::string &name, std::function< void( const _Eina_Value * ) > cb) = 0; + static DBusWrapper *Installed(); + static void Install(std::unique_ptr); + + StringStorage Strings; +}; + +namespace detail { + enum class MethodType { + Method, Getter, Setter + }; +} + +namespace std { + template <> struct hash> { + size_t operator () (const std::tuple &a) const { + auto a1 = std::hash()(std::get<0>(a)); + auto a2 = std::hash()(std::get<1>(a)); + auto a3 = std::hash()(std::get<2>(a)); + size_t v = a1; + v = (v * 11400714819323198485llu) + a2; + v = (v * 11400714819323198485llu) + a3; + return v; + } + }; + template <> struct hash> { + size_t operator () (const std::tuple &a) const { + auto a1 = std::hash()(std::get<0>(a)); + auto a2 = std::hash()(std::get<1>(a)); + auto a3 = std::hash()(std::get<2>(a)); + auto a4 = static_cast(std::get<3>(a)); + size_t v = a1; + v = (v * 11400714819323198485llu) + a2; + v = (v * 11400714819323198485llu) + a3; + v = (v * 11400714819323198485llu) + a4; + return v; + } + }; + template <> struct hash> { + size_t operator () (const std::tuple &a) const { + auto a1 = std::hash()(std::get<0>(a)); + auto a2 = std::hash()(std::get<1>(a)); + auto a3 = std::get<2>(a); + size_t v = a1; + v = (v * 11400714819323198485llu) + a2; + v = (v * 11400714819323198485llu) + a3; + return v; + } + }; +} +namespace detail { + template struct DBusSigImpl { enum { value = 0, end = 0 }; }; + template struct DBusSig { enum { value = DBusSigImpl::type>::value, end = DBusSigImpl::type>::end }; }; + template struct IndexFromTypeTupleImpl { + enum { value = std::is_same::type>::type, Q>::value ? I : IndexFromTypeTupleImpl::value }; + }; + template struct IndexFromTypeTupleImpl { enum { value = S }; }; + template struct IndexFromTypeTuple { + enum { value = IndexFromTypeTupleImpl::type, 0, std::tuple_size::value>::value }; + }; + template struct Encoder; + template struct EncoderTuple; +} +struct TestDBusWrapper : public DBusWrapper +{ + using MethodType = detail::MethodType; + ConnectionPtr connection; + std::unordered_map, std::function> testMethods; + std::unordered_map, std::function> daliMethods; + std::unordered_map, std::string> daliSignalsMap; + std::unordered_map, std::function> daliSignals; + std::unordered_map, std::function> propertyChangeListeners; + + std::vector> asyncCalls; + + template struct Variant { + T value; + Variant() = default; + Variant(T t) : value(std::move(t)) { } + }; + template void Encode(const MessagePtr &m, const std::tuple &args) { + auto iter = eldbus_message_iter_get_impl(m, true); + detail::EncoderTuple<0, sizeof...(ARGS), ARGS...>::encode(*this, iter, args); + } + template void Decode(const MessagePtr &m, std::tuple &args) { + auto iter = eldbus_message_iter_get_impl(m, false); + detail::EncoderTuple<0, sizeof...(ARGS), ARGS...>::decode(*this, args, iter); + } + MessagePtr newMessage(const std::string &path, const std::string &interface, const std::string &name, bool reply); + MessagePtr newReplyMessage(const MessagePtr &m); + + template void fromTestEmitSignal(std::string path, const std::string &interface, const std::string &name, const std::tuple &args) { + if (path.empty()) throw error{}; + if (path[0] != '/') path = "/org/a11y/atspi/accessible/" + path; + auto msg = newMessage(path, interface, name, false); + Encode(msg, args); + auto it = daliSignals.find(std::tuple{ path, interface, name }); + if (it == daliSignals.end()) throw error{}; + it->second(msg); + } + static std::shared_ptr<_Eina_Value> createEinaValue(bool); + template void fromTestChangeProperty(std::string path, const std::string &interface, const std::string &name, T new_value) { + auto v = createEinaValue(new_value); + if (path.empty()) throw error{}; + if (path[0] != '/') path = "/org/a11y/atspi/accessible/" + path; + auto it = propertyChangeListeners.find(std::tuple{ path, interface, name }); + if (it == propertyChangeListeners.end()) throw error{}; + it->second(v.get()); + } + template std::tuple fromTestCall(const std::string &path, const std::string &interface, const std::string &name, const std::tuple &args) { + auto msg = newMessage(path, interface, name, false); + Encode(msg, args); + auto res = call(daliMethods, "daliMethods", msg, MethodType::Method); + std::string a, b; + if (eldbus_message_error_get_impl(res, a, b)) throw error{} << "call to " << path << " " << interface << " " << name << " failed: " << a << ": " << b; + std::tuple tmp; + Decode(res, tmp); + return tmp; + } + template T fromTestGet(const std::string &path, const std::string &interface, const std::string &name) { + auto msg = newMessage(path, interface, name, false); + auto res = call(daliMethods, "daliMethods", msg, MethodType::Getter); + std::string a, b; + if (eldbus_message_error_get_impl(res, a, b)) throw error{} << "call to " << path << " " << interface << " " << name << " failed: " << a << ": " << b; + std::tuple tmp; + Decode(res, tmp); + return std::move(std::get<0>(tmp)); + } + template std::tuple fromTestSet(const std::string &path, const std::string &interface, const std::string &name, const T &arg) { + auto msg = newMessage(path, interface, name, false); + Encode(msg, TestDBusWrapper::Variant{ arg }); + auto res = call(daliMethods, "daliMethods", msg, MethodType::Setter); + std::string a, b; + if (eldbus_message_error_get_impl(res, a, b)) throw error{} << "call to " << path << " " << interface << " " << name << " failed: " << a << ": " << b; + std::tuple tmp; + Decode(res, tmp); + return tmp; + } + + TestDBusWrapper(); + ~TestDBusWrapper(); + + class error : public std::exception { + std::shared_ptr temp = std::make_shared(); + mutable std::string text; + public: + error() = default; + + template error &operator << (T &&t) { + *temp << std::forward(t); + return *this; + } + const char *what() const noexcept override { + text = temp->str(); + return text.c_str(); + } + }; + #define DEFINE_TYPE(name) struct name ## Impl; static name ## Impl *get(const name ## Ptr &); + DEFINE_TYPE(Connection) + DEFINE_TYPE(Proxy) + DEFINE_TYPE(Object) + DEFINE_TYPE(Message) + DEFINE_TYPE(MessageIter) + #undef DEFINE_TYPE + + class Element; + using ElementList = std::list; + using ElementMap = std::map; + using ElementTuple = std::tuple< + uint8_t, uint16_t, uint32_t, uint64_t, int16_t, int32_t, int64_t, double, bool, std::string, ObjectPath, + ElementList + >; + + class Element { + ElementTuple data; + char signature_ = 0, end_ = 0, index_ = 0; + template void set(T &&t, char signature = detail::DBusSig::value) { + signature_ = signature; + end_ = detail::DBusSig::end; + index_ = static_cast(detail::IndexFromTypeTuple::value); + get() = std::forward(t); + } + public: + template Element(T &&t, typename std::enable_if::value != 0>::type* = nullptr) { set(std::forward(t)); } + Element(ElementList t, int signature) { set(std::move(t), static_cast(signature)); } + + char signature() const { return signature_; } + char end() const { return end_; } + char index() const { return index_; } + bool isContainer() const { return index_ == detail::IndexFromTypeTuple::value; } + template ::value != 0>::type> + bool is() const { return index_ == detail::IndexFromTypeTuple::value; } + template ::value != 0>::type> + const T &get() const { if (!is()) throw error{}; return std::get::value>(data); } + template ::value != 0>::type> + T &get() { if (!is()) throw error{}; return std::get::value>(data); } + }; + + ConnectionPtr eldbus_address_connection_get_impl(const std::string &addr) override; + +#define eldbus_message_iter_arguments_append_impl_basic_impl(type_set, type_get, sig) \ + void eldbus_message_iter_arguments_append_impl(const MessageIterPtr &it, type_set src) override; \ + bool eldbus_message_iter_get_and_next_impl(const MessageIterPtr &it, type_get &dst) override; +#define eldbus_message_iter_arguments_append_impl_basic(type, sig) \ + eldbus_message_iter_arguments_append_impl_basic_impl(type, type, sig) + + eldbus_message_iter_arguments_append_impl_basic(uint8_t, y) + eldbus_message_iter_arguments_append_impl_basic(uint16_t, q) + eldbus_message_iter_arguments_append_impl_basic(uint32_t, u) + eldbus_message_iter_arguments_append_impl_basic(uint64_t, t) + eldbus_message_iter_arguments_append_impl_basic(int16_t, n) + eldbus_message_iter_arguments_append_impl_basic(int32_t, i) + eldbus_message_iter_arguments_append_impl_basic(int64_t, x) + eldbus_message_iter_arguments_append_impl_basic(double, d) + eldbus_message_iter_arguments_append_impl_basic(bool, b) + eldbus_message_iter_arguments_append_impl_basic_impl(const std::string &, std::string, s) + eldbus_message_iter_arguments_append_impl_basic_impl(const ObjectPath &, ObjectPath, o) + +#undef eldbus_message_iter_arguments_append_impl_basic +#undef eldbus_message_iter_arguments_append_impl_basic_impl + + MessageIterPtr eldbus_message_iter_container_new_impl(const MessageIterPtr &it, int type, const std::string &sig) override; + MessageIterPtr eldbus_message_iter_get_and_next_by_type_impl(const MessageIterPtr &it, int type) override; + MessageIterPtr eldbus_message_iter_get_impl(const MessagePtr &it, bool write) override; + MessagePtr eldbus_proxy_method_call_new_impl(const ProxyPtr &proxy, const std::string &funcName) override; + MessagePtr eldbus_proxy_send_and_block_impl(const ProxyPtr &proxy, const MessagePtr &msg) override; + bool eldbus_message_error_get_impl(const MessagePtr &msg, std::string &name, std::string &text) override; + std::string eldbus_message_signature_get_impl(const MessagePtr &msg) override; + PendingPtr eldbus_proxy_send_impl(const ProxyPtr &proxy, const MessagePtr &msg, const SendCallback &callback) override; + std::string eldbus_proxy_interface_get_impl(const ProxyPtr &) override; + void eldbus_proxy_signal_handler_add_impl(const ProxyPtr &proxy, const std::string &member, const std::function &cb) override; + std::string eldbus_message_iter_signature_get_impl(const MessageIterPtr &iter) override; + MessagePtr eldbus_message_method_return_new_impl( const MessagePtr &msg) override; + MessagePtr eldbus_message_error_new_impl( const MessagePtr &msg, const std::string &err, const std::string &txt ) override; + PendingPtr eldbus_connection_send_impl(const ConnectionPtr &conn, const MessagePtr &msg) override; + MessagePtr eldbus_message_signal_new_impl(const std::string &path, const std::string &iface, const std::string &name) override; + MessagePtr eldbus_message_ref_impl(const MessagePtr &msg) override; + ConnectionPtr eldbus_connection_get_impl(ConnectionType type) override; + std::string eldbus_connection_unique_name_get_impl(const ConnectionPtr &conn) override; + ObjectPtr eldbus_object_get_impl( const ConnectionPtr &conn, const std::string &bus, const std::string &path ) override; + ProxyPtr eldbus_proxy_get_impl( const ObjectPtr &obj, const std::string &interface ) override; + ProxyPtr eldbus_proxy_copy_impl( const ProxyPtr &ptr) override; + void add_property_changed_event_listener_impl( const ProxyPtr &proxy, const std::string &interface, const std::string &name, std::function< void( const _Eina_Value * ) > cb) override; + void add_interface_impl( bool fallback, const std::string& pathName, + const ConnectionPtr &connection, + std::vector> &destructors, + const std::string& interfaceName, + std::vector< MethodInfo >& dscrMethods, + std::vector< PropertyInfo >& dscrProperties, + std::vector< SignalInfo >& dscrSignals ) override; + static bool completed(const MessageIterPtr &iter); +private: + MessagePtr call(std::unordered_map, std::function> &mp, const std::string &name, const MessagePtr &msg, MethodType type); +}; + +namespace detail { + template <> struct DBusSigImpl { enum { value = 'y', end = 0 }; }; + template <> struct DBusSigImpl { enum { value = 'q', end = 0 }; }; + template <> struct DBusSigImpl { enum { value = 'u', end = 0 }; }; + template <> struct DBusSigImpl { enum { value = 't', end = 0 }; }; + template <> struct DBusSigImpl { enum { value = 'n', end = 0 }; }; + template <> struct DBusSigImpl { enum { value = 'i', end = 0 }; }; + template <> struct DBusSigImpl { enum { value = 'x', end = 0 }; }; + template <> struct DBusSigImpl { enum { value = 'd', end = 0 }; }; + template <> struct DBusSigImpl { enum { value = 'b', end = 0 }; }; + template <> struct DBusSigImpl { enum { value = 's', end = 0 }; }; + template <> struct DBusSigImpl { enum { value = 'o', end = 0 }; }; + template <> struct DBusSigImpl { enum { value = '(', end = ')' }; }; + template <> struct DBusSigImpl { enum { value = '{', end = '}' }; }; + + template struct Encoder { + static void encode(TestDBusWrapper &w, const TestDBusWrapper::MessageIterPtr &tgt, const T &src) { + w.eldbus_message_iter_arguments_append_impl(tgt, src); + } + static void decode(TestDBusWrapper &w, T &tgt, const TestDBusWrapper::MessageIterPtr &src) { + if (!w.eldbus_message_iter_get_and_next_impl(src, tgt)) throw TestDBusWrapper::error{}; + } + }; + template struct Encoder::value>::type> { + static void encode(TestDBusWrapper &w, const TestDBusWrapper::MessageIterPtr &tgt, const T &src) { + Encoder::type>::encode(w, tgt, static_cast::type>(src)); + } + static void decode(TestDBusWrapper &w, T &tgt, const TestDBusWrapper::MessageIterPtr &src) { + typename std::underlying_type::type v = 0; + Encoder::type>::decode(w, v, src); + tgt = static_cast(v); + } + }; + template struct Encoder { + static void encode(TestDBusWrapper &w, const TestDBusWrapper::MessageIterPtr &tgt, const T &src) { + Encoder::encode(w, tgt, src); + } + static void decode(TestDBusWrapper &w, T &tgt, const TestDBusWrapper::MessageIterPtr &src) { + Encoder::decode(w, tgt, src); + } + }; + template struct Encoder { + static void encode(TestDBusWrapper &w, const TestDBusWrapper::MessageIterPtr &tgt, const T &src) { + Encoder::encode(w, tgt, src); + } + static void decode(TestDBusWrapper &w, T &tgt, const TestDBusWrapper::MessageIterPtr &src) { + Encoder::decode(w, tgt, src); + } + }; + template struct Encoder { + static void encode(TestDBusWrapper &w, const TestDBusWrapper::MessageIterPtr &tgt, const T &src) { + Encoder::encode(w, tgt, src); + } + static void decode(TestDBusWrapper &w, T &tgt, const TestDBusWrapper::MessageIterPtr &src) { + Encoder::decode(w, tgt, src); + } + }; + template struct EncoderTuple { + static void encode(TestDBusWrapper &w, const TestDBusWrapper::MessageIterPtr &tgt, const std::tuple &src) { + Encoder>::type>::encode(w, tgt, std::get(src)); + EncoderTuple::encode(w, tgt, src); + } + static void decode(TestDBusWrapper &w, std::tuple &tgt, const TestDBusWrapper::MessageIterPtr &src) { + Encoder>::type>::decode(w, std::get(tgt), src); + EncoderTuple::decode(w, tgt, src); + } + }; + template struct EncoderTuple { + static void encode(TestDBusWrapper &w, const TestDBusWrapper::MessageIterPtr &tgt, const std::tuple &src) { } + static void decode(TestDBusWrapper &w, std::tuple &tgt, const TestDBusWrapper::MessageIterPtr &src) { } + }; + template struct Encoder> { + static void encode(TestDBusWrapper &w, const TestDBusWrapper::MessageIterPtr &tgt, const std::tuple &src) { + auto var = w.eldbus_message_iter_container_new_impl( tgt, 'r', ""); + EncoderTuple<0, sizeof...(ARGS), ARGS...>::encode(w, var, src); + } + static void decode(TestDBusWrapper &w, std::tuple &tgt, const TestDBusWrapper::MessageIterPtr &src) { + auto s = w.eldbus_message_iter_get_and_next_by_type_impl( src, 'r' ); + if( !s ) throw TestDBusWrapper::error{}; + EncoderTuple<0, sizeof...(ARGS), ARGS...>::decode(w, tgt, s); + } + }; + template struct Encoder> { + static void encode(TestDBusWrapper &w, const TestDBusWrapper::MessageIterPtr &tgt, const std::pair &src, char type = 'r') { + auto var = w.eldbus_message_iter_container_new_impl( tgt, type, ""); + Encoder::encode(w, var, src.first); + Encoder::encode(w, var, src.second); + } + static void decode(TestDBusWrapper &w, std::pair &tgt, const TestDBusWrapper::MessageIterPtr &src, char type = 'r') { + auto s = w.eldbus_message_iter_get_and_next_by_type_impl( src, type ); + if( !s ) throw TestDBusWrapper::error{}; + Encoder::decode(w, tgt.first, s); + Encoder::decode(w, tgt.second, s); + } + }; + template struct Encoder> { + static void encode(TestDBusWrapper &w, const TestDBusWrapper::MessageIterPtr &tgt, const TestDBusWrapper::Variant &src) { + //w.eldbus_message_iter_arguments_append_impl(tgt, src); + auto var = w.eldbus_message_iter_container_new_impl( tgt, 'v', ""); + Encoder::encode(w, var, src.value); + } + static void decode(TestDBusWrapper &w, TestDBusWrapper::Variant &tgt, const TestDBusWrapper::MessageIterPtr &src) { + auto s = w.eldbus_message_iter_get_and_next_by_type_impl( src, 'v' ); + if( !s ) throw TestDBusWrapper::error{}; + Encoder::decode(w, tgt.value, s); + } + }; + template struct Encoder> { + static void encode(TestDBusWrapper &w, const TestDBusWrapper::MessageIterPtr &tgt, const std::vector &src) { + auto var = w.eldbus_message_iter_container_new_impl( tgt, 'a', ""); + for(auto &q : src) + Encoder::encode(w, var, q); + } + static void decode(TestDBusWrapper &w, std::vector &tgt, const TestDBusWrapper::MessageIterPtr &src) { + auto s = w.eldbus_message_iter_get_and_next_by_type_impl( src, 'a' ); + if( !s ) throw TestDBusWrapper::error{}; + while(!TestDBusWrapper::completed(s)) { + tgt.push_back({}); + Encoder::decode(w, tgt.back(), s); + } + } + }; + template struct Encoder> { + static void encode(TestDBusWrapper &w, const TestDBusWrapper::MessageIterPtr &tgt, const std::unordered_map &src) { + auto var = w.eldbus_message_iter_container_new_impl( tgt, 'a', ""); + for(auto &q : src){ + Encoder::value_type>::encode(w, var, q, 'e'); + } + } + static void decode(TestDBusWrapper &w, std::unordered_map &tgt, const TestDBusWrapper::MessageIterPtr &src) { + auto s = w.eldbus_message_iter_get_and_next_by_type_impl( src, 'a' ); + if( !s ) throw TestDBusWrapper::error{}; + while(!TestDBusWrapper::completed(s)) { + std::pair tmp; + Encoder>::decode(w, tmp, s, 'e'); + tgt.insert(std::move(tmp)); + } + } + }; + template struct Encoder> { + static void encode(TestDBusWrapper &w, const TestDBusWrapper::MessageIterPtr &tgt, const std::array &src) { + auto var = w.eldbus_message_iter_container_new_impl( tgt, 'a', ""); + for(auto &q : src) + Encoder::encode(w, var, q); + } + static void decode(TestDBusWrapper &w, std::array &tgt, const TestDBusWrapper::MessageIterPtr &src) { + auto s = w.eldbus_message_iter_get_and_next_by_type_impl( src, 'a' ); + if( !s ) throw TestDBusWrapper::error{}; + size_t i = 0; + while(!TestDBusWrapper::completed(s)) { + if(i >= tgt.size()) throw TestDBusWrapper::error{}; + Encoder::decode(w, tgt[i], s); + ++i; + } + if( i!=tgt.size()) throw TestDBusWrapper::error{}; + } + }; + template <> struct Encoder { + static void encode(TestDBusWrapper &w, const TestDBusWrapper::MessageIterPtr &tgt, const Dali::Accessibility::Address &src) { + Encoder>::encode(w, tgt,std::tuple { + src.GetBus(), ObjectPath{ "/org/a11y/atspi/accessible/" + src.GetPath() } } + ); + } + static void decode(TestDBusWrapper &w, Dali::Accessibility::Address &tgt, const TestDBusWrapper::MessageIterPtr &src) { + std::tuple tmp; + Encoder>::decode(w, tmp, src); + static const size_t prefixSize = std::string{ "/org/a11y/atspi/accessible/" }.size(); + tgt = { std::move(std::get<0>(tmp)), std::get<1>(tmp).value.substr(prefixSize) }; + } + }; + template <> struct Encoder { + static void encode(TestDBusWrapper &w, const TestDBusWrapper::MessageIterPtr &tgt, const char *src) { + Encoder::encode(w, tgt, src); + } + }; +} +/// \endcond + +#define DBUS_DEBUG( ... ) \ + do \ + { \ + DBus::debugPrint( __FILE__, __LINE__, __VA_ARGS__ ); \ + } while( 0 ) + +#define DBUS_W DBusWrapper::Installed() + +/** + * @brief Template based, single file, wrapper library around eldbus for DBUS based communication. + * + * Main motivation was missing asynchronous calls in AT-SPI library and difficulties, + * when using eldbus from C++. + * + * The library: + * - takes care of marshalling arguments to and from DBUS calls. + * - allows synchronous and asynchronous calls. + * - allows synchronous and asynchronous listeners on signals. + * - manages all involved objects' lifetimes. + * - errors are passed as optional-alike objects, no exceptions are used. + * - allows setting additional debug-print function for more details about + * what's going on + * + * DBUS's method signatures (and expected return values) are specified as template argument, + * using functor syntax. For example: + * \code{.cpp} + * auto dbus = DBusClient{ ... }; + * auto v = dbus.method(float, float, std::string)>("foo").call(1.0f, 2.0f, "qwe"); + * \endcode + * means (synchronous) call on dbus object, which takes three arguments (thus making call signature \b dds) + * of types float, float and string (float will be automatically converted to double). + * Expected return value is std::tuple, which gives signature (id) - std::tuple acts + * as struct container. Returned value v will be of type ValueOrError>.\n + * Slightly different (asynchronous) example: + * \code{.cpp} + * auto dbus = DBusClient{ ... }; + * std::function)> callback; + * dbus.method(float, float, std::string)>("foo").asyncCall(callback, 1.0f, 2.0f, "qwe"); + * \endcode + * Now the call takes the same arguments and has the same signature. But expected values are different - + * now the signature is simply \b id. ValueOrError acts in this case as placeholder for list of values, + * which DBUS allows as return data. The call itself is asynchronous - instead of expecting reply + * you need to pass a callback, which will be called either with received data and error message. + * + * Library is not thread-safe, the same object shouldn't be called from different threads without + * synchronization. There's no guarantee, that callbacks will be executed on the same thread. + */ +namespace DBus +{ +/// \cond +class DBusServer; +class DBusClient; +class DBusInterfaceDescription; + +/** + * @brief Formats debug message and calls debug printer (if any) with it + */ +void debugPrint( const char* file, size_t line, const char* format, ... ); + +/** + * @brief Sets debug printer callback, which will be called with debug messages + * + * Callback will be called in various moments of DBus activity. First value passed to callback + * is pointer to text, second it's length. Text is ended with 0 (not counted towards it's size), + * user can safely printf it. + */ +void setDebugPrinter( std::function< void( const char*, size_t ) > ); + +struct Error +{ + std::string message; + + Error() = default; + Error( std::string msg ) : message( std::move( msg ) ) + { + bart_assert( !message.empty() ); + } +}; + +struct Success +{ +}; +/// \endcond + +/** + * @brief Value representing data, that came from DBUS or error message + * + * Object of this class either helds series of values (of types ARGS...) + * or error message. This object will be true in boolean context, if has data + * and false, if an error occured. + * It's valid to create ValueOrError object with empty argument list or void: + * \code{.cpp} + * ValueOrError<> v1; + * ValueOrError v2; + * \endcode + * Both mean the same - ValueOrError containing no real data and being a marker, + * wherever operation successed or failed and containing possible error message. + */ +template < typename... ARGS > +class ValueOrError +{ +public: + /** + * @brief Empty constructor. Valid only, if all ARGS types are default constructible. + */ + ValueOrError() = default; + + /** + * @brief Value constructor. + * + * This will be initialized as success with passed in values. + */ + ValueOrError( ARGS... t ) : value( std::move( t )... ) {} + + /** + * @brief Alternative Value constructor. + * + * This will be initialized as success with passed in values. + */ + ValueOrError( std::tuple< ARGS... > t ) : value( std::move( t ) ) {} + + /** + * @brief Error constructor. This will be initialized as failure with given error message. + */ + ValueOrError( Error e ) : error( std::move( e ) ) + { + bart_assert( !error.message.empty() ); + } + + /** + * @brief bool operator. + * + * Returns true, if operation was successful (getValues member is callable), or false + * when operation failed (getError is callable). + */ + explicit operator bool() const + { + return error.message.empty(); + } + + /** + * @brief Returns error message object. + * + * Returns object containing error message associated with the failed operation. + * Only callable, if operation actually failed, otherwise will bart_assert. + */ + const Error& getError() const + { + return error; + } + + /** + * @brief Returns modifiable tuple of held data. + * + * Returns reference to the internal tuple containing held data. + * User can modify (or move) data safely. + * Only callable, if operation actually successed, otherwise will bart_assert. + */ + std::tuple< ARGS... >& getValues() + { + bart_assert( *this ); + return value; + } + + /** + * @brief Returns const tuple of held data. + * + * Returns const reference to the internal tuple containing held data. + * Only callable, if operation actually successed, otherwise will bart_assert. + */ + const std::tuple< ARGS... >& getValues() const + { + bart_assert( *this ); + return value; + } + +protected: + /// \cond + std::tuple< ARGS... > value; + Error error; + /// \endcond +}; + +/// \cond +template <> +class ValueOrError<> +{ +public: + ValueOrError() = default; + ValueOrError( std::tuple<> t ) {} + ValueOrError( Error e ) : error( std::move( e ) ) + { + bart_assert( !error.message.empty() ); + } + + explicit operator bool() const + { + return error.message.empty(); + } + const Error& getError() const + { + return error; + } + std::tuple<>& getValues() + { + bart_assert( *this ); + static std::tuple<> t; + return t; + } + std::tuple<> getValues() const + { + bart_assert( *this ); + return {}; + } + +protected: + Error error; +}; + +template <> +class ValueOrError< void > +{ +public: + ValueOrError() = default; + ValueOrError( Success ) {} + ValueOrError( Error e ) : error( std::move( e ) ) + { + bart_assert( !error.message.empty() ); + } + + explicit operator bool() const + { + return error.message.empty(); + } + const Error& getError() const + { + return error; + } + std::tuple<>& getValues() + { + bart_assert( *this ); + static std::tuple<> t; + return t; + } + std::tuple<> getValues() const + { + bart_assert( *this ); + return {}; + } + +protected: + Error error; +}; + +using ObjectPath = ObjectPath; + +/// \endcond + +/** + * @brief Class used to marshall DBUS's variant type + * + * Minimalistic class, that allows user to specify DBUS variant type + * as argument or return value. You need to pass real type hidden under variant as + * template type \b A. At this point library doesn't allow to expected one of few classes + * as return data in variant. So for example user can't specify method call, which on return + * expects DBUS variant holding either string or int. + */ +template < typename A > +struct EldbusVariant +{ + A value; +}; + +/** + * @brief Namespace for private, internal functions and classes + */ +namespace detail +{ +/// \cond +template < typename T, typename = void > +struct signature; +template < typename... ARGS > +struct signature< std::tuple< ARGS... > >; +template < typename A, typename B > +struct signature< std::pair< A, B > >; +template < typename A > +struct signature< std::vector< A > >; +template < typename A, size_t N > +struct signature< std::array< A, N > >; +template < typename A, typename B > +struct signature< std::unordered_map< A, B > >; +template < typename A, typename B > +struct signature< std::map< A, B > >; +/// \endcond + +/** + * @brief Signature class for marshalling uint8 type. + */ +template <> +struct signature< uint8_t > +{ + /** + * @brief Returns name of type marshalled, for informative purposes + */ + static std::string name() + { + return "uint8_t"; + } + + /** + * @brief Returns DBUS' signature of type marshalled + */ + static std::string sig() + { + return "y"; + } + + /** + * @brief Marshals value v as marshalled type into message + */ + static void set( const DBusWrapper::MessageIterPtr &iter, uint8_t v ) + { + DBUS_W->eldbus_message_iter_arguments_append_impl(iter, v); + } + + /** + * @brief Marshals value from marshalled type into variable v + */ + static bool get( const DBusWrapper::MessageIterPtr &iter, uint8_t& v ) + { + return DBUS_W->eldbus_message_iter_get_and_next_impl(iter, v); + } +}; + +/** + * @brief Signature class for marshalling uint16 type. + */ +template <> +struct signature< uint16_t > +{ + /** + * @brief Returns name of type marshalled, for informative purposes + */ + static std::string name() + { + return "uint16_t"; + } + + /** + * @brief Returns DBUS' signature of type marshalled + */ + static std::string sig() + { + return "q"; + } + + /** + * @brief Marshals value v as marshalled type into message + */ + static void set( const DBusWrapper::MessageIterPtr &iter, uint16_t v ) + { + DBUS_W->eldbus_message_iter_arguments_append_impl(iter, v); + } + + /** + * @brief Marshals value from marshalled type into variable v + */ + static bool get( const DBusWrapper::MessageIterPtr &iter, uint16_t& v ) + { + return DBUS_W->eldbus_message_iter_get_and_next_impl(iter, v); + } +}; + +/** + * @brief Signature class for marshalling uint32 type. + */ +template <> +struct signature< uint32_t > +{ + /** + * @brief Returns name of type marshalled, for informative purposes + */ + static std::string name() + { + return "uint32_t"; + } + + /** + * @brief Returns DBUS' signature of type marshalled + */ + static std::string sig() + { + return "u"; + } + + /** + * @brief Marshals value v as marshalled type into message + */ + static void set( const DBusWrapper::MessageIterPtr &iter, uint32_t v ) + { + DBUS_W->eldbus_message_iter_arguments_append_impl(iter, v); + } + + /** + * @brief Marshals value from marshalled type into variable v + */ + static bool get( const DBusWrapper::MessageIterPtr &iter, uint32_t& v ) + { + return DBUS_W->eldbus_message_iter_get_and_next_impl(iter, v); + } +}; + +/** + * @brief Signature class for marshalling uint64 type. + */ +template <> +struct signature< uint64_t > +{ + /** + * @brief Returns name of type marshalled, for informative purposes + */ + static std::string name() + { + return "uint64_t"; + } + + /** + * @brief Returns DBUS' signature of type marshalled + */ + static std::string sig() + { + return "t"; + } + + /** + * @brief Marshals value v as marshalled type into message + */ + static void set( const DBusWrapper::MessageIterPtr &iter, uint64_t v ) + { + DBUS_W->eldbus_message_iter_arguments_append_impl(iter, v); + } + + /** + * @brief Marshals value from marshalled type into variable v + */ + static bool get( const DBusWrapper::MessageIterPtr &iter, uint64_t& v ) + { + return DBUS_W->eldbus_message_iter_get_and_next_impl(iter, v); + } +}; + +/** + * @brief Signature class for marshalling int16 type. + */ +template <> +struct signature< int16_t > +{ + /** + * @brief Returns name of type marshalled, for informative purposes + */ + static std::string name() + { + return "int16_t"; + } + + /** + * @brief Returns DBUS' signature of type marshalled + */ + static std::string sig() + { + return "n"; + } + + /** + * @brief Marshals value v as marshalled type into message + */ + static void set( const DBusWrapper::MessageIterPtr &iter, int16_t v ) + { + DBUS_W->eldbus_message_iter_arguments_append_impl(iter, v); + } + + /** + * @brief Marshals value from marshalled type into variable v + */ + static bool get( const DBusWrapper::MessageIterPtr &iter, int16_t& v ) + { + return DBUS_W->eldbus_message_iter_get_and_next_impl(iter, v); + } +}; + +/** + * @brief Signature class for marshalling int32 type. + */ +template <> +struct signature< int32_t > +{ + /** + * @brief Returns name of type marshalled, for informative purposes + */ + static std::string name() + { + return "int32_t"; + } + + /** + * @brief Returns DBUS' signature of type marshalled + */ + static std::string sig() + { + return "i"; + } + + /** + * @brief Marshals value v as marshalled type into message + */ + static void set( const DBusWrapper::MessageIterPtr &iter, int32_t v ) + { + DBUS_W->eldbus_message_iter_arguments_append_impl(iter, v); + } + + /** + * @brief Marshals value from marshalled type into variable v + */ + static bool get( const DBusWrapper::MessageIterPtr &iter, int32_t& v ) + { + return DBUS_W->eldbus_message_iter_get_and_next_impl(iter, v); + } +}; + +/** + * @brief Signature class for marshalling int64 type. + */ +template <> +struct signature< int64_t > +{ + /** + * @brief Returns name of type marshalled, for informative purposes + */ + static std::string name() + { + return "int64_t"; + } + + /** + * @brief Returns DBUS' signature of type marshalled + */ + static std::string sig() + { + return "x"; + } + + /** + * @brief Marshals value v as marshalled type into message + */ + static void set( const DBusWrapper::MessageIterPtr &iter, int64_t v ) + { + DBUS_W->eldbus_message_iter_arguments_append_impl(iter, v); + } + + /** + * @brief Marshals value from marshalled type into variable v + */ + static bool get( const DBusWrapper::MessageIterPtr &iter, int64_t& v ) + { + return DBUS_W->eldbus_message_iter_get_and_next_impl(iter, v); + } +}; + +/** + * @brief Signature class for marshalling double type. + */ +template <> +struct signature< double > +{ + /** + * @brief Returns name of type marshalled, for informative purposes + */ + static std::string name() + { + return "double"; + } + + /** + * @brief Returns DBUS' signature of type marshalled + */ + static std::string sig() + { + return "d"; + } + + /** + * @brief Marshals value v as marshalled type into message + */ + static void set( const DBusWrapper::MessageIterPtr &iter, double v ) + { + DBUS_W->eldbus_message_iter_arguments_append_impl(iter, v); + } + + /** + * @brief Marshals value from marshalled type into variable v + */ + static bool get( const DBusWrapper::MessageIterPtr &iter, double& v ) + { + return DBUS_W->eldbus_message_iter_get_and_next_impl(iter, v); + } + + /** + * @brief Marshals value from marshalled type into variable v + */ + static bool get( const DBusWrapper::MessageIterPtr &iter, float& v2 ) + { + double v = 0; + auto r = DBUS_W->eldbus_message_iter_get_and_next_impl(iter, v); + v2 = static_cast< float >( v ); + return r; + } +}; + +/** + * @brief Signature class for marshalling float type. + */ +template <> +struct signature< float > +{ + /** + * @brief Returns name of type marshalled, for informative purposes + */ + static std::string name() + { + return "float"; + } + + /** + * @brief Returns DBUS' signature of type marshalled + */ + static std::string sig() + { + return "d"; + } + + /** + * @brief Marshals value v as marshalled type into message + */ + static void set( const DBusWrapper::MessageIterPtr &iter, float v ) + { + DBUS_W->eldbus_message_iter_arguments_append_impl(iter, v); + } + + /** + * @brief Marshals value from marshalled type into variable v + */ + static bool get( const DBusWrapper::MessageIterPtr &iter, double& v ) + { + return DBUS_W->eldbus_message_iter_get_and_next_impl(iter, v); + } + + /** + * @brief Marshals value from marshalled type into variable v + */ + static bool get( const DBusWrapper::MessageIterPtr &iter, float& v2 ) + { + double v = 0; + auto r = DBUS_W->eldbus_message_iter_get_and_next_impl(iter, v); + v2 = static_cast< float >( v ); + return r; + } +}; + +/** + * @brief Signature class for marshalling boolean type. + */ +template <> +struct signature< bool > +{ + /** + * @brief Returns name of type marshalled, for informative purposes + */ + static std::string name() + { + return "bool"; + } + + /** + * @brief Returns DBUS' signature of type marshalled + */ + static std::string sig() + { + return "b"; + } + + /** + * @brief Marshals value v as marshalled type into message + */ + static void set( const DBusWrapper::MessageIterPtr &iter, bool v ) + { + DBUS_W->eldbus_message_iter_arguments_append_impl(iter, v); + } + + /** + * @brief Marshals value from marshalled type into variable v + */ + static bool get( const DBusWrapper::MessageIterPtr &iter, bool& v ) + { + return DBUS_W->eldbus_message_iter_get_and_next_impl(iter, v); + } +}; + +template < typename T > +struct signature< T, typename std::enable_if< std::is_enum< T >::value, void >::type > +{ + /** + * @brief Returns name of type marshalled, for informative purposes + */ + static std::string name() + { + return "enum"; + } + + /** + * @brief Returns DBUS' signature of type marshalled + */ + static std::string sig() + { + return signature::type>::sig(); + } + + /** + * @brief Marshals value v as marshalled type into message + */ + static void set( const DBusWrapper::MessageIterPtr &iter, T v ) + { + signature::type>::set(iter, static_cast< int64_t >( v )); + } + + /** + * @brief Marshals value from marshalled type into variable v + */ + static bool get( const DBusWrapper::MessageIterPtr &iter, T& v ) + { + typename std::underlying_type::type q = 0; + if (!signature::type>::get(iter, q)) + return false; + + v = static_cast< T >( q ); + return true; + } +}; + +/** + * @brief Signature class for marshalling string type. + * + * Both (const) char * and std::string types are accepted as value to send. + * Only std::string is accepted as value to receive. + */ +template <> +struct signature< std::string > +{ + /** + * @brief Returns name of type marshalled, for informative purposes + */ + static std::string name() + { + return "string"; + } + + /** + * @brief Returns DBUS' signature of type marshalled + */ + static std::string sig() + { + return "s"; + } + + /** + * @brief Marshals value v as marshalled type into message + */ + static void set( const DBusWrapper::MessageIterPtr &iter, const std::string& v ) + { + DBUS_W->eldbus_message_iter_arguments_append_impl(iter, v); + } + +// /** +// * @brief Marshals value v as marshalled type into message +// */ +// static void set( const DBusWrapper::MessageIterPtr &iter, const char* v ) +// { +// DBUS_W->eldbus_message_iter_arguments_append_impl(iter, v); +// } + + /** + * @brief Marshals value from marshalled type into variable v + */ + static bool get( const DBusWrapper::MessageIterPtr &iter, std::string& v ) + { + return DBUS_W->eldbus_message_iter_get_and_next_impl(iter, v); + } +}; + +template <> +struct signature< ObjectPath > +{ + /** + * @brief Returns name of type marshalled, for informative purposes + */ + static std::string name() + { + return "path"; + } + + /** + * @brief Returns DBUS' signature of type marshalled + */ + static std::string sig() + { + return "o"; + } + + /** + * @brief Marshals value v as marshalled type into message + */ + static void set( const DBusWrapper::MessageIterPtr &iter, const std::string& v ) + { + DBUS_W->eldbus_message_iter_arguments_append_impl(iter, ObjectPath{ v }); + } + + /** + * @brief Marshals value v as marshalled type into message + */ + static void set( const DBusWrapper::MessageIterPtr &iter, const ObjectPath& v ) + { + DBUS_W->eldbus_message_iter_arguments_append_impl(iter, v); + } + + /** + * @brief Marshals value v as marshalled type into message + */ + static void set( const DBusWrapper::MessageIterPtr &iter, const char* v ) + { + DBUS_W->eldbus_message_iter_arguments_append_impl(iter, ObjectPath{ v }); + } + + /** + * @brief Marshals value from marshalled type into variable v + */ + static bool get( const DBusWrapper::MessageIterPtr &iter, ObjectPath& v ) + { + return DBUS_W->eldbus_message_iter_get_and_next_impl(iter, v); + } + + /** + * @brief Marshals value from marshalled type into variable v + */ + static bool get( const DBusWrapper::MessageIterPtr &iter, std::string& v ) + { + ObjectPath q; + if (!DBUS_W->eldbus_message_iter_get_and_next_impl(iter, q)) return false; + v = std::move(q.value); + return true; + } +}; + +/** + * @brief Signature class for marshalling (const) char * type. + * + * Both (const) char * and std::string types are accepted as value to send. + * You can't use (const) char * variable type to receive value. + */ +template <> +struct signature< char* > +{ + /** + * @brief Returns name of type marshalled, for informative purposes + */ + static std::string name() + { + return "string"; + } + + /** + * @brief Returns DBUS' signature of type marshalled + */ + static std::string sig() + { + return "s"; + } + + /** + * @brief Marshals value v as marshalled type into message + */ + static void set( const DBusWrapper::MessageIterPtr &iter, const std::string& v ) + { + DBUS_W->eldbus_message_iter_arguments_append_impl(iter, v); + } + + /** + * @brief Marshals value v as marshalled type into message + */ + static void set( const DBusWrapper::MessageIterPtr &iter, const char* v ) + { + DBUS_W->eldbus_message_iter_arguments_append_impl(iter, std::string{ v }); + } +}; +/// \cond + +template < size_t INDEX, typename A, typename... ARGS > +struct signature_tuple_element_type_helper +{ + using type = typename signature_tuple_element_type_helper< INDEX - 1, ARGS... >::type; +}; + +template < typename A, typename... ARGS > +struct signature_tuple_element_type_helper< 0, A, ARGS... > +{ + using type = A; +}; +/// \endcond + +/** + * @brief Helper class to marshall tuples + * + * This class marshals all elements of the tuple value starting at the index INDEX + * and incrementing. This class recursively calls itself with increasing INDEX value + * until INDEX is equal to SIZE, where recursive calling ends. + */ +template < size_t INDEX, size_t SIZE, typename... ARGS > +struct signature_tuple_helper +{ + using current_type = typename signature_tuple_element_type_helper< INDEX, ARGS... >::type; + + /** + * @brief Returns name of type marshalled, for informative purposes + */ + static std::string name() + { + if( INDEX + 1 >= SIZE ) + return signature< current_type >::name(); + return signature< current_type >::name() + ", " + signature_tuple_helper< INDEX + 1, SIZE, ARGS... >::name(); + } + + /** + * @brief Returns DBUS' signature of type marshalled + */ + static std::string sig() + { + return signature< current_type >::sig() + signature_tuple_helper< INDEX + 1, SIZE, ARGS... >::sig(); + } + + /** + * @brief Marshals value v as marshalled type into message + */ + static void set( const DBusWrapper::MessageIterPtr &iter, const std::tuple< ARGS... >& args ) + { + signature< current_type >::set( iter, std::get< INDEX >( args ) ); + signature_tuple_helper< INDEX + 1, SIZE, ARGS... >::set( iter, args ); + } + + /** + * @brief Marshals value from marshalled type into variable v + */ + static bool get( const DBusWrapper::MessageIterPtr &iter, std::tuple< ARGS... >& args ) + { + return signature< current_type >::get( iter, std::get< INDEX >( args ) ) && + signature_tuple_helper< INDEX + 1, SIZE, ARGS... >::get( iter, args ); + } +}; + +/** + * @brief Helper class to marshall tuples + * + * This class marks end of the tuple marshalling. Members of this class are called + * when INDEX value is equal to SIZE. + */ +template < size_t SIZE, typename... ARGS > +struct signature_tuple_helper< SIZE, SIZE, ARGS... > +{ + /** + * @brief Returns name of type marshalled, for informative purposes + */ + static std::string name() + { + return ""; + } + + /** + * @brief Returns DBUS' signature of type marshalled + */ + static std::string sig() + { + return ""; + } + + /** + * @brief Marshals value v as marshalled type into message + */ + static void set( const DBusWrapper::MessageIterPtr &iter, const std::tuple< ARGS... >& args ) + { + } + + /** + * @brief Marshals value from marshalled type into variable v + */ + static bool get( const DBusWrapper::MessageIterPtr &iter, std::tuple< ARGS... >& args ) + { + return true; + } +}; + +/** + * @brief Signature class for marshalling tuple of values + * + * This class marshalls tuple of values. This represents + * DBUS struct typle, encoded with character 'r' + */ +template < typename... ARGS > +struct signature< std::tuple< ARGS... > > +{ + /** + * @brief Returns name of type marshalled, for informative purposes + */ + static std::string name() + { + return "tuple<" + signature_tuple_helper< 0, sizeof...( ARGS ), ARGS... >::name() + ">"; + } + + /** + * @brief Returns DBUS' signature of type marshalled + */ + static std::string sig() + { + return "(" + signature_tuple_helper< 0, sizeof...( ARGS ), ARGS... >::sig() + ")"; + } + + /** + * @brief Marshals value v as marshalled type into message + */ + static void set( const DBusWrapper::MessageIterPtr &iter, const std::tuple< ARGS... >& args ) + { + auto entry = DBUS_W->eldbus_message_iter_container_new_impl( iter, 'r', ""); + signature_tuple_helper< 0, sizeof...( ARGS ), ARGS... >::set( entry, args ); + } + + /** + * @brief Marshals value from marshalled type into variable v + */ + static bool get( const DBusWrapper::MessageIterPtr &iter, std::tuple< ARGS... >& args ) + { + auto entry = DBUS_W->eldbus_message_iter_get_and_next_by_type_impl( iter, 'r' ); + if (!entry) return false; + return signature_tuple_helper< 0, sizeof...( ARGS ), ARGS... >::get( entry, args ); + } +}; + +/** + * @brief Signature class for marshalling ValueOrError template type + * + * ValueOrError template type is used to marshall list of values passed to + * DBUS (or received from) at the "top" level. For example ss(s) is represented as + * \code{.cpp} ValueOrError> \endcode + * While (ss(s)) is represented as + * \code{.cpp} std::tuple> \endcode + * or + * \code{.cpp} ValueOrError>> \endcode + */ +template < typename... ARGS > +struct signature< ValueOrError< ARGS... > > +{ + /** + * @brief Returns name of type marshalled, for informative purposes + */ + static std::string name() + { + return "ValueOrError<" + signature_tuple_helper< 0, sizeof...( ARGS ), ARGS... >::name() + ">"; + } + + /** + * @brief Returns DBUS' signature of type marshalled + */ + static std::string sig() + { + return signature_tuple_helper< 0, sizeof...( ARGS ), ARGS... >::sig(); + } + + /** + * @brief Marshals value v as marshalled type into message + */ + static void set( const DBusWrapper::MessageIterPtr &iter, const ValueOrError< ARGS... >& args ) + { + signature_tuple_helper< 0, sizeof...( ARGS ), ARGS... >::set( iter, args.getValues() ); + } + + /** + * @brief Marshals value from marshalled type into variable v + */ + static bool get( const DBusWrapper::MessageIterPtr &iter, ValueOrError< ARGS... >& args ) + { + return signature_tuple_helper< 0, sizeof...( ARGS ), ARGS... >::get( iter, args.getValues() ); + } +}; + +/** + * @brief Signature class for marshalling ValueOrError type + */ +template <> +struct signature< ValueOrError< void > > +{ + /** + * @brief Returns name of type marshalled, for informative purposes + */ + static std::string name() + { + return "ValueOrError"; + } + + /** + * @brief Returns DBUS' signature of type marshalled + */ + static std::string sig() + { + return ""; + } + + /** + * @brief Marshals value v as marshalled type into message + */ + static void set( const DBusWrapper::MessageIterPtr &iter, const ValueOrError< void >& args ) + { + } + + /** + * @brief Marshals value from marshalled type into variable v + */ + static bool get( const DBusWrapper::MessageIterPtr &iter, ValueOrError< void >& args ) + { + return true; + } +}; + +/** + * @brief Signature class for marshalling ValueOrError<> type + */ +template <> +struct signature< ValueOrError<> > +{ + /** + * @brief Returns name of type marshalled, for informative purposes + */ + static std::string name() + { + return "ValueOrError<>"; + } + + /** + * @brief Returns DBUS' signature of type marshalled + */ + static std::string sig() + { + return ""; + } + + /** + * @brief Marshals value v as marshalled type into message + */ + static void set( const DBusWrapper::MessageIterPtr &iter, const ValueOrError<>& args ) + { + } + + /** + * @brief Marshals value from marshalled type into variable v + */ + static bool get( const DBusWrapper::MessageIterPtr &iter, ValueOrError<>& args ) + { + return true; + } +}; + +/** + * @brief Signature class for marshalling pair of types + */ +template < typename A, typename B > +struct signature< std::pair< A, B > > +{ + /** + * @brief Returns name of type marshalled, for informative purposes + */ + static std::string name() + { + return "pair<" + signature_tuple_helper< 0, 2, A, B >::name() + ">"; + } + + /** + * @brief Returns DBUS' signature of type marshalled + */ + static std::string sig() + { + return "(" + signature_tuple_helper< 0, 2, A, B >::sig() + ")"; + } + + /** + * @brief Marshals value v as marshalled type into message + */ + static void set( const DBusWrapper::MessageIterPtr &iter, const std::pair< A, B >& ab, bool dictionary = false ) + { + auto entry = DBUS_W->eldbus_message_iter_container_new_impl( iter, dictionary ? 'e' : 'r', ""); + signature_tuple_helper< 0, 2, A, B >::set( entry, ab ); + } + + /** + * @brief Marshals value from marshalled type into variable v + */ + static bool get( const DBusWrapper::MessageIterPtr &iter, std::pair< A, B >& ab ) + { + auto entry = DBUS_W->eldbus_message_iter_get_and_next_by_type_impl( iter, 'r' ); + if (!entry) { + entry = DBUS_W->eldbus_message_iter_get_and_next_by_type_impl( iter, '{' ); + if (!entry) return false; + } + + std::tuple< A, B > ab_tmp; + auto z = signature_tuple_helper< 0, 2, A, B >::get( entry, ab_tmp ); + if( z ) + { + ab.first = std::move( std::get< 0 >( ab_tmp ) ); + ab.second = std::move( std::get< 1 >( ab_tmp ) ); + } + return z; + } +}; + +/** + * @brief Signature class for marshalling std::vector template type + * + * This marshals container's content as DBUS a ascii character type code. + */ +template < typename A > +struct signature< std::vector< A > > +{ + /** + * @brief Returns name of type marshalled, for informative purposes + */ + static std::string name() + { + return "vector<" + signature< A >::name() + ">"; + } + + /** + * @brief Returns DBUS' signature of type marshalled + */ + static std::string sig() + { + return "a" + signature< A >::sig(); + } + + /** + * @brief Marshals value v as marshalled type into message + */ + static void set( const DBusWrapper::MessageIterPtr &iter, const std::vector< A >& v ) + { + auto lst = DBUS_W->eldbus_message_iter_container_new_impl( iter, 'a', signature< A >::sig()); + bart_assert( lst ); + for( auto& a : v ) + { + signature< A >::set( lst, a ); + } + } + + /** + * @brief Marshals value from marshalled type into variable v + */ + static bool get( const DBusWrapper::MessageIterPtr &iter, std::vector< A >& v ) + { + auto s = DBUS_W->eldbus_message_iter_get_and_next_by_type_impl( iter, 'a' ); + if (!s) return false; + v.clear(); + A a; + while( signature< A >::get( s, a ) ) + v.push_back( std::move( a ) ); + + return true; + } +}; + +/** + * @brief Signature class for marshalling std::array template type + * + * This marshals container's content as DBUS a ascii character type code. + */ +template < typename A, size_t N > +struct signature< std::array< A, N > > +{ + /** + * @brief Returns name of type marshalled, for informative purposes + */ + static std::string name() + { + return "array<" + signature< A >::name() + ", " + std::to_string( N ) + ">"; + } + + /** + * @brief Returns DBUS' signature of type marshalled + */ + static std::string sig() + { + return "a" + signature< A >::sig(); + } + + /** + * @brief Marshals value v as marshalled type into message + */ + static void set( const DBusWrapper::MessageIterPtr &iter, const std::array< A, N >& v ) + { + auto lst = DBUS_W->eldbus_message_iter_container_new_impl( iter, 'a', signature< A >::sig()); + bart_assert( lst ); + for( auto& a : v ) + { + signature< A >::set( lst, a ); + } + } + + /** + * @brief Marshals value from marshalled type into variable v + */ + static bool get( const DBusWrapper::MessageIterPtr &iter, std::array< A, N >& v ) + { + auto s = DBUS_W->eldbus_message_iter_get_and_next_by_type_impl( iter, 'a' ); + if ( !s ) + return false; + for( auto& a : v ) + { + if( !signature< A >::get( s, a ) ) + return false; + } + return true; + } +}; + +/** + * @brief Signature class for marshalling EldbusVariant type + * + * This marshals variant's content as DBUS v ascii character type code. + */ +template < typename A > +struct signature< EldbusVariant< A > > +{ + /** + * @brief Returns name of type marshalled, for informative purposes + */ + static std::string name() + { + return "variant<" + signature< A >::name() + ">"; + } + + /** + * @brief Returns DBUS' signature of type marshalled + */ + static std::string sig() + { + return "v"; + } + + /** + * @brief Marshals value v as marshalled type into message + */ + static void set( const DBusWrapper::MessageIterPtr &iter, const EldbusVariant< A >& v ) + { + set( iter, v.value ); + } + + /** + * @brief Marshals value v as marshalled type into message + */ + static void set( const DBusWrapper::MessageIterPtr &iter, const A& v ) + { + auto var = DBUS_W->eldbus_message_iter_container_new_impl( iter, 'v', signature< A >::sig()); + signature< A >::set( var, v ); + } + + /** + * @brief Marshals value from marshalled type into variable v + */ + static bool get( const DBusWrapper::MessageIterPtr &iter, EldbusVariant< A >& v ) + { + auto s = DBUS_W->eldbus_message_iter_get_and_next_by_type_impl( iter, 'v' ); + if( !s ) + return false; + return signature< A >::get( s, v.value ); + } +}; + +/** + * @brief Signature class for marshalling std::unordered_map template type + * + * This marshals container's content as DBUS {} ascii character type code. + * Note, that library doesnt check, if the key is basic type, as DBUS + * specification mandates. + * User can always exchange std::unordered_map for std::map and the reverse. + * User can receive such values as std::vector of std::pair values. + * Order of such values is unspecified. + */ +template < typename A, typename B > +struct signature< std::unordered_map< A, B > > +{ + /** + * @brief Returns name of type marshalled, for informative purposes + */ + static std::string name() + { + return "unordered_map<" + signature< A >::name() + ", " + signature< B >::name() + ">"; + } + + /** + * @brief Returns DBUS' signature of type marshalled + */ + static std::string sig() + { + return "a{" + signature_tuple_helper< 0, 2, A, B >::sig() + "}"; + } + + /** + * @brief Marshals value v as marshalled type into message + */ + static void set( const DBusWrapper::MessageIterPtr &iter, const std::unordered_map< A, B >& v ) + { + auto sig = "{" + signature_tuple_helper< 0, 2, A, B >::sig() + "}"; + auto lst = DBUS_W->eldbus_message_iter_container_new_impl( iter, 'a', sig); + bart_assert( lst ); + for( auto& a : v ) + { + signature< std::pair< A, B > >::set( lst, a, true ); + } + } + + /** + * @brief Marshals value from marshalled type into variable v + */ + static bool get( const DBusWrapper::MessageIterPtr &iter, std::unordered_map< A, B >& v ) + { + auto s = DBUS_W->eldbus_message_iter_get_and_next_by_type_impl( iter, 'a' ); + v.clear(); + if( !s ) + return false; + std::pair< A, B > a; + while( signature< std::pair< A, B > >::get( s, a ) ) + v.insert( std::move( a ) ); + return true; + } +}; + +/** + * @brief Signature class for marshalling std::unordered_map template type + * + * This marshals container's content as DBUS {} ascii character type code. + * Note, that library doesnt check, if the key is basic type, as DBUS + * specification mandates. + * User can always exchange std::unordered_map for std::map and the reverse. + * User can receive such values as std::vector of std::pair values. + * Order of such values is unspecified. + */ +template < typename A, typename B > +struct signature< std::map< A, B > > +{ + /** + * @brief Returns name of type marshalled, for informative purposes + */ + static std::string name() + { + return "map<" + signature< A >::name() + ", " + signature< B >::name() + ">"; + } + + /** + * @brief Returns DBUS' signature of type marshalled + */ + static std::string sig() + { + return "a{" + signature_tuple_helper< 0, 2, A, B >::sig() + "}"; + } + + /** + * @brief Marshals value v as marshalled type into message + */ + static void set( const DBusWrapper::MessageIterPtr &iter, const std::map< A, B >& v ) + { + auto sig = "{" + signature_tuple_helper< 0, 2, A, B >::sig() + "}"; + auto lst = DBUS_W->eldbus_message_iter_container_new_impl( iter, 'a', sig); + bart_assert( lst ); + for( auto& a : v ) + { + signature< std::pair< A, B > >::set( lst, a, true ); + } + } + + /** + * @brief Marshals value from marshalled type into variable v + */ + static bool get( const DBusWrapper::MessageIterPtr &iter, std::map< A, B >& v ) + { + auto s = DBUS_W->eldbus_message_iter_get_and_next_by_type_impl( iter, 'a' ); + if( !s ) + return false; + std::pair< A, B > a; + while( signature< std::pair< A, B > >::get( s, a ) ) + v.insert( std::move( a ) ); + return true; + } +}; + +/** + * @brief Signature helper class for marshalling const reference types + */ +template < typename A > +struct signature< const A& > +{ + /** + * @brief Returns name of type marshalled, for informative purposes + */ + static std::string name() + { + return "const " + signature< A >::name() + "&"; + } + + /** + * @brief Returns DBUS' signature of type marshalled + */ + static std::string sig() + { + return signature< A >::sig(); + } + + /** + * @brief Marshals value v as marshalled type into message + */ + static void set( const DBusWrapper::MessageIterPtr &iter, const A& v ) + { + signature< A >::set( iter, v ); + } + + /** + * @brief Marshals value from marshalled type into variable v + */ + static void get( const DBusWrapper::MessageIterPtr &iter, A& v ) + { + signature< A >::get( iter, v ); + } +}; + +/** + * @brief Signature helper class for marshalling reference types + */ +template < typename A > +struct signature< A& > +{ + /** + * @brief Returns name of type marshalled, for informative purposes + */ + static std::string name() + { + return signature< A >::name() + "&"; + } + + /** + * @brief Returns DBUS' signature of type marshalled + */ + static std::string sig() + { + return signature< A >::sig(); + } + + /** + * @brief Marshals value v as marshalled type into message + */ + static void set( const DBusWrapper::MessageIterPtr &iter, const A& v ) + { + signature< A >::set( iter, v ); + } + + /** + * @brief Marshals value from marshalled type into variable v + */ + static void get( const DBusWrapper::MessageIterPtr &iter, A& v ) + { + signature< A >::get( iter, v ); + } +}; + +/** + * @brief Signature helper class for marshalling const types + */ +template < typename A > +struct signature< const A > +{ + /** + * @brief Returns name of type marshalled, for informative purposes + */ + static std::string name() + { + return "const " + signature< A >::name(); + } + + /** + * @brief Returns DBUS' signature of type marshalled + */ + static std::string sig() + { + return signature< A >::sig(); + } + + /** + * @brief Marshals value v as marshalled type into message + */ + static void set( const DBusWrapper::MessageIterPtr &iter, const A& v ) + { + signature< A >::set( iter, v ); + } + + /** + * @brief Marshals value from marshalled type into variable v + */ + static void get( const DBusWrapper::MessageIterPtr &iter, A& v ) + { + signature< A >::get( iter, v ); + } +}; + +/// \cond +using CallId = DBusWrapper::CallId; + +template < typename ValueType > +ValueType unpackValues( CallId callId, const DBusWrapper::MessagePtr &msg ) +{ + auto iter = DBUS_W->eldbus_message_iter_get_impl( msg, false ); + ValueType r; + + if( iter ) + { + if( !signature< ValueType >::get( iter, r ) ) + { + DBUS_DEBUG( "ValueType is %s", signature< ValueType >::name().c_str() ); + r = Error{"call " + std::to_string( callId.id ) + ": failed to unpack values, got signature '" + + DBUS_W->eldbus_message_signature_get_impl( msg ) + "', expected '" + signature< ValueType >::sig() + "'"}; + } + } + else + { + r = Error{"call " + std::to_string( callId.id ) + ": failed to get iterator"}; + } + return r; +} + +inline void packValues_helper( const DBusWrapper::MessageIterPtr& ) {} + +template < typename A, typename... ARGS > +void packValues_helper( const DBusWrapper::MessageIterPtr &iter, A&& a, ARGS&&... r ) +{ + signature< A >::set( iter, std::forward< A >( a ) ); + packValues_helper( iter, std::forward< ARGS >( r )... ); +} + +template < typename... ARGS > +void packValues( CallId callId, const DBusWrapper::MessagePtr &msg, ARGS&&... r ) +{ + auto iter = DBUS_W->eldbus_message_iter_get_impl( msg, true ); + packValues_helper( iter, std::forward< ARGS >( r )... ); +} + +template < typename > +struct ReturnType; +template < typename R, typename... ARGS > +struct ReturnType< R( ARGS... ) > +{ + using type = R; +}; + +template < typename R, typename... ARGS > +struct ReturnType< std::function< R( ARGS... ) > > +{ + using type = R; +}; + +template < int... > +struct sequence +{ +}; + +template < int N, int... S > +struct sequence_gen : sequence_gen< N - 1, N - 1, S... > +{ +}; + +template < int... S > +struct sequence_gen< 0, S... > +{ + typedef sequence< S... > type; +}; + +template < typename C, typename... ARGS > +struct apply_helper +{ + const std::function< C >& c; + const std::tuple< ARGS... >& args; + + template < int... S > + auto apply_2( sequence< S... > ) const -> decltype( c( std::get< S >( args )... ) ) + { + return c( std::get< S >( args )... ); + } + auto apply_1() const -> decltype( apply_2( typename sequence_gen< sizeof...( ARGS ) >::type() ) ) + { + return apply_2( typename sequence_gen< sizeof...( ARGS ) >::type() ); + } +}; + +template < typename C, typename A, typename... ARGS > +struct apply_helper_2 +{ + const std::function< C >& c; + const A& a; + const std::tuple< ARGS... >& args; + + template < int... S > + auto apply_2( sequence< S... > ) const -> decltype( c( a, std::get< S >( args )... ) ) + { + return c( a, std::get< S >( args )... ); + } + auto apply_1() const -> decltype( apply_2( typename sequence_gen< sizeof...( ARGS ) >::type() ) ) + { + return apply_2( typename sequence_gen< sizeof...( ARGS ) >::type() ); + } +}; + +template < typename C, typename... ARGS > +auto apply( const std::function< C >& c, const std::tuple< ARGS... >& args ) -> typename ReturnType< C >::type +{ + apply_helper< C, ARGS... > ah{c, args}; + return ah.apply_1(); +} + +template < typename C, typename D, typename... ARGS > +auto apply( const std::function< C >& c, const D& d, const std::tuple< ARGS... >& args ) -> typename ReturnType< C >::type +{ + apply_helper_2< C, D, ARGS... > ah{c, d, args}; + return ah.apply_1(); +} + +struct ConnectionState +{ + DBusWrapper::ConnectionPtr connection; + DBusWrapper::ObjectPtr object; + DBusWrapper::ProxyPtr proxy, propertiesProxy; +}; + +template < typename RETTYPE, typename... ARGS > +RETTYPE call( CallId callId, const ConnectionState& connectionState, bool property, const std::string& funcName, const ARGS&... args ) +{ + const auto &proxy = property ? connectionState.propertiesProxy : connectionState.proxy; + if( !proxy ) + { + DBUS_DEBUG( "call %d: not initialized", callId.id ); + return Error{"not initialized"}; + } + + DBUS_DEBUG( "call %d: calling '%s'", callId.id, funcName.c_str() ); + auto msg = DBUS_W->eldbus_proxy_method_call_new_impl(proxy, funcName); + if( !msg ) + { + DBUS_DEBUG( "call %d: failed", callId.id ); + return Error{"failed to create message"}; + } + + detail::packValues( callId, msg, args... ); + auto reply = DBUS_W->eldbus_proxy_send_and_block_impl( proxy, msg ); + DBUS_DEBUG( "call %d: calling '%s' done", callId.id, funcName.c_str() ); + if( !reply ) + { + DBUS_DEBUG( "call %d: failed", callId.id ); + return Error{"eldbus returned null as reply"}; + } + std::string errname, errmsg; + if( DBUS_W->eldbus_message_error_get_impl( reply, errname, errmsg ) ) + { + DBUS_DEBUG( "call %d: %s: %s", callId.id, errname.c_str(), errmsg.c_str() ); + return Error{errname + ": " + errmsg}; + } + DBUS_DEBUG( "call %d: got reply with signature '%s'", callId.id, + DBUS_W->eldbus_message_signature_get_impl( reply ).c_str() ); + return detail::unpackValues< RETTYPE >( callId, reply ); +} + +template < typename RETTYPE, typename... ARGS > +void asyncCall( CallId callId, const ConnectionState &connectionState, + bool property, const std::string& funcName, + std::function< void( RETTYPE ) > callback, const ARGS&... args ) +{ + const auto &proxy = property ? connectionState.propertiesProxy : connectionState.proxy; + if( !proxy ) + { + DBUS_DEBUG( "call %d: not initialized", callId.id ); + callback( Error{"not initialized"} ); + return; + } + + auto msg = DBUS_W->eldbus_proxy_method_call_new_impl( proxy, funcName ); + if( !msg ) + { + DBUS_DEBUG( "call %d: failed", callId.id ); + callback( Error{"failed to create message"} ); + return; + } + + detail::packValues( callId, msg, args... ); + auto pending = DBUS_W->eldbus_proxy_send_impl( proxy, msg, [callback, callId, proxy]( const DBusWrapper::MessagePtr &reply ) { + DBUS_DEBUG( "call %d: calling done", callId.id ); + if( !reply ) + { + DBUS_DEBUG( "call %d: failed", callId.id ); + callback( Error{"eldbus returned null as reply"} ); + } + else + { + std::string errname, errmsg; + if( DBUS_W->eldbus_message_error_get_impl( reply, errname, errmsg ) ) + { + DBUS_DEBUG( "call %d: %s: %s", callId.id, errname.c_str(), errmsg.c_str() ); + callback( Error{errname + ": " + errmsg} ); + } + else + { + DBUS_DEBUG( "call %d: got reply with signature '%s'", callId.id, + DBUS_W->eldbus_message_signature_get_impl( reply ).c_str() ); + callback( detail::unpackValues< RETTYPE >( callId, reply ) ); + } + } + } + ); + if( pending ) + { + DBUS_DEBUG( "call %d: call sent", callId.id ); + } + else + { + DBUS_DEBUG( "call %d: failed to send call", callId.id ); + callback( Error{"failed to send call"} ); + } +} + +inline void displayDebugCallInfo( CallId callId, const std::string& funcName, const std::string& info, const std::string& interfaceName ) +{ + DBUS_DEBUG( "call %d: %s iname = %s fname = %s", callId.id, info.c_str(), interfaceName.c_str(), funcName.c_str() ); +} + +inline void displayDebugCallInfoSignal( CallId callId, const std::string& funcName, const std::string& info, const std::string& interfaceName ) +{ + DBUS_DEBUG( "call %d: %s signal iname = %s fname = %s", callId.id, info.c_str(), interfaceName.c_str(), funcName.c_str() ); +} + +inline void displayDebugCallInfoProperty( CallId callId, const std::string& funcName, std::string info, const std::string& interfaceName, + const std::string& propertyName ) +{ + DBUS_DEBUG( "call %d: %s iname = %s pname = %s", callId.id, info.c_str(), interfaceName.c_str(), propertyName.c_str() ); +} + +using StringStorage = DBusWrapper::StringStorage; + +template < typename A, typename... ARGS > +struct EldbusArgGenerator_Helper +{ + static void add( std::vector< std::pair >& r ) + { + auto s = r.size(); + auto sig = signature< A >::sig(); + bart_assert( !sig.empty() ); + auto name = "p" + std::to_string( s + 1 ); + r.push_back({ std::move(sig), std::move(name) }); + EldbusArgGenerator_Helper::add( r ); + } +}; + +template <> +struct EldbusArgGenerator_Helper< void > +{ + static void add( std::vector< std::pair >& ) + { + } +}; + +template <> +struct EldbusArgGenerator_Helper< ValueOrError< void >, void > +{ + static void add( std::vector< std::pair >& ) + { + } +}; +template <> +struct EldbusArgGenerator_Helper< ValueOrError<>, void > +{ + static void add( std::vector< std::pair >& ) + { + } +}; + +template < typename... ARGS > +struct EldbusArgGenerator_Helper< std::tuple< ARGS... > > +{ + static void add( std::vector< std::pair >& r ) + { + EldbusArgGenerator_Helper< ARGS..., void >::add( r ); + } +}; + +template < typename RetType > +struct dbus_interface_return_type_traits +{ + using type = ValueOrError< RetType >; +}; + +template < typename... ARGS > +struct dbus_interface_return_type_traits< ValueOrError< ARGS... > > +{ + using type = ValueOrError< ARGS... >; +}; + +template < typename T > +struct dbus_interface_traits; +template < typename RetType, typename... ARGS > +struct dbus_interface_traits< RetType( ARGS... ) > +{ + using Ret = typename dbus_interface_return_type_traits< RetType >::type; + using SyncCB = std::function< Ret( ARGS... ) >; + using AsyncCB = std::function< void( std::function< void( Ret ) >, ARGS... ) >; + using VEArgs = ValueOrError< ARGS... >; +}; + +template < typename T > +struct EldbusArgGenerator_Args; +template < typename RetType, typename... ARGS > +struct EldbusArgGenerator_Args< RetType( ARGS... ) > +{ + static std::string name() + { + return signature_tuple_helper< 0, sizeof...( ARGS ), ARGS... >::name(); + } + static std::vector< std::pair > get() + { + std::vector< std::pair > tmp; + EldbusArgGenerator_Helper< ARGS..., void >::add( tmp ); + return tmp; + } +}; + +template < typename T > +struct EldbusArgGenerator_ReturnType; +template < typename RetType, typename... ARGS > +struct EldbusArgGenerator_ReturnType< RetType( ARGS... ) > +{ + static std::string name() + { + return signature< RetType >::name(); + } + static std::vector< std::pair > get( ) + { + std::vector< std::pair > tmp; + EldbusArgGenerator_Helper< RetType, void >::add( tmp ); + return tmp; + } +}; + +template < typename T > +struct EldbusArgGenerator_ReturnType; +template < typename... ARGS > +struct EldbusArgGenerator_ReturnType< void( ARGS... ) > +{ + static std::string name() + { + return ""; + } + static std::vector< std::pair > get( ) + { + return {}; + } +}; +/// \endcond +} + +using ConnectionType = DBusWrapper::ConnectionType; + +/** + * @brief Class representing client's end of DBUS connection + * + * Allows calling (synchronous and asynchronos) methods on selected interface + * Allows (synchronous and asynchronos) setting / getting properties. + * Allows registering signals. + */ +class DBusClient +{ + /// \cond + struct ConnectionInfo + { + std::string interfaceName, busName, pathName; + }; + /// \endcond +public: + /** + * @brief Default constructor, creates non-connected object. + */ + DBusClient() = default; + + /** + * @brief Connects to dbus choosen by tp, using given arguments + * + * @param bus_name name of the bus to connect to + * @param path_name object's path + * @param interface_name interface name + */ + DBusClient( std::string busName_, std::string pathName_, std::string interfaceName_, + ConnectionType tp ); + + /** + * @brief Connects to dbus using connection conn + * + * @param bus_name name of the bus to connect to + * @param path_name object's path + * @param interface_name interface name + * @param conn connection object from getDBusConnectionByType call + */ + DBusClient( std::string busName_, std::string pathName_, std::string interfaceName_, + const DBusWrapper::ConnectionPtr &conn = {} ); + + /** + * @brief Destructor object. + * + * All signals added will be disconnected. + * All asynchronous calls will be cancelled, their callback's will be called + * with failure message. + */ + ~DBusClient() = default; + DBusClient( const DBusClient& ) = delete; + DBusClient( DBusClient&& ) = default; + + DBusClient& operator=( DBusClient&& ) = default; + DBusClient& operator=( const DBusClient& ) = delete; + + /** + * @brief bool operator + * + * Returns true, if object is connected to DBUS + */ + explicit operator bool() const + { + return bool( connectionState->proxy ); + } + + /** + * @brief Helper class for calling a method + * + * Template type T defines both arguments sent to the method + * and expected values. Receiving different values will be reported as + * error. For example: + * \code{.cpp} Method \endcode + * defines method, which takes two arguments (two floats) and return + * single value of type int. + */ + template < typename T > + struct Method + { + /// \cond + using RetType = typename detail::dbus_interface_traits< T >::Ret; + const detail::ConnectionState &connectionState; + std::string funcName; + std::string info; + std::shared_ptr< ConnectionInfo > connectionInfo; + /// \endcond + + /** + * @brief Executes synchronous call on DBUS's method + * + * The function returns ValueOrError<...> object, which + * contains either received values or error message. + * + * @param args arguments to pass to the method + */ + template < typename... ARGS > + RetType call( const ARGS&... args ) + { + detail::CallId callId; + detail::displayDebugCallInfo( callId, funcName, info, connectionInfo->interfaceName ); + return detail::call< RetType >( callId, connectionState, false, funcName, args... ); + } + + /** + * @brief Executes asynchronous call on DBUS's method + * + * The function calls callback with either received values or error message. + * + * @param callback callback functor, which will be called with return value(s) or error message + * @param args arguments to pass to the method + */ + template < typename... ARGS > + void asyncCall( std::function< void( RetType ) > callback, const ARGS&... args ) + { + detail::CallId callId; + detail::displayDebugCallInfo( callId, funcName, info, connectionInfo->interfaceName ); + detail::asyncCall< RetType >( callId, connectionState, false, funcName, std::move( callback ), args... ); + } + }; + + /** + * @brief Helper class for calling a property + * + * Template type T defines type of the value hidden under property. + * Note, that library automatically wraps both sent and received value into + * DBUS's wrapper type. + */ + template < typename T > + struct Property + { + /// \cond + using RetType = typename detail::dbus_interface_return_type_traits< T >::type; + using VariantRetType = typename detail::dbus_interface_return_type_traits< EldbusVariant< T > >::type; + const detail::ConnectionState &connectionState; + std::string propName; + std::string info; + std::shared_ptr< ConnectionInfo > connectionInfo; + /// \endcond + + /** + * @brief executes synchronous get on property + * + * The function returns ValueOrError<...> object, which + * contains either received values or error message. + */ + RetType get() + { + detail::CallId callId; + detail::displayDebugCallInfoProperty( callId, "Get", info, connectionInfo->interfaceName, propName ); + auto z = detail::call< VariantRetType >( callId, connectionState, true, "Get", connectionInfo->interfaceName, propName ); + if( !z ) + return z.getError(); + return {std::get< 0 >( z.getValues() ).value}; + } + + /** + * @brief executes asynchronous get on property + * + * The function calls callback with either received values or error message. + * + * @param callback callback functor, which will be called with return value(s) or error message + */ + void asyncGet( std::function< void( RetType ) > callback ) + { + detail::CallId callId; + detail::displayDebugCallInfoProperty( callId, "Get", info, connectionInfo->interfaceName, propName ); + auto cc = [callback]( VariantRetType reply ) { + if( reply ) + callback( std::move( std::get< 0 >( reply.getValues() ).value ) ); + else + callback( reply.getError() ); + }; + detail::asyncCall< VariantRetType >( callId, connectionState, true, "Get", std::move( cc ), connectionInfo->interfaceName, propName ); + } + + /** + * @brief executes synchronous set on property + * + * The function returns ValueOrError object, with + * possible error message. + */ + ValueOrError< void > set( const T& r ) + { + detail::CallId callId; + detail::displayDebugCallInfoProperty( callId, "Set", info, connectionInfo->interfaceName, propName ); + EldbusVariant< T > variantValue{std::move( r )}; + return detail::call< ValueOrError< void > >( callId, connectionState, true, "Set", connectionInfo->interfaceName, propName, variantValue ); + } + + /** + * @brief executes asynchronous get on property + * + * The function calls callback with either received values or error message. + * + * @param callback callback functor, which will be called with return value(s) or error message + */ + void asyncSet( std::function< void( ValueOrError< void > ) > callback, const T& r ) + { + detail::CallId callId; + detail::displayDebugCallInfoProperty( callId, "Set", info, connectionInfo->interfaceName, propName ); + EldbusVariant< T > variantValue{std::move( r )}; + detail::asyncCall< ValueOrError< void > >( callId, connectionState, true, "Set", std::move( callback ), connectionInfo->interfaceName, propName, variantValue ); + } + }; + + /** + * @brief Constructs Property<...> object for calling properties + * + * The function constructs and returns proxy object for calling property. + * + * @param propName property name to set and / or query + */ + template < typename PropertyType > + Property< PropertyType > property( std::string propName ) + { + return Property< PropertyType >{*connectionState, std::move( propName ), info, connectionInfo}; + } + + /** + * @brief Constructs Method<...> object for calling methods + * + * The function constructs and returns proxy object for calling method. + * + * @param funcName function name to call + */ + template < typename MethodType > + Method< MethodType > method( std::string funcName ) + { + return Method< MethodType >{*connectionState, std::move( funcName ), info, connectionInfo}; + } + + /** + * @brief Registers notification callback, when property has changed + * + * The callback will be called with new value, when property's value has changed. + * Note, that template type V must match expected type, otherwise undefined behavior will occur, + * there's no check for this. + */ + template < typename V > + void addPropertyChangedEvent( std::string propertyName, std::function< void( V ) > callback ) + { + detail::CallId callId; + detail::displayDebugCallInfoSignal( callId, propertyName, info, connectionInfo->interfaceName ); + DBUS_DEBUG( "call %d: adding property", callId.id ); + auto &cI = this->connectionInfo; + DBUS_W->add_property_changed_event_listener_impl(connectionState->proxy, cI->interfaceName, propertyName, + [callback]( const _Eina_Value *newValue ) { + V val = 0; + if( !getFromEinaValue( newValue, &val ) ) + { + DBUS_DEBUG( "unable to get property's value" ); + return; + } + callback( val ); + }); + } + + /** + * @brief Registers callback on the DBUS' signal + * + * The function registers callback signalName. When signal comes, callback will be called. + * Callback object will exists as long as signal is registered. You can unregister signal + * by destroying DBusClient object. + * + * @param signalName name of the signal to register + * @param callback callback to call + */ + template < typename SignalType > + void addSignal( std::string signalName, std::function< SignalType > callback ) + { + detail::CallId callId; + detail::displayDebugCallInfoSignal( callId, signalName, info, connectionInfo->interfaceName ); + DBUS_W->eldbus_proxy_signal_handler_add_impl( connectionState->proxy, signalName, + [callId, callback, signalName]( const DBusWrapper::MessagePtr &msg ) -> void { + std::string errname, aux; + if( DBUS_W->eldbus_message_error_get_impl( msg, errname, aux ) ) + { + DBUS_DEBUG( "call %d: Eldbus error: %s %s", callId.id, errname.c_str(), aux.c_str() ); + return; + } + DBUS_DEBUG( "call %d: received signal with signature '%s'", callId.id, DBUS_W->eldbus_message_signature_get_impl( msg ).c_str() ); + using ParamsType = typename detail::dbus_interface_traits< SignalType >::VEArgs; + auto params = detail::unpackValues< ParamsType >( callId, msg ); + if( !params ) + { + DBUS_DEBUG( "call %d: failed: %s", callId.id, params.getError().message.c_str() ); + return; + } + try + { + detail::apply( callback, params.getValues() ); + } + catch( ... ) + { + DBUS_DEBUG( "unhandled exception" ); + bart_assert( 0 ); + } + }); + } + +private: + /// \cond + std::unique_ptr connectionState{ new detail::ConnectionState }; + std::string info; + std::shared_ptr< ConnectionInfo > connectionInfo; + + static bool getFromEinaValue(const _Eina_Value *v, void *dst); + /// \endcond +}; + +/** + * @brief Helper class describing DBUS's server interface + * + */ +class DBusInterfaceDescription +{ + friend class DBusServer; + +public: + /// \cond + using MethodInfo = DBusWrapper::MethodInfo; + using SignalInfo = DBusWrapper::SignalInfo; + using PropertyInfo = DBusWrapper::PropertyInfo; + using SignalId = DBusWrapper::SignalId; + /// \endcond + + /** + * @brief Creates empty interface description with given name + * + * @param interfaceName name of the interface + */ + DBusInterfaceDescription( std::string interfaceName ); + + /** + * @brief adds new, synchronous method to the interface + * + * When method memberName is called on DBUS, callback functor will be called + * with values received from DBUS. callback won't be called, if method was + * called with invalid signature. Value returned from functor (or error message) + * will be marshalled back to the caller. + * + * Template type T defines both arguments sent to the method + * and expected values. Receiving different values will be reported as + * error. For example: + * \code{.cpp} Method \endcode + * defines method, which takes two arguments (two floats) and return + * single value of type int. + * + * @param memberName name of the method + * @param callback functor, which will be called + */ + template < typename T > + void addMethod( const std::string& memberName, typename detail::dbus_interface_traits< T >::SyncCB callback ) + { + detail::CallId callId; + MethodInfo mi; + methods.push_back( std::move( mi ) ); + auto& z = methods.back(); + z.in = detail::EldbusArgGenerator_Args< T >::get( ); + z.out = detail::EldbusArgGenerator_ReturnType< T >::get( ); + z.memberName = memberName; + DBUS_DEBUG( "call %d: method %s, in %s, out %s", callId.id, memberName.c_str(), + detail::EldbusArgGenerator_Args< T >::name().c_str(), + detail::EldbusArgGenerator_ReturnType< T >::name().c_str() ); + z.callback = construct< T >( callId, callback ); + z.id = callId; + } + + /** + * @brief adds new, synchronous property to the interface + * + * When property memberName is called on DBUS, respective callback functor will be called + * with values received from DBUS. callback won't be called, if method was + * called with invalid signature. Value returned from functor (or error message) + * will be marshalled back to the caller. + * + * Template type T defines type of the value hidden under property. + * Note, that library automatically wraps both sent and received value into + * DBUS's wrapper type. + * + * @param memberName name of the method + * @param getter functor, which will be called when property is being read + * @param setter functor, which will be called when property is being set + */ + template < typename T > + void addProperty( const std::string& memberName, std::function< ValueOrError< T >() > getter, std::function< ValueOrError< void >( T ) > setter ) + { + properties.push_back( {} ); + auto& z = properties.back(); + z.memberName = memberName; + z.typeSignature = detail::signature< T >::sig(); + if( getter ) + { + detail::CallId getterId; + z.getterId = getterId; + DBUS_DEBUG( "call %d: property %s (get) type %s", getterId.id, memberName.c_str(), detail::signature< T >::name().c_str() ); + z.getCallback = [=]( const DBusWrapper::MessagePtr &src, const DBusWrapper::MessageIterPtr &dst ) -> std::string { + try + { + auto v = detail::apply( getter, std::tuple<>{} ); + if( v ) + { + detail::signature< T >::set( dst, std::get< 0 >( v.getValues() ) ); + DBUS_DEBUG( "call %d: success", getterId.id ); + return ""; + } + DBUS_DEBUG( "call %d: failed: %s", getterId.id, v.getError().message.c_str() ); + return v.getError().message; + } + catch( std::exception& e ) + { + return std::string( "unhandled exception (" ) + e.what() + ")"; + } + catch( ... ) + { + return "unhandled exception"; + } + }; + } + if( setter ) + { + detail::CallId setterId; + z.setterId = setterId; + DBUS_DEBUG( "call %d: property %s (set) type %s", setterId.id, memberName.c_str(), detail::signature< T >::name().c_str() ); + z.setCallback = [=]( const DBusWrapper::MessagePtr &src, const DBusWrapper::MessageIterPtr &src_iter ) -> std::string { + std::tuple< T > value; + auto src_signature = DBUS_W->eldbus_message_iter_signature_get_impl( src_iter ); + if( detail::signature< T >::get( src_iter, std::get< 0 >( value ) ) ) + { + try + { + auto v = detail::apply( setter, std::move( value ) ); + if( v ) + { + DBUS_DEBUG( "call %d: success", setterId.id ); + return ""; + } + DBUS_DEBUG( "call %d: failed: %s", setterId.id, v.getError().message.c_str() ); + return v.getError().message; + } + catch( std::exception& e ) + { + return std::string( "unhandled exception (" ) + e.what() + ")"; + } + catch( ... ) + { + return "unhandled exception"; + } + } + DBUS_DEBUG( "call %d: failed to unpack values, got signature '%s', expected '%s'", setterId.id, + src_signature.c_str(), detail::signature< T >::sig().c_str() ); + return "call " + std::to_string( setterId.id ) + ": failed to unpack values, got signature '" + + src_signature + "', expected '" + detail::signature< T >::sig() + "'"; + }; + } + } + + /** + * @brief adds new signal to the interface + * + * Template types ARGS defines values, which will be emited with the signal + * + * @param memberName name of the method + */ + template < typename... ARGS > + SignalId addSignal( const std::string& memberName ) + { + detail::CallId callId; + signals.push_back( {} ); + auto& z = signals.back(); + z.memberName = memberName; + z.args = detail::EldbusArgGenerator_Args< void( ARGS... ) >::get( DBUS_W->Strings ); + z.id = callId; + DBUS_DEBUG( "call %d: signal %s", callId.id, memberName.c_str() ); + return SignalId{callId}; + } + +private: + /// \cond + std::vector< MethodInfo > methods; + std::vector< PropertyInfo > properties; + std::vector< SignalInfo > signals; + std::string interfaceName; + + template < typename T > + std::function< DBusWrapper::MessagePtr( const DBusWrapper::MessagePtr &msg ) > construct( detail::CallId callId, + typename detail::dbus_interface_traits< T >::SyncCB callback ) + { + using VEArgs = typename detail::dbus_interface_traits< T >::VEArgs; + return [=]( const DBusWrapper::MessagePtr &msg ) -> DBusWrapper::MessagePtr { + DBUS_DEBUG( "call %d: entering", callId.id ); + DBusWrapper::MessagePtr ret = {}; + auto args = detail::unpackValues< VEArgs >( callId, msg ); + if( args ) + { + try + { + auto v = detail::apply( callback, std::move( args.getValues() ) ); + if( v ) + { + DBUS_DEBUG( "call %d: success", callId.id ); + ret = DBUS_W->eldbus_message_method_return_new_impl( msg ); + detail::packValues( callId, ret, v ); + } + else + { + DBUS_DEBUG( "call %d: failed: %s", callId.id, v.getError().message.c_str() ); + ret = DBUS_W->eldbus_message_error_new_impl( msg, "org.freedesktop.DBus.Error.Failed", v.getError().message ); + } + } + catch( std::exception& e ) + { + auto txt = std::string( "unhandled exception (" ) + e.what() + ")"; + DBUS_DEBUG( "call %d: failed: %s", callId.id, txt.c_str() ); + ret = DBUS_W->eldbus_message_error_new_impl( msg, "org.freedesktop.DBus.Error.Failed", txt ); + } + catch( ... ) + { + DBUS_DEBUG( "call %d: failed: %s", callId.id, "unhandled exception" ); + ret = DBUS_W->eldbus_message_error_new_impl( msg, "org.freedesktop.DBus.Error.Failed", "unhandled exception" ); + } + } + else + { + std::ostringstream err; + err << "expected signature '" << detail::signature< VEArgs >::sig() << "', got '" << DBUS_W->eldbus_message_signature_get_impl( msg ) << "'"; + auto str = err.str(); + DBUS_DEBUG( "call %d: failed: %s", callId.id, str.c_str() ); + ret = DBUS_W->eldbus_message_error_new_impl( msg, "org.freedesktop.DBus.Error.InvalidArgs", str ); + } + return ret; + }; + } + /// \endcond +}; + +/** + * @brief Class representing server's end of DBUS connection + * + * Allows listening (synchronously and asynchronosly) on methods on selected interface + * Allows listening (synchronously and asynchronosly) on setting / getting properties. + * Allows emiting signals. + */ +class DBusServer +{ +public: + /** + * @brief Constructs non-connected dbus server. + */ + DBusServer() = default; + + /** + * @brief Constructs dbus server on either system or user dbus connection. + */ + DBusServer( ConnectionType tp ); + + /** + * @brief Constructs dbus server on connection from getDBusConnectionByType + */ + DBusServer( const DBusWrapper::ConnectionPtr &conn ); + + /** + * @brief Destructor + * + * Destructor will properly destroy everything. Destructor will cancel + * pending replies. + */ + ~DBusServer() = default; + + DBusServer( const DBusServer& ) = delete; + DBusServer( DBusServer&& ) = default; + + DBusServer& operator=( DBusServer&& ) = default; + DBusServer& operator=( const DBusServer& ) = delete; + + /** + * @brief Registers interface on given path name + * + * @param pathName path object to register interface on. + * @param dscr + * @param fallback + */ + void addInterface( const std::string& pathName, DBusInterfaceDescription& dscr, bool fallback = false ); + + /** + * @brief Gets bus name of the current connection (must be connected) + */ + std::string getBusName() const; + + /** + * @brief Returns connection object for this dbus server object + * + * @return connection object + */ + DBusWrapper::ConnectionPtr getConnection(); + + /** + * @brief Emits signal + * + * Emits signal based only on data passed to the function + * + * @param signal identifier of the signal + * @param args values to emit + */ + template < typename... ARGS > + void emit2( const std::string& path, const std::string& interfaceName, + const std::string& signalName, const ARGS&... args ) + { + auto msg = DBUS_W->eldbus_message_signal_new_impl( path, interfaceName, signalName ); + detail::CallId id; + detail::packValues( id, msg, args... ); + DBUS_W->eldbus_connection_send_impl( connection, msg ); + } + + /** + * @brief Returns current object path, when handling call to property / method + * + * User can call this function from inside callback used to handle property / method calls. + * It will retrieve object's path used in the call. Note, that in asynchronous handling + * of those calls user need to retrieve and store the current object / current connection + * as the value will change at the moment user's callback handling will exit. For example: + * \code{.cpp} + * DBusInterfaceDescription interface{"name"}; + * auto handler_later = [](std::function done_cb) { + * // process something later on + * DBusServer::getCurrentObjectPath(); // this will return empty string + * }; + * interface.addAsyncMethod("m", [=](std::function done_cb) { + * DBusServer::getCurrentObjectPath(); // this will current object's path + * + * // do some processing later on and call done_cb, when it's done + * register_to_call_sometime_later_on(std::move(done_cb)); + * }; + * \endcode + */ + static std::string getCurrentObjectPath() { return currentObjectPath; } + + /** + * @brief Returns current connection object, when handling call to property / method + * + * User can call this function from inside callback used to handle property / method calls. + * It will retrieve object's path used in the call. Note, that in asynchronous handling + * of those calls user need to retrieve and store the current object / current connection + * as the value will change at the moment user's callback handling will exit. For example: + * \code{.cpp} + * DBusInterfaceDescription interface{"name"}; + * auto handler_later = [](std::function done_cb) { + * // process something later on + * DBusServer::getCurrentObjectPath(); // this will return empty string + * }; + * interface.addAsyncMethod("m", [=](std::function done_cb) { + * DBusServer::getCurrentObjectPath(); // this will current object's path + * + * // do some processing later on and call done_cb, when it's done + * register_to_call_sometime_later_on(std::move(done_cb)); + * }; + * \endcode + */ + static const DBusWrapper::ConnectionPtr &getCurrentConnection() { return currentConnection; } + + /// \cond + class CurrentObjectSetter + { + public: + CurrentObjectSetter( DBusWrapper::ConnectionPtr con, std::string path ) + { + currentObjectPath = std::move(path); + currentConnection = std::move( con ); + } + ~CurrentObjectSetter() + { + currentObjectPath = ""; + currentConnection = {}; + } + CurrentObjectSetter( const CurrentObjectSetter& ) = delete; + CurrentObjectSetter( CurrentObjectSetter&& ) = delete; + void operator=( const CurrentObjectSetter& ) = delete; + void operator=( CurrentObjectSetter&& ) = delete; + }; + /// \endcond + +private: + /// \cond + DBusWrapper::ConnectionPtr connection; + struct DestructorObject { + std::vector> destructors; + ~DestructorObject() { + for(auto &a : destructors) a(); + } + }; + + std::unique_ptr destructorObject { new DestructorObject() }; + static thread_local std::string currentObjectPath; + static thread_local DBusWrapper::ConnectionPtr currentConnection; + + /// \endcond +}; + +/// \cond +DBusWrapper::ConnectionPtr getDBusConnectionByType( ConnectionType tp ); +DBusWrapper::ConnectionPtr getDBusConnectionByName( const std::string& name ); +std::string getConnectionName( const DBusWrapper::ConnectionPtr& ); +/// \endcond +} + +/// \cond +namespace std +{ +template < size_t INDEX, typename... ARGS > +inline auto get( DBus::ValueOrError< ARGS... >& v ) -> decltype( std::get< INDEX >( v.getValues() ) ) & +{ + return std::get< INDEX >( v.getValues() ); +} + +template < size_t INDEX, typename... ARGS > +inline auto get( const DBus::ValueOrError< ARGS... >& v ) -> decltype( std::get< INDEX >( v.getValues() ) ) +{ + return std::get< INDEX >( v.getValues() ); +} +} +/// \endcond + +#endif // DALI_INTERNAL_ACCESSIBILITY_BRIDGE_DBUS_H diff --git a/automated-tests/src/dali-toolkit-internal/utc-Dali-Accessibility-Accessible.cpp b/automated-tests/src/dali-toolkit-internal/utc-Dali-Accessibility-Accessible.cpp index 9c4f611..9b103e8 100644 --- a/automated-tests/src/dali-toolkit-internal/utc-Dali-Accessibility-Accessible.cpp +++ b/automated-tests/src/dali-toolkit-internal/utc-Dali-Accessibility-Accessible.cpp @@ -25,9 +25,12 @@ #include #include +#include + void utc_dali_toolkit_accessibility_accessible_startup(void) { test_return_value = TET_UNDEF; + DBusWrapper::Install(std::unique_ptr(new TestDBusWrapper)); } void utc_dali_toolkit_accessibility_accessible_cleanup(void) diff --git a/automated-tests/src/dali-toolkit-internal/utc-Dali-Accessibility-Controls-BridgeUp.cpp b/automated-tests/src/dali-toolkit-internal/utc-Dali-Accessibility-Controls-BridgeUp.cpp index ace41ba..01edef0 100644 --- a/automated-tests/src/dali-toolkit-internal/utc-Dali-Accessibility-Controls-BridgeUp.cpp +++ b/automated-tests/src/dali-toolkit-internal/utc-Dali-Accessibility-Controls-BridgeUp.cpp @@ -9,6 +9,8 @@ #include #include +#include + using namespace Dali::Toolkit; //using DBusWrapper = Dali::Accessibility::DBusWrapper; // FIXME @@ -18,6 +20,7 @@ namespace Dali::Accessibility::TestDBusWrapper { struct error {}; } // FIXME void utc_dali_accessibility_controls_bridge_up_startup(void) { test_return_value = TET_UNDEF; + DBusWrapper::Install(std::unique_ptr(new TestDBusWrapper)); } void utc_dali_accessibility_controls_bridge_up_cleanup(void) diff --git a/automated-tests/src/dali-toolkit-internal/utc-Dali-Accessibility-Controls.cpp b/automated-tests/src/dali-toolkit-internal/utc-Dali-Accessibility-Controls.cpp index 43c3707..dd13f0a 100644 --- a/automated-tests/src/dali-toolkit-internal/utc-Dali-Accessibility-Controls.cpp +++ b/automated-tests/src/dali-toolkit-internal/utc-Dali-Accessibility-Controls.cpp @@ -6,6 +6,8 @@ #include #include +#include + using namespace Dali::Toolkit; //using DBusWrapper = Dali::Accessibility::DBusWrapper; // FIXME @@ -13,6 +15,7 @@ using namespace Dali::Toolkit; void utc_dali_accessibility_controls_startup(void) { test_return_value = TET_UNDEF; + DBusWrapper::Install(std::unique_ptr(new TestDBusWrapper)); } void utc_dali_accessibility_controls_cleanup(void) diff --git a/automated-tests/src/dali-toolkit-internal/utc-Dali-Accessibility-Text.cpp b/automated-tests/src/dali-toolkit-internal/utc-Dali-Accessibility-Text.cpp index 5d5a9a2..7bc4fe1 100644 --- a/automated-tests/src/dali-toolkit-internal/utc-Dali-Accessibility-Text.cpp +++ b/automated-tests/src/dali-toolkit-internal/utc-Dali-Accessibility-Text.cpp @@ -25,9 +25,12 @@ #include #include +#include + void utc_dali_toolkit_accessibility_text_startup(void) { test_return_value = TET_UNDEF; + DBusWrapper::Install(std::unique_ptr(new TestDBusWrapper)); } void utc_dali_toolkit_accessibility_text_cleanup(void) diff --git a/automated-tests/src/dali-toolkit-internal/utc-Dali-Accessibility-Value.cpp b/automated-tests/src/dali-toolkit-internal/utc-Dali-Accessibility-Value.cpp index 0b26c3f..87c1f0b 100644 --- a/automated-tests/src/dali-toolkit-internal/utc-Dali-Accessibility-Value.cpp +++ b/automated-tests/src/dali-toolkit-internal/utc-Dali-Accessibility-Value.cpp @@ -28,11 +28,14 @@ #include +#include + using namespace Dali::Accessibility; void utc_dali_toolkit_accessibility_value_startup(void) { test_return_value = TET_UNDEF; + DBusWrapper::Install(std::unique_ptr(new TestDBusWrapper)); } void utc_dali_toolkit_accessibility_value_cleanup(void) diff --git a/automated-tests/src/dali-toolkit-internal/utc-Dali-ColorConversion.cpp b/automated-tests/src/dali-toolkit-internal/utc-Dali-ColorConversion.cpp index 14cef1c..6e41930 100644 --- a/automated-tests/src/dali-toolkit-internal/utc-Dali-ColorConversion.cpp +++ b/automated-tests/src/dali-toolkit-internal/utc-Dali-ColorConversion.cpp @@ -18,12 +18,15 @@ #include #include +#include + using namespace Dali; using namespace Dali::Toolkit; void dali_color_conversion_startup(void) { test_return_value = TET_UNDEF; + DBusWrapper::Install(std::unique_ptr(new TestDBusWrapper)); } void dali_color_conversion_cleanup(void) diff --git a/automated-tests/src/dali-toolkit-internal/utc-Dali-DebugRendering.cpp b/automated-tests/src/dali-toolkit-internal/utc-Dali-DebugRendering.cpp index fe01738..12276e8 100755 --- a/automated-tests/src/dali-toolkit-internal/utc-Dali-DebugRendering.cpp +++ b/automated-tests/src/dali-toolkit-internal/utc-Dali-DebugRendering.cpp @@ -25,6 +25,9 @@ #include #include // for setting environment variable: DALI_DEBUG_RENDERING + +#include + #include "dummy-control.h" using namespace Dali; @@ -75,6 +78,7 @@ void TestDebugVisual( Integration::Scene scene, Visual::Base& visual, Visual::T void dali_debug_rendering_startup(void) { test_return_value = TET_UNDEF; + DBusWrapper::Install(std::unique_ptr(new TestDBusWrapper)); } void dali_debug_rendering_cleanup(void) diff --git a/automated-tests/src/dali-toolkit-internal/utc-Dali-PropertyHelper.cpp b/automated-tests/src/dali-toolkit-internal/utc-Dali-PropertyHelper.cpp index 58fc510..df76c38 100644 --- a/automated-tests/src/dali-toolkit-internal/utc-Dali-PropertyHelper.cpp +++ b/automated-tests/src/dali-toolkit-internal/utc-Dali-PropertyHelper.cpp @@ -18,12 +18,15 @@ #include #include +#include + using namespace Dali; using namespace Dali::Toolkit; void dali_property_helper_startup(void) { test_return_value = TET_UNDEF; + DBusWrapper::Install(std::unique_ptr(new TestDBusWrapper)); } void dali_property_helper_cleanup(void) diff --git a/automated-tests/src/dali-toolkit-internal/utc-Dali-TextSelectionPopup-internal.cpp b/automated-tests/src/dali-toolkit-internal/utc-Dali-TextSelectionPopup-internal.cpp index 13c7ca8..06ce4c8 100644 --- a/automated-tests/src/dali-toolkit-internal/utc-Dali-TextSelectionPopup-internal.cpp +++ b/automated-tests/src/dali-toolkit-internal/utc-Dali-TextSelectionPopup-internal.cpp @@ -24,12 +24,15 @@ #include #undef private +#include + using namespace Dali; using namespace Toolkit; void dali_textselectionpopupinternal_startup(void) { test_return_value = TET_UNDEF; + DBusWrapper::Install(std::unique_ptr(new TestDBusWrapper)); } void dali_textselectionpopupinternal_cleanup(void) diff --git a/automated-tests/src/dali-toolkit-internal/utc-Dali-TextureManager.cpp b/automated-tests/src/dali-toolkit-internal/utc-Dali-TextureManager.cpp index a6b7460..c1ea9ad 100644 --- a/automated-tests/src/dali-toolkit-internal/utc-Dali-TextureManager.cpp +++ b/automated-tests/src/dali-toolkit-internal/utc-Dali-TextureManager.cpp @@ -27,13 +27,15 @@ #include #include -using namespace Dali::Toolkit::Internal; +#include +using namespace Dali::Toolkit::Internal; void utc_dali_toolkit_texture_manager_startup(void) { setenv( "LOG_TEXTURE_MANAGER", "3", 1 ); test_return_value = TET_UNDEF; + DBusWrapper::Install(std::unique_ptr(new TestDBusWrapper)); } void utc_dali_toolkit_texture_manager_cleanup(void) diff --git a/automated-tests/src/dali-toolkit/dali-toolkit-test-utils/toolkit-test-application.h b/automated-tests/src/dali-toolkit/dali-toolkit-test-utils/toolkit-test-application.h index 85df495..49a4c6f 100644 --- a/automated-tests/src/dali-toolkit/dali-toolkit-test-utils/toolkit-test-application.h +++ b/automated-tests/src/dali-toolkit/dali-toolkit-test-utils/toolkit-test-application.h @@ -29,7 +29,7 @@ #include #include "test-application.h" -#undef assert +//#undef assert namespace Dali { -- 2.7.4