From ed02c0fade719c5fa4913e809a62889a8aa227b1 Mon Sep 17 00:00:00 2001 From: "adam.b" Date: Fri, 24 Jul 2020 12:42:51 +0100 Subject: [PATCH] AddOn support Added devel-api to make use of addons easier Change-Id: I98fa1da7cdfae7e101d53f5f3b9b3ca1806cba00 --- .gitignore | 1 + automated-tests/src/dali-internal/CMakeLists.txt | 3 +- automated-tests/src/dali/CMakeLists.txt | 22 +- .../dali-test-suite-utils/test-addon-manager.cpp | 210 ++++++++++++++++ .../dali-test-suite-utils/test-addon-manager.h | 89 +++++++ automated-tests/src/dali/test-sample-addon.cpp | 92 +++++++ automated-tests/src/dali/utc-Dali-AddOn.cpp | 80 ++++++ dali/devel-api/addons/addon-base.h | 270 ++++++++++++++++++++ dali/devel-api/addons/addon-dispatch-table.h | 139 +++++++++++ dali/devel-api/common/addon-binder.cpp | 29 +++ dali/devel-api/common/addon-binder.h | 177 +++++++++++++ dali/devel-api/file.list | 7 + dali/integration-api/addon-manager.cpp | 38 +++ dali/integration-api/addon-manager.h | 277 +++++++++++++++++++++ dali/integration-api/file.list | 2 + 15 files changed, 1434 insertions(+), 2 deletions(-) create mode 100644 automated-tests/src/dali/dali-test-suite-utils/test-addon-manager.cpp create mode 100644 automated-tests/src/dali/dali-test-suite-utils/test-addon-manager.h create mode 100644 automated-tests/src/dali/test-sample-addon.cpp create mode 100644 automated-tests/src/dali/utc-Dali-AddOn.cpp create mode 100644 dali/devel-api/addons/addon-base.h create mode 100644 dali/devel-api/addons/addon-dispatch-table.h create mode 100644 dali/devel-api/common/addon-binder.cpp create mode 100644 dali/devel-api/common/addon-binder.h create mode 100644 dali/integration-api/addon-manager.cpp create mode 100644 dali/integration-api/addon-manager.h diff --git a/.gitignore b/.gitignore index 559051e..2912c11 100644 --- a/.gitignore +++ b/.gitignore @@ -40,6 +40,7 @@ libdali2-core.so* *.creator.user /docs/generated/* /build/tizen/doc +/build/tizen-cmake/doc /build/tizen/.cov /build/desktop /packaging/home* diff --git a/automated-tests/src/dali-internal/CMakeLists.txt b/automated-tests/src/dali-internal/CMakeLists.txt index f39572a..22a472d 100644 --- a/automated-tests/src/dali-internal/CMakeLists.txt +++ b/automated-tests/src/dali-internal/CMakeLists.txt @@ -27,6 +27,7 @@ LIST(APPEND TC_SOURCES ../dali/dali-test-suite-utils/test-platform-abstraction.cpp ../dali/dali-test-suite-utils/test-render-controller.cpp ../dali/dali-test-suite-utils/test-trace-call-stack.cpp + ../dali/dali-test-suite-utils/test-addon-manager.cpp ) PKG_CHECK_MODULES(${CAPI_LIB} REQUIRED @@ -51,7 +52,7 @@ INCLUDE_DIRECTORIES( ADD_EXECUTABLE(${EXEC_NAME} ${EXEC_NAME}.cpp ${TC_SOURCES}) TARGET_LINK_LIBRARIES(${EXEC_NAME} ${${CAPI_LIB}_LIBRARIES} - --coverage + --coverage -ldl ) INSTALL(PROGRAMS ${EXEC_NAME} diff --git a/automated-tests/src/dali/CMakeLists.txt b/automated-tests/src/dali/CMakeLists.txt index cd300d0..632363b 100644 --- a/automated-tests/src/dali/CMakeLists.txt +++ b/automated-tests/src/dali/CMakeLists.txt @@ -7,6 +7,7 @@ SET(CAPI_LIB "dali") SET(TC_SOURCES utc-Dali-Actor.cpp + utc-Dali-AddOn.cpp utc-Dali-AlphaFunction.cpp utc-Dali-AngleAxis.cpp utc-Dali-Animation.cpp @@ -119,6 +120,7 @@ LIST(APPEND TC_SOURCES dali-test-suite-utils/test-platform-abstraction.cpp dali-test-suite-utils/test-render-controller.cpp dali-test-suite-utils/test-trace-call-stack.cpp + dali-test-suite-utils/test-addon-manager.cpp ) PKG_CHECK_MODULES(${CAPI_LIB} REQUIRED @@ -138,12 +140,30 @@ INCLUDE_DIRECTORIES( dali-test-suite-utils ) +ADD_DEFINITIONS( -DADDON_LIBS_PATH=\"${CMAKE_CURRENT_BINARY_DIR}\" ) + ADD_EXECUTABLE(${EXEC_NAME} ${EXEC_NAME}.cpp ${TC_SOURCES}) TARGET_LINK_LIBRARIES(${EXEC_NAME} ${${CAPI_LIB}_LIBRARIES} - -lpthread --coverage + -lpthread -ldl --coverage ) +# Path to installed precompiled addons + INSTALL(PROGRAMS ${EXEC_NAME} DESTINATION ${BIN_DIR}/${EXEC_NAME} ) + +# build addons +MESSAGE( STATUS "BINDIR: ${CMAKE_CURRENT_BINARY_DIR}") +SET(ADDON_NAME SampleAddOn ) +SET(ADDON_SOURCES test-sample-addon.cpp ) +ADD_LIBRARY( ${ADDON_NAME} SHARED ${ADDON_SOURCES} ) +TARGET_LINK_LIBRARIES(${ADDON_NAME} + -lpthread -ldl --coverage + ) + +INSTALL( TARGETS ${ADDON_NAME} DESTINATION ${BIN_DIR} ) + +# store AddOn list +FILE( WRITE ${CMAKE_CURRENT_BINARY_DIR}/addons.txt lib${ADDON_NAME}.so ) diff --git a/automated-tests/src/dali/dali-test-suite-utils/test-addon-manager.cpp b/automated-tests/src/dali/dali-test-suite-utils/test-addon-manager.cpp new file mode 100644 index 0000000..307e0a0 --- /dev/null +++ b/automated-tests/src/dali/dali-test-suite-utils/test-addon-manager.cpp @@ -0,0 +1,210 @@ +/* + * Copyright (c) 2020 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. + * + */ + +#include "test-addon-manager.h" +#include +#include +#include + +#ifndef ADDON_LIBS_PATH +#define ADDON_LIBS_PATH "" +#endif + +namespace Dali +{ +namespace Test +{ + +std::vector AddOnManager::EnumerateAddOns() +{ + std::string listFileName(ADDON_LIBS_PATH); + listFileName += "/addons.txt"; + + // Read list of available test addons + tet_printf("Enumerating addons, file: %s\n", listFileName.c_str()); + std::vector addons{}; + auto* fin = fopen( listFileName.c_str(), "r" ); + char* lineBuf = new char[256]; + size_t n = 256; + while( getline( &lineBuf, &n, fin ) > 0 ) + { + tet_printf("Adding %s\n", lineBuf); + addons.emplace_back( lineBuf ); + } + fclose(fin); + delete [] lineBuf; + std::vector retval{}; + // Open addons + for( auto& name : addons ) + { + std::string path(ADDON_LIBS_PATH); + path += "/"; + path += name; + + mAddOnCache.emplace_back(); + mAddOnCache.back().handle = dlopen( path.c_str(), RTLD_DEEPBIND|RTLD_LAZY ); + if( !mAddOnCache.back().handle ) + { + mAddOnCache.back().valid = false; + tet_printf( "Can't open addon lib: %s\n", path.c_str()); + continue; + } + // Here addon must self register + if( !mAddOnCache.back().valid ) + { + puts("Addon invalid!"); + } + else + { + tet_printf( "Valid AddOn: %s\n", mAddOnCache.back().name.c_str() ); + retval.emplace_back( mAddOnCache.back().name ); + } + } + + return retval; + + /** + * Check for self-registering addons + */ +} + +void AddOnManager::RegisterAddOnDispatchTable( const AddOnDispatchTable* dispatchTable ) +{ + // Register the dispatch table + auto& entry = mAddOnCache.back(); + entry.name = dispatchTable->name; + tet_printf( "Registering AddOn: %s\n", entry.name.c_str()); + entry.GetGlobalProc = dispatchTable->GetGlobalProc; + entry.GetInstanceProc = dispatchTable->GetInstanceProc; + entry.GetAddOnInfo = dispatchTable->GetAddOnInfo; + entry.OnStart = dispatchTable->OnStart; + entry.OnStop = dispatchTable->OnStop; + entry.OnPause = dispatchTable->OnPause; + entry.OnResume = dispatchTable->OnResume; + entry.valid = true; +} + +bool AddOnManager::GetAddOnInfo(const std::string& name, AddOnInfo& info ) +{ + auto retval = false; + std::find_if( mAddOnCache.begin(), mAddOnCache.end(), + [&retval, name, &info]( AddOnCacheEntry& entry ) + { + if(entry.name == name) + { + entry.GetAddOnInfo( info ); + retval = true; + return true; + } + return false; + }); + return retval; +} + +std::vector AddOnManager::LoadAddOns( const std::vector& addonNames ) +{ + if(mAddOnCache.empty()) + { + EnumerateAddOns(); + } + + std::vector retval{}; + for( auto& name : addonNames) + { + size_t index = 0; + auto iter = std::find_if( mAddOnCache.begin(), mAddOnCache.end(), + [&retval, name, &index]( AddOnCacheEntry& entry ) + { + index++; + if(entry.name == name) + { + return true; + } + return false; + }); + if( iter != mAddOnCache.end() ) + { + retval.emplace_back( *reinterpret_cast( &index ) ); + } + else + { + retval.emplace_back( nullptr ); + } + } + + return retval; +} + +void* AddOnManager::GetGlobalProc( const Dali::AddOnLibrary& addOnLibrary, const char* procName ) +{ + auto index = *reinterpret_cast( &addOnLibrary ); + return mAddOnCache[index-1].GetGlobalProc( procName ); +} + +void* AddOnManager::GetInstanceProc( const Dali::AddOnLibrary& addOnLibrary, const char* procName ) +{ + auto index = *reinterpret_cast( &addOnLibrary ); + return mAddOnCache[index-1].GetInstanceProc( procName ); +} + +void AddOnManager::Start() +{ + for( auto& entry : mAddOnCache ) + { + if(entry.OnStart) + { + entry.OnStart(); + } + } +} + +void AddOnManager::Resume() +{ + for( auto& entry : mAddOnCache ) + { + if(entry.OnResume) + { + entry.OnResume(); + } + } +} + +void AddOnManager::Stop() +{ + for( auto& entry : mAddOnCache ) + { + if(entry.OnStop) + { + entry.OnStop(); + } + } +} + +void AddOnManager::Pause() +{ + for( auto& entry : mAddOnCache ) + { + if(entry.OnPause) + { + entry.OnPause(); + } + } +} + +} +} + diff --git a/automated-tests/src/dali/dali-test-suite-utils/test-addon-manager.h b/automated-tests/src/dali/dali-test-suite-utils/test-addon-manager.h new file mode 100644 index 0000000..e924876 --- /dev/null +++ b/automated-tests/src/dali/dali-test-suite-utils/test-addon-manager.h @@ -0,0 +1,89 @@ +#ifndef TEST_ADDON_MANAGER_H +#define TEST_ADDON_MANAGER_H + +/* + * Copyright (c) 2020 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. + * + */ + +#include +#include +#include + +namespace Dali +{ +namespace Test +{ +class AddOnManager : public Dali::Integration::AddOnManager +{ +public: + + /** + * @brief Constructor, initialised by the Adaptor + */ + AddOnManager() = default; + + /** + * @brief Destructor + */ + virtual ~AddOnManager() = default; + + std::vector EnumerateAddOns() override; + + bool GetAddOnInfo(const std::string& name, AddOnInfo& info ) override; + + std::vector LoadAddOns( const std::vector& addonNames ) override; + + void* GetGlobalProc( const Dali::AddOnLibrary& addOnLibrary, const char* procName ) override; + + void* GetInstanceProc( const Dali::AddOnLibrary& addOnLibrary, const char* procName ) override; + + void RegisterAddOnDispatchTable( const AddOnDispatchTable* dispatchTable ) override; + + void Start() override; + + void Resume() override; + + void Stop() override; + + void Pause() override; + + struct AddOnCacheEntry + { + std::string name{}; + AddOnInfo info{}; + + // library handle + void* handle {nullptr}; + + // main function pointers + void(*GetAddOnInfo)(AddOnInfo& ) = nullptr; ///< Returns AddOnInfo structure + void*(*GetInstanceProc)( const char* ) = nullptr; ///< Returns pointer of instance function (member funtion) + void*(*GetGlobalProc)( const char* ) = nullptr; ///< Returns pointer of global function (non-member function) + + void(*OnStart)() = nullptr; + void(*OnResume)() = nullptr; + void(*OnPause)() = nullptr; + void(*OnStop)() = nullptr; + + bool valid = false; + }; + + std::vector mAddOnCache; +}; +} // Namespace Test +} // namespace Dali + +#endif // TEST_ADDON_MANAGER_H diff --git a/automated-tests/src/dali/test-sample-addon.cpp b/automated-tests/src/dali/test-sample-addon.cpp new file mode 100644 index 0000000..8a3931e --- /dev/null +++ b/automated-tests/src/dali/test-sample-addon.cpp @@ -0,0 +1,92 @@ +/* + * Copyright (c) 2020 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. + * + */ + +#include +#include +#include + +static const std::string DUMMY_ADDON_NAME = "SampleAddOn"; + +int StringLen( const char* str ) +{ + return strlen( str ); +} + +int DoSum( int a, int b ) +{ + return a+b; +} + +class TestDummyAddOn : public Dali::AddOns::AddOnBase +{ +public: + + void GetAddOnInfo( Dali::AddOnInfo& info ) override + { + info.type = Dali::AddOnType::GENERIC; + info.name = "SampleAddOn"; + info.version = Dali::DALI_ADDON_VERSION( 1, 0, 0 ); + info.next = nullptr; + tet_printf( "SampleAddOn: GetAddOnInfo() : name = %s\n", info.name.c_str()); + } + + /** + * Dispatch table for global functions + * @return + */ + Dali::AddOns::DispatchTable* GetGlobalDispatchTable() override + { + static Dali::AddOns::DispatchTable dispatchTable{}; + if( dispatchTable.Empty() ) + { + dispatchTable["DoSum"] = DoSum; + dispatchTable["StringLen"] = StringLen; + } + return &dispatchTable; + } + + /** + * Lifecycle + */ + + void OnStart() override + { + } + + void OnStop() override + { + } + + void OnPause() override + { + } + + void OnResume() override + { + } + + /** + * Dispatch table for instance functions + * @return + */ + Dali::AddOns::DispatchTable* GetInstanceDispatchTable() override + { + return nullptr; + } +}; + +REGISTER_ADDON_CLASS( TestDummyAddOn ); diff --git a/automated-tests/src/dali/utc-Dali-AddOn.cpp b/automated-tests/src/dali/utc-Dali-AddOn.cpp new file mode 100644 index 0000000..f221d51 --- /dev/null +++ b/automated-tests/src/dali/utc-Dali-AddOn.cpp @@ -0,0 +1,80 @@ +/* + * Copyright (c) 2020 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. + * + */ + +#include +#include +#include "dali-test-suite-utils/test-addon-manager.h" + +struct DummyAddOn : public Dali::AddOn::AddOnBinder +{ + DummyAddOn() : Dali::AddOn::AddOnBinder( "SampleAddOn" ) + {} + + ~DummyAddOn() = default; + + ADDON_BIND_FUNCTION( DoSum, int(int, int) ); + + ADDON_BIND_FUNCTION( StringLen, int() ); +}; + +int UtcDaliAddOnBinderP(void) +{ + TestApplication application; + + auto* addOnManager = new Dali::Test::AddOnManager(); + + tet_infoline("Testing Dali::AddOn::AddOnBinder"); + + DummyAddOn addon; + + // Test whether library handle is non-null + DALI_TEST_EQUALS( addon.GetHandle(), (void*)1, TEST_LOCATION ); + + // Test whether addon is valid + auto isValid = addon.IsValid(); + DALI_TEST_EQUALS( isValid, true, TEST_LOCATION ); + + // Test AddOnInfo + const auto& info = addon.GetAddOnInfo(); + DALI_TEST_EQUALS( info.name, "SampleAddOn", TEST_LOCATION ); + + delete addOnManager; + + END_TEST; +} + +int UtcDaliAddOnManagerNotSupportedP(void) +{ + TestApplication application; + + tet_infoline("Testing Dali::AddOn::AddOnBinder when AddOnManager not supported"); + + // Not supported + using VoidPtr = void*; + DALI_TEST_EQUALS( VoidPtr(Dali::Integration::AddOnManager::Get()), VoidPtr(nullptr), TEST_LOCATION ); + + DummyAddOn addon{}; + + // Test whether library handle is non-null + DALI_TEST_EQUALS( addon.GetHandle(), (void*)0, TEST_LOCATION ); + + // Test whether addon is valid + auto isValid = addon.IsValid(); + DALI_TEST_EQUALS( isValid, false, TEST_LOCATION ); + + END_TEST; +} diff --git a/dali/devel-api/addons/addon-base.h b/dali/devel-api/addons/addon-base.h new file mode 100644 index 0000000..f3c3279 --- /dev/null +++ b/dali/devel-api/addons/addon-base.h @@ -0,0 +1,270 @@ +#ifndef DALI_ADDONS_ADDON_BASE_H +#define DALI_ADDONS_ADDON_BASE_H + +/* + * Copyright (c) 2020 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. + * + */ + +#include +#include +#include + +namespace Dali +{ +namespace AddOns +{ + +/** + * @class AddOnBase + * + * @brief AddOnBase is a base class for AddOns and should be used rather + * than writing exported functions of the AddOn directly. + * + */ +class AddOnBase +{ +protected: + + /** + * @brief Constructor + */ + AddOnBase() + { + mSingleton = this; + } + +public: + + /** + * @brief Destructor + */ + virtual ~AddOnBase() = default; + + /** + * @brief Retrieves AddOn info + * @param[out] addonInfo AddOnInfo structure to fill by the function + */ + virtual void GetAddOnInfo( Dali::AddOnInfo& addonInfo ) = 0; + + /** + * @brief Returns a dispatch table for global functions. + * @return Valid dispatch table object or nullptr + */ + virtual DispatchTable* GetGlobalDispatchTable() = 0; + + /** + * @brief Returns a dispatch table for instance functions + * @return Valid dispatch table object or nullptr + */ + virtual DispatchTable* GetInstanceDispatchTable() = 0; + + /** + * @brief OnStart event. + * It's optional and should be implemented by the AddOn when it's required to handle the event. + */ + virtual void OnStart() {} + + /** + * @brief OnResume event. + * It's optional and should be implemented by the AddOn when it's required to handle the event. + */ + virtual void OnResume() {} + + /** + * @brief OnPause event. + * It's optional and should be implemented by the AddOn when it's required to handle the event. + */ + virtual void OnPause() {} + + /** + * @brief OnStop event. + * It's optional and should be implemented by the AddOn when it's required to handle the event. + */ + virtual void OnStop() {} + + /** + * @brief Getter of static singleton + * @return Returns valid singleton pointer + */ + static AddOnBase* Get() + { + return mSingleton; + } + +private: + + static AddOnBase* mSingleton; +}; + +/** + * @brief The function is automatically added to the AddOn code + * with use of REGISTER_ADDON_CLASS() macro + * @return The valid pointer to the AddOnBase interface + */ +extern Dali::AddOns::AddOnBase* CreateAddOn(); + +/** + * Fills AddOnInfo structure. + * @param[out] info Reference to AddOnInfo structure + */ +template +void GetAddOnInfo( Dali::AddOnInfo& info ) +{ + auto* addon = Dali::AddOns::AddOnBase::Get(); + addon->GetAddOnInfo( info ); +} + +/** + * Returns pointer to AddOn's global function + * @param[in] funcname Name of function + * @return Valid pointer or nullptr if function not found. + */ +template +void* GetGlobalProc( const char* funcname ) +{ + if( !funcname ) + { + return nullptr; + } + auto* addon = Dali::AddOns::AddOnBase::Get(); + + // AddOn must be initialised up to this point! + if( !addon ) + { + return nullptr; + } + + static Dali::AddOns::DispatchTable* globalDispatchTable = addon->GetGlobalDispatchTable(); + + if( globalDispatchTable ) + { + return globalDispatchTable->Find( funcname ); + } + return nullptr; +} + +/** + * Returns pointer to AddOn's instance function + * @param[in] funcname Name of function + * @return Valid pointer or nullptr if function not found. + */ +template +void* GetInstanceProc( const char* funcname ) +{ + if( !funcname ) + { + return nullptr; + } + + auto* addon = Dali::AddOns::AddOnBase::Get(); + + // AddOn must be initialised up to this point! + if( !addon ) + { + return nullptr; + } + + static Dali::AddOns::DispatchTable* instanceDispatchTable = addon->GetInstanceDispatchTable(); + + if( instanceDispatchTable ) + { + return instanceDispatchTable->Find( funcname ); + } + return nullptr; +} + +/** + * Lifecycle functions to export. + * They will be instantiated only if macro REGISTER_ADDON_CLASS is used + */ +template +void OnStart() +{ + auto* addon = Dali::AddOns::AddOnBase::Get(); + addon->OnStart(); +} + +template +void OnPause() +{ + auto* addon = Dali::AddOns::AddOnBase::Get(); + addon->OnPause(); +} + +template +void OnResume() +{ + auto* addon = Dali::AddOns::AddOnBase::Get(); + addon->OnResume(); +} + +template +void OnStop() +{ + auto* addon = Dali::AddOns::AddOnBase::Get(); + addon->OnStop(); +} + +/** + * @brief AddOn library internal constructor. + */ +inline void AddOnConstructorInternal() +{ + auto* addon = Dali::AddOns::CreateAddOn(); + + Dali::AddOnInfo info{}; + addon->GetAddOnInfo( info ); + + // Generate dispatch tables + addon->GetGlobalDispatchTable(); + addon->GetInstanceDispatchTable(); + + // Bind basic functions + Dali::AddOnDispatchTable table; + table.name = info.name; + table.GetAddOnInfo = GetAddOnInfo; + table.GetGlobalProc = GetGlobalProc; + table.GetInstanceProc = GetInstanceProc; + table.OnStart = OnStart; + table.OnStop = OnStop; + table.OnResume = OnResume; + table.OnPause = OnPause; + + // Register dispatch table + Dali::Integration::AddOnManager::Get()->RegisterAddOnDispatchTable( &table ); +} + +} // namespace AddOns +} // namespace Dali + +/** + * Macro must be used in order to auto-register AddOn with the AddOnManager. + * Note: The macro requires GCC/Clang compiler and currently only Linux-based environment + * is supported. + */ +#define REGISTER_ADDON_CLASS( ADDON_CLASS_WITH_FULL_NAMESPACE ) \ +namespace Dali { namespace AddOns { \ +__attribute__((constructor)) void AddOnConstructor() { \ + AddOnConstructorInternal();\ +}\ +AddOnBase* AddOnBase::mSingleton = nullptr; \ +AddOnBase* CreateAddOn() \ +{\ + return new ADDON_CLASS_WITH_FULL_NAMESPACE();\ +}\ +}} + +#endif // DALI_ADDON_BASE_H diff --git a/dali/devel-api/addons/addon-dispatch-table.h b/dali/devel-api/addons/addon-dispatch-table.h new file mode 100644 index 0000000..1699a5c --- /dev/null +++ b/dali/devel-api/addons/addon-dispatch-table.h @@ -0,0 +1,139 @@ +#ifndef DALI_ADDON_DISPATCH_TABLE_H +#define DALI_ADDON_DISPATCH_TABLE_H + +/* + * Copyright (c) 2020 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. + * + */ + +namespace Dali +{ +namespace AddOns +{ + +/** + * DispatchTable contains essential function pointers + * needed to register the AddOn with AddOnManager. + */ +struct DispatchTable +{ + typedef void* FunctionPointer; + /** + * Struct Entry + * The structure contains details of a single function binding + */ + struct Entry + { + /** + * @brief Assignment operator. Converts function pointer into void*. + * @param[in] funcPtr Function pointer + * @return Reference to self + */ + template + Entry &operator=(T funcPtr) + { + functionPtr = reinterpret_cast( funcPtr ); + if (index < 0) + { + index = int32_t(table->entries.size()); + table->entries.emplace_back(*this); + } + else + { + table->entries[index].functionPtr = reinterpret_cast( funcPtr ); + } + return *this; + } + + std::string functionName{}; ///< Name of function + void* functionPtr{nullptr}; ///< Function pointer + DispatchTable* table{nullptr}; ///< DispatchTable associated with entry + int32_t index = -1; ///< Index within the dispatch table + }; + + /** + * @brief Accessor of an indexed entry from the dispatch table + * @param[in] functionName name of function + * @return Returns Entry object + */ + Entry operator[](const char *functionName) + { + auto iter = std::find_if(entries.begin(), entries.end(), [functionName](Entry &entry) + { + if (entry.functionName == functionName) + { + return true; + } + return false; + }); + if (iter == entries.end()) + { + Entry retval; + retval.table = this; + retval.functionName = functionName; + return retval; + } + return Entry(*iter); + } + + /** + * @brief Tests whether cache is empty + * @return True if empty, False otherwise + */ + bool Empty() const + { + return entries.empty(); + } + + /** + * @brief Returns size of cache + * @return Size of cache + */ + uint32_t Size() const + { + return entries.size(); + } + + /** + * @brief Function pointer lookup + * @param[in] funcName Name of function + * @return valid pointer or nullptr if function not found + */ + FunctionPointer Find(const char *funcName) + { + if (entries.empty()) + { + return nullptr; + } + + auto iter = std::find_if(entries.begin(), entries.end(), [funcName, entries = &this->entries](Entry &entry) + { + return (entry.functionName == funcName); + }); + if (iter != entries.end()) + { + return iter->functionPtr; + } + return nullptr; + } + + std::vector entries; +}; + +} // namespace AddOns + +} // namespace Dali + +#endif // DALI_ADDON_DISPATCH_TABLE_H diff --git a/dali/devel-api/common/addon-binder.cpp b/dali/devel-api/common/addon-binder.cpp new file mode 100644 index 0000000..3f56e79 --- /dev/null +++ b/dali/devel-api/common/addon-binder.cpp @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2018 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. + */ + +#include "addon-binder.h" + +namespace Dali +{ + +namespace AddOn +{ + +Dali::Integration::AddOnManager* AddOnBinder::mAddOnManager = nullptr; + +} // namespace AddOn + +} // namespace Dali diff --git a/dali/devel-api/common/addon-binder.h b/dali/devel-api/common/addon-binder.h new file mode 100644 index 0000000..b43a90e --- /dev/null +++ b/dali/devel-api/common/addon-binder.h @@ -0,0 +1,177 @@ +#ifndef DALI_ADDON_BINDER_H +#define DALI_ADDON_BINDER_H + +/* + * Copyright (c) 2020 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. + */ + +#include + +namespace Dali +{ +namespace AddOn +{ + +/** + * Class automates binding an AddOn interface. + * + * This interface is meant to be used as a base + * for classes that use function binding macros. + * + * Sample use: + * \code{.cpp} + * #include + * struct AddOnImageLoader : public Dali::DevelAddOn::AddOnBinder + * { + * // Constructor requires the AddOn name and optional version (0 - any version accepted) + * AddOnImageLoader( const char* addonname ) : Dali::DevelAddOn::AddOnBinder( addonname, 0 ) {} + * + * ~AddOnImageLoader() = default; + * + * // Binding block + * // Using ADDON_BIND_FUNCTION() macro requires function name (resolved by the AddOn) and function + * // signature. It will generate member function with correct binding. + * + * // LoadBitmap + * ADDON_BIND_FUNCTION( LoadBitmap, bool(const Dali::ImageLoader::Input&, Dali::Devel::PixelBuffer&) ); + * + * // LoadHeader + * ADDON_BIND_FUNCTION( LoadHeader, bool(const Dali::ImageLoader::Input&, unsigned int&, unsigned int&) ); + * + * // GetFormatExtension + * ADDON_BIND_FUNCTION( GetFormatExtension, const char*() ); + * + * // GetFormatMagicNumber + * ADDON_BIND_FUNCTION( GetFormatMagicNumber, uint16_t() ); + * + * // GetBitmapProfile + * ADDON_BIND_FUNCTION( GetBitmapProfile, Bitmap::Profile() ); + * }; + * \endcode + */ + +class AddOnBinder +{ +public: + + /** + * @brief Constructor. Opens an AddOn and creates interface + * @param[in] addonName Name of AddOn + * @param[in] version Version of AddOn + */ + explicit AddOnBinder( const char* addonName, uint32_t version = 0u ) + { + mAddOnManager = Dali::Integration::AddOnManager::Get(); + if (mAddOnManager) + { + mAddOnHandle = mAddOnManager->GetAddOn(addonName); + if (mAddOnHandle) + { + mAddOnManager->GetAddOnInfo(addonName, mAddOnInfo); + } + } + } + + /** + * @brief Destructor + */ + virtual ~AddOnBinder() = default; + + /** + * @brief Looks up and converts c-style void* function into pointer of type T + * @param[in] funcName name of the function + * @return Returns a new pointer + */ + template + T* ConvertFunction( const std::string& funcName ) + { + if(mAddOnHandle) + { + auto ptr = mAddOnManager->GetGlobalProc(mAddOnHandle, funcName.c_str()); + return *reinterpret_cast(&ptr); + } + return nullptr; + } + + /** + * @brief Returns a handle to the AddOn library + * @return Handle object + */ + Dali::AddOnLibrary GetHandle() + { + return mAddOnHandle; + } + + /** + * @brief Returns pointer to the global AddOn function. + * @param[in] name Name of the function + * @return Valid pointer or nullptr + */ + void* GetGlobalProc( const char* name ) + { + return mAddOnManager ? mAddOnManager->GetGlobalProc( mAddOnHandle, name ) : nullptr; + } + + /** + * @brief Returns pointer to the instance AddOn function. + * @param[in] name Name of the function + * @return Valid pointer or nullptr + */ + void* GetInstanceProc( const char* name ) + { + return mAddOnManager ? mAddOnManager->GetInstanceProc( mAddOnHandle, name ) : nullptr; + } + + /** + * @brief Tests whether the interface is valid + * @return True if valid, false otherwise + */ + virtual bool IsValid() + { + return GetHandle() != nullptr; + } + + /** + * @brief Returns AddOn info structure + * @return AddOn info structure. + */ + const AddOnInfo& GetAddOnInfo() const + { + return mAddOnInfo; + } + +protected: + + static DALI_CORE_API Dali::Integration::AddOnManager* mAddOnManager; ///< Pointer to the AddOn manager + + Dali::AddOnLibrary mAddOnHandle { nullptr }; ///< Handle to the AddOn library + Dali::AddOnInfo mAddOnInfo {}; ///< Stored AddOnInfo structure +}; + + +/** + * Macro binds function as a member function of the class, for example, the call: + * + * ADDON_BIND_FUNCTION( SomeAddOnFunction, void(int, char*) ); + * + * will create a std::function object named SomeAddOnFunction and bound to the AddOn library. + */ +#define ADDON_BIND_FUNCTION( FUNCNAME, FUNCTYPE ) \ +std::function FUNCNAME{ConvertFunction( std::string(#FUNCNAME) )}; + +} // namespace AddOn +} // namespace Dali + +#endif // DALI_ADDON_BINDER_H diff --git a/dali/devel-api/file.list b/dali/devel-api/file.list index afe91fc..b450186 100644 --- a/dali/devel-api/file.list +++ b/dali/devel-api/file.list @@ -8,6 +8,7 @@ SET( devel_api_src_files ${devel_api_src_dir}/animation/animation-data.cpp ${devel_api_src_dir}/animation/animation-devel.cpp ${devel_api_src_dir}/animation/path-constrainer.cpp + ${devel_api_src_dir}/common/addon-binder.cpp ${devel_api_src_dir}/common/hash.cpp ${devel_api_src_dir}/common/singleton-service.cpp ${devel_api_src_dir}/common/stage-devel.cpp @@ -41,6 +42,10 @@ SET( devel_api_core_actors_header_files ${devel_api_src_dir}/actors/camera-actor-devel.h ) +SET( devel_api_core_addons_header_files + ${devel_api_src_dir}/addons/addon-base.h + ${devel_api_src_dir}/addons/addon-dispatch-table.h +) SET( devel_api_core_animation_header_files ${devel_api_src_dir}/animation/animation-data.h @@ -50,6 +55,7 @@ SET( devel_api_core_animation_header_files SET( devel_api_core_common_header_files + ${devel_api_src_dir}/common/addon-binder.h ${devel_api_src_dir}/common/bitwise-enum.h ${devel_api_src_dir}/common/circular-queue.h ${devel_api_src_dir}/common/hash.h @@ -126,6 +132,7 @@ SET( SOURCES ${SOURCES} SET( DEVEL_API_HEADERS ${DEVEL_API_HEADERS} ${devel_api_core_actors_header_files} + ${devel_api_core_addons_header_files} ${devel_api_core_animation_header_files} ${devel_api_core_common_header_files} ${devel_api_core_events_header_files} diff --git a/dali/integration-api/addon-manager.cpp b/dali/integration-api/addon-manager.cpp new file mode 100644 index 0000000..1376386 --- /dev/null +++ b/dali/integration-api/addon-manager.cpp @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2020 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. + * + */ + +#include "addon-manager.h" + +namespace Dali +{ +namespace Integration +{ +AddOnManager* AddOnManager::mSingleton = nullptr; + +AddOnManager::AddOnManager() +{ + mSingleton = this; +} + +AddOnManager::~AddOnManager() = default; + +AddOnManager* AddOnManager::Get() +{ + return mSingleton; +} +} +} diff --git a/dali/integration-api/addon-manager.h b/dali/integration-api/addon-manager.h new file mode 100644 index 0000000..f957f62 --- /dev/null +++ b/dali/integration-api/addon-manager.h @@ -0,0 +1,277 @@ +#ifndef DALI_INTEGRATION_ADDON_MANAGER_H +#define DALI_INTEGRATION_ADDON_MANAGER_H + +/* + * Copyright (c) 2020 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. + * + */ + +// INTERNAL INCLUDES +#include + +// EXTERNAL EXCLUDES +#include +#include +#include +#include +#include + +namespace Dali +{ +// Type of extensions (may be used internally) +enum class AddOnType +{ + GENERIC, + IMAGE_LOADER +}; + +/** + * @brief Helper function building the version number as 32-bit integer. + * The return value should be used to encode AddOnInfo::version field. + * + * @param[in] maj Major version number + * @param[in] min Minor version number + * @param[in] rev Revision version number + * @return returns 32-bit version number + */ +constexpr uint32_t DALI_ADDON_VERSION( uint32_t maj, uint32_t min, uint32_t rev ) +{ + return ((maj&0xff) << 24) | ((min & 0xfff) << 16); +} + +/** + * Structure describes AddOn details + */ +struct AddOnInfo +{ + AddOnType type; /// may be use in order to classify extension + void* next; /// holds pointer to additional data-structures + + std::string name; /// Name of the extension + uint32_t version; + + /** + * Structure contains details of build + */ + struct BuildInfo + { + uint32_t libCoreVersion; + uint32_t libAdaptorVersion; + uint32_t libToolkitVersion; + } buildInfo; +}; + +/** + * The structure contains essential function pointers which AddOnManager + * requires in order to use AddOns. + */ +struct AddOnDispatchTable +{ + std::string name; + void (*GetAddOnInfo)( Dali::AddOnInfo&) = nullptr; + void*(*GetGlobalProc)(const char*) = nullptr; + void*(*GetInstanceProc)(const char*) = nullptr; + + // Lifecycle callbacks + void(*OnStart)() = nullptr; + void(*OnResume)() = nullptr; + void(*OnPause)() = nullptr; + void(*OnStop)() = nullptr; +}; + +/** + * The AddOnLibrary type represents fully opaque object which hides + * the actual handle to the library and other related data. + */ +typedef void* AddOnLibrary; + +namespace Integration +{ +/** + * AddOnManager class + * + * Handles DALi AddOn support. The object of AddOnManager exists as a singleton and + * is created by the Adaptor. The AddOnManager is used by: + * + * 1) Application - query the AddOns and obtain AddOn interfaces + * 2) DALi - handling lifecycle events + * 3) AddOn - self-registering the AddOn dispatch table + * + * It is up to the implementation how the AddOn libraries are enumerated and opened. Any + * caching (functions, open libraries) must be handled by the implementation. + */ +class DALI_CORE_API AddOnManager +{ +protected: + /** + * @brief Constructor, initialised by the Adaptor + */ + AddOnManager(); + +public: + + /** + * @brief Destructor + */ + virtual ~AddOnManager(); + + // Functions called by the application +public: + /** + * @brief Retrieves list of the available AddOns + * @return List of AddOn names + */ + virtual std::vector EnumerateAddOns() = 0; + + /** + * @brief Returns AddOnInfo structure for specified AddOn name + * @param[in] name Name of AddOn + * @param[out]] info Output reference + * @return True on success, False if extension info cannot be retrieved + */ + virtual bool GetAddOnInfo(const std::string& name, AddOnInfo& info ) = 0; + + /** + * @brief Loads and initialises specified extensions + * @param[in] extensionNames Array of extension names + * @return vector of initialised extension handles + */ + virtual std::vector LoadAddOns( const std::vector& addonNames ) = 0; + + /** + * @brief Loads AddOn with specified name + * @param[in] addOnName Name of AddOn to be acquired + * @return Returns a valid handle or nullptr + */ + inline AddOnLibrary GetAddOn( const std::string& addonName ) + { + return LoadAddOns( { addonName } )[0]; + } + + /** + * @brief Returns AddOn global function pointer + * @param[in] addOnLibrary valid AddOn library object + * @param[in] procName Name of the function to retrieve + * @return Pointer to the function or null if function doesn't exist + */ + virtual void* GetGlobalProc( const Dali::AddOnLibrary& addOnLibrary, const char* procName ) = 0; + + /** + * @brief Returns addon instance function pointer + * @param[in] addOnLibrary valid AddOn library object + * @param[in] procName Name of the function to retrieve + * @return Pointer to the function or null if function doesn't exist + */ + virtual void* GetInstanceProc( const Dali::AddOnLibrary& addOnLibrary, const char* procName ) = 0; + + /** + * @brief Returns addon global function of specified type + * @param[in] addOnLibrary valid AddOn library object + * @param[in] procName Name of the function to retrieve + * @return std::function object or null if function doesn't exist + */ + template + DALI_INTERNAL std::function GetGlobalProc( const Dali::AddOnLibrary& addonlibrary, const char* procName ) + { + auto ptr = GetGlobalProc( addonlibrary, procName ); + if( ptr ) + { + return std::function( *reinterpret_cast(&ptr) ); + } + return {}; + }; + + /** + * @brief Returns AddOn instance function of specified type + * @param[in] addOnLibrary valid AddOn library object + * @param[in] procName Name of the function to retrieve + * @return std::function object or null if function doesn't exist + */ + template + DALI_INTERNAL std::function GetInstanceProc( const Dali::AddOnLibrary& addOnLibrary, const char* procName ) + { + auto ptr = GetInstanceProc( addOnLibrary, procName ); + if( ptr ) + { + return std::function( *reinterpret_cast(&ptr) ); + } + return {}; + }; + + /** + * @brief Invokes global function by name + * @param[in] addOnLibrary valid AddOn library object + * @param[in] functionName Name of function to be called + * @param args[in] Arguments + * @return Result of called function + */ + template + DALI_INTERNAL R InvokeGlobalProc( AddOnLibrary addOnLibrary, const char* functionName, Args&&... args) + { + return std::move(GetGlobalProc( addOnLibrary, functionName )( args... )); + } + + // Lifecycle events, functions are called by the Adaptor +public: + + /** + * @brief Lifecycle pause function + */ + virtual void Pause() = 0; + + /** + * @brief Lifecycle resume function + */ + virtual void Resume() = 0; + + /** + * @brief Lifecycle start function + */ + virtual void Start() = 0; + + /** + * @brief Lifecycle stop function + */ + virtual void Stop() = 0; + + // Functions called by the AddOn +public: + + /** + * @brief Registers the dispatch table with AddOnManager. + * + * The function must be called by the AddOn in order to self-register and add + * the dispatch table. The platform-dependent implementation must override it + * in order to store the dispatch table. The way the dispatch table is stored + * depends on the implementation. + * + * @param[in] dispatchTable Pointer to the valid dispatch table + */ + virtual void RegisterAddOnDispatchTable( const AddOnDispatchTable* dispatchTable ) = 0; + + /** + * @brief Retrieves AddOnManager singleton + * @return pointer to the AddOnManager + */ + static AddOnManager* Get(); + +protected: + + static AddOnManager* mSingleton; ///< Singleton storing an instance of AddOnManager +}; +} // namespace Integration +} // namespace Dali + +#endif // DALI_INTEGRATION_ADDON_MANAGER diff --git a/dali/integration-api/file.list b/dali/integration-api/file.list index 940771c..f5ffa3c 100644 --- a/dali/integration-api/file.list +++ b/dali/integration-api/file.list @@ -3,6 +3,7 @@ SET( platform_abstraction_src_dir ${ROOT_SRC_DIR}/dali/integration-api ) # Add platform abstraction source files here SET( platform_abstraction_src_files + ${platform_abstraction_src_dir}/addon-manager.cpp ${platform_abstraction_src_dir}/bitmap.cpp ${platform_abstraction_src_dir}/core.cpp ${platform_abstraction_src_dir}/debug.cpp @@ -24,6 +25,7 @@ SET( platform_abstraction_src_files SET( platform_abstraction_header_files + ${platform_abstraction_src_dir}/addon-manager.h ${platform_abstraction_src_dir}/core.h ${platform_abstraction_src_dir}/core-enumerations.h ${platform_abstraction_src_dir}/context-notifier.h -- 2.7.4