From c7445a69ae335ad6a034bf20f48ad49bc56f5b04 Mon Sep 17 00:00:00 2001 From: Radoslaw Cybulski Date: Wed, 25 Apr 2018 15:33:21 +0200 Subject: [PATCH] [Tizen][ATSPI] Accessibility initial implementation Change-Id: Ida2c9d1be49d2c09a3bf1dd0e461955eb42fe433 --- build/tizen/adaptor/Makefile.am | 6 + build/tizen/adaptor/module.list | 6 +- dali/dali-bridge/file.list | 14 + dali/dali-bridge/src/Accessible.cpp | 157 + dali/dali-bridge/src/BridgeAccessible.cpp | 732 +++++ dali/dali-bridge/src/BridgeAccessible.hpp | 80 + dali/dali-bridge/src/BridgeAction.cpp | 80 + dali/dali-bridge/src/BridgeAction.hpp | 28 + dali/dali-bridge/src/BridgeBase.cpp | 165 ++ dali/dali-bridge/src/BridgeBase.hpp | 215 ++ dali/dali-bridge/src/BridgeCollection.cpp | 366 +++ dali/dali-bridge/src/BridgeCollection.hpp | 49 + dali/dali-bridge/src/BridgeComponent.cpp | 75 + dali/dali-bridge/src/BridgeComponent.hpp | 32 + dali/dali-bridge/src/BridgeEditableText.cpp | 45 + dali/dali-bridge/src/BridgeEditableText.hpp | 21 + dali/dali-bridge/src/BridgeImpl.cpp | 160 + dali/dali-bridge/src/BridgeImpl.hpp | 4 + dali/dali-bridge/src/BridgeObject.cpp | 401 +++ dali/dali-bridge/src/BridgeObject.hpp | 38 + dali/dali-bridge/src/BridgeText.cpp | 74 + dali/dali-bridge/src/BridgeText.hpp | 26 + dali/dali-bridge/src/BridgeValue.cpp | 48 + dali/dali-bridge/src/BridgeValue.hpp | 28 + dali/dali-bridge/src/Common.hpp | 267 ++ dali/dali-bridge/src/Component.cpp | 29 + dali/dali-bridge/src/DBus.cpp | 490 +++ dali/dali-bridge/src/DBus.hpp | 3104 ++++++++++++++++++++ dali/dali-bridge/src/Optional.hpp | 281 ++ dali/dali-bridge/src/dbusLocators.hpp | 98 + dali/devel-api/adaptor-framework/accessibility.h | 248 ++ dali/devel-api/file.list | 5 +- dali/internal/accessibility/accessibility-impl.cpp | 646 ++++ dali/internal/accessibility/file.list | 1 + dali/internal/adaptor/common/adaptor-impl.cpp | 42 +- dali/internal/adaptor/common/adaptor-impl.h | 17 +- dali/public-api/adaptor-framework/accessibility.h | 503 ++++ dali/public-api/file.list | 1 + packaging/dali-adaptor.spec | 0 39 files changed, 8577 insertions(+), 5 deletions(-) create mode 100644 dali/dali-bridge/file.list create mode 100755 dali/dali-bridge/src/Accessible.cpp create mode 100755 dali/dali-bridge/src/BridgeAccessible.cpp create mode 100644 dali/dali-bridge/src/BridgeAccessible.hpp create mode 100755 dali/dali-bridge/src/BridgeAction.cpp create mode 100644 dali/dali-bridge/src/BridgeAction.hpp create mode 100755 dali/dali-bridge/src/BridgeBase.cpp create mode 100644 dali/dali-bridge/src/BridgeBase.hpp create mode 100644 dali/dali-bridge/src/BridgeCollection.cpp create mode 100644 dali/dali-bridge/src/BridgeCollection.hpp create mode 100644 dali/dali-bridge/src/BridgeComponent.cpp create mode 100644 dali/dali-bridge/src/BridgeComponent.hpp create mode 100644 dali/dali-bridge/src/BridgeEditableText.cpp create mode 100644 dali/dali-bridge/src/BridgeEditableText.hpp create mode 100644 dali/dali-bridge/src/BridgeImpl.cpp create mode 100644 dali/dali-bridge/src/BridgeImpl.hpp create mode 100644 dali/dali-bridge/src/BridgeObject.cpp create mode 100644 dali/dali-bridge/src/BridgeObject.hpp create mode 100644 dali/dali-bridge/src/BridgeText.cpp create mode 100644 dali/dali-bridge/src/BridgeText.hpp create mode 100644 dali/dali-bridge/src/BridgeValue.cpp create mode 100644 dali/dali-bridge/src/BridgeValue.hpp create mode 100755 dali/dali-bridge/src/Common.hpp create mode 100644 dali/dali-bridge/src/Component.cpp create mode 100644 dali/dali-bridge/src/DBus.cpp create mode 100755 dali/dali-bridge/src/DBus.hpp create mode 100755 dali/dali-bridge/src/Optional.hpp create mode 100644 dali/dali-bridge/src/dbusLocators.hpp create mode 100644 dali/devel-api/adaptor-framework/accessibility.h mode change 100644 => 100755 dali/devel-api/file.list create mode 100644 dali/internal/accessibility/accessibility-impl.cpp create mode 100644 dali/public-api/adaptor-framework/accessibility.h mode change 100644 => 100755 packaging/dali-adaptor.spec diff --git a/build/tizen/adaptor/Makefile.am b/build/tizen/adaptor/Makefile.am index 190aab1..90f7104 100644 --- a/build/tizen/adaptor/Makefile.am +++ b/build/tizen/adaptor/Makefile.am @@ -40,6 +40,7 @@ LIBDALI_ADAPTOR_LA_SOURCES = \ $(adaptor_clipboard_common_src_files) \ $(adaptor_clipboard_ubuntu_x11_src_files) \ $(devel_api_src_files) \ + $(bridge_src_files) \ $(adaptor_devel_api_text_abstraction_src_files) \ $(adaptor_graphics_common_src_files) \ $(adaptor_graphics_gles20_src_files) \ @@ -95,6 +96,7 @@ LIBDALI_ADAPTOR_LA_SOURCES = \ $(adaptor_adaptor_tizen_wayland_src_files) \ $(adaptor_clipboard_common_src_files) \ $(devel_api_src_files) \ + $(bridge_src_files) \ $(adaptor_devel_api_text_abstraction_src_files) \ $(adaptor_graphics_common_src_files) \ $(adaptor_graphics_gles20_src_files) \ @@ -158,6 +160,7 @@ LIBDALI_ADAPTOR_LA_SOURCES = \ $(adaptor_adaptor_tizen_wayland_src_files) \ $(adaptor_clipboard_common_src_files) \ $(devel_api_src_files) \ + $(bridge_src_files) \ $(adaptor_devel_api_text_abstraction_src_files) \ $(adaptor_graphics_common_src_files) \ $(adaptor_graphics_gles20_src_files) \ @@ -221,6 +224,7 @@ LIBDALI_ADAPTOR_LA_SOURCES = \ $(adaptor_adaptor_tizen_wayland_src_files) \ $(adaptor_clipboard_common_src_files) \ $(devel_api_src_files) \ + $(bridge_src_files) \ $(adaptor_devel_api_text_abstraction_src_files) \ $(adaptor_graphics_common_src_files) \ $(adaptor_graphics_gles20_src_files) \ @@ -282,6 +286,7 @@ LIBDALI_ADAPTOR_LA_SOURCES = \ $(adaptor_adaptor_tizen_wayland_src_files) \ $(adaptor_clipboard_common_src_files) \ $(devel_api_src_files) \ + $(bridge_src_files) \ $(adaptor_devel_api_text_abstraction_src_files) \ $(adaptor_graphics_common_src_files) \ $(adaptor_graphics_gles20_src_files) \ @@ -346,6 +351,7 @@ LIBDALI_ADAPTOR_LA_SOURCES = \ $(adaptor_adaptor_tizen_wearable_src_files) \ $(adaptor_clipboard_common_src_files) \ $(devel_api_src_files) \ + $(bridge_src_files) \ $(adaptor_devel_api_text_abstraction_src_files) \ $(adaptor_graphics_common_src_files) \ $(adaptor_graphics_gles20_src_files) \ diff --git a/build/tizen/adaptor/module.list b/build/tizen/adaptor/module.list index 297e0b3..c38a303 100755 --- a/build/tizen/adaptor/module.list +++ b/build/tizen/adaptor/module.list @@ -7,6 +7,10 @@ include ../../../dali/internal/accessibility/file.list adaptor_adaptor_dir = ../../../dali/internal/adaptor include ../../../dali/internal/adaptor/file.list +# Module: atspi-bridge +bridge_src_dir = ../../../dali/dali-bridge +include ../../../dali/dali-bridge/file.list + # Module: clipboard adaptor_clipboard_dir = ../../../dali/internal/clipboard include ../../../dali/internal/clipboard/file.list @@ -59,7 +63,7 @@ include ../../../dali/internal/video/file.list adaptor_web_engine_dir = ../../../dali/internal/web-engine-lite include ../../../dali/internal/web-engine-lite/file.list -# Module: window-system +# Module: window-system adaptor_window_system_dir = ../../../dali/internal/window-system include ../../../dali/internal/window-system/file.list diff --git a/dali/dali-bridge/file.list b/dali/dali-bridge/file.list new file mode 100644 index 0000000..0cb402d --- /dev/null +++ b/dali/dali-bridge/file.list @@ -0,0 +1,14 @@ +bridge_src_files = \ + $(bridge_src_dir)/src/Accessible.cpp \ + $(bridge_src_dir)/src/Component.cpp \ + $(bridge_src_dir)/src/BridgeAccessible.cpp \ + $(bridge_src_dir)/src/BridgeCollection.cpp \ + $(bridge_src_dir)/src/BridgeBase.cpp \ + $(bridge_src_dir)/src/BridgeComponent.cpp \ + $(bridge_src_dir)/src/BridgeAction.cpp \ + $(bridge_src_dir)/src/BridgeValue.cpp \ + $(bridge_src_dir)/src/BridgeObject.cpp \ + $(bridge_src_dir)/src/BridgeImpl.cpp \ + $(bridge_src_dir)/src/DBus.cpp \ + $(bridge_src_dir)/src/BridgeText.cpp \ + $(bridge_src_dir)/src/BridgeEditableText.cpp diff --git a/dali/dali-bridge/src/Accessible.cpp b/dali/dali-bridge/src/Accessible.cpp new file mode 100755 index 0000000..9612143 --- /dev/null +++ b/dali/dali-bridge/src/Accessible.cpp @@ -0,0 +1,157 @@ +#include "Common.hpp" + +using namespace Dali::Accessibility; + +std::vector< std::string > Accessible::GetInterfaces() +{ + std::vector< std::string > tmp; + tmp.push_back( ATSPI_DBUS_INTERFACE_ACCESSIBLE ); + if( dynamic_cast< Collection* >( this ) ) + tmp.push_back( ATSPI_DBUS_INTERFACE_COLLECTION ); + if( dynamic_cast< Text* >( this ) ) + tmp.push_back( ATSPI_DBUS_INTERFACE_TEXT ); + if( dynamic_cast< Value* >( this ) ) + tmp.push_back( ATSPI_DBUS_INTERFACE_VALUE ); + if( dynamic_cast< Component* >( this ) ) + tmp.push_back( ATSPI_DBUS_INTERFACE_COMPONENT ); + if( auto d = dynamic_cast< Action* >( this ) ) + { + if( d->GetActionCount() > 0 ) + tmp.push_back( ATSPI_DBUS_INTERFACE_ACTION ); + } + return tmp; +} + +thread_local std::atomic< Bridge* > threadLocalBridge{}; +std::atomic< Bridge* > allThreads{}; + +Bridge* Bridge::GetCurrentBridge() +{ + auto p = threadLocalBridge.load(); + if( !p ) + p = allThreads.load(); + return p; +} + +Accessible::Accessible() +{ +} + +Accessible::~Accessible() +{ + auto b = bridgeData.lock(); + if( b ) + b->objects.erase( it ); +} + +void Bridge::MakePublic( Visibility vis ) +{ + bool res = false; + Bridge* expected = this; + + switch( vis ) + { + case Visibility::hidden: + { + threadLocalBridge.compare_exchange_strong( expected, nullptr ); + allThreads.compare_exchange_strong( expected, nullptr ); + break; + } + case Visibility::thisThreadOnly: + { + res = threadLocalBridge.exchange( this ); + assert( !res ); + break; + } + case Visibility::allThreads: + { + res = allThreads.exchange( this ); + assert( !res ); + break; + } + } +} + +void Accessible::EmitShowing( bool showing ) +{ + if( auto b = GetBridgeData() ) + { + b->bridge->EmitStateChanged( this, State::Showing, showing ? 1 : 0, 0 ); + } +} + +void Accessible::EmitVisible( bool visible ) +{ + if( auto b = GetBridgeData() ) + { + b->bridge->EmitStateChanged( this, State::Visible, visible ? 1 : 0, 0 ); + } +} + +void Accessible::EmitHighlighted( bool set ) +{ + if( auto b = GetBridgeData() ) + { + b->bridge->EmitStateChanged( this, State::Highlighted, set ? 1 : 0, 0 ); + } +} + +void Accessible::Emit( WindowEvent we, unsigned int detail1 ) +{ + if( auto b = GetBridgeData() ) + { + b->bridge->Emit( this, we, detail1 ); + } +} + +std::vector< Accessible* > Accessible::GetChildren() +{ + std::vector< Accessible* > tmp( GetChildCount() ); + for( auto i = 0u; i < tmp.size(); ++i ) + { + tmp[i] = GetChildAtIndex( i ); + } + return tmp; +} + +std::shared_ptr< Bridge::Data > Accessible::GetBridgeData() +{ + auto b = bridgeData.lock(); + if( !b ) + { + auto p = Bridge::GetCurrentBridge(); + if( !p ) + return {}; + b = p->data; + } + return b; +} + +Address Accessible::GetAddress() +{ + auto b = bridgeData.lock(); + if( !b ) + { + b = GetBridgeData(); + assert( b ); + b->bridge->RegisterOnBridge( this ); + } + return {b->busName, b->root == this ? "root" : std::to_string( it->first )}; +} + +void Bridge::RegisterOnBridge( Accessible* obj ) +{ + assert( !obj->bridgeData.lock() || obj->bridgeData.lock() == data ); + if( !obj->bridgeData.lock() ) + { + assert( data ); + auto oid = ++data->objectId; + obj->it = data->objects.insert( {oid, obj} ).first; + obj->bridgeData = data; + } +} + +bool Accessible::IsProxy() +{ + return false; +} diff --git a/dali/dali-bridge/src/BridgeAccessible.cpp b/dali/dali-bridge/src/BridgeAccessible.cpp new file mode 100755 index 0000000..eafa947 --- /dev/null +++ b/dali/dali-bridge/src/BridgeAccessible.cpp @@ -0,0 +1,732 @@ +#include "BridgeAccessible.hpp" +#include + +using namespace Dali::Accessibility; + +#define GET_NAVIGABLE_AT_POINT_MAX_RECURSION_DEPTH 10000 + +BridgeAccessible::BridgeAccessible() +{ +} + +void BridgeAccessible::RegisterInterfaces() +{ + DBus::DBusInterfaceDescription desc{ATSPI_DBUS_INTERFACE_ACCESSIBLE}; + AddGetPropertyToInterface( desc, "ChildCount", &BridgeAccessible::GetChildCount ); + AddGetPropertyToInterface( desc, "Name", &BridgeAccessible::GetName ); + AddGetPropertyToInterface( desc, "Description", &BridgeAccessible::GetDescription ); + AddGetPropertyToInterface( desc, "Parent", &BridgeAccessible::GetParent ); + AddFunctionToInterface( desc, "GetRole", &BridgeAccessible::GetRole ); + AddFunctionToInterface( desc, "GetRoleName", &BridgeAccessible::GetRoleName ); + AddFunctionToInterface( desc, "GetLocalizedRoleName", &BridgeAccessible::GetLocalizedRoleName ); + AddFunctionToInterface( desc, "GetState", &BridgeAccessible::GetStates ); + AddFunctionToInterface( desc, "GetAttributes", &BridgeAccessible::GetAttributes ); + AddFunctionToInterface( desc, "GetInterfaces", &BridgeAccessible::GetInterfaces ); + AddFunctionToInterface( desc, "GetChildAtIndex", &BridgeAccessible::GetChildAtIndex ); + AddFunctionToInterface( desc, "GetChildren", &BridgeAccessible::GetChildren ); + AddFunctionToInterface( desc, "GetIndexInParent", &BridgeAccessible::GetIndexInParent ); + AddFunctionToInterface( desc, "GetNavigableAtPoint", &BridgeAccessible::GetNavigableAtPoint ); + AddFunctionToInterface( desc, "GetNeighbor", &BridgeAccessible::GetNeighbor ); + AddFunctionToInterface( desc, "GetDefaultLabelInfo", &BridgeAccessible::GetDefaultLabelInfo ); + AddFunctionToInterface( desc, "DoGesture", &BridgeAccessible::DoGesture ); + AddFunctionToInterface( desc, "GetReadingMaterial", &BridgeAccessible::GetReadingMaterial ); + dbusServer.addInterface( "/", desc, true ); +} + +static bool AcceptObjectCheckRole( Component* obj ) +{ + if( !obj ) + return false; + switch( obj->GetRole() ) + { + case Role::Application: + case Role::Filler: + case Role::ScrollPane: + case Role::SplitPane: + case Role::Window: + case Role::Image: + case Role::ImageMap: + case Role::List: + case Role::Icon: + case Role::ToolBar: + case Role::RedundantObject: + case Role::ColorChooser: + case Role::TreeTable: + case Role::PageTabList: + case Role::PageTab: + case Role::SpinButton: + case Role::InputMethodWindow: + case Role::Embedded: + case Role::Invalid: + case Role::Notification: + case Role::DateEditor: + { + return false; + } + default: + { + break; + } + } + + return true; +} + +static Component* GetScrollableParent( Accessible* obj ) +{ + while( obj ) + { + obj = obj->GetParent(); + auto comp = dynamic_cast< Component* >( obj ); + if( comp && comp->IsScrollable() ) + return comp; + } + return nullptr; +} + +static bool ObjectIsItem( Component* obj ) +{ + if( !obj ) + return false; + auto role = obj->GetRole(); + return role == Role::ListItem || role == Role::MenuItem; +} + +static bool ObjectIsCollapsed( Component* obj ) +{ + if( !obj ) + return false; + const auto states = obj->GetStates(); + return states[State::Expandable] && !states[State::Expanded]; +} + +static bool OobjectIsZeroSize( Component* obj ) +{ + if( !obj ) + return false; + auto size = obj->GetExtents( CoordType::Window ).size; + return size.height == 0 || size.width == 0; +} + +static bool AcceptObject( Component* obj ) +{ + if( !obj ) + return false; + const auto states = obj->GetStates(); + if( !states[State::Visible] ) + return false; + if( !AcceptObjectCheckRole( obj ) ) + return false; +/* + TODO: add relations + if (CALL(get_object_in_relation_by_type, obj, ATSPI_RELATION_CONTROLLED_BY) != NULL) return 0; +*/ + if( !states[State::Highlightable] ) + return false; + + if( GetScrollableParent( obj ) != nullptr ) + { + auto parent = dynamic_cast< Component* >( obj->GetParent() ); + + if( parent ) + { + return !ObjectIsItem( obj ) || !ObjectIsCollapsed( parent ); + } + } + else + { + if( OobjectIsZeroSize( obj ) ) + { + return false; + } + if( !states[State::Showing] ) + { + return false; + } + } + return true; +} + +static bool AcceptObject( Accessible* obj ) +{ + auto c = dynamic_cast< Component* >( obj ); + return AcceptObject( c ); +} + +static bool AcceptObjectOrProxy( Component* obj ) +{ + return obj->IsProxy() || AcceptObject( obj ); +} + +static Component* CalculateNavigableAccessibleAtPoint( Accessible* root, Point p, CoordType cType, unsigned int maxRecursionDepth ) +{ + if( !root || maxRecursionDepth == 0 ) + return nullptr; + auto root_component = dynamic_cast< Component* >( root ); + if( root_component && !root_component->Contains( p, cType ) ) + { + return nullptr; + } + + auto children = root->GetChildren(); + for( auto childIt = children.rbegin(); childIt != children.rend(); childIt++ ) + { + auto result = CalculateNavigableAccessibleAtPoint( *childIt, p, cType, maxRecursionDepth - 1 ); + if( result ) + return result; + } + if( root_component && AcceptObjectOrProxy( root_component ) ) + return root_component; + return nullptr; + + /* + TODO: add relations + void *relation_obj = CALL(get_object_in_relation_by_type, root, ATSPI_RELATION_CONTROLLED_BY); + unsigned char contains = 0; + if (relation_obj) + { + contains = CALL(object_contains, relation_obj, x, y, coordinates_are_screen_based); + if (contains) root = relation_obj; + } + */ +} + +BridgeAccessible::ReadingMaterialType BridgeAccessible::GetReadingMaterial() +{ + auto self = FindSelf(); + auto attributes = self->GetAttributes(); + auto name = self->GetName(); + std::string labeledByName = ""; + std::string textIfceName = ""; + auto role = static_cast< uint32_t >( self->GetRole() ); + auto states = self->GetStates(); + auto localizedName = self->GetLocalizedRoleName(); + auto childCount = static_cast< int32_t >( self->GetChildCount() ); + + double currentValue = 0.0; + double minimumIncrement = 0.0; + double maximumValue = 0.0; + double minimumValue = 0.0; + + auto description = self->GetDescription(); + auto indexInParent = static_cast< int32_t >( self->GetIndexInParent() ); + bool isSelectedInParent = false; + bool hasCheckBoxChild = false; + int32_t firstSelectedChildIndex = 0; + int32_t selectedChildCount = 0; + + for( auto i = 0u; i < static_cast< size_t >( childCount ); ++i ) + { + auto q = self->GetChildAtIndex( i ); + auto s = q->GetStates(); + if( s[State::Selectable] ) + { + ++selectedChildCount; + if( s[State::Selected] ) + { + if( firstSelectedChildIndex < 0 ) + firstSelectedChildIndex = static_cast< int32_t >( i ); + } + } + if( q->GetRole() == Role::CheckBox ) + hasCheckBoxChild = true; + } + + int32_t listChildrenCount = 0; + Accessible* parent = self->GetParent(); + auto parentStateSet = parent ? parent->GetStates() : States{}; + auto parentChildCount = parent ? static_cast< int32_t >( parent->GetChildCount() ) : 0; + auto parentRole = static_cast< uint32_t >( parent ? parent->GetRole() : Role{} ); + Accessible* describedByObject = nullptr; + + return { + attributes, + name, + labeledByName, + textIfceName, + role, + states, + localizedName, + childCount, + currentValue, + minimumIncrement, + maximumValue, + minimumValue, + description, + indexInParent, + isSelectedInParent, + hasCheckBoxChild, + listChildrenCount, + firstSelectedChildIndex, + parent, + parentStateSet, + parentChildCount, + parentRole, + selectedChildCount, + describedByObject}; +} + +DBus::ValueOrError< bool > BridgeAccessible::DoGesture( int32_t, int32_t, int32_t, int32_t, int32_t, int32_t, uint32_t ) +{ + return false; +} + +DBus::ValueOrError< Accessible*, uint8_t, Accessible* > BridgeAccessible::GetNavigableAtPoint( int32_t x, int32_t y, uint32_t coordType ) +{ + SCOPE(); + Accessible* deputy = nullptr; + auto accessible = FindSelf(); + auto cType = static_cast< CoordType >( coordType ); + auto component = CalculateNavigableAccessibleAtPoint( accessible, {x, y}, cType, GET_NAVIGABLE_AT_POINT_MAX_RECURSION_DEPTH ); + bool recurse = false; + if( component ) + { + const auto states = component->GetStates(); + if( states[State::Modal] ) + { + component = nullptr; + } + } + if( component ) + { + recurse = component->IsProxy(); + } + //TODO: add deputy + return {component, recurse, deputy}; +} + +static bool CheckChainEndWithAttribute( Accessible* obj, unsigned char forward ) +{ + if( !obj ) + return false; + auto attrs = obj->GetAttributes(); + for( auto& attr : attrs ) + { + if( attr.first == "relation_chain_end" ) + { + if( ( attr.second == "prev,end" && forward == 0 ) || ( attr.second == "next,end" && forward == 1 ) || attr.second == "prev,next,end" ) + { + return true; + } + } + } + return false; +} + +static Accessible* DeputyOfProxyInParentGet( Accessible* obj ) +{ + return nullptr; +/* +if (!obj) + return nullptr; + +Accessible *deputy = nullptr; +auto children = obj->GetChildren(); +unsigned int index = 0; +for (auto child : children) { + if (child->IsProxy()) { + if (index == 0) { + //WRN("Proxy does not have deputy object"); + break; + } + deputy = children[index - 1]; + break; + } + index++; +} +return deputy; +*/ +} + +Accessible* BridgeAccessible::GetCurrentlyHighlighted() +{ + //TODO: add currently highlighted object + return nullptr; +} + +std::vector< Accessible* > BridgeAccessible::ValidChildrenGet( const std::vector< Accessible* >& children, Accessible* start, Accessible* root ) +{ + /* condition to find first(last) object regardless of scrollable parent. + looping navigation does not care scrollable parent. + 1. currently highlighted object exists + 2. both start and root are same */ + + /* TODO: add code, we need a scrollable implementation first + Accessible *current = GetCurrentlyHighlighted(); + if (current && start == root) return children; + if(children.size() == 0) return {}; + + Eo *child = children[0]; + + if (child) + { + Evas_Coord x = 0, y = 0, w = 0, h = 0; + Evas_Coord sx = 0, sy = 0, sw = 0, sh = 0; + + if (_new_scrollable_parent_viewport_geometry_get(child, start, + &sx, &sy, &sw, &sh)) + { + Eina_List *l, *l_next; + EINA_LIST_FOREACH_SAFE(children, l, l_next, child) + { + eo_do(child, + elm_interface_atspi_component_extents_get(EINA_FALSE, + &x, &y, &w, &h)); + if (w == 0 || h == 0 || + !ELM_RECTS_INTERSECT(x, y, w, h, sx, sy, sw, sh)) + children = eina_list_remove_list(children, l); + } + } + } + */ + return children; +} + +static bool DeputyIs( Accessible* obj ) +{ + //TODO: add deputy + return false; +} + +static Accessible* GetObjectInRelationFlow( Accessible* ptr, RelationType type ) +{ + //TODO: add relations + return nullptr; +} +static Accessible* ProxyInParentGet( Accessible* obj ) +{ + if( !obj ) + return nullptr; + auto children = obj->GetChildren(); + for( auto& child : children ) + { + if( child->IsProxy() ) + return child; + } + return nullptr; +} + +static bool ObjectRoleIsAcceptableWhenNavigatingNextPrev( Accessible* obj ) +{ + if( !obj ) + return false; + auto role = obj->GetRole(); + return role != Role::PopupMenu && role != Role::Dialog; +} + +template < class T > +struct CycleDetection +{ + CycleDetection( const T value ) : key( value ), currentSearchSize( 1 ), counter( 1 ) {} + bool check( const T value ) + { + if( key == value ) + return true; + if( --counter == 0 ) + { + currentSearchSize <<= 1; + if( currentSearchSize == 0 ) + return true; // UNDEFINED BEHAVIOR + counter = currentSearchSize; + key = value; + } + return false; + } + T key; + unsigned int currentSearchSize; + unsigned int counter; +}; + +static Accessible* FindNonDefunctChild( const std::vector< Accessible* >& children, unsigned int currentIndex, unsigned char forward ) +{ + unsigned int childrenCount = children.size(); + for( ; currentIndex < childrenCount; forward ? ++currentIndex : --currentIndex ) + { + Accessible* n = children[currentIndex]; + if( n && !n->GetStates()[State::Defunct] ) + return n; + } + return nullptr; +} + +static Accessible* DirectionalDepthFirstSearchTryNonDefunctChild( Accessible* node, const std::vector< Accessible* >& children, unsigned char forward ) +{ + if( !node ) + return nullptr; + auto childrenCount = children.size(); + if( childrenCount > 0 ) + { + const bool isShowing = GetScrollableParent( node ) == nullptr ? node->GetStates()[State::Showing] : true; + if( isShowing ) + { + return FindNonDefunctChild( children, forward ? 0 : childrenCount - 1, forward ); + } + } + return nullptr; +} + +Accessible* BridgeAccessible::GetNextNonDefunctSibling( Accessible* obj, Accessible* start, Accessible* root, unsigned char forward ) +{ + if( !obj ) + return nullptr; + auto parent = obj->GetParent(); + if( !parent ) + return nullptr; + + auto children = ValidChildrenGet( parent->GetChildren(), start, root ); + + unsigned int children_count = children.size(); + if( children_count == 0 ) + { + return nullptr; + } + unsigned int current = 0; + for( ; current < children_count && children[current] != obj; ++current ) + ; + if( current >= children_count ) + { + return nullptr; + } + forward ? ++current : --current; + auto ret = FindNonDefunctChild( children, current, forward ); + return ret; +} + +Accessible* BridgeAccessible::DirectionalDepthFirstSearchTryNonDefunctSibling( bool& all_children_visited, Accessible* node, Accessible* start, Accessible* root, unsigned char forward ) +{ + while( true ) + { + Accessible* sibling = GetNextNonDefunctSibling( node, start, root, forward ); + if( sibling ) + { + node = sibling; + all_children_visited = false; + break; + } + // walk up... + node = node->GetParent(); + if( node == nullptr || node == root ) + return nullptr; + + // in backward traversing stop the walk up on parent + if( !forward ) + break; + } + return node; +} + +Accessible* BridgeAccessible::CalculateNeighbor( Accessible* root, Accessible* start, unsigned char forward, BridgeAccessible::GetNeighborSearchMode search_mode ) +{ + if( start && CheckChainEndWithAttribute( start, forward ) ) + return start; + if( root && root->GetStates()[State::Defunct] ) + return NULL; + if( start && start->GetStates()[State::Defunct] ) + { + start = NULL; + forward = 1; + } + + if( search_mode == BridgeAccessible::GetNeighborSearchMode::recurseToOutside ) + { + /* This only works if we navigate backward, and it is not possible to + find in embedded process. In this case the deputy should be used */ + return DeputyOfProxyInParentGet( start ); + } + + Accessible* node = start ? start : root; + if( !node ) + return nullptr; + + // initialization of all-children-visited flag for start node - we assume + // that when we begin at start node and we navigate backward, then all children + // are visited, so navigation will ignore start's children and go to + // previous sibling available. + /* Regarding condtion (start != root): + The last object can be found only if all_children_visited is false. + The start is same with root, when looking for the last object. */ + bool all_children_visited = ( start != root ) && ( search_mode != BridgeAccessible::GetNeighborSearchMode::recurseFromRoot && !forward ); + // true, if starting element should be ignored. this is only used in rare case of + // recursive search failing to find an object. + // consider tree, where element A on bus BUS_A has child B on bus BUS_B. when going "next" from + // element A algorithm has to descend into BUS_B and search element B and its children. this is done + // by returning to our caller object B with special flag set (meaning - continue the search from B on bus BUS_B). + // if next object will be found there (on BUS_B), then search ends. but if not, then our caller will find it out + // and will call us again with object A and flag search_mode set to NEIGHBOR_SEARCH_MODE_CONTINUE_AFTER_FAILED_RECURSING. + // this flag means, that object A was already checked previously and we should skip it and its children. + bool force_next = ( search_mode == BridgeAccessible::GetNeighborSearchMode::continueAfterFailedRecursion ); + + CycleDetection< Accessible* > cycleDetection( node ); + while( node ) + { + if( node->GetStates()[State::Defunct] ) + return nullptr; + + // always accept proxy object from different world + if( !force_next && node->IsProxy() ) + return node; + + auto children = node->GetChildren(); + children = ValidChildrenGet( children, start, root ); + + // do accept: + // 1. not start node + // 2. parent after all children in backward traversing + // 3. Nodes with roles: ATSPI_ROLE_PAGE_TAB, ATSPI_ROLE_POPUP_MENU and ATSPI_ROLE_DIALOG, only when looking for first or last element. + // Objects with those roles shouldnt be reachable, when navigating next / prev. + bool all_children_visited_or_moving_forward = ( children.size() == 0 || forward || all_children_visited ); + if( !force_next && node != start && all_children_visited_or_moving_forward && AcceptObject( node ) ) + { + if( start == NULL || ObjectRoleIsAcceptableWhenNavigatingNextPrev( node ) ) + return node; + } + + Accessible* next_related_in_direction = !force_next ? GetObjectInRelationFlow( node, forward ? RelationType::FlowsTo : RelationType::FlowsFrom ) : nullptr; + /* force_next means that the search_mode is NEIGHBOR_SEARCH_MODE_CONTINUE_AFTER_FAILED_RECURSING + in this case the node is elm_layout which is parent of proxy object. + There is an access object working for the proxy object, and the access + object could have relation information. This relation information should + be checked first before using the elm_layout as a node. */ + if( force_next && forward ) + { + auto deputy = DeputyOfProxyInParentGet( node ); + next_related_in_direction = + GetObjectInRelationFlow( deputy, forward ? RelationType::FlowsTo : RelationType::FlowsFrom ); + } + + if( next_related_in_direction && start->GetStates()[State::Defunct] ) + next_related_in_direction = NULL; + unsigned char want_cycle_detection = 0; + if( next_related_in_direction ) + { + /* Check next_related_in_direction is deputy object */ + Accessible* parent; + if( !forward ) + { + /* If the prev object is deputy, then go to inside of its proxy first */ + if( DeputyIs( next_related_in_direction ) ) + { + parent = next_related_in_direction->GetParent(); + next_related_in_direction = ProxyInParentGet( parent ); + } + } + else + { + /* If current object is deputy, and it has relation next object, + then do not use the relation next object, and use proxy first */ + if( DeputyIs( node ) ) + { + parent = node->GetParent(); + next_related_in_direction = ProxyInParentGet( parent ); + } + } + node = next_related_in_direction; + want_cycle_detection = 1; + } + else + { + auto child = !force_next && !all_children_visited ? DirectionalDepthFirstSearchTryNonDefunctChild( node, children, forward ) : nullptr; + if( child ) + { + want_cycle_detection = 1; + } + else + { + if( !force_next && node == root ) + return NULL; + all_children_visited = true; + child = DirectionalDepthFirstSearchTryNonDefunctSibling( all_children_visited, node, start, root, forward ); + } + node = child; + } + force_next = 0; + if( want_cycle_detection && cycleDetection.check( node ) ) + { + return NULL; + } + } + return NULL; +} + +DBus::ValueOrError< Accessible*, uint8_t > BridgeAccessible::GetNeighbor( std::string rootPath, int32_t direction, int32_t search_mode ) +{ + auto start = FindSelf(); + rootPath = StripPrefix( rootPath ); + auto root = !rootPath.empty() ? Find( rootPath ) : nullptr; + auto accessible = CalculateNeighbor( root, start, direction == 1, static_cast< GetNeighborSearchMode >( search_mode ) ); + unsigned char recurse = 0; + if( accessible ) + { + recurse = accessible->IsProxy(); + } + return {accessible, recurse}; +} + +Accessible* BridgeAccessible::GetParent() +{ + // NOTE: currently bridge supports single application root element. + // only element set as application root might return nullptr from GetParent + // if you want more, then you need to change setApplicationRoot to + // add/remove ApplicationRoot and make roots a vector. + auto z = FindSelf(); + auto p = z->GetParent(); + assert( p ); + return p; +} +DBus::ValueOrError< std::vector< Accessible* > > BridgeAccessible::GetChildren() +{ + return FindSelf()->GetChildren(); +} +std::string BridgeAccessible::GetDescription() +{ + return FindSelf()->GetDescription(); +} +DBus::ValueOrError< uint32_t > BridgeAccessible::GetRole() +{ + return static_cast< unsigned int >( FindSelf()->GetRole() ); +} +DBus::ValueOrError< std::string > BridgeAccessible::GetRoleName() +{ + return FindSelf()->GetRoleName(); +} +DBus::ValueOrError< std::string > BridgeAccessible::GetLocalizedRoleName() +{ + return FindSelf()->GetLocalizedRoleName(); +} +DBus::ValueOrError< int32_t > BridgeAccessible::GetIndexInParent() +{ + return FindSelf()->GetIndexInParent(); +} +DBus::ValueOrError< std::array< uint32_t, 2 > > BridgeAccessible::GetStates() +{ + return FindSelf()->GetStates().GetRawData(); +} +DBus::ValueOrError< std::unordered_map< std::string, std::string > > BridgeAccessible::GetAttributes() +{ + return FindSelf()->GetAttributes(); +} +DBus::ValueOrError< std::vector< std::string > > BridgeAccessible::GetInterfaces() +{ + return FindSelf()->GetInterfaces(); +} +int BridgeAccessible::GetChildCount() +{ + return FindSelf()->GetChildCount(); +} +DBus::ValueOrError< Accessible* > BridgeAccessible::GetChildAtIndex( int index ) +{ + if( index < 0 ) + throw AccessibleError{"negative index (" + std::to_string( index ) + ")"}; + return FindSelf()->GetChildAtIndex( static_cast< size_t >( index ) ); +} + +std::string BridgeAccessible::GetName() +{ + return FindSelf()->GetName(); +} + +DBus::ValueOrError< Accessible*, uint32_t > BridgeAccessible::GetDefaultLabelInfo() +{ + auto p = FindSelf(); + return {p, static_cast< uint32_t >( p->GetRole() )}; +} diff --git a/dali/dali-bridge/src/BridgeAccessible.hpp b/dali/dali-bridge/src/BridgeAccessible.hpp new file mode 100644 index 0000000..fcc4217 --- /dev/null +++ b/dali/dali-bridge/src/BridgeAccessible.hpp @@ -0,0 +1,80 @@ +#ifndef BRIDGE_ACCESSIBLE_HPP +#define BRIDGE_ACCESSIBLE_HPP + +#include "BridgeBase.hpp" +#include +#include +#include +#include + +class BridgeAccessible : public virtual BridgeBase +{ +protected: + BridgeAccessible(); + + void RegisterInterfaces(); + +public: + enum class GetNeighborSearchMode + { + normal = 0, + recurseFromRoot = 1, + continueAfterFailedRecursion = 2, + recurseToOutside = 3, + }; + int GetChildCount(); + DBus::ValueOrError< Dali::Accessibility::Accessible* > GetChildAtIndex( int index ); + Dali::Accessibility::Accessible* GetParent(); + DBus::ValueOrError< std::vector< Dali::Accessibility::Accessible* > > GetChildren(); + std::string GetName(); + std::string GetDescription(); + DBus::ValueOrError< uint32_t > GetRole(); + DBus::ValueOrError< std::string > GetRoleName(); + DBus::ValueOrError< std::string > GetLocalizedRoleName(); + DBus::ValueOrError< int32_t > GetIndexInParent(); + DBus::ValueOrError< std::array< uint32_t, 2 > > GetStates(); + DBus::ValueOrError< std::unordered_map< std::string, std::string > > GetAttributes(); + DBus::ValueOrError< std::vector< std::string > > GetInterfaces(); + DBus::ValueOrError< Dali::Accessibility::Accessible*, uint8_t, Dali::Accessibility::Accessible* > GetNavigableAtPoint( int32_t x, int32_t y, uint32_t coordType ); + DBus::ValueOrError< Dali::Accessibility::Accessible*, uint8_t > GetNeighbor( std::string root_path, int32_t direction, int32_t search_mode ); + DBus::ValueOrError< Dali::Accessibility::Accessible*, uint32_t > GetDefaultLabelInfo(); + using ReadingMaterialType = DBus::ValueOrError< + std::unordered_map< std::string, std::string >, // attributes + std::string, // name + std::string, // labeledByName + std::string, // textIfceName + uint32_t, + Dali::Accessibility::States, + std::string, // localized name + int32_t, // child count + double, // current value + double, // minimum increment + double, // maximum value + double, // minimum value + std::string, // description + int32_t, // index in parent + bool, // isSelectedInParent + bool, // hasCheckBoxChild + int32_t, // listChildrenCount + int32_t, // firstSelectedChildIndex + Dali::Accessibility::Accessible*, // parent + Dali::Accessibility::States, // parentStateSet + int32_t, // parentChildCount + uint32_t, // parentRole + int32_t, // selectedChildCount, + Dali::Accessibility::Accessible* // describedByObject + >; + + ReadingMaterialType GetReadingMaterial(); + + DBus::ValueOrError< bool > DoGesture( int32_t, int32_t, int32_t, int32_t, int32_t, int32_t, uint32_t ); + +private: + Dali::Accessibility::Accessible* CalculateNeighbor( Dali::Accessibility::Accessible* root, Dali::Accessibility::Accessible* start, unsigned char forward, GetNeighborSearchMode search_mode ); + std::vector< Dali::Accessibility::Accessible* > ValidChildrenGet( const std::vector< Dali::Accessibility::Accessible* >& children, Dali::Accessibility::Accessible* start, Dali::Accessibility::Accessible* root ); + Dali::Accessibility::Accessible* GetCurrentlyHighlighted(); + Dali::Accessibility::Accessible* DirectionalDepthFirstSearchTryNonDefunctSibling( bool& all_children_visited, Dali::Accessibility::Accessible* node, Dali::Accessibility::Accessible* start, Dali::Accessibility::Accessible* root, unsigned char forward ); + Dali::Accessibility::Accessible* GetNextNonDefunctSibling( Dali::Accessibility::Accessible* obj, Dali::Accessibility::Accessible* start, Dali::Accessibility::Accessible* root, unsigned char forward ); +}; + +#endif diff --git a/dali/dali-bridge/src/BridgeAction.cpp b/dali/dali-bridge/src/BridgeAction.cpp new file mode 100755 index 0000000..664a665 --- /dev/null +++ b/dali/dali-bridge/src/BridgeAction.cpp @@ -0,0 +1,80 @@ +#include "BridgeAction.hpp" +#include + +using namespace Dali::Accessibility; + +void BridgeAction::RegisterInterfaces() +{ + DBus::DBusInterfaceDescription desc{ATSPI_DBUS_INTERFACE_ACTION}; + + AddGetPropertyToInterface( desc, "NActions", &BridgeAction::GetActionCount ); + + AddFunctionToInterface( desc, "GetName", &BridgeAction::GetActionName ); + AddFunctionToInterface( desc, "GetLocalizedName", &BridgeAction::GetLocalizedActionName ); + AddFunctionToInterface( desc, "GetDescription", &BridgeAction::GetActionDescription ); + AddFunctionToInterface( desc, "GetKeyBinding", &BridgeAction::GetActionKeyBinding ); + AddFunctionToInterface( desc, "DoAction", &BridgeAction::DoAction ); + AddFunctionToInterface( desc, "DoActionName", &BridgeAction::DoActionName ); + dbusServer.addInterface( "/", desc, true ); +} + +Action* BridgeAction::FindSelf() const +{ + auto s = BridgeBase::FindSelf(); + assert( s ); + auto s2 = dynamic_cast< Action* >( s ); + if( !s2 ) + throw AccessibleError{"object " + s->GetAddress().ToString() + " doesn't have Action interface"}; + return s2; +} + +DBus::ValueOrError< std::string > BridgeAction::GetActionName( int32_t index ) +{ + auto self = FindSelf(); + return self->GetActionName( index ); +} + +DBus::ValueOrError< std::string > BridgeAction::GetLocalizedActionName( int32_t index ) +{ + auto self = FindSelf(); + return self->GetLocalizedActionName( index ); +} + +DBus::ValueOrError< std::string > BridgeAction::GetActionDescription( int32_t index ) +{ + auto self = FindSelf(); + return self->GetActionDescription( index ); +} + +DBus::ValueOrError< std::string > BridgeAction::GetActionKeyBinding( int32_t index ) +{ + auto self = FindSelf(); + return self->GetActionKeyBinding( index ); +} + +DBus::ValueOrError< int32_t > BridgeAction::GetActionCount() +{ + auto self = FindSelf(); + return self->GetActionCount(); + ; +} + +DBus::ValueOrError< bool > BridgeAction::DoAction( int32_t index ) +{ + auto self = FindSelf(); + return self->DoAction( index ); +} + +DBus::ValueOrError< bool > BridgeAction::DoActionName( std::string name ) +{ + auto self = FindSelf(); + auto cnt = self->GetActionCount(); + for( auto i = 0u; i < cnt; ++i ) + { + if( self->GetActionName( i ) == name ) + { + return self->DoAction( i ); + } + } + throw AccessibleError{"object " + self->GetAddress().ToString() + " doesn't have action '" + name + "'"}; +} diff --git a/dali/dali-bridge/src/BridgeAction.hpp b/dali/dali-bridge/src/BridgeAction.hpp new file mode 100644 index 0000000..5ff10e8 --- /dev/null +++ b/dali/dali-bridge/src/BridgeAction.hpp @@ -0,0 +1,28 @@ +#ifndef BRIDGE_ACTION_HPP +#define BRIDGE_ACTION_HPP + +#include "BridgeBase.hpp" + +#include +#include + +class BridgeAction : public virtual BridgeBase +{ +protected: + BridgeAction() = default; + + void RegisterInterfaces(); + + Dali::Accessibility::Action* FindSelf() const; + +public: + DBus::ValueOrError< std::string > GetActionName( int32_t index ); + DBus::ValueOrError< std::string > GetLocalizedActionName( int32_t index ); + DBus::ValueOrError< std::string > GetActionDescription( int32_t index ); + DBus::ValueOrError< std::string > GetActionKeyBinding( int32_t index ); + DBus::ValueOrError< int32_t > GetActionCount(); + DBus::ValueOrError< bool > DoAction( int32_t index ); + DBus::ValueOrError< bool > DoActionName( std::string name ); +}; + +#endif diff --git a/dali/dali-bridge/src/BridgeBase.cpp b/dali/dali-bridge/src/BridgeBase.cpp new file mode 100755 index 0000000..d607c01 --- /dev/null +++ b/dali/dali-bridge/src/BridgeBase.cpp @@ -0,0 +1,165 @@ +#include "BridgeBase.hpp" +#include +#include + +using namespace Dali::Accessibility; + +BridgeBase::BridgeBase() +{ +} + +BridgeBase::ForceUpResult BridgeBase::ForceUp() +{ + if( Bridge::ForceUp() == ForceUpResult::alreadyUp ) + return ForceUpResult::alreadyUp; + auto proxy = DBus::DBusClient{dbusLocators::atspi::BUS, dbusLocators::atspi::OBJ_PATH, + dbusLocators::atspi::BUS_INTERFACE, DBus::ConnectionType::SESSION}; + auto addr = proxy.method< std::string() >( dbusLocators::atspi::GET_ADDRESS ).call(); + + if( !addr ) + throw AccessibleError{std::string( "failed at call '" ) + dbusLocators::atspi::GET_ADDRESS + + "': " + addr.getError().message}; + + con = std::make_shared< DBus::EldbusConnection >( eldbus_address_connection_get( std::get< 0 >( addr ).c_str() ) ); + data->busName = DBus::getConnectionName( con ); + data->root = &application; + dbusServer = {con}; + + { + DBus::DBusInterfaceDescription desc{"org.a11y.atspi.Cache"}; + AddFunctionToInterface( desc, "GetItems", &BridgeBase::GetItems ); + dbusServer.addInterface( "/org/a11y/atspi/cache", desc ); + } + { + DBus::DBusInterfaceDescription desc{"org.a11y.atspi.Application"}; + AddGetSetPropertyToInterface( desc, "Id", &BridgeBase::IdGet, &BridgeBase::IdSet ); + dbusServer.addInterface( ATSPI_PATH, desc ); + } + return ForceUpResult::justStarted; +} + +void BridgeBase::ForceDown() +{ + Bridge::ForceDown(); + dbusServer = {}; + con = {}; +} + +const std::string& BridgeBase::GetBusName() const +{ + static std::string empty; + return data ? data->busName : empty; +} + +Accessible* BridgeBase::FindByPath( const std::string& name ) const +{ + try + { + return Find( name ); + } + catch( AccessibleError ) + { + return nullptr; + } +} + +void BridgeBase::SetApplicationChild( Accessible* root ) +{ + // for now you can set root only once. + // to set it multiple times you'd have to remove old one first, which usually involves + // a lot of messy events and cornercases (imagine old root becoming child of new-not-yet-announced one) + application.children.push_back( root ); + SetIsOnRootLevel( root ); +} + +// Accessible *BridgeBase::getApplicationRoot() const +// { +// return rootElement; +// } + +std::string BridgeBase::StripPrefix( const std::string& path ) +{ + auto size = strlen( ATSPI_PATH ); + return path.substr( size + 1 ); +} + +Accessible* BridgeBase::Find( const std::string& path ) const +{ + if( path == "root" ) + return &application; + char* p; + auto val = std::strtoll( path.c_str(), &p, 10 ); + if( p == path.c_str() ) + throw AccessibleError{"invalid path '" + path + "'"}; + auto it = data->objects.find( val ); + if( it == data->objects.end() ) + throw AccessibleError{"unknown object '" + path + "'"}; + return it->second; +} + +Accessible* BridgeBase::Find( const Address& ptr ) const +{ + assert( ptr.GetBus() == data->busName ); + return Find( ptr.GetPath() ); +} + +Accessible* BridgeBase::FindSelf() const +{ + auto pth = DBus::DBusServer::getCurrentObjectPath(); + auto size = strlen( ATSPI_PATH ); + if( pth.size() <= size ) + throw AccessibleError{"invalid path '" + pth + "'"}; + if( pth.substr( 0, size ) != ATSPI_PATH ) + throw AccessibleError{"invalid path '" + pth + "'"}; + if( pth[size] != '/' ) + throw AccessibleError{"invalid path '" + pth + "'"}; + return Find( StripPrefix( pth ) ); +} + +void BridgeBase::IdSet( int id ) +{ + this->id = id; +} +int BridgeBase::IdGet() +{ + return this->id; +} + +auto BridgeBase::GetItems() -> DBus::ValueOrError< std::vector< CacheElementType > > +{ + auto root = &application; + + std::vector< CacheElementType > res; + + res.emplace_back( std::move( CreateCacheElement( root ) ) ); + for( auto const& it : data->objects ) + res.emplace_back( std::move( CreateCacheElement( it.second ) ) ); + + return res; +} + +auto BridgeBase::CreateCacheElement( Accessible* item ) -> CacheElementType +{ + if( !item ) + return {}; + + auto root = &application; + auto parent = item->GetParent(); + + std::vector< Address > children; + for( auto i = 0u; i < item->GetChildCount(); ++i ) + { + children.emplace_back( item->GetChildAtIndex( i )->GetAddress() ); + } + + return std::make_tuple( + item->GetAddress(), + root->GetAddress(), + parent->GetAddress(), + children, + item->GetInterfaces(), + item->GetName(), + item->GetRole(), + item->GetDescription(), + item->GetStates().GetRawData() ); +} diff --git a/dali/dali-bridge/src/BridgeBase.hpp b/dali/dali-bridge/src/BridgeBase.hpp new file mode 100644 index 0000000..10a16a1 --- /dev/null +++ b/dali/dali-bridge/src/BridgeBase.hpp @@ -0,0 +1,215 @@ +#ifndef BRIDGE_HPP +#define BRIDGE_HPP + +#include "Common.hpp" +#include + +class AppAccessible : public virtual Dali::Accessibility::Accessible, public virtual Dali::Accessibility::Collection +{ +public: + Dali::Accessibility::EmptyAccessibleWithAddress parent; + std::vector< Dali::Accessibility::Accessible* > children; + std::string name; + + std::string GetName() override + { + return name; + } + std::string GetDescription() override + { + return ""; + } + Dali::Accessibility::Accessible* GetParent() override + { + return &parent; + } + size_t GetChildCount() override + { + return children.size(); + } + Dali::Accessibility::Accessible* GetChildAtIndex( size_t index ) override + { + auto s = children.size(); + if( index >= s ) + throw Dali::Accessibility::AccessibleError{"invalid index " + std::to_string( index ) + " for object with " + std::to_string( s ) + " children"}; + return children[index]; + } + size_t GetIndexInParent() override + { + throw Dali::Accessibility::AccessibleError{"can't call GetIndexInParent on application object"}; + } + Dali::Accessibility::Role GetRole() override + { + return Dali::Accessibility::Role::Application; + } + Dali::Accessibility::States GetStates() override + { + return {}; + } + Dali::Accessibility::Attributes GetAttributes() override + { + return {}; + } + Dali::Accessibility::Accessible* getActiveWindow() + { + return children.empty() ? nullptr : children[0]; + } +}; + +class BridgeBase : public Dali::Accessibility::Bridge +{ +public: + const std::string& GetBusName() const override; + void SetApplicationChild( Dali::Accessibility::Accessible* root ) override; + Dali::Accessibility::Accessible* GetApplication() const override + { + return &application; + } + + template < typename SELF, typename... RET, typename... ARGS > + void AddFunctionToInterface( + DBus::DBusInterfaceDescription& desc, const std::string& funcName, + DBus::ValueOrError< RET... > ( SELF::*funcPtr )( ARGS... ) ) + { + desc.addMethod< DBus::ValueOrError< RET... >( ARGS... ) >( funcName, + [=]( ARGS... args ) -> DBus::ValueOrError< RET... > { + try + { + return ( dynamic_cast< SELF* >( this )->*funcPtr )( std::move( args )... ); + } + catch( Dali::Accessibility::AccessibleError& e ) + { + return DBus::Error{e.what()}; + } + } ); + } + template < typename T, typename SELF > + void AddGetPropertyToInterface( DBus::DBusInterfaceDescription& desc, + const std::string& funcName, T ( SELF::*funcPtr )() ) + { + desc.addProperty< T >( funcName, + [=]() -> DBus::ValueOrError< T > { + try + { + return ( dynamic_cast< SELF* >( this )->*funcPtr )(); + } + catch( Dali::Accessibility::AccessibleError& e ) + { + return DBus::Error{e.what()}; + } + }, + {} ); + } + template < typename T, typename SELF > + void AddSetPropertyToInterface( DBus::DBusInterfaceDescription& desc, + const std::string& funcName, void ( SELF::*funcPtr )( T ) ) + { + desc.addProperty< T >( funcName, {}, + [=]( T t ) -> DBus::ValueOrError< void > { + try + { + ( dynamic_cast< SELF* >( this )->*funcPtr )( std::move( t ) ); + return {}; + } + catch( Dali::Accessibility::AccessibleError& e ) + { + return DBus::Error{e.what()}; + } + } ); + } + template < typename T, typename T1, typename SELF > + void AddGetSetPropertyToInterface( DBus::DBusInterfaceDescription& desc, + const std::string& funcName, T1 ( SELF::*funcPtrGet )(), DBus::ValueOrError< void > ( SELF::*funcPtrSet )( T ) ) + { + desc.addProperty< T >( funcName, + [=]() -> DBus::ValueOrError< T > { + try + { + return ( dynamic_cast< SELF* >( this )->*funcPtrGet )(); + } + catch( Dali::Accessibility::AccessibleError& e ) + { + return DBus::Error{e.what()}; + } + }, + [=]( T t ) -> DBus::ValueOrError< void > { + try + { + ( dynamic_cast< SELF* >( this )->*funcPtrSet )( std::move( t ) ); + return {}; + } + catch( Dali::Accessibility::AccessibleError& e ) + { + return DBus::Error{e.what()}; + } + } ); + } + template < typename T, typename T1, typename SELF > + void AddGetSetPropertyToInterface( DBus::DBusInterfaceDescription& desc, + const std::string& funcName, T1 ( SELF::*funcPtrGet )(), void ( SELF::*funcPtrSet )( T ) ) + { + desc.addProperty< T >( funcName, + [=]() -> DBus::ValueOrError< T > { + try + { + return ( dynamic_cast< SELF* >( this )->*funcPtrGet )(); + } + catch( Dali::Accessibility::AccessibleError& e ) + { + return DBus::Error{e.what()}; + } + }, + [=]( T t ) -> DBus::ValueOrError< void > { + try + { + ( dynamic_cast< SELF* >( this )->*funcPtrSet )( std::move( t ) ); + return {}; + } + catch( Dali::Accessibility::AccessibleError& e ) + { + return DBus::Error{e.what()}; + } + } ); + } + static std::string StripPrefix( const std::string& path ); + + Dali::Accessibility::Accessible* Find( const std::string& path ) const; + Dali::Accessibility::Accessible* Find( const Dali::Accessibility::Address& ptr ) const; + Dali::Accessibility::Accessible* FindSelf() const; + Dali::Accessibility::Accessible* FindByPath( const std::string& name ) const override; + void SetApplicationName( std::string name ) override + { + application.name = std::move( name ); + } + +protected: + mutable AppAccessible application; + +private: + void IdSet( int id ); + int IdGet(); + + using CacheElementType = std::tuple< + Dali::Accessibility::Address, Dali::Accessibility::Address, Dali::Accessibility::Address, + std::vector< Dali::Accessibility::Address >, + std::vector< std::string >, + std::string, + Dali::Accessibility::Role, + std::string, + std::array< uint32_t, 2 > >; + DBus::ValueOrError< std::vector< CacheElementType > > GetItems(); + CacheElementType CreateCacheElement( Dali::Accessibility::Accessible* item ); + +protected: + BridgeBase(); + virtual ~BridgeBase() = default; + + ForceUpResult ForceUp() override; + void ForceDown() override; + + DBus::DBusServer dbusServer; + std::shared_ptr< DBus::EldbusConnection > con; + int id = 0; +}; + +#endif diff --git a/dali/dali-bridge/src/BridgeCollection.cpp b/dali/dali-bridge/src/BridgeCollection.cpp new file mode 100644 index 0000000..22fcddc --- /dev/null +++ b/dali/dali-bridge/src/BridgeCollection.cpp @@ -0,0 +1,366 @@ +#include "BridgeCollection.hpp" +#include +#include +#include +#include + +using namespace Dali::Accessibility; + +void BridgeCollection::RegisterInterfaces() +{ + DBus::DBusInterfaceDescription desc{ATSPI_DBUS_INTERFACE_COLLECTION}; + AddFunctionToInterface( desc, "GetMatches", &BridgeCollection::GetMatches ); + dbusServer.addInterface( "/", desc, true ); +} + +Collection* BridgeCollection::FindSelf() const +{ + auto s = BridgeBase::FindSelf(); + assert( s ); + auto s2 = dynamic_cast< Collection* >( s ); + if( !s2 ) + throw AccessibleError{"object " + s->GetAddress().ToString() + " doesn't have Collection interface"}; + return s2; +} + +enum +{ + ATSPI_Collection_MATCH_INVALID, + ATSPI_Collection_MATCH_ALL, + ATSPI_Collection_MATCH_ANY, + ATSPI_Collection_MATCH_NONE, + ATSPI_Collection_MATCH_EMPTY, + ATSPI_Collection_MATCH_LAST_DEFINED, +}; + +struct BridgeCollection::Comparer +{ + using Mode = MatchType; + + enum class CompareFuncExit + { + firstFound, + firstNotFound + }; + + static Mode ConvertToMatchType( int32_t mode ) + { + switch( mode ) + { + case ATSPI_Collection_MATCH_INVALID: + { + return Mode::Invalid; + } + case ATSPI_Collection_MATCH_ALL: + { + return Mode::All; + } + case ATSPI_Collection_MATCH_ANY: + { + return Mode::Any; + } + case ATSPI_Collection_MATCH_NONE: + { + return Mode::None; + } + case ATSPI_Collection_MATCH_EMPTY: + { + return Mode::Empty; + } + } + return Mode::Invalid; + } + + struct ComparerInterfaces + { + std::unordered_set< std::string > object; + std::vector< std::string > requested; + Mode mode = Mode::Invalid; + + ComparerInterfaces( MatchRule* rule ) : mode( ConvertToMatchType( std::get< Index::InterfacesMatchType >( *rule ) ) ) + { + requested = {std::get< Index::Interfaces >( *rule ).begin(), std::get< Index::Interfaces >( *rule ).end()}; + } + void Update( Accessible* obj ) + { + object.clear(); + for( auto& q : obj->GetInterfaces() ) + object.insert( std::move( q ) ); + } + bool RequestEmpty() const { return requested.empty(); } + bool ObjectEmpty() const { return object.empty(); } + bool Compare( CompareFuncExit exit ) + { + bool foundAny = false; + for( auto& iname : requested ) + { + bool found = ( object.find( iname ) != object.end() ); + if( found ) + foundAny = true; + if( found == ( exit == CompareFuncExit::firstFound ) ) + return found; + } + return foundAny; + } + }; + struct ComparerAttributes + { + std::unordered_map< std::string, std::string > requested, object; + Mode mode = Mode::Invalid; + + ComparerAttributes( MatchRule* rule ) : mode( ConvertToMatchType( std::get< Index::AttributesMatchType >( *rule ) ) ) + { + requested = std::get< Index::Attributes >( *rule ); + } + void Update( Accessible* obj ) + { + object = obj->GetAttributes(); + } + bool RequestEmpty() const { return requested.empty(); } + bool ObjectEmpty() const { return object.empty(); } + bool Compare( CompareFuncExit exit ) + { + bool foundAny = false; + for( auto& iname : requested ) + { + auto it = object.find( iname.first ); + bool found = it != object.end() && iname.second == it->second; + if( found ) + foundAny = true; + if( found == ( exit == CompareFuncExit::firstFound ) ) + { + return found; + } + } + return foundAny; + } + }; + struct ComparerRoles + { + using Roles = BitStates< 4, Role >; + Roles requested, object; + Mode mode = Mode::Invalid; + + ComparerRoles( MatchRule* rule ) : mode( ConvertToMatchType( std::get< Index::RolesMatchType >( *rule ) ) ) + { + requested = Roles{std::get< Index::Roles >( *rule )}; + } + void Update( Accessible* obj ) + { + object = {}; + object[obj->GetRole()] = true; + assert( object ); + } + bool RequestEmpty() const { return !requested; } + bool ObjectEmpty() const { return !object; } + bool Compare( CompareFuncExit exit ) + { + switch( mode ) + { + case Mode::Invalid: + { + return true; + } + case Mode::Empty: + case Mode::All: + { + return requested == ( object & requested ); + } + case Mode::Any: + { + return bool( object & requested ); + } + case Mode::None: + { + return bool( object & requested ); + } + } + return false; + } + }; + struct ComparerStates + { + States requested, object; + Mode mode = Mode::Invalid; + + ComparerStates( MatchRule* rule ) : mode( ConvertToMatchType( std::get< Index::StatesMatchType >( *rule ) ) ) + { + requested = States{std::get< Index::States >( *rule )}; + } + void Update( Accessible* obj ) + { + object = obj->GetStates(); + } + bool RequestEmpty() const { return !requested; } + bool ObjectEmpty() const { return !object; } + bool Compare( CompareFuncExit exit ) + { + switch( mode ) + { + case Mode::Invalid: + { + return true; + } + case Mode::Empty: + case Mode::All: + { + return requested == ( object & requested ); + } + case Mode::Any: + { + return bool( object & requested ); + } + case Mode::None: + { + return bool( object & requested ); + } + } + return false; + } + }; + + template < typename T > + bool compareFunc( T& cmp, Accessible* obj ) + { + if( cmp.mode == Mode::Invalid ) + return true; + cmp.Update( obj ); + switch( cmp.mode ) + { + case Mode::Any: + { + if( cmp.RequestEmpty() || cmp.ObjectEmpty() ) + return false; + break; + } + case Mode::All: + { + if( cmp.RequestEmpty() ) + return true; + if( cmp.ObjectEmpty() ) + return false; + break; + } + case Mode::None: + { + if( cmp.RequestEmpty() || cmp.ObjectEmpty() ) + return true; + break; + } + case Mode::Empty: + { + if( cmp.RequestEmpty() && cmp.ObjectEmpty() ) + return true; + if( cmp.RequestEmpty() || cmp.ObjectEmpty() ) + return false; + break; + } + case Mode::Invalid: + { + return true; + } + } + + switch( cmp.mode ) + { + case Mode::Empty: + case Mode::All: + { + if( !cmp.Compare( CompareFuncExit::firstNotFound ) ) + return false; + break; + } + case Mode::Any: + { + if( cmp.Compare( CompareFuncExit::firstFound ) ) + return true; + break; + } + case Mode::None: + { + if( cmp.Compare( CompareFuncExit::firstFound ) ) + return false; + break; + } + case Mode::Invalid: + { + return true; + } + } + switch( cmp.mode ) + { + case Mode::Empty: + case Mode::All: + case Mode::None: + { + return true; + } + case Mode::Any: + { + return false; + } + case Mode::Invalid: + { + return true; + } + } + return false; + } + + ComparerInterfaces ci; + ComparerAttributes ca; + ComparerRoles cr; + ComparerStates cs; + + Comparer( MatchRule* mr ) : ci( mr ), ca( mr ), cr( mr ), cs( mr ) {} + + bool operator()( Accessible* obj ) + { + return compareFunc( ci, obj ) && + compareFunc( ca, obj ) && + compareFunc( cr, obj ) && + compareFunc( cs, obj ); + } +}; + +void BridgeCollection::VisitNodes( Accessible* obj, std::vector< Accessible* >& result, Comparer& cmp, size_t maxCount ) +{ + if( result.size() >= maxCount ) + return; + + if( cmp( obj ) ) + result.emplace_back( obj ); + + for( auto i = 0u; i < obj->GetChildCount(); ++i ) + VisitNodes( obj->GetChildAtIndex( i ), result, cmp, maxCount ); +} + +DBus::ValueOrError< std::vector< Accessible* > > BridgeCollection::GetMatches( MatchRule rule, uint32_t sortBy, int32_t count, bool traverse ) +{ + std::vector< Accessible* > res; + auto self = BridgeBase::FindSelf(); + auto matcher = Comparer{&rule}; + VisitNodes( self, res, matcher, count ); + + switch( static_cast< SortOrder >( sortBy ) ) + { + case SortOrder::Canonical: + { + break; + } + + case SortOrder::ReverseCanonical: + { + std::reverse( res.begin(), res.end() ); + break; + } + + default: + { + throw AccessibleError{"unsupported sorting order"}; + } + //TODO: other cases + } + + return res; +} diff --git a/dali/dali-bridge/src/BridgeCollection.hpp b/dali/dali-bridge/src/BridgeCollection.hpp new file mode 100644 index 0000000..ba92f30 --- /dev/null +++ b/dali/dali-bridge/src/BridgeCollection.hpp @@ -0,0 +1,49 @@ +#ifndef BRIDGE_COLLECTION_HPP +#define BRIDGE_COLLECTION_HPP + +#include "BridgeBase.hpp" + +#include +#include +#include +#include + +class BridgeCollection : public virtual BridgeBase +{ +private: + struct Comparer; + static void VisitNodes( Dali::Accessibility::Accessible* obj, std::vector< Dali::Accessibility::Accessible* >& result, Comparer& cmp, size_t maxCount ); + +protected: + BridgeCollection() = default; + + void RegisterInterfaces(); + + Dali::Accessibility::Collection* FindSelf() const; + +public: + using MatchRule = std::tuple< + std::array< int32_t, 2 >, int32_t, + std::unordered_map< std::string, std::string >, int32_t, + std::array< int32_t, 4 >, int32_t, + std::vector< std::string >, int32_t, + bool >; + struct Index + { + enum + { + States, + StatesMatchType, + Attributes, + AttributesMatchType, + Roles, + RolesMatchType, + Interfaces, + InterfacesMatchType, + }; + }; + + DBus::ValueOrError< std::vector< Dali::Accessibility::Accessible* > > GetMatches( MatchRule rule, uint32_t sortBy, int32_t count, bool traverse ); +}; + +#endif diff --git a/dali/dali-bridge/src/BridgeComponent.cpp b/dali/dali-bridge/src/BridgeComponent.cpp new file mode 100644 index 0000000..6bbfaff --- /dev/null +++ b/dali/dali-bridge/src/BridgeComponent.cpp @@ -0,0 +1,75 @@ +#include "BridgeComponent.hpp" +#include + +#define DBUS_INTERFACE_PROPERTIES "org.freedesktop.DBus.Properties" + +using namespace Dali::Accessibility; + +BridgeComponent::BridgeComponent() +{ +} + +void BridgeComponent::RegisterInterfaces() +{ + DBus::DBusInterfaceDescription desc{ATSPI_DBUS_INTERFACE_COMPONENT}; + AddFunctionToInterface( desc, "Contains", &BridgeComponent::Contains ); + AddFunctionToInterface( desc, "GetAccessibleAtPoint", &BridgeComponent::GetAccessibleAtPoint ); + AddFunctionToInterface( desc, "GetExtents", &BridgeComponent::GetExtents ); + AddFunctionToInterface( desc, "GetPosition", &BridgeComponent::GetPosition ); + AddFunctionToInterface( desc, "GetSize", &BridgeComponent::GetSize ); + AddFunctionToInterface( desc, "GetLayer", &BridgeComponent::GetLayer ); + AddFunctionToInterface( desc, "GetAlpha", &BridgeComponent::GetAlpha ); + AddFunctionToInterface( desc, "GrabHighlight", &BridgeComponent::GrabHighlight ); + AddFunctionToInterface( desc, "ClearHighlight", &BridgeComponent::ClearHighlight ); + dbusServer.addInterface( "/", desc, true ); +} + +Component* BridgeComponent::FindSelf() const +{ + auto s = BridgeBase::FindSelf(); + assert( s ); + auto s2 = dynamic_cast< Component* >( s ); + if( !s2 ) + throw AccessibleError{"object " + s->GetAddress().ToString() + " doesn't have Component interface"}; + return s2; +} + +DBus::ValueOrError< bool > BridgeComponent::Contains( int32_t x, int32_t y, uint32_t coordType ) +{ + return FindSelf()->Contains( {x, y}, static_cast< CoordType >( coordType ) ); +} +DBus::ValueOrError< Accessible* > BridgeComponent::GetAccessibleAtPoint( int32_t x, int32_t y, uint32_t coordType ) +{ + return FindSelf()->GetAccessibleAtPoint( {x, y}, static_cast< CoordType >( coordType ) ); +} +DBus::ValueOrError< std::tuple< int32_t, int32_t, int32_t, int32_t > > BridgeComponent::GetExtents( uint32_t coordType ) +{ + auto p = FindSelf()->GetExtents( static_cast< CoordType >( coordType ) ); + return std::tuple< int32_t, int32_t, int32_t, int32_t >{p.position.x, p.position.y, p.size.width, p.size.height}; +} +DBus::ValueOrError< int32_t, int32_t > BridgeComponent::GetPosition( uint32_t coordType ) +{ + auto p = FindSelf()->GetExtents( static_cast< CoordType >( coordType ) ); + return {p.position.x, p.position.y}; +} +DBus::ValueOrError< int32_t, int32_t > BridgeComponent::GetSize( uint32_t coordType ) +{ + auto p = FindSelf()->GetExtents( static_cast< CoordType >( coordType ) ); + return {p.size.width, p.size.height}; +} +DBus::ValueOrError< ComponentLayer > BridgeComponent::GetLayer() +{ + return FindSelf()->GetLayer(); +} +DBus::ValueOrError< double > BridgeComponent::GetAlpha() +{ + return FindSelf()->GetAlpha(); +} +DBus::ValueOrError< bool > BridgeComponent::GrabHighlight() +{ + return FindSelf()->GrabHighlight(); +} +DBus::ValueOrError< bool > BridgeComponent::ClearHighlight() +{ + return FindSelf()->ClearHighlight(); +} diff --git a/dali/dali-bridge/src/BridgeComponent.hpp b/dali/dali-bridge/src/BridgeComponent.hpp new file mode 100644 index 0000000..8a2a5d4 --- /dev/null +++ b/dali/dali-bridge/src/BridgeComponent.hpp @@ -0,0 +1,32 @@ +#ifndef BRIDGE_COMPONENT_HPP +#define BRIDGE_COMPONENT_HPP + +#include "BridgeBase.hpp" +#include +#include +#include +#include +#include + +class BridgeComponent : public virtual BridgeBase +{ +protected: + BridgeComponent(); + + void RegisterInterfaces(); + + Dali::Accessibility::Component* FindSelf() const; + +public: + DBus::ValueOrError< bool > Contains( int32_t x, int32_t y, uint32_t coordType ); + DBus::ValueOrError< Dali::Accessibility::Accessible* > GetAccessibleAtPoint( int32_t x, int32_t y, uint32_t coordType ); + DBus::ValueOrError< std::tuple< int32_t, int32_t, int32_t, int32_t > > GetExtents( uint32_t coordType ); + DBus::ValueOrError< int32_t, int32_t > GetPosition( uint32_t coordType ); + DBus::ValueOrError< int32_t, int32_t > GetSize( uint32_t coordType ); + DBus::ValueOrError< Dali::Accessibility::ComponentLayer > GetLayer(); + DBus::ValueOrError< double > GetAlpha(); + DBus::ValueOrError< bool > GrabHighlight(); + DBus::ValueOrError< bool > ClearHighlight(); +}; + +#endif diff --git a/dali/dali-bridge/src/BridgeEditableText.cpp b/dali/dali-bridge/src/BridgeEditableText.cpp new file mode 100644 index 0000000..4c3e5e9 --- /dev/null +++ b/dali/dali-bridge/src/BridgeEditableText.cpp @@ -0,0 +1,45 @@ +#include "BridgeEditableText.hpp" + +#include +#include + +using namespace Dali::Accessibility; + +void BridgeEditableText::RegisterInterfaces() +{ + DBus::DBusInterfaceDescription desc{ATSPI_DBUS_INTERFACE_EDITABLE_TEXT}; + AddFunctionToInterface( desc, "CopyText", &BridgeEditableText::CopyText ); + AddFunctionToInterface( desc, "CutText", &BridgeEditableText::CutText ); + AddFunctionToInterface( desc, "PasteText", &BridgeEditableText::PasteText ); + dbusServer.addInterface( "/", desc, true ); +} + +EditableText* BridgeEditableText::FindSelf() const +{ + auto s = BridgeBase::FindSelf(); + assert( s ); + auto s2 = dynamic_cast< EditableText* >( s ); + if( !s2 ) + throw AccessibleError{"object " + s->GetAddress().ToString() + " doesn't have Text interface"}; + return s2; +} + +DBus::ValueOrError< bool > BridgeEditableText::CopyText( int32_t startPos, int32_t endPos ) +{ + return FindSelf()->CopyText( startPos, endPos ); +} + +DBus::ValueOrError< bool > BridgeEditableText::CutText( int32_t startPos, int32_t endPos ) +{ + return FindSelf()->CutText( startPos, endPos ); +} + +DBus::ValueOrError< bool > BridgeEditableText::PasteText( int32_t pos ) +{ + auto imfManager = Dali::Internal::Adaptor::ImfManager::Get(); + imfManager.SetCursorPosition( pos ); + auto clipboard = Dali::Internal::Adaptor::Clipboard::Get(); + clipboard.RequestItem(); + + return true; +} diff --git a/dali/dali-bridge/src/BridgeEditableText.hpp b/dali/dali-bridge/src/BridgeEditableText.hpp new file mode 100644 index 0000000..37008a4 --- /dev/null +++ b/dali/dali-bridge/src/BridgeEditableText.hpp @@ -0,0 +1,21 @@ +#ifndef BRIDGE_EDITABLE_TEXT_HPP +#define BRIDGE_EDITABLE_TEXT_HPP + +#include "BridgeBase.hpp" + +class BridgeEditableText : public virtual BridgeBase +{ +protected: + BridgeEditableText() = default; + + void RegisterInterfaces(); + + Dali::Accessibility::EditableText* FindSelf() const; + +public: + DBus::ValueOrError< bool > CopyText( int32_t startPos, int32_t endPos ); + DBus::ValueOrError< bool > CutText( int32_t startPos, int32_t endPos ); + DBus::ValueOrError< bool > PasteText( int32_t pos ); +}; + +#endif diff --git a/dali/dali-bridge/src/BridgeImpl.cpp b/dali/dali-bridge/src/BridgeImpl.cpp new file mode 100644 index 0000000..6ad9a1b --- /dev/null +++ b/dali/dali-bridge/src/BridgeImpl.cpp @@ -0,0 +1,160 @@ +#include "BridgeImpl.hpp" +#include "BridgeAccessible.hpp" +#include "BridgeAction.hpp" +#include "BridgeCollection.hpp" +#include "BridgeComponent.hpp" +#include "BridgeObject.hpp" +#include "BridgeValue.hpp" +#include "DBus.hpp" +#include + +#include + +using namespace Dali::Accessibility; + +class BridgeImpl : public virtual BridgeBase, public BridgeAccessible, public BridgeObject, public BridgeComponent, public BridgeCollection, public BridgeAction, public BridgeValue +{ + DBus::DBusClient listenOnAtspiEnabledSignalClient; + DBus::DBusClient registryClient; + Accessible* currentWindow = nullptr; + bool screenReaderEnabled = false, isEnabled = false; + +public: + BridgeImpl() + { + DBus::setDebugPrinter( []( const char* buf, size_t len ) { + std::string s{buf, len}; + Dali::Integration::Log::LogMessage( Dali::Integration::Log::DebugInfo, "%s", s.c_str() ); + } ); + } + + Consumed Emit( KeyEventType type, unsigned int keyCode, const std::string& keyName, unsigned int timeStamp, bool isText ) override + { + unsigned int evType = 0; + + switch( type ) + { + case KeyEventType::KeyPressed: + evType = 0; + { + break; + } + case KeyEventType::KeyReleased: + evType = 1; + { + break; + } + default: + { + return Consumed::No; + } + } + auto m = registryClient.method< bool( std::tuple< uint32_t, int32_t, int32_t, int32_t, int32_t, std::string, bool > ) >( "NotifyListenersSync" ); + auto result = m.call( std::tuple< uint32_t, int32_t, int32_t, int32_t, int32_t, std::string, bool >{evType, 0, static_cast< int32_t >( keyCode ), 0, static_cast< int32_t >( timeStamp ), keyName, isText ? 1 : 0} ); + if( !result ) + { + LOG() << result.getError().message; + return Consumed::No; + } + return std::get< 0 >( result ) ? Consumed::Yes : Consumed::No; + } + + void ForceDown() override + { + ApplicationHidden(); + BridgeAccessible::ForceDown(); + registryClient = {}; + } + + ForceUpResult ForceUp() override + { + if( BridgeAccessible::ForceUp() == ForceUpResult::alreadyUp ) + return ForceUpResult::alreadyUp; + + BridgeObject::RegisterInterfaces(); + BridgeAccessible::RegisterInterfaces(); + BridgeComponent::RegisterInterfaces(); + BridgeCollection::RegisterInterfaces(); + BridgeAction::RegisterInterfaces(); + BridgeValue::RegisterInterfaces(); + + RegisterOnBridge( &application ); + + registryClient = {ATSPI_DBUS_NAME_REGISTRY, ATSPI_DBUS_PATH_DEC, ATSPI_DBUS_INTERFACE_DEC, con}; + auto proxy = DBus::DBusClient{ATSPI_DBUS_NAME_REGISTRY, ATSPI_DBUS_PATH_ROOT, ATSPI_DBUS_INTERFACE_SOCKET, con}; + Address root{ + data->busName, "root"}; + auto res = proxy.method< Address( Address ) >( "Embed" ).call( root ); + assert( res ); + application.parent.SetAddress( std::move( std::get< 0 >( res ) ) ); + ApplicationShown(); + return ForceUpResult::justStarted; + } + + void ApplicationHidden() override + { + if( currentWindow ) + { + currentWindow->Emit( WindowEvent::Deactivate, 0 ); + currentWindow = nullptr; + } + } + void ApplicationShown() override + { + auto win = application.getActiveWindow(); + if( win && win != currentWindow ) + { + currentWindow = win; + win->Emit( WindowEvent::Activate, 0 ); + } + } + void Initialize() override + { + auto req = DBus::DBusClient{A11Y_DBUS_NAME, A11Y_DBUS_PATH, A11Y_DBUS_STATUS_INTERFACE, DBus::ConnectionType::SESSION}; + auto p = req.property< bool >( "ScreenReaderEnabled" ).get(); + if( p ) + screenReaderEnabled = std::get< 0 >( p ); + p = req.property< bool >( "IsEnabled" ).get(); + if( p ) + isEnabled = std::get< 0 >( p ); + if( screenReaderEnabled || isEnabled ) + ForceUp(); + } + static std::shared_ptr< Bridge > Create() + { + auto ptr = std::make_shared< BridgeImpl >(); + ptr->MakePublic( Bridge::Visibility::allThreads ); + ptr->listenOnAtspiEnabledSignalClient = DBus::DBusClient{A11Y_DBUS_NAME, A11Y_DBUS_PATH, A11Y_DBUS_STATUS_INTERFACE, + DBus::ConnectionType::SESSION}; + { + auto p = ptr.get(); + ptr->listenOnAtspiEnabledSignalClient.addPropertyChangedEvent< bool >( "ScreenReaderEnabled", [p]( bool res ) { + p->screenReaderEnabled = res; + if( p->screenReaderEnabled || p->isEnabled ) + p->ForceUp(); + else + p->ForceDown(); + } ); + ptr->listenOnAtspiEnabledSignalClient.addPropertyChangedEvent< bool >( "IsEnabled", [p]( bool res ) { + p->isEnabled = res; + if( p->screenReaderEnabled || p->isEnabled ) + p->ForceUp(); + else + p->ForceDown(); + } ); + } + return ptr; + } +}; + +std::shared_ptr< Bridge > Dali::Accessibility::CreateBridge() +{ + try + { + return BridgeImpl::Create(); + } + catch( AccessibleError& e ) + { + return {}; + } +} diff --git a/dali/dali-bridge/src/BridgeImpl.hpp b/dali/dali-bridge/src/BridgeImpl.hpp new file mode 100644 index 0000000..e068478 --- /dev/null +++ b/dali/dali-bridge/src/BridgeImpl.hpp @@ -0,0 +1,4 @@ +#ifndef BRIDGE_IMPL_HPP +#define BRIDGE_IMPL_HPP + +#endif diff --git a/dali/dali-bridge/src/BridgeObject.cpp b/dali/dali-bridge/src/BridgeObject.cpp new file mode 100644 index 0000000..f28dcbe --- /dev/null +++ b/dali/dali-bridge/src/BridgeObject.cpp @@ -0,0 +1,401 @@ +#include "BridgeObject.hpp" +#include +#include + +using namespace Dali::Accessibility; + +BridgeObject::BridgeObject() +{ +} + +void BridgeObject::RegisterInterfaces() +{ + // DBus::DBusInterfaceDescription desc{ ATSPI_DBUS_INTERFACE_EVENT_OBJECT }; + // stateChanged = addSignal, Accessible*>(desc, "StateChanged"); + // dbusServer.addInterface("/", desc, true); +} + +void BridgeObject::Emit( Accessible* obj, WindowEvent we, unsigned int detail1 ) +{ + const char* name = nullptr; + switch( we ) + { + case WindowEvent::PropertyChange: + { + name = "PropertyChange"; + break; + } + case WindowEvent::Minimize: + { + name = "Minimize"; + break; + } + case WindowEvent::Maximize: + { + name = "Maximize"; + break; + } + case WindowEvent::Restore: + { + name = "Restore"; + break; + } + case WindowEvent::Close: + { + name = "Close"; + break; + } + case WindowEvent::Create: + { + name = "Create"; + break; + } + case WindowEvent::Reparent: + { + name = "Reparent"; + break; + } + case WindowEvent::DesktopCreate: + { + name = "DesktopCreate"; + break; + } + case WindowEvent::DesktopDestroy: + { + name = "DesktopDestroy"; + break; + } + case WindowEvent::Destroy: + { + name = "Destroy"; + break; + } + case WindowEvent::Activate: + { + name = "Activate"; + break; + } + case WindowEvent::Deactivate: + { + name = "Deactivate"; + break; + } + case WindowEvent::Raise: + { + name = "Raise"; + break; + } + case WindowEvent::Lower: + { + name = "Lower"; + break; + } + case WindowEvent::Move: + { + name = "Move"; + break; + } + case WindowEvent::Resize: + { + name = "Resize"; + break; + } + case WindowEvent::Shade: + { + name = "Shade"; + break; + } + case WindowEvent::UuShade: + { + name = "uUshade"; + break; + } + case WindowEvent::Restyle: + { + name = "Restyle"; + break; + } + } + if( name ) + { + auto addr = obj->GetAddress(); + const auto prefixPath = "/org/a11y/atspi/accessible/"; + const auto nullPath = "/org/a11y/atspi/null"; + std::string p; + if( addr ) + p = prefixPath + addr.GetPath(); + else + p = nullPath; + dbusServer.emit2< std::string, int, int, DBus::EldbusVariant< int >, Address >( + p, + ATSPI_DBUS_INTERFACE_EVENT_WINDOW, + name, + "", + detail1, + 0, + {0}, + {GetBusName(), "root"} ); + } +} + +void BridgeObject::EmitStateChanged( Accessible* obj, State state, int newValue1, int newValue2 ) +{ + const char* stateName = nullptr; + switch( state ) + { + case State::Invalid: + { + stateName = "invalid"; + break; + } + case State::Active: + { + stateName = "active"; + break; + } + case State::Armed: + { + stateName = "armed"; + break; + } + case State::Busy: + { + stateName = "busy"; + break; + } + case State::Checked: + { + stateName = "checked"; + break; + } + case State::Collapsed: + { + stateName = "collapsed"; + break; + } + case State::Defunct: + { + stateName = "defunct"; + break; + } + case State::Editable: + { + stateName = "editable"; + break; + } + case State::Enabled: + { + stateName = "enabled"; + break; + } + case State::Expandable: + { + stateName = "expandable"; + break; + } + case State::Expanded: + { + stateName = "expanded"; + break; + } + case State::Focusable: + { + stateName = "focusable"; + break; + } + case State::Focused: + { + stateName = "focused"; + break; + } + case State::HasTooltip: + { + stateName = "has-tooltip"; + break; + } + case State::Horizontal: + { + stateName = "horizontal"; + break; + } + case State::Iconified: + { + stateName = "iconified"; + break; + } + case State::Modal: + { + stateName = "modal"; + break; + } + case State::MultiLine: + { + stateName = "multi-line"; + break; + } + case State::MultiSelectable: + { + stateName = "multiselectable"; + break; + } + case State::Opaque: + { + stateName = "opaque"; + break; + } + case State::Pressed: + { + stateName = "pressed"; + break; + } + case State::Resizeable: + { + stateName = "resizable"; + break; + } + case State::Selectable: + { + stateName = "selectable"; + break; + } + case State::Selected: + { + stateName = "selected"; + break; + } + case State::Sensitive: + { + stateName = "sensitive"; + break; + } + case State::Showing: + { + stateName = "showing"; + break; + } + case State::SingleLine: + { + stateName = "single-line"; + break; + } + case State::Stale: + { + stateName = "stale"; + break; + } + case State::Transient: + { + stateName = "transient"; + break; + } + case State::Vertical: + { + stateName = "vertical"; + break; + } + case State::Visible: + { + stateName = "visible"; + break; + } + case State::ManagesDescendants: + { + stateName = "manages-descendants"; + break; + } + case State::Indeterminate: + { + stateName = "indeterminate"; + break; + } + case State::Required: + { + stateName = "required"; + break; + } + case State::Truncated: + { + stateName = "truncated"; + break; + } + case State::Animated: + { + stateName = "animated"; + break; + } + case State::InvalidEntry: + { + stateName = "invalid-entry"; + break; + } + case State::SupportsAutocompletion: + { + stateName = "supports-autocompletion"; + break; + } + case State::SelectableText: + { + stateName = "selectable-text"; + break; + } + case State::IsDefault: + { + stateName = "is-default"; + break; + } + case State::Visited: + { + stateName = "visited"; + break; + } + case State::Checkable: + { + stateName = "checkable"; + break; + } + case State::HasPopup: + { + stateName = "has-popup"; + break; + } + case State::ReadOnly: + { + stateName = "read-only"; + break; + } + case State::Highlighted: + { + stateName = "highlighted"; + break; + } + case State::Highlightable: + { + stateName = "highlightable"; + break; + } + case State::_Count: + { + break; + } + } + if( stateName ) + { + auto addr = obj->GetAddress(); + const auto prefixPath = "/org/a11y/atspi/accessible/"; + const auto nullPath = "/org/a11y/atspi/null"; + std::string p; + if( addr ) + p = prefixPath + addr.GetPath(); + else + p = nullPath; + dbusServer.emit2< std::string, int, int, DBus::EldbusVariant< int >, Address >( + p, + ATSPI_DBUS_INTERFACE_EVENT_OBJECT, + "StateChanged", + stateName, + newValue1, + newValue2, + {0}, + {GetBusName(), "root"} ); + } +} diff --git a/dali/dali-bridge/src/BridgeObject.hpp b/dali/dali-bridge/src/BridgeObject.hpp new file mode 100644 index 0000000..af496d1 --- /dev/null +++ b/dali/dali-bridge/src/BridgeObject.hpp @@ -0,0 +1,38 @@ +#ifndef BRIDGE_OBJECT_HPP +#define BRIDGE_OBJECT_HPP + +#include "BridgeBase.hpp" +#include +#include +#include +#include + +class BridgeObject : public virtual BridgeBase +{ +protected: + BridgeObject(); + + void RegisterInterfaces(); + + DBus::DBusInterfaceDescription::SignalId stateChanged; + + void EmitStateChanged( Dali::Accessibility::Accessible* obj, Dali::Accessibility::State state, int val1, int val2 ) override; + void Emit( Dali::Accessibility::Accessible* obj, Dali::Accessibility::WindowEvent we, unsigned int detail1 ) override; + +public: + int GetChildCount(); + DBus::ValueOrError< Dali::Accessibility::Accessible* > GetChildAtIndex( int index ); + Dali::Accessibility::Accessible* GetParent(); + DBus::ValueOrError< std::vector< Dali::Accessibility::Accessible* > > GetChildren(); + std::string GetName(); + std::string GetDescription(); + DBus::ValueOrError< uint32_t > GetRole(); + DBus::ValueOrError< std::string > GetRoleName(); + DBus::ValueOrError< std::string > GetLocalizedRoleName(); + DBus::ValueOrError< int32_t > GetIndexInParent(); + DBus::ValueOrError< std::array< uint32_t, 2 > > GetStates(); + DBus::ValueOrError< std::unordered_map< std::string, std::string > > GetAttributes(); + DBus::ValueOrError< std::vector< std::string > > GetInterfaces(); +}; + +#endif \ No newline at end of file diff --git a/dali/dali-bridge/src/BridgeText.cpp b/dali/dali-bridge/src/BridgeText.cpp new file mode 100644 index 0000000..651b981 --- /dev/null +++ b/dali/dali-bridge/src/BridgeText.cpp @@ -0,0 +1,74 @@ +#include "BridgeText.hpp" + +#include + +using namespace Dali::Accessibility; + +void BridgeText::RegisterInterfaces() +{ + DBus::DBusInterfaceDescription desc{ATSPI_DBUS_INTERFACE_TEXT}; + AddFunctionToInterface( desc, "GetText", &BridgeText::GetText ); + AddGetPropertyToInterface( desc, "CharacterCount", &BridgeText::GetCharacterCount ); + AddGetPropertyToInterface( desc, "CaretOffset", &BridgeText::GetCaretOffset ); + AddFunctionToInterface( desc, "SetCaretOffset", &BridgeText::SetCaretOffset ); + AddFunctionToInterface( desc, "GetTextAtOffset", &BridgeText::GetTextAtOffset ); + AddFunctionToInterface( desc, "GetSelection", &BridgeText::GetSelection ); + AddFunctionToInterface( desc, "SetSelection", &BridgeText::SetSelection ); + AddFunctionToInterface( desc, "RemoveSelection", &BridgeText::RemoveSelection ); + dbusServer.addInterface( "/", desc, true ); +} + +Text* BridgeText::FindSelf() const +{ + auto s = BridgeBase::FindSelf(); + assert( s ); + auto s2 = dynamic_cast< Text* >( s ); + if( !s2 ) + throw AccessibleError{"object " + s->GetAddress().ToString() + " doesn't have Text interface"}; + return s2; +} + +DBus::ValueOrError< std::string > BridgeText::GetText( int startOffset, int endOffset ) +{ + return FindSelf()->GetText( startOffset, endOffset ); +} + +DBus::ValueOrError< int32_t > BridgeText::GetCharacterCount() +{ + return FindSelf()->GetCharacterCount(); +} + +DBus::ValueOrError< int32_t > BridgeText::GetCaretOffset() +{ + auto imfManager = Dali::Internal::Adaptor::ImfManager::Get(); + return imfManager.GetCursorPosition(); +} + +DBus::ValueOrError< bool > BridgeText::SetCaretOffset( int32_t offset ) +{ + auto imfManager = Dali::Internal::Adaptor::ImfManager::Get(); + imfManager.SetCursorPosition( offset ); + return true; +} + +DBus::ValueOrError< std::string, int, int > BridgeText::GetTextAtOffset( int32_t offset, uint32_t boundary ) +{ + auto r = FindSelf()->GetTextAtOffset( offset, static_cast< TextBoundary >( boundary ) ); + return {r.content, static_cast< int >( r.startOffset ), static_cast< int >( r.endOffset )}; +} + +DBus::ValueOrError< int, int > BridgeText::GetSelection( int32_t selectionNum ) +{ + auto r = FindSelf()->GetSelection( selectionNum ); + return {static_cast< int >( r.startOffset ), static_cast< int >( r.endOffset )}; +} + +DBus::ValueOrError< bool > BridgeText::RemoveSelection( int32_t selectionNum ) +{ + return FindSelf()->RemoveSelection( selectionNum ); +} + +DBus::ValueOrError< bool > BridgeText::SetSelection( int32_t selectionNum, int32_t startOffset, int32_t endOffset ) +{ + return FindSelf()->SetSelection( selectionNum, startOffset, endOffset ); +} diff --git a/dali/dali-bridge/src/BridgeText.hpp b/dali/dali-bridge/src/BridgeText.hpp new file mode 100644 index 0000000..fdb0894 --- /dev/null +++ b/dali/dali-bridge/src/BridgeText.hpp @@ -0,0 +1,26 @@ +#ifndef BRIDGE_TEXT_HPP +#define BRIDGE_TEXT_HPP + +#include "BridgeBase.hpp" + +class BridgeText : public virtual BridgeBase +{ +protected: + BridgeText() = default; + + void RegisterInterfaces(); + + Dali::Accessibility::Text* FindSelf() const; + +public: + DBus::ValueOrError< std::string > GetText( int startOffset, int endOffset ); + DBus::ValueOrError< int32_t > GetCharacterCount(); + DBus::ValueOrError< int32_t > GetCaretOffset(); + DBus::ValueOrError< bool > SetCaretOffset( int32_t offset ); + DBus::ValueOrError< std::string, int, int > GetTextAtOffset( int32_t offset, uint32_t boundary ); + DBus::ValueOrError< int, int > GetSelection( int32_t selectionNum ); + DBus::ValueOrError< bool > RemoveSelection( int32_t selectionNum ); + DBus::ValueOrError< bool > SetSelection( int32_t selectionNum, int32_t startOffset, int32_t endOffset ); +}; + +#endif diff --git a/dali/dali-bridge/src/BridgeValue.cpp b/dali/dali-bridge/src/BridgeValue.cpp new file mode 100644 index 0000000..7c158b6 --- /dev/null +++ b/dali/dali-bridge/src/BridgeValue.cpp @@ -0,0 +1,48 @@ +#include "BridgeValue.hpp" +#include + +using namespace Dali::Accessibility; + +BridgeValue::BridgeValue() +{ +} + +void BridgeValue::RegisterInterfaces() +{ + DBus::DBusInterfaceDescription desc{ATSPI_DBUS_INTERFACE_VALUE}; + AddGetSetPropertyToInterface( desc, "CurrentValue", &BridgeValue::GetCurrentValue, &BridgeValue::SetCurrentValue ); + AddGetPropertyToInterface( desc, "MaximumValue", &BridgeValue::GetMaximumValue ); + AddGetPropertyToInterface( desc, "MinimumIncrement", &BridgeValue::GetMinimumIncrement ); + AddGetPropertyToInterface( desc, "MinimumValue", &BridgeValue::GetMinimumValue ); + dbusServer.addInterface( "/", desc, true ); +} + +Value* BridgeValue::FindSelf() const +{ + auto s = BridgeBase::FindSelf(); + assert( s ); + auto s2 = dynamic_cast< Value* >( s ); + if( !s2 ) + throw AccessibleError{"object " + s->GetAddress().ToString() + " doesn't have Value interface"}; + return s2; +} +double BridgeValue::GetCurrentValue() +{ + return FindSelf()->GetCurrent(); +} +void BridgeValue::SetCurrentValue( double new_value ) +{ + FindSelf()->SetCurrent( new_value ); +} +double BridgeValue::GetMaximumValue() +{ + return FindSelf()->GetMaximum(); +} +double BridgeValue::GetMinimumIncrement() +{ + return FindSelf()->GetMinimumIncrement(); +} +double BridgeValue::GetMinimumValue() +{ + return FindSelf()->GetMinimum(); +} diff --git a/dali/dali-bridge/src/BridgeValue.hpp b/dali/dali-bridge/src/BridgeValue.hpp new file mode 100644 index 0000000..beb1d86 --- /dev/null +++ b/dali/dali-bridge/src/BridgeValue.hpp @@ -0,0 +1,28 @@ +#ifndef BRIDGE_VALUE_HPP +#define BRIDGE_VALUE_HPP + +#include "BridgeBase.hpp" +#include +#include +#include +#include +#include + +class BridgeValue : public virtual BridgeBase +{ +protected: + BridgeValue(); + + void RegisterInterfaces(); + + Dali::Accessibility::Value* FindSelf() const; + +public: + double GetCurrentValue(); + void SetCurrentValue( double new_value ); + double GetMaximumValue(); + double GetMinimumIncrement(); + double GetMinimumValue(); +}; + +#endif diff --git a/dali/dali-bridge/src/Common.hpp b/dali/dali-bridge/src/Common.hpp new file mode 100755 index 0000000..acbef6f --- /dev/null +++ b/dali/dali-bridge/src/Common.hpp @@ -0,0 +1,267 @@ +#ifndef COMMON_HPP +#define COMMON_HPP + +#include "DBus.hpp" +#include "dbusLocators.hpp" +#include +#include +#include +#include +#include + +#define A11Y_DBUS_NAME "org.a11y.Bus" +#define A11Y_DBUS_PATH "/org/a11y/bus" +#define A11Y_DBUS_STATUS_INTERFACE "org.a11y.Status" +#define ATSPI_DBUS_NAME_REGISTRY "org.a11y.atspi.Registry" +#define ATSPI_DBUS_PATH_ROOT "/org/a11y/atspi/accessible/root" +#define ATSPI_DBUS_INTERFACE_SOCKET "org.a11y.atspi.Socket" +#define ATSPI_PATH "/org/a11y/atspi/accessible" +#define ATSPI_DBUS_INTERFACE_ACCESSIBLE "org.a11y.atspi.Accessible" +#define ATSPI_DBUS_INTERFACE_ACTION "org.a11y.atspi.Action" +#define ATSPI_DBUS_INTERFACE_APPLICATION "org.a11y.atspi.Application" +#define ATSPI_DBUS_INTERFACE_COLLECTION "org.a11y.atspi.Collection" +#define ATSPI_DBUS_INTERFACE_COMPONENT "org.a11y.atspi.Component" +#define ATSPI_DBUS_INTERFACE_DOCUMENT "org.a11y.atspi.Document" +#define ATSPI_DBUS_INTERFACE_EDITABLE_TEXT "org.a11y.atspi.EditableText" +#define ATSPI_DBUS_INTERFACE_EVENT_KEYBOARD "org.a11y.atspi.Event.Keyboard" +#define ATSPI_DBUS_INTERFACE_EVENT_MOUSE "org.a11y.atspi.Event.Mouse" +#define ATSPI_DBUS_INTERFACE_EVENT_OBJECT "org.a11y.atspi.Event.Object" +#define ATSPI_DBUS_INTERFACE_HYPERLINK "org.a11y.atspi.Hyperlink" +#define ATSPI_DBUS_INTERFACE_HYPERTEXT "org.a11y.atspi.Hypertext" +#define ATSPI_DBUS_INTERFACE_IMAGE "org.a11y.atspi.Image" +#define ATSPI_DBUS_INTERFACE_SELECTION "org.a11y.atspi.Selection" +#define ATSPI_DBUS_INTERFACE_TABLE "org.a11y.atspi.Table" +#define ATSPI_DBUS_INTERFACE_TABLE_CELL "org.a11y.atspi.TableCell" +#define ATSPI_DBUS_INTERFACE_TEXT "org.a11y.atspi.Text" +#define ATSPI_DBUS_INTERFACE_VALUE "org.a11y.atspi.Value" +#define ATSPI_DBUS_INTERFACE_SOCKET "org.a11y.atspi.Socket" +#define ATSPI_DBUS_INTERFACE_EVENT_WINDOW "org.a11y.atspi.Event.Window" + +#define ATSPI_DBUS_PATH_DEC "/org/a11y/atspi/registry/deviceeventcontroller" +#define ATSPI_DBUS_INTERFACE_DEC "org.a11y.atspi.DeviceEventController" +#define ATSPI_DBUS_INTERFACE_DEVICE_EVENT_LISTENER "org.a11y.atspi.DeviceEventListener" + +namespace DBus +{ +class CurrentBridgePtr +{ + static Dali::Accessibility::Bridge*& get() + { + static thread_local Dali::Accessibility::Bridge* b = nullptr; + return b; + } + Dali::Accessibility::Bridge* prev; + CurrentBridgePtr( const CurrentBridgePtr& ) = delete; + CurrentBridgePtr( CurrentBridgePtr&& ) = delete; + CurrentBridgePtr& operator=( const CurrentBridgePtr& ) = delete; + CurrentBridgePtr& operator=( CurrentBridgePtr&& ) = delete; + +public: + CurrentBridgePtr( Dali::Accessibility::Bridge* b ) : prev( get() ) { get() = b; } + ~CurrentBridgePtr() { get() = prev; } + + static Dali::Accessibility::Bridge* current() { return get(); } +}; +namespace detail +{ +template < typename T > +struct signature_accessible_impl +{ + using subtype = std::pair< std::string, DBus::ObjectPath >; + + /** + * @brief Returns name of type marshalled, for informative purposes + */ + static std::string name() + { + return "AtspiAccessiblePtr"; + } + /** + * @brief Returns DBUS' signature of type marshalled + */ + static std::string sig() + { + return "(so)"; + } + /** + * @brief Marshals value v as marshalled type into message + */ + static void set( Eldbus_Message_Iter* iter, T* t ) + { + const auto prefixPath = "/org/a11y/atspi/accessible/"; + const auto nullPath = "/org/a11y/atspi/null"; + + if( t ) + { + auto v = t->GetAddress(); + signature< subtype >::set( iter, {v.GetBus(), DBus::ObjectPath{std::string{prefixPath} + v.GetPath()}} ); + } + else + { + signature< subtype >::set( iter, {"", DBus::ObjectPath{nullPath}} ); + } + } + /** + * @brief Marshals value from marshalled type into variable v + */ + static bool get( Eldbus_Message_Iter* iter, T*& v ) + { + const auto prefixPath = "/org/a11y/atspi/accessible/"; + const auto nullPath = "/org/a11y/atspi/null"; + subtype tmp; + if( !signature< subtype >::get( iter, tmp ) ) + return false; + if( tmp.second.value == nullPath ) + { + v = nullptr; + return true; + } + if( tmp.second.value.substr( 0, strlen( prefixPath ) ) != prefixPath ) + return false; + auto b = CurrentBridgePtr::current(); + if( b->GetBusName() != tmp.first ) + return false; + v = b->FindByPath( tmp.second.value.substr( strlen( prefixPath ) ) ); + return v != nullptr; + } +}; +template <> +struct signature< Dali::Accessibility::Accessible* > : public signature_accessible_impl< Dali::Accessibility::Accessible > +{ +}; + +template <> +struct signature< Dali::Accessibility::Address > +{ + using subtype = std::pair< std::string, DBus::ObjectPath >; + + /** + * @brief Returns name of type marshalled, for informative purposes + */ + static std::string name() + { + return "AtspiAccessiblePtr"; + } + /** + * @brief Returns DBUS' signature of type marshalled + */ + static std::string sig() + { + return "(so)"; + } + /** + * @brief Marshals value v as marshalled type into message + */ + static void set( Eldbus_Message_Iter* iter, const Dali::Accessibility::Address& v ) + { + const auto prefixPath = "/org/a11y/atspi/accessible/"; + const auto nullPath = "/org/a11y/atspi/null"; + + if( v ) + { + signature< subtype >::set( iter, {v.GetBus(), DBus::ObjectPath{std::string{prefixPath} + v.GetPath()}} ); + } + else + { + signature< subtype >::set( iter, {v.GetBus(), DBus::ObjectPath{nullPath}} ); + } + } + /** + * @brief Marshals value from marshalled type into variable v + */ + static bool get( Eldbus_Message_Iter* iter, Dali::Accessibility::Address& v ) + { + const auto prefixPath = "/org/a11y/atspi/accessible/"; + const auto nullPath = "/org/a11y/atspi/null"; + subtype tmp; + if( !signature< subtype >::get( iter, tmp ) ) + return false; + if( tmp.second.value == nullPath ) + { + v = {}; + return true; + } + if( tmp.second.value.substr( 0, strlen( prefixPath ) ) != prefixPath ) + return false; + v = {std::move( tmp.first ), tmp.second.value.substr( strlen( prefixPath ) )}; + return true; + } +}; +template <> +struct signature< Dali::Accessibility::States > +{ + using subtype = std::array< uint32_t, 2 >; + + /** + * @brief Returns name of type marshalled, for informative purposes + */ + static std::string name() + { + return signature< subtype >::name(); + } + /** + * @brief Returns DBUS' signature of type marshalled + */ + static std::string sig() + { + return signature< subtype >::sig(); + } + /** + * @brief Marshals value v as marshalled type into message + */ + static void set( Eldbus_Message_Iter* iter, const Dali::Accessibility::States& v ) + { + signature< subtype >::set( iter, v.GetRawData() ); + } + /** + * @brief Marshals value from marshalled type into variable v + */ + static bool get( Eldbus_Message_Iter* iter, Dali::Accessibility::States& v ) + { + subtype tmp; + if( !signature< subtype >::get( iter, tmp ) ) + return false; + v = Dali::Accessibility::States{tmp}; + return true; + } +}; +} +} + +struct _Logger +{ + const char* file; + int line; + std::ostringstream tmp; + + _Logger( const char* f, int l ) : file( f ), line( l ) {} + ~_Logger() + { + Dali::Integration::Log::LogMessage( Dali::Integration::Log::DebugInfo, "%s:%d: %s", file, line, tmp.str().c_str() ); + } + template < typename T > + _Logger& operator<<( T&& t ) + { + tmp << std::forward< T >( t ); + return *this; + } +}; + +struct _LoggerScope +{ + const char* file; + int line; + + _LoggerScope( const char* f, int l ) : file( f ), line( l ) + { + Dali::Integration::Log::LogMessage( Dali::Integration::Log::DebugInfo, "%s:%d: +", file, line ); + } + ~_LoggerScope() + { + Dali::Integration::Log::LogMessage( Dali::Integration::Log::DebugInfo, "%s:%d: -", file, line ); + } +}; +#define LOG() _Logger( __FILE__, __LINE__ ) +#define SCOPE() _LoggerScope _l##__LINE__( __FILE__, __LINE__ ) + +#endif diff --git a/dali/dali-bridge/src/Component.cpp b/dali/dali-bridge/src/Component.cpp new file mode 100644 index 0000000..8085569 --- /dev/null +++ b/dali/dali-bridge/src/Component.cpp @@ -0,0 +1,29 @@ +#include "Common.hpp" +#include + +using namespace Dali::Accessibility; + +bool Component::Contains( Point p, CoordType ctype ) +{ + auto extents = GetExtents( ctype ); + return p.x >= extents.position.x && p.y >= extents.position.y && p.x <= extents.position.x + extents.size.width && p.y <= extents.position.y + extents.size.height; +} + +Component* Component::GetAccessibleAtPoint( Point p, CoordType ctype ) +{ + auto children = GetChildren(); + for( auto childIt = children.rbegin(); childIt != children.rend(); childIt++ ) + { + auto component = dynamic_cast< Component* >( *childIt ); + if( component && component->Contains( p, ctype ) ) + { + return component; + } + } + return nullptr; +} + +bool Component::IsScrollable() +{ + return false; +} diff --git a/dali/dali-bridge/src/DBus.cpp b/dali/dali-bridge/src/DBus.cpp new file mode 100644 index 0000000..f8af9bc --- /dev/null +++ b/dali/dali-bridge/src/DBus.cpp @@ -0,0 +1,490 @@ +/* + * Copyright 2017 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 "DBus.hpp" +#include +//#include "Atspi.hpp" +#include +#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) + +std::atomic< unsigned int > DBus::detail::CallId::LastId{0}; +static std::function< void( const char*, size_t ) > debugPrinter; +static std::function< void( DBus::DBusAction ) > notificationCallback; +static std::mutex debugLock, notificationLock; + +DBus::detail::CallOnDestructionList& DBus::detail::CallOnDestructionList::operator=( DBus::detail::CallOnDestructionList&& d ) +{ + for( auto& q : functions ) + { + q(); + } + functions = std::move( d.functions ); + d.functions.clear(); + return *this; +} + +void DBus::detail::CallOnDestructionList::add( const std::function< void() >& c ) +{ + functions.push_back( c ); +} + +void DBus::setDBusActionNotifier( std::function< void( DBus::DBusAction ) > callback ) +{ + std::lock_guard< std::mutex > lock( notificationLock ); + notificationCallback = std::move( callback ); +} + +void DBus::detail::emitNotification( const char* bus, const char* path, const char* interface, const char* member, DBus::DBusActionType type ) +{ + std::lock_guard< std::mutex > lock( notificationLock ); + if( notificationCallback ) + { + notificationCallback( DBusAction{type, bus, path, interface, member} ); + } +} + +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() ); +} + +std::shared_ptr< DBus::EldbusConnection > DBus::getDBusConnectionByName( const std::string& name ) +{ + eldbus_init(); + auto z = getDBusConnectionByType( ConnectionType::SYSTEM ); + auto connection = eldbus_address_connection_get( name.c_str() ); + auto ptr = std::make_shared< EldbusConnection >( connection ); + eldbus_shutdown(); + return ptr; +} + +std::shared_ptr< DBus::EldbusConnection > DBus::getDBusConnectionByType( ConnectionType connectionType ) +{ + Eldbus_Connection_Type eldbusType = ELDBUS_CONNECTION_TYPE_SYSTEM; + + switch( connectionType ) + { + case ConnectionType::SYSTEM: + { + eldbusType = ELDBUS_CONNECTION_TYPE_SYSTEM; + break; + } + case ConnectionType::SESSION: + { + eldbusType = ELDBUS_CONNECTION_TYPE_SESSION; + break; + } + } + + eldbus_init(); + auto connection = eldbus_connection_get( eldbusType ); + auto ptr = std::make_shared< EldbusConnection >( connection ); + eldbus_shutdown(); + return ptr; +} + +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 ) ) +{ +} + +struct caller_eldbus_object_unref +{ + void operator()( Eldbus_Object* p ) const + { + eldbus_object_unref( p ); + } +}; + +DBus::DBusClient::DBusClient( std::string busName, std::string pathName, std::string interfaceName, const std::shared_ptr< DBus::EldbusConnection >& conn ) +{ + if( !conn ) + connectionState.connection = getDBusConnectionByType( ConnectionType::SESSION ); + else + connectionState.connection = conn; + + std::ostringstream o; + o << "bus = " << busName << " path = " << pathName << " connection = " << eldbus_connection_unique_name_get( connectionState.connection->get() ); + info = o.str(); + + auto c = connectionState.connection; + connectionState.object = { + eldbus_object_get( connectionState.connection->get(), busName.c_str(), pathName.c_str() ), + [c]( Eldbus_Object* p ) { + eldbus_object_unref( p ); + }}; + if( connectionState.object ) + { + auto obj = connectionState.object; + connectionState.proxy = { + eldbus_proxy_get( connectionState.object.get(), interfaceName.c_str() ), + [obj]( Eldbus_Proxy* p ) { + eldbus_proxy_unref( p ); + }}; + if( interfaceName != DBUS_INTERFACE_PROPERTIES ) + { + auto obj = connectionState.object; + connectionState.propertiesProxy = { + eldbus_proxy_get( connectionState.object.get(), DBUS_INTERFACE_PROPERTIES ), + [obj]( Eldbus_Proxy* p ) { + eldbus_proxy_unref( p ); + }}; + } + else + { + connectionState.propertiesProxy = 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 std::shared_ptr< DBus::EldbusConnection >& conn ) +{ + if( !conn ) + connection = getDBusConnectionByType( ConnectionType::SESSION ); + else + connection = conn; +} + +DBus::DBusInterfaceDescription::DBusInterfaceDescription( std::string interfaceName ) : interfaceName( std::move( interfaceName ) ) +{ +} + +struct Implementation +{ + Eldbus_Service_Interface_Desc dsc; + std::vector< Eldbus_Method > methods; + std::vector< Eldbus_Signal > signals; + std::vector< Eldbus_Property > properties; + DBus::detail::StringStorage strings; + + 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; + + std::shared_ptr< DBus::EldbusConnection > connection; +}; + +static std::unordered_map< const Eldbus_Service_Interface*, std::unique_ptr< Implementation > > globalEntries; +static std::mutex globalEntriesMutex; +static thread_local const char* currentObjectPath = ""; +static thread_local std::shared_ptr< DBus::EldbusConnection > currentConnection; + +class CurrentObjectSetter +{ +public: + CurrentObjectSetter( std::shared_ptr< DBus::EldbusConnection > con, const Eldbus_Message* m ) + { + currentObjectPath = eldbus_message_path_get( m ); + currentConnection = std::move( con ); + } + ~CurrentObjectSetter() + { + currentObjectPath = ""; + currentConnection = {}; + } + CurrentObjectSetter( const CurrentObjectSetter& ) = delete; + CurrentObjectSetter( CurrentObjectSetter&& ) = delete; + void operator=( const CurrentObjectSetter& ) = delete; + void operator=( CurrentObjectSetter&& ) = delete; +}; + +std::string DBus::DBusServer::getCurrentObjectPath() +{ + return currentObjectPath; +} + +std::shared_ptr< DBus::EldbusConnection > DBus::DBusServer::getCurrentConnection() +{ + return currentConnection; +} + +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; + + CurrentObjectSetter currentObjectSetter( impl->connection, message ); + auto reply = it->second.getCallback( message, iter ); + if( !reply ) + { + if( error ) + *error = eldbus_message_error_new( message, "org.freedesktop.DBus.Error.Failed", reply.getError().message.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; + } + CurrentObjectSetter currentObjectSetter( impl->connection, message ); + auto reply = it->second.setCallback( message, iter ); + + Eldbus_Message* ret = nullptr; + if( !reply ) + { + ret = eldbus_message_error_new( message, "org.freedesktop.DBus.Error.Failed", reply.getError().message.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; + } + CurrentObjectSetter currentObjectSetter( impl->connection, message ); + auto reply = it->second.callback( message ); + return reply; +} + +static void addInterfaceImpl( bool fallback, const std::string& pathName, + const std::shared_ptr< DBus::EldbusConnection >& connection, + const std::string& interfaceName, + std::unordered_map< unsigned int, std::pair< const Eldbus_Service_Interface*, unsigned int > >& signalData, + DBus::detail::StringStorage& strings, + std::vector< DBus::DBusInterfaceDescription::MethodInfo >& dscrMethods, + std::vector< DBus::DBusInterfaceDescription::PropertyInfo >& dscrProperties, + std::vector< DBus::DBusInterfaceDescription::SignalInfo >& dscrSignals, + DBus::detail::CallOnDestructionList& destructors ) +{ + std::vector< Eldbus_Method > methods; + std::vector< Eldbus_Signal > signals; + std::vector< Eldbus_Property > properties; + 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() ); + for( auto& ee : dscrMethods ) + { + auto key = ee.memberName; + DBUS_DEBUG( "adding method %s", ee.memberName.c_str() ); + for( auto& r : ee.in ) + { + if( !r.name ) + break; + DBUS_DEBUG( "in %s '%s'", r.name, r.signature ); + } + for( auto& r : ee.out ) + { + if( !r.name ) + break; + DBUS_DEBUG( "out %s '%s'", r.name, r.signature ); + } + auto& e = ( methodsMap[key] = std::move( ee ) ); + methods.push_back( {} ); + auto& m = methods.back(); + m.member = e.memberName.c_str(); + m.in = e.in.data(); + m.out = e.out.data(); + 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; + } + unsigned int signalIndex = 0; + std::vector< unsigned int > signalIds; + for( auto& ee : dscrSignals ) + { + DBUS_DEBUG( "adding signal %s", ee.memberName.c_str() ); + auto& e = ( signalsMap[ee.id.id] = std::move( ee ) ); + signals.push_back( {} ); + auto& m = signals.back(); + m.name = e.memberName.c_str(); + m.args = e.args.data(); + m.flags = 0; + signalData[e.id.id].second = signalIndex++; + signalIds.push_back( e.id.id ); + } + 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( methods ), + std::move( signals ), + std::move( properties ), + std::move( strings ), + 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( connection->get(), pathName.c_str(), &impl->dsc ) : eldbus_service_interface_register( connection->get(), pathName.c_str(), &impl->dsc ); + assert( v ); + globalEntries[v] = std::move( impl ); + DBUS_DEBUG( "registering interface %p (%d)", v, fallback ? 1 : 0 ); + destructors.add( [=]() { + eldbus_service_interface_unregister( v ); + std::lock_guard< std::mutex > lock( globalEntriesMutex ); + globalEntries.erase( v ); + DBUS_DEBUG( "unregistering interface %p (%d)", v, fallback ? 1 : 0 ); + } ); + for( auto id : signalIds ) + { + signalData[id].first = v; + } + } +} + +std::shared_ptr< DBus::EldbusConnection > DBus::DBusServer::getConnection() +{ + return connection; +} + +void DBus::DBusServer::addInterface( const std::string& pathName, DBusInterfaceDescription& dscr, bool fallback ) +{ + addInterfaceImpl( fallback, pathName, connection, dscr.interfaceName, signalData, dscr.strings, dscr.methods, dscr.properties, dscr.signals, destructors ); +} + +std::string DBus::DBusServer::getBusName() const +{ + return getConnectionName( connection ); +} + +std::string DBus::getConnectionName( const std::shared_ptr< DBus::EldbusConnection >& c ) +{ + return eldbus_connection_unique_name_get( c->get() ); +} diff --git a/dali/dali-bridge/src/DBus.hpp b/dali/dali-bridge/src/DBus.hpp new file mode 100755 index 0000000..5e956df --- /dev/null +++ b/dali/dali-bridge/src/DBus.hpp @@ -0,0 +1,3104 @@ +/* + * Copyright 2017 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. + */ + +#ifndef DBUS_HPP +#define DBUS_HPP + +#include +#include + +#include "Optional.hpp" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define DBUS_DEBUG( ... ) \ + do \ + { \ + DBus::debugPrint( __FILE__, __LINE__, __VA_ARGS__ ); \ + } while( 0 ) + +/** + * @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 DBus action enumeration + * + * @param METHOD_CALL DBus is about to call a method on some external target + * @param SETTER_CALL DBus is about to call a setter method on some external target + * @param GETTER_CALL DBus is about to call a getter method on some external target + * @param SIGNAL_RECEIVED DBus just received a signal + * @param METHOD_RESPONSE DBus server received a method call + * @param SETTER_RESPONSE DBus server received a setter call + * @param GETTER_RESPONSE DBus server received a getter call + * @param SIGNAL_EMIT DBus server is about to emit a signal + */ +enum class DBusActionType +{ + METHOD_CALL, + SETTER_CALL, + GETTER_CALL, + SIGNAL_RECEIVED, + METHOD_RESPONSE, + SETTER_RESPONSE, + GETTER_RESPONSE, + SIGNAL_EMIT, +}; + +/** + * @brief Structure containing information about DBus activity, when calling notification callback + * + * @param type type of the action + * @param path path of the object, that's involved in action + * @param interface interface, on which member was acted upon. Note, that in case of getters and setters + * this will be real interface, not org.freedesktop.DBus.Properties + * @param member member name, that was involved (either method / setter / getter / signal) + */ +struct DBusAction +{ + const DBusActionType type; + const char* const bus = nullptr; + const char* const path = nullptr; + const char* const interface = nullptr; + const char* const member = nullptr; + + DBusAction( const DBusActionType type, + const char* const bus = nullptr, + const char* const path = nullptr, + const char* const interface = nullptr, + const char* const member = nullptr ) : type( type ), bus( bus ), path( path ), interface( interface ), member( member ) {} +}; + +/** + * @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 ) > ); + +/** + * @brief Sets notification callback about processing of DBus call + * + * Notification callback can be set independently either on client or server. + * On client's side callback will be called, when user calls method / getter / setter + * or when client has received a signal. + * On server's side callback will be called, when sever has received a request to + * handle method / getter / setter or when server is going to emit a signal. + * Callback should returns as fast as possible. + * User can't call setDBusActionNotifier from inside the callback call - + * it will cause a deadlock + */ +void setDBusActionNotifier( std::function< void( DBusAction ) > callback ); + +namespace detail +{ +void emitNotification( const char* bus, const char* path, const char* interface, const char* member, DBusActionType type ); +} + +struct Error +{ + std::string message; + + Error() = default; + Error( std::string msg ) : message( std::move( msg ) ) + { + 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 ) ) + { + 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 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 assert. + */ + std::tuple< ARGS... >& getValues() + { + 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 assert. + */ + const std::tuple< ARGS... >& getValues() const + { + 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 ) ) + { + assert( !error.message.empty() ); + } + + explicit operator bool() const + { + return error.message.empty(); + } + const Error& getError() const + { + return error; + } + std::tuple<>& getValues() + { + assert( *this ); + static std::tuple<> t; + return t; + } + std::tuple<> getValues() const + { + assert( *this ); + return {}; + } + +protected: + Error error; +}; + +template <> +class ValueOrError< void > +{ +public: + ValueOrError() = default; + ValueOrError( Success ) {} + ValueOrError( Error e ) : error( std::move( e ) ) + { + assert( !error.message.empty() ); + } + + explicit operator bool() const + { + return error.message.empty(); + } + const Error& getError() const + { + return error; + } + std::tuple<>& getValues() + { + assert( *this ); + static std::tuple<> t; + return t; + } + std::tuple<> getValues() const + { + assert( *this ); + return {}; + } + +protected: + Error error; +}; +struct ObjectPath +{ + std::string value; +}; +namespace detail +{ +class CallOnDestructionList +{ +public: + CallOnDestructionList() = default; + CallOnDestructionList( const CallOnDestructionList& ) = delete; + CallOnDestructionList( CallOnDestructionList&& ) = default; + + CallOnDestructionList& operator=( const CallOnDestructionList& ) = delete; + CallOnDestructionList& operator=( CallOnDestructionList&& ); + + void add( const std::function< void() >& c ); + +private: + std::vector< std::function< void() > > functions; +}; + +struct caller_eldbus_connection_unref +{ + void operator()( Eldbus_Connection* p ) const + { + eldbus_connection_unref( p ); + } +}; + +struct caller_eldbus_message_unref +{ + void operator()( Eldbus_Message* p ) const + { + eldbus_message_unref( p ); + } +}; + +struct caller_eldbus_proxy_unref +{ + void operator()( Eldbus_Proxy* p ) const + { + eldbus_proxy_unref( p ); + } +}; +} +/// \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; +}; + +/// \cond +class EldbusConnection +{ + Eldbus_Connection* ptr = nullptr; + +public: + EldbusConnection( Eldbus_Connection* c ) : ptr( c ) + { + eldbus_init(); + } + EldbusConnection() = delete; + EldbusConnection( const EldbusConnection& ) = delete; + EldbusConnection( EldbusConnection&& ) = delete; + ~EldbusConnection() + { + eldbus_connection_unref( ptr ); + eldbus_shutdown(); + } + + Eldbus_Connection* get() const + { + return ptr; + } +}; +//using EldbusConnectionCallbackHandle = std::shared_ptr; +using EldbusMessageCallbackHandle = std::unique_ptr< Eldbus_Message, detail::caller_eldbus_message_unref >; +using EldbusObjectCallbackHandle = std::shared_ptr< Eldbus_Object >; +using EldbusProxyHandle = std::shared_ptr< Eldbus_Proxy >; +/// \endcond + +/** + * @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 > >; + +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() + { + // TODO: add check for failure in marshalling arguments + return "i"; + } + /** + * @brief Marshals value v as marshalled type into message + */ + static void set( Eldbus_Message_Iter* iter, T v ) + { + eldbus_message_iter_arguments_append( iter, sig().c_str(), static_cast< int >( v ) ); + } + /** + * @brief Marshals value from marshalled type into variable v + */ + static bool get( Eldbus_Message_Iter* iter, T& v ) + { + int q; + auto z = eldbus_message_iter_get_and_next( iter, sig()[0], &q ); + v = static_cast< T >( q ); + return z; + } +}; +/// \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( Eldbus_Message_Iter* iter, uint8_t v ) + { + eldbus_message_iter_arguments_append( iter, sig().c_str(), v ); + } + /** + * @brief Marshals value from marshalled type into variable v + */ + static bool get( Eldbus_Message_Iter* iter, uint8_t& v ) + { + return eldbus_message_iter_get_and_next( iter, sig()[0], &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( Eldbus_Message_Iter* iter, uint16_t v ) + { + eldbus_message_iter_arguments_append( iter, sig().c_str(), v ); + } + /** + * @brief Marshals value from marshalled type into variable v + */ + static bool get( Eldbus_Message_Iter* iter, uint16_t& v ) + { + return eldbus_message_iter_get_and_next( iter, sig()[0], &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( Eldbus_Message_Iter* iter, uint32_t v ) + { + eldbus_message_iter_arguments_append( iter, sig().c_str(), v ); + } + /** + * @brief Marshals value from marshalled type into variable v + */ + static bool get( Eldbus_Message_Iter* iter, uint32_t& v ) + { + return eldbus_message_iter_get_and_next( iter, sig()[0], &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( Eldbus_Message_Iter* iter, uint64_t v ) + { + eldbus_message_iter_arguments_append( iter, sig().c_str(), v ); + } + /** + * @brief Marshals value from marshalled type into variable v + */ + static bool get( Eldbus_Message_Iter* iter, uint64_t& v ) + { + return eldbus_message_iter_get_and_next( iter, sig()[0], &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( Eldbus_Message_Iter* iter, int16_t v ) + { + eldbus_message_iter_arguments_append( iter, sig().c_str(), v ); + } + /** + * @brief Marshals value from marshalled type into variable v + */ + static bool get( Eldbus_Message_Iter* iter, int16_t& v ) + { + return eldbus_message_iter_get_and_next( iter, sig()[0], &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( Eldbus_Message_Iter* iter, int32_t v ) + { + eldbus_message_iter_arguments_append( iter, sig().c_str(), v ); + } + /** + * @brief Marshals value from marshalled type into variable v + */ + static bool get( Eldbus_Message_Iter* iter, int32_t& v ) + { + return eldbus_message_iter_get_and_next( iter, sig()[0], &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( Eldbus_Message_Iter* iter, int64_t v ) + { + eldbus_message_iter_arguments_append( iter, sig().c_str(), v ); + } + /** + * @brief Marshals value from marshalled type into variable v + */ + static bool get( Eldbus_Message_Iter* iter, int64_t& v ) + { + return eldbus_message_iter_get_and_next( iter, sig()[0], &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( Eldbus_Message_Iter* iter, double v ) + { + eldbus_message_iter_arguments_append( iter, sig().c_str(), v ); + } + /** + * @brief Marshals value from marshalled type into variable v + */ + static bool get( Eldbus_Message_Iter* iter, double& v ) + { + return eldbus_message_iter_get_and_next( iter, sig()[0], &v ); + } + /** + * @brief Marshals value from marshalled type into variable v + */ + static bool get( Eldbus_Message_Iter* iter, float& v2 ) + { + double v = 0; + auto r = eldbus_message_iter_get_and_next( iter, sig()[0], &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( Eldbus_Message_Iter* iter, float v ) + { + eldbus_message_iter_arguments_append( iter, sig().c_str(), v ); + } + /** + * @brief Marshals value from marshalled type into variable v + */ + static bool get( Eldbus_Message_Iter* iter, double& v ) + { + return eldbus_message_iter_get_and_next( iter, sig()[0], &v ); + } + /** + * @brief Marshals value from marshalled type into variable v + */ + static bool get( Eldbus_Message_Iter* iter, float& v2 ) + { + double v = 0; + auto r = eldbus_message_iter_get_and_next( iter, sig()[0], &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( Eldbus_Message_Iter* iter, bool v ) + { + eldbus_message_iter_arguments_append( iter, sig().c_str(), v ? 1 : 0 ); + } + /** + * @brief Marshals value from marshalled type into variable v + */ + static bool get( Eldbus_Message_Iter* iter, bool& v ) + { + unsigned char q; + auto z = eldbus_message_iter_get_and_next( iter, sig()[0], &q ); + v = q != 0; + return z; + } +}; + +/** + * @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( Eldbus_Message_Iter* iter, const std::string& v ) + { + eldbus_message_iter_arguments_append( iter, sig().c_str(), v.c_str() ); + } + /** + * @brief Marshals value v as marshalled type into message + */ + static void set( Eldbus_Message_Iter* iter, const char* v ) + { + eldbus_message_iter_arguments_append( iter, sig().c_str(), v ); + } + /** + * @brief Marshals value from marshalled type into variable v + */ + static bool get( Eldbus_Message_Iter* iter, std::string& v ) + { + 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; + } + v = q; + return true; + } +}; +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( Eldbus_Message_Iter* iter, const std::string& v ) + { + eldbus_message_iter_arguments_append( iter, sig().c_str(), v.c_str() ); + } + /** + * @brief Marshals value v as marshalled type into message + */ + static void set( Eldbus_Message_Iter* iter, const ObjectPath& v ) + { + eldbus_message_iter_arguments_append( iter, sig().c_str(), v.value.c_str() ); + } + /** + * @brief Marshals value v as marshalled type into message + */ + static void set( Eldbus_Message_Iter* iter, const char* v ) + { + eldbus_message_iter_arguments_append( iter, sig().c_str(), v ); + } + /** + * @brief Marshals value from marshalled type into variable v + */ + static bool get( Eldbus_Message_Iter* iter, ObjectPath& v ) + { + const char* q; + if( !eldbus_message_iter_get_and_next( iter, 'o', &q ) ) + return false; + v.value = q; + return true; + } + /** + * @brief Marshals value from marshalled type into variable v + */ + static bool get( Eldbus_Message_Iter* iter, std::string& v ) + { + const char* q; + if( !eldbus_message_iter_get_and_next( iter, 'o', &q ) ) + return false; + v = q; + 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( Eldbus_Message_Iter* iter, const std::string& v ) + { + eldbus_message_iter_arguments_append( iter, sig().c_str(), v.c_str() ); + } + /** + * @brief Marshals value v as marshalled type into message + */ + static void set( Eldbus_Message_Iter* iter, const char* v ) + { + eldbus_message_iter_arguments_append( iter, sig().c_str(), v ); + } +}; + +/** + * @brief Signature class for marshalling (const) char[N] type. + * + * Both (const) char[N] and std::string types are accepted as value to send. + * You can't use (const) char[N] variable type to receive value. + */ +template < size_t N > +struct signature< char[N] > +{ + /** + * @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( Eldbus_Message_Iter* iter, const std::string& v ) + { + eldbus_message_iter_arguments_append( iter, sig().c_str(), v.c_str() ); + } + /** + * @brief Marshals value v as marshalled type into message + */ + static void set( Eldbus_Message_Iter* iter, const char* v ) + { + eldbus_message_iter_arguments_append( iter, sig().c_str(), 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( Eldbus_Message_Iter* 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( Eldbus_Message_Iter* 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( Eldbus_Message_Iter* iter, const std::tuple< ARGS... >& args ) + { + } + /** + * @brief Marshals value from marshalled type into variable v + */ + static bool get( Eldbus_Message_Iter* 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( Eldbus_Message_Iter* iter, const std::tuple< ARGS... >& args ) + { + auto entry = eldbus_message_iter_container_new( iter, 'r', NULL ); + signature_tuple_helper< 0, sizeof...( ARGS ), ARGS... >::set( entry, args ); + eldbus_message_iter_container_close( iter, entry ); + } + /** + * @brief Marshals value from marshalled type into variable v + */ + static bool get( Eldbus_Message_Iter* iter, std::tuple< ARGS... >& args ) + { + Eldbus_Message_Iter* entry; + if( !eldbus_message_iter_get_and_next( iter, 'r', &entry ) ) + return false; + auto z = signature_tuple_helper< 0, sizeof...( ARGS ), ARGS... >::get( entry, args ); + return z; + } +}; +/** + * @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( Eldbus_Message_Iter* 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( Eldbus_Message_Iter* 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( Eldbus_Message_Iter* iter, const ValueOrError< void >& args ) + { + } + /** + * @brief Marshals value from marshalled type into variable v + */ + static bool get( Eldbus_Message_Iter* 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( Eldbus_Message_Iter* iter, const ValueOrError<>& args ) + { + } + /** + * @brief Marshals value from marshalled type into variable v + */ + static bool get( Eldbus_Message_Iter* 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( Eldbus_Message_Iter* iter, const std::pair< A, B >& ab, bool dictionary = false ) + { + auto entry = eldbus_message_iter_container_new( iter, dictionary ? 'e' : 'r', NULL ); + signature_tuple_helper< 0, 2, A, B >::set( entry, ab ); + eldbus_message_iter_container_close( iter, entry ); + } + /** + * @brief Marshals value from marshalled type into variable v + */ + static bool get( Eldbus_Message_Iter* iter, std::pair< A, B >& ab ) + { + char sg = 'r'; + char* t = eldbus_message_iter_signature_get( iter ); + if( t && t[0] == '{' ) + sg = '{'; + free( t ); + + Eldbus_Message_Iter* entry; + if( !eldbus_message_iter_get_and_next( iter, sg, &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( Eldbus_Message_Iter* iter, const std::vector< A >& v ) + { + auto lst = eldbus_message_iter_container_new( iter, 'a', signature< A >::sig().c_str() ); + assert( lst ); + for( auto& a : v ) + { + signature< A >::set( lst, a ); + } + eldbus_message_iter_container_close( iter, lst ); + } + /** + * @brief Marshals value from marshalled type into variable v + */ + static bool get( Eldbus_Message_Iter* iter, std::vector< A >& v ) + { + Eldbus_Message_Iter* s; + v.clear(); + if( !eldbus_message_iter_get_and_next( iter, 'a', &s ) ) + return false; + 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( Eldbus_Message_Iter* iter, const std::array< A, N >& v ) + { + auto lst = eldbus_message_iter_container_new( iter, 'a', signature< A >::sig().c_str() ); + assert( lst ); + for( auto& a : v ) + { + signature< A >::set( lst, a ); + } + eldbus_message_iter_container_close( iter, lst ); + } + /** + * @brief Marshals value from marshalled type into variable v + */ + static bool get( Eldbus_Message_Iter* iter, std::array< A, N >& v ) + { + Eldbus_Message_Iter* s; + if( !eldbus_message_iter_get_and_next( iter, 'a', &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( Eldbus_Message_Iter* iter, const EldbusVariant< A >& v ) + { + set( iter, v.value ); + } + /** + * @brief Marshals value v as marshalled type into message + */ + static void set( Eldbus_Message_Iter* iter, const A& v ) + { + auto var = eldbus_message_iter_container_new( iter, 'v', signature< A >::sig().c_str() ); + signature< A >::set( var, v ); + eldbus_message_iter_container_close( iter, var ); + } + /** + * @brief Marshals value from marshalled type into variable v + */ + static bool get( Eldbus_Message_Iter* iter, EldbusVariant< A >& v ) + { + Eldbus_Message_Iter* s; + if( !eldbus_message_iter_get_and_next( iter, 'v', &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( Eldbus_Message_Iter* iter, const std::unordered_map< A, B >& v ) + { + auto sig = "{" + signature_tuple_helper< 0, 2, A, B >::sig() + "}"; + auto lst = eldbus_message_iter_container_new( iter, 'a', sig.c_str() ); + assert( lst ); + for( auto& a : v ) + { + signature< std::pair< A, B > >::set( lst, a, true ); + } + eldbus_message_iter_container_close( iter, lst ); + } + /** + * @brief Marshals value from marshalled type into variable v + */ + static bool get( Eldbus_Message_Iter* iter, std::unordered_map< A, B >& v ) + { + Eldbus_Message_Iter* s; + v.clear(); + if( !eldbus_message_iter_get_and_next( iter, 'a', &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( Eldbus_Message_Iter* iter, const std::map< A, B >& v ) + { + auto sig = "{" + signature_tuple_helper< 0, 2, A, B >::sig() + "}"; + auto lst = eldbus_message_iter_container_new( iter, 'a', sig.c_str() ); + assert( lst ); + for( auto& a : v ) + { + signature< std::pair< A, B > >::set( lst, a, true ); + } + eldbus_message_iter_container_close( iter, lst ); + } + /** + * @brief Marshals value from marshalled type into variable v + */ + static bool get( Eldbus_Message_Iter* iter, std::map< A, B >& v ) + { + Eldbus_Message_Iter* s; + if( !eldbus_message_iter_get_and_next( iter, 'a', &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( Eldbus_Message_Iter* iter, const A& v ) + { + signature< A >::set( iter, v ); + } + /** + * @brief Marshals value from marshalled type into variable v + */ + static void get( Eldbus_Message_Iter* 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( Eldbus_Message_Iter* iter, const A& v ) + { + signature< A >::set( iter, v ); + } + /** + * @brief Marshals value from marshalled type into variable v + */ + static void get( Eldbus_Message_Iter* 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( Eldbus_Message_Iter* iter, const A& v ) + { + signature< A >::set( iter, v ); + } + /** + * @brief Marshals value from marshalled type into variable v + */ + static void get( Eldbus_Message_Iter* iter, A& v ) + { + signature< A >::get( iter, v ); + } +}; +// /** +// * @brief Signature helper class for marshalling AT-SPI Accessible pointer values +// * +// * In AT-SPI specification those values are mandated to be marshalled as struct (so) +// * where o is object (exactly as string, but with different ascii +// * character code. +// */ +// template <> struct signature> { +// using subtype = std::pair; + +// /** +// * @brief Returns name of type marshalled, for informative purposes +// */ +// static std::string name() +// { +// return "AtspiAccessiblePtr"; +// } +// /** +// * @brief Returns DBUS' signature of type marshalled +// */ +// static std::string sig() +// { +// return "(so)"; +// } +// /** +// * @brief Marshals value v as marshalled type into message +// */ +// static void set(Eldbus_Message_Iter *iter, const std::shared_ptr &v) +// { +// const auto prefixPath = "/org/a11y/atspi/accessible/"; +// const auto nullPath = "/org/a11y/atspi/null"; + +// if (v) { +// auto bus = atspi_accessible_get_bus_name(v.get(), NULL); +// auto path = atspi_accessible_get_path(v.get(), NULL); +// signature::set(iter, { bus, std::string{prefixPath} + path }); +// g_free(path); +// g_free(bus); +// } else { +// signature::set(iter, { {}, std::string{nullPath} }); +// } +// } +// /** +// * @brief Marshals value from marshalled type into variable v +// */ +// static bool get(Eldbus_Message_Iter *iter, std::shared_ptr &v); +// }; +/// \cond +struct CallId +{ + friend class ::DBus::DBusServer; + friend class ::DBus::DBusClient; + friend class ::DBus::DBusInterfaceDescription; + static std::atomic< unsigned int > LastId; + unsigned int id = ++LastId; +}; +template < typename ValueType > +ValueType unpackValues( CallId callId, const Eldbus_Message* msg ) +{ + auto iter = eldbus_message_iter_get( msg ); + 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 '" + + eldbus_message_signature_get( msg ) + "', expected '" + signature< ValueType >::sig() + "'"}; + } + } + else + { + r = Error{"call " + std::to_string( callId.id ) + ": failed to get iterator"}; + } + return r; +} +inline void packValues_helper( Eldbus_Message_Iter* iter ) {} +template < typename A, typename... ARGS > +void packValues_helper( Eldbus_Message_Iter* 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, Eldbus_Message* msg, ARGS&&... r ) +{ + auto iter = eldbus_message_iter_get( msg ); + 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 EldbusProxyBase +{ + EldbusProxyBase() + { + eldbus_init(); + } + ~EldbusProxyBase() + { + eldbus_shutdown(); + } +}; + +constexpr static int ELDBUS_CALL_TIMEOUT = 1000; + +struct ConnectionState +{ + std::shared_ptr< DBus::EldbusConnection > connection; + EldbusObjectCallbackHandle object; + EldbusProxyHandle proxy; + EldbusProxyHandle propertiesProxy; +}; +using CallAsyncDataType = std::tuple< CallId, std::function< void( const Eldbus_Message* ) > >; + +static void callAsyncCb( void* data, const Eldbus_Message* msg, Eldbus_Pending* pending ) +{ + auto d = static_cast< CallAsyncDataType* >( data ); + DBUS_DEBUG( "call %d: got reply", std::get< 0 >( *d ).id ); + std::get< 1 > ( *d )( msg ); +} +static void pendingFreeCb( void* data, const void* ) +{ + auto d = static_cast< CallAsyncDataType* >( data ); + DBUS_DEBUG( "call %d: deleting", std::get< 0 >( *d ).id ); + delete d; +} +template < typename RETTYPE, typename... ARGS > +RETTYPE call( CallId callId, ConnectionState& connectionState, bool property, const std::string& funcName, const ARGS&... args ) +{ + 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() ); + EldbusMessageCallbackHandle msg{eldbus_proxy_method_call_new( proxy.get(), funcName.c_str() )}; + if( !msg ) + { + DBUS_DEBUG( "call %d: failed", callId.id ); + return Error{"failed to create message"}; + } + + detail::packValues( callId, msg.get(), args... ); + auto replyRawPtr = eldbus_proxy_send_and_block( proxy.get(), msg.release(), ELDBUS_CALL_TIMEOUT ); + EldbusMessageCallbackHandle reply{replyRawPtr}; + 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"}; + } + const char *errname, *errmsg; + if( eldbus_message_error_get( reply.get(), &errname, &errmsg ) ) + { + DBUS_DEBUG( "call %d: %s: %s", callId.id, errname, errmsg ); + return Error{std::string( errname ) + ": " + errmsg}; + } + DBUS_DEBUG( "call %d: got reply with signature '%s'", callId.id, eldbus_message_signature_get( reply.get() ) ); + return detail::unpackValues< RETTYPE >( callId, reply.get() ); +} + +template < typename RETTYPE, typename... ARGS > +void asyncCall( CallId callId, ConnectionState connectionState, + bool property, const std::string& funcName, + std::function< void( RETTYPE ) > callback, const ARGS&... args ) +{ + auto proxy = property ? connectionState.propertiesProxy : connectionState.proxy; + if( !proxy ) + { + DBUS_DEBUG( "call %d: not initialized", callId.id ); + callback( Error{"not initialized"} ); + return; + } + + EldbusMessageCallbackHandle msg{eldbus_proxy_method_call_new( proxy.get(), funcName.c_str() )}; + if( !msg ) + { + DBUS_DEBUG( "call %d: failed", callId.id ); + callback( Error{"failed to create message"} ); + return; + } + + auto cbData = new CallAsyncDataType{callId, [callback, callId, proxy]( const Eldbus_Message* 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 + { + const char *errname, *errmsg; + if( eldbus_message_error_get( reply, &errname, &errmsg ) ) + { + DBUS_DEBUG( "call %d: %s: %s", callId.id, errname, errmsg ); + callback( Error{std::string( errname ) + ": " + errmsg} ); + } + else + { + DBUS_DEBUG( "call %d: got reply with signature '%s'", callId.id, eldbus_message_signature_get( reply ) ); + callback( detail::unpackValues< RETTYPE >( callId, reply ) ); + } + } + }}; + detail::packValues( callId, msg.get(), args... ); + auto pending = eldbus_proxy_send( proxy.get(), msg.release(), callAsyncCb, cbData, ELDBUS_CALL_TIMEOUT ); + if( pending ) + { + eldbus_pending_free_cb_add( pending, pendingFreeCb, cbData ); + 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() ); +} + +class StringStorage +{ + struct char_ptr_deleter + { + void operator()( char* p ) + { + free( p ); + } + }; + std::vector< std::unique_ptr< char, char_ptr_deleter > > storage; + +public: + 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() ); + } +}; +template < typename A, typename... ARGS > +struct EldbusArgGenerator_Helper +{ + static void add( std::vector< Eldbus_Arg_Info >& r, StringStorage& strings ) + { + auto s = r.size(); + auto sig = signature< A >::sig(); + assert( !sig.empty() ); + auto name = "p" + std::to_string( s + 1 ); + r.push_back( Eldbus_Arg_Info{strings.add( sig ), strings.add( name )} ); + EldbusArgGenerator_Helper< ARGS... >::add( r, strings ); + } +}; +template <> +struct EldbusArgGenerator_Helper< void > +{ + static void add( std::vector< Eldbus_Arg_Info >&, StringStorage& ) + { + } +}; +template <> +struct EldbusArgGenerator_Helper< ValueOrError< void >, void > +{ + static void add( std::vector< Eldbus_Arg_Info >&, StringStorage& ) + { + } +}; +template <> +struct EldbusArgGenerator_Helper< ValueOrError<>, void > +{ + static void add( std::vector< Eldbus_Arg_Info >&, StringStorage& ) + { + } +}; +template < typename... ARGS > +struct EldbusArgGenerator_Helper< std::tuple< ARGS... > > +{ + static void add( std::vector< Eldbus_Arg_Info >& r, StringStorage& strings ) + { + EldbusArgGenerator_Helper< ARGS..., void >::add( r, strings ); + } +}; +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< Eldbus_Arg_Info > get( StringStorage& strings ) + { + std::vector< Eldbus_Arg_Info > tmp; + EldbusArgGenerator_Helper< ARGS..., void >::add( tmp, strings ); + tmp.push_back( {nullptr, nullptr} ); + 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< Eldbus_Arg_Info > get( StringStorage& strings ) + { + std::vector< Eldbus_Arg_Info > tmp; + EldbusArgGenerator_Helper< RetType, void >::add( tmp, strings ); + tmp.push_back( {nullptr, nullptr} ); + return tmp; + } +}; +template < typename T > +struct EldbusArgGenerator_ReturnType; +template < typename... ARGS > +struct EldbusArgGenerator_ReturnType< void( ARGS... ) > +{ + static std::string name() + { + return ""; + } + static std::vector< Eldbus_Arg_Info > get( StringStorage& strings ) + { + std::vector< Eldbus_Arg_Info > tmp; + tmp.push_back( {nullptr, nullptr} ); + return tmp; + } +}; +/// \endcond +} + +/** + * @brief Enumeration determining, which DBUS session user wants to connect to. + */ +enum class ConnectionType +{ + SYSTEM, + SESSION +}; + +/** + * @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 : private detail::EldbusProxyBase +{ + /// \cond + struct ConnectionInfo + { + std::string interfaceName, busName, pathName; + void emit( const char* member, DBusActionType type ) + { + detail::emitNotification( busName.c_str(), pathName.c_str(), interfaceName.c_str(), member, type ); + } + }; + /// \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 std::shared_ptr< DBus::EldbusConnection >& 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; + 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 ); + auto connectionState = this->connectionState; + 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; + 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() + { + connectionInfo->emit( propName.c_str(), DBusActionType::GETTER_CALL ); + 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 ) + { + connectionInfo->emit( propName.c_str(), DBusActionType::GETTER_CALL ); + detail::CallId callId; + detail::displayDebugCallInfoProperty( callId, "Get", info, connectionInfo->interfaceName, propName ); + auto connectionState = this->connectionState; + 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 ) + { + connectionInfo->emit( propName.c_str(), DBusActionType::SETTER_CALL ); + 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 ) + { + connectionInfo->emit( propName.c_str(), DBusActionType::SETTER_CALL ); + 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 cS = this->connectionState; + auto cI = this->connectionInfo; + auto callbackLambdaPtr = new std::function< void( Eldbus_Proxy_Event_Property_Changed* ) >; + *callbackLambdaPtr = [callId, cS, callback, propertyName, cI]( Eldbus_Proxy_Event_Property_Changed* ev ) { + const char* ifc = eldbus_proxy_interface_get( ev->proxy ); + DBUS_DEBUG( "call %d: property changed iname = %s pname = %s (name %s iface %s)", + callId.id, cI->interfaceName.c_str(), propertyName.c_str(), ev->name, ifc ); + V val = 0; + if( ev->name && ev->name == propertyName && ifc && cI->interfaceName == ifc ) + { + if( !eina_value_get( ev->value, &val ) ) + { + DBUS_DEBUG( "unable to get property's value" ); + return; + } + DBUS_DEBUG( ". %d", val ); + callback( val ); + DBUS_DEBUG( "." ); + } + DBUS_DEBUG( "." ); + }; + auto p = connectionState.proxy.get(); + eldbus_proxy_event_callback_add( p, ELDBUS_PROXY_EVENT_PROPERTY_CHANGED, + listenerEventChangedCallback, callbackLambdaPtr ); + destructors.add( [=]() { + eldbus_proxy_event_callback_del( p, ELDBUS_PROXY_EVENT_PROPERTY_CHANGED, + listenerEventChangedCallback, callbackLambdaPtr ); + delete callbackLambdaPtr; + } ); + } + /** + * @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 ); + auto cS = this->connectionState; + auto cI = this->connectionInfo; + auto callbackLambda = [callId, cS, callback, signalName, cI]( const Eldbus_Message* msg ) -> void { + const char *errname, *aux; + if( eldbus_message_error_get( msg, &errname, &aux ) ) + { + DBUS_DEBUG( "call %d: Eldbus error: %s %s", callId.id, errname, aux ); + return; + } + cI->emit( signalName.c_str(), DBusActionType::SIGNAL_RECEIVED ); + DBUS_DEBUG( "call %d: received signal with signature '%s'", callId.id, eldbus_message_signature_get( msg ) ); + 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" ); + assert( 0 ); + } + }; + auto tmp = new std::function< void( const Eldbus_Message* msg ) >{std::move( callbackLambda )}; + auto handler = eldbus_proxy_signal_handler_add( connectionState.proxy.get(), signalName.c_str(), listenerCallback, tmp ); + destructors.add( [=]() { + eldbus_signal_handler_del( handler ); + delete tmp; + } ); + } + +private: + /// \cond + detail::ConnectionState connectionState; + detail::CallOnDestructionList destructors; + std::string info; + std::shared_ptr< ConnectionInfo > connectionInfo; + void emitNotification( DBusActionType type ); + + static void listenerCallback( void* data, const Eldbus_Message* msg ) + { + auto p = static_cast< std::function< void( const Eldbus_Message* msg ) >* >( data ); + ( *p )( msg ); + } + 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 ) ); + } + /// \endcond +}; + +/** + * @brief Helper class describing DBUS's server interface + * + */ +class DBusInterfaceDescription +{ + friend class DBusServer; + +public: + /// \cond + struct MethodInfo + { + detail::CallId id; + std::string memberName; + std::vector< Eldbus_Arg_Info > in, out; + std::function< Eldbus_Message*( const Eldbus_Message* msg ) > callback; + }; + struct SignalInfo + { + detail::CallId id; + std::string memberName; + std::vector< Eldbus_Arg_Info > args; + unsigned int uniqueId; + }; + struct PropertyInfo + { + detail::CallId setterId, getterId; + std::string memberName, typeSignature; + std::function< ValueOrError< void >( const Eldbus_Message*src, Eldbus_Message_Iter*dst ) > getCallback, setCallback; + }; + class SignalId + { + friend class ::DBus::DBusServer; + friend class ::DBus::DBusClient; + friend class ::DBus::DBusInterfaceDescription; + detail::CallId id; + + SignalId( detail::CallId id ) : id( id ) {} + + public: + SignalId() = default; + }; + /// \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( strings ); + z.out = detail::EldbusArgGenerator_ReturnType< T >::get( strings ); + 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, asynchronous 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. callback will called with reply callback functor. + * Reply callback functor must be called with reply value, when it's ready. + * It's safe to ignore calling reply callback, but some resources might be kept + * as long as either reply callback exists or reply timeout hasn't yet been met. + * + * 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 addAsyncMethod( const std::string& memberName, typename detail::dbus_interface_traits< T >::AsyncCB callback ); + + /** + * @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 Eldbus_Message* src, Eldbus_Message_Iter* dst ) -> ValueOrError< void > { + detail::emitNotification( eldbus_message_sender_get( src ), + eldbus_message_path_get( src ), interfaceName.c_str(), memberName.c_str(), DBusActionType::GETTER_RESPONSE ); + 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 Success{}; + } + DBUS_DEBUG( "call %d: failed: %s", getterId.id, v.getError().message.c_str() ); + return v.getError(); + } + catch( std::exception& e ) + { + return Error{std::string( "unhandled exception (" ) + e.what() + ")"}; + } + catch( ... ) + { + return Error{"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 Eldbus_Message* src, Eldbus_Message_Iter* src_iter ) -> ValueOrError< void > { + detail::emitNotification( eldbus_message_sender_get( src ), + eldbus_message_path_get( src ), interfaceName.c_str(), memberName.c_str(), DBusActionType::SETTER_RESPONSE ); + std::tuple< T > value; + auto src_signature = eldbus_message_iter_signature_get( 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 Success{}; + } + DBUS_DEBUG( "call %d: failed: %s", setterId.id, v.getError().message.c_str() ); + free( src_signature ); + return v.getError(); + } + catch( std::exception& e ) + { + return Error{std::string( "unhandled exception (" ) + e.what() + ")"}; + } + catch( ... ) + { + return Error{"unhandled exception"}; + } + } + DBUS_DEBUG( "call %d: failed to unpack values, got signature '%s', expected '%s'", setterId.id, + src_signature, detail::signature< T >::sig().c_str() ); + return Error{"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( 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; + detail::StringStorage strings; + + template < typename T > + std::function< Eldbus_Message*( const Eldbus_Message* msg ) > construct( detail::CallId callId, + typename detail::dbus_interface_traits< T >::SyncCB callback ) + { + using VEArgs = typename detail::dbus_interface_traits< T >::VEArgs; + return [=]( const Eldbus_Message* msg ) -> Eldbus_Message* { + DBUS_DEBUG( "call %d: entering", callId.id ); + detail::emitNotification( eldbus_message_sender_get( msg ), + eldbus_message_path_get( msg ), interfaceName.c_str(), eldbus_message_member_get( msg ), DBusActionType::METHOD_RESPONSE ); + Eldbus_Message* ret = nullptr; + 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 = eldbus_message_method_return_new( msg ); + packValues( callId, ret, v ); + } + else + { + DBUS_DEBUG( "call %d: failed: %s", callId.id, v.getError().message.c_str() ); + ret = eldbus_message_error_new( msg, "org.freedesktop.DBus.Error.Failed", v.getError().message.c_str() ); + } + } + catch( std::exception& e ) + { + auto txt = std::string( "unhandled exception (" ) + e.what() + ")"; + DBUS_DEBUG( "call %d: failed: %s", callId.id, txt.c_str() ); + ret = eldbus_message_error_new( msg, "org.freedesktop.DBus.Error.Failed", txt.c_str() ); + } + catch( ... ) + { + DBUS_DEBUG( "call %d: failed: %s", callId.id, "unhandled exception" ); + ret = eldbus_message_error_new( msg, "org.freedesktop.DBus.Error.Failed", "unhandled exception" ); + } + } + else + { + std::ostringstream err; + err << "expected signature '" << detail::signature< VEArgs >::sig() << "', got '" << eldbus_message_signature_get( msg ) << "'"; + auto str = err.str(); + DBUS_DEBUG( "call %d: failed: %s", callId.id, str.c_str() ); + ret = eldbus_message_error_new( msg, "org.freedesktop.DBus.Error.InvalidArgs", str.c_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 : private detail::EldbusProxyBase +{ +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 std::shared_ptr< DBus::EldbusConnection >& 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 + */ + std::shared_ptr< DBus::EldbusConnection > getConnection(); + + /** + * @brief Emits signal + * + * You must pass identifier of the signal, got as result of calling DBusInterfaceDescription::addSignal. + * Types of the arguments must match EXACTLY types used to call addSignal. + * + * @param signal identifier of the signal + * @param args values to emit + */ + template < typename... ARGS > + void emit( DBusInterfaceDescription::SignalId signal, const ARGS&... args ) + { + auto it = signalData.find( signal.id.id ); + if( it != signalData.end() ) + { + auto msg = eldbus_service_signal_new( it->second.first, it->second.second ); + detail::packValues( signal.id, msg, args... ); + eldbus_service_signal_send( it->second.first, msg ); + } + else + { + DBUS_DEBUG( "signal %d not found", signal.id.id ); + } + } + + /** + * @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 = eldbus_message_signal_new( path.c_str(), interfaceName.c_str(), signalName.c_str() ); + detail::CallId id; + detail::packValues( id, msg, args... ); + eldbus_connection_send( connection->get(), msg, nullptr, nullptr, -1 ); + } + /** + * @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(); + + /** + * @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 std::shared_ptr< DBus::EldbusConnection > getCurrentConnection(); + +private: + /// \cond + std::shared_ptr< DBus::EldbusConnection > connection; + detail::CallOnDestructionList destructors; + std::unordered_map< unsigned int, std::pair< const Eldbus_Service_Interface*, unsigned int > > signalData; + /// \endcond +}; + +template < typename T > +void DBusInterfaceDescription::addAsyncMethod( const std::string& memberName, typename detail::dbus_interface_traits< T >::AsyncCB callback ) +{ + detail::CallId callId; + MethodInfo mi; + methods.push_back( std::move( mi ) ); + auto& z = methods.back(); + z.in = detail::EldbusArgGenerator_Args< T >::get( strings ); + z.out = detail::EldbusArgGenerator_ReturnType< T >::get( strings ); + 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() ); + using VEArgs = typename detail::dbus_interface_traits< T >::VEArgs; + z.callback = [=]( const Eldbus_Message* msg ) -> Eldbus_Message* { + detail::emitNotification( eldbus_message_sender_get( msg ), + eldbus_message_path_get( msg ), interfaceName.c_str(), memberName.c_str(), DBusActionType::METHOD_RESPONSE ); + struct CallState + { + bool replyRunning = true; + Eldbus_Message* reply = nullptr; + EldbusMessageCallbackHandle message; + }; + auto callState = std::make_shared< CallState >(); + callState->message.reset( eldbus_message_ref( const_cast< Eldbus_Message* >( msg ) ) ); + auto connection = DBusServer::getCurrentConnection(); + auto retCallback = [=]( typename detail::dbus_interface_traits< T >::Ret v ) { + if( v ) + { + callState->reply = eldbus_message_method_return_new( callState->message.get() ); + packValues( callId, callState->reply, v ); + } + else + { + DBUS_DEBUG( "call %d: failed: %s", callId.id, v.getError().message.c_str() ); + callState->reply = eldbus_message_error_new( callState->message.get(), "org.freedesktop.DBus.Error.Failed", v.getError().message.c_str() ); + } + if( !callState->replyRunning ) + { + eldbus_connection_send( connection->get(), callState->reply, NULL, NULL, -1 ); + } + }; + Eldbus_Message* ret = nullptr; + auto args = detail::unpackValues< VEArgs >( callId, msg ); + if( args ) + { + auto error = [&]( const std::string& txt ) { + if( !callState->reply ) + { + DBUS_DEBUG( "call %d: failed: %s", callId.id, txt.c_str() ); + callState->reply = eldbus_message_error_new( callState->message.get(), "org.freedesktop.DBus.Error.Failed", txt.c_str() ); + } + }; + try + { + detail::apply( callback, std::move( retCallback ), std::move( args.getValues() ) ); + } + catch( std::exception& e ) + { + error( std::string( "unhandled exception (" ) + e.what() + ")" ); + } + catch( ... ) + { + error( "unhandled exception" ); + } + + callState->replyRunning = false; + ret = callState->reply; + } + else + { + std::ostringstream err; + err << "expected signature '" << detail::signature< VEArgs >::sig() << "', got '" << eldbus_message_signature_get( msg ) << "'"; + auto str = err.str(); + ret = eldbus_message_error_new( msg, "org.freedesktop.DBus.Error.InvalidArgs", str.c_str() ); + } + return ret; + }; + + z.id = callId; +} + +/// \cond +std::shared_ptr< EldbusConnection > getDBusConnectionByType( ConnectionType tp ); +std::shared_ptr< EldbusConnection > getDBusConnectionByName( const std::string& name ); +std::string getConnectionName( const std::shared_ptr< EldbusConnection >& ); +/// \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 diff --git a/dali/dali-bridge/src/Optional.hpp b/dali/dali-bridge/src/Optional.hpp new file mode 100755 index 0000000..d4401fe --- /dev/null +++ b/dali/dali-bridge/src/Optional.hpp @@ -0,0 +1,281 @@ +/* + * Copyright 2017 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. + */ + +#ifndef OPTIONAL_HPP +#define OPTIONAL_HPP + +/** + * Minimalistic implementation of standard library std::optional (c++17) for c++11 compiler. + * + * After project conversion to C++17 standard, this template class will be deleted and + * Optional will point to std::optional. + * + * Allowed operations (note, to make code simplier, than original, value class must have accessible copy and move constructor): + * - constructing empty (valueless) object + * - copying Optional (with and without value) + * - moving Optional (with and without value) + * - querying, if Optional has value (via explicit operator bool), for example: + * Optional v = ...; + * if (v) ... // if v has value, then do something + * - accessing value (via operator *), for example: + * Optional v = ...; + * auto z = *v; // z now has the same int, as v (copied) + * auto &y = *v; // y now has REFERENCE to int inside v, so modifying y modifies v + */ + +#include +#include +#include +#include + +template < typename A > +class Optional +{ + /// \cond + union + { + A place; + }; + bool hasValue = false; + /// \endcond +public: + /** + * @brief Empty constructor. + * Creates empty Optional object, which will be false in boolean context. + * So: + * \code{.cpp} + * Optional o; + * if (o) printf("1\n"); + * \endcode + * won't print 1. + */ + Optional() {} + + /** + * @brief Single element constructor, when implicit convertion can be applied. + * + * This constructor will be selected, when type of given argument (U) is + * implicitly convertable to expected type A. In other words following + * code must be valid: + * \code{.cpp} + * A foo() { + * return U(); + * } + * \endcode + * + * @param a value held by Optional object will be initialized from a. + */ + template < typename U = A, typename std::enable_if< + std::is_convertible< U&&, A >::value && + std::is_constructible< A, U&& >::value && + !std::is_same< typename std::decay< U >::type, Optional< A > >::value, + int* >::type = nullptr > + constexpr Optional( U&& a ) + : place( std::forward< U >( a ) ), hasValue( true ) + { + } + + /** + * @brief Single element constructor, when only explicit convertion can be applied. + * + * This constructor will be selected, when type of given argument (U) is + * convertable to expected type A. + * + * @param a value held by Optional object will be initialized from a. + */ + template < typename U = A, typename std::enable_if< + !std::is_convertible< U&&, A >::value && + std::is_constructible< A, U&& >::value && + !std::is_same< typename std::decay< U >::type, Optional< A > >::value, + int* >::type = nullptr > + explicit constexpr Optional( U&& a ) + : place( std::forward< U >( a ) ), hasValue( true ) + { + } + + /** + * @brief Copy constructor. + * + * @param v Optional value to copy from. Will cause to copy data held by object v, + * if v has data. + */ + Optional( const Optional& v ) : hasValue( v.hasValue ) + { + if( hasValue ) + new( &place ) A( v.place ); + } + + /** + * @brief Move constructor. + * + * @param v Optional value to copy from. Will move data help by v, if any. + * After construction \code{.cpp} bool(v) \endcode will be false. + */ + Optional( Optional&& v ) : hasValue( v.hasValue ) + { + if( hasValue ) + new( &place ) A( std::move( v.place ) ); + } + + /** + * @brief Destructor. + */ + ~Optional() + { + if( hasValue ) + { + place.~A(); + } + } + + /** + * @brief Explicit bool operator + * + * Will return true if and only if object is helding data. + */ + explicit operator bool() const + { + return hasValue; + } + + /** + * @brief Accessor (*) operator + * + * Will return modifiable reference to held object. Will assert, if not object is held. + */ + A& operator*() + { + assert( hasValue ); + return place; + } + + /** + * @brief Accessor (*) const operator + * + * Will return const reference to held object. Will assert, if not object is held. + */ + const A& operator*() const + { + assert( hasValue ); + return place; + } + + /** + * @brief Accessor (->) operator + * + * Will return pointer to held object allowing access to the value's members. + * Will assert, if not object is held. + */ + A* operator->() + { + assert( hasValue ); + return &place; + } + + /** + * @brief Accessor (->) operator + * + * Will return pointer to (const) held object allowing access to the value's members. + * Will assert, if not object is held. + */ + const A* operator->() const + { + assert( hasValue ); + return &place; + } + + /** + * @brief Assignment operator + * + * Will copy held value from v, if any. + * + * @param v Value to copy from + */ + Optional& operator=( const Optional& v ) + { + if( this != &v ) + { + if( hasValue != v.hasValue ) + { + if( v.hasValue ) + new( &place ) A( v.place ); + else + place.~A(); + hasValue = v.hasValue; + } + else if( hasValue ) + { + place = v.place; + } + } + return *this; + } + + /** + * @brief Assignment move operator + * + * Will move held value from v, if any. In all cases v won't held a value + * after assignment is done. + * + * @param v Value to copy from + */ + Optional& operator=( Optional&& v ) + { + if( this != &v ) + { + if( hasValue != v.hasValue ) + { + if( v.hasValue ) + new( &place ) A( std::move( v.place ) ); + else + place.~A(); + hasValue = v.hasValue; + } + else if( hasValue ) + { + place = std::move( v.place ); + } + } + return *this; + } + + /** + * @brief Assignment operator from value of type held. + * + * Will initialize held value from given parameter a. + * Type of the parameter must be the same (barring cv convertions), + * as the type of the value held. + * + * @param a Value to copy from + */ + template < class U, class = typename std::enable_if< + std::is_same< typename std::remove_reference< U >::type, A >::value && + std::is_constructible< A, U >::value && + std::is_assignable< A&, U >::value >::type > + Optional& operator=( U&& a ) + { + if( hasValue ) + place = std::forward< U >( a ); + else + { + hasValue = true; + new( &place ) A( std::forward< U >( a ) ); + } + return *this; + } +}; + +#endif diff --git a/dali/dali-bridge/src/dbusLocators.hpp b/dali/dali-bridge/src/dbusLocators.hpp new file mode 100644 index 0000000..9b34bbe --- /dev/null +++ b/dali/dali-bridge/src/dbusLocators.hpp @@ -0,0 +1,98 @@ +/* + * Copyright 2017 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. + */ + +#ifndef DBUS_LOCATORS_HPP +#define DBUS_LOCATORS_HPP + +namespace dbusLocators +{ +namespace callmgr +{ +static constexpr const char* BUS = "org.tizen.callmgr"; +static constexpr const char* OBJ_PATH = "/org/tizen/callmgr"; +static constexpr const char* INTERFACE = "org.tizen.callmgr"; +} + +namespace accessibilityEMod +{ +static constexpr const char* BUS = "org.enlightenment.wm-screen-reader"; +static constexpr const char* OBJ_PATH = "/org/tizen/GestureNavigation"; +static constexpr const char* INTERFACE = "org.tizen.GestureNavigation"; + +static constexpr const char* ACCESSORIES_SP_ENABLED = "AccessoriesSwitchProviderEnabled"; +static constexpr const char* KEY_DOWN_SIGNAL = "KeyDown"; +static constexpr const char* KEY_UP_SIGNAL = "KeyUp"; + +static constexpr const char* SCREEN_SP_ENABLED = "ScreenSwitchProviderEnabled"; +static constexpr const char* MOUSE_DOWN_SIGNAL = "MouseDown"; +static constexpr const char* MOUSE_UP_SIGNAL = "MouseUp"; + +static constexpr const char* BACK_BUTTON_INTERCEPTION_ENABLED = "BackButtonInterceptionEnabled"; +static constexpr const char* BACK_BUTTON_DOWN_SIGNAL = "BackButtonDown"; +static constexpr const char* BACK_BUTTON_UP_SIGNAL = "BackButtonUp"; +} + +namespace freeDesktop +{ +static constexpr const char* BUS = "org.freedesktop.DBus"; +static constexpr const char* OBJ_PATH = "/org/freedesktop/DBus"; +static constexpr const char* INTERFACE = "org.freedesktop.DBus"; +static constexpr const char* PROPERTIES_INTERFACE = "org.freedesktop.DBus.Properties"; +static constexpr const char* GET_CONNECTION_UNIX_PROCESS_ID = "GetConnectionUnixProcessID"; +static constexpr const char* SET = "Set"; +static constexpr const char* GET = "Get"; +} + +namespace windowManager +{ +static constexpr const char* BUS = "org.enlightenment.wm"; +static constexpr const char* OBJ_PATH = "/org/enlightenment/wm"; +static constexpr const char* INTERFACE = "org.enlightenment.wm.proc"; +static constexpr const char* GET_VISIBLE_WIN_INFO = "GetVisibleWinInfo"; +static constexpr const char* GET_FOCUS_PROC = "GetFocusProc"; +} + +namespace atspi +{ +static constexpr const char* BUS = "org.a11y.Bus"; +static constexpr const char* OBJ_PATH = "/org/a11y/bus"; +static constexpr const char* BUS_INTERFACE = "org.a11y.Bus"; +static constexpr const char* STATUS_INTERFACE = "org.a11y.Status"; + +static constexpr const char* GET_ADDRESS = "GetAddress"; +static constexpr const char* IS_ENABLED = "IsEnabled"; +static constexpr const char* GET_ATTRIBUTES = "GetAttributes"; +static constexpr const char* DO_ACTION_NAME = "DoActionName"; +static constexpr const char* PARENT = "Parent"; +static constexpr const char* GET_MATCHES = "GetMatches"; +static constexpr const char* GET_INDEX_IN_PARENT = "GetIndexInParent"; +static constexpr const char* SELECT_CHILD = "SelectChild"; +static constexpr const char* NAME = "Name"; +static constexpr const char* GET_ROLE = "GetRole"; +static constexpr const char* CHILD_COUNT = "ChildCount"; +static constexpr const char* GET_CHILD_AT_INDEX = "GetChildAtIndex"; +static constexpr const char* GET_STATE = "GetState"; +static constexpr const char* GET_RELATION_SET = "GetRelationSet"; +static constexpr const char* GET_EXTENTS = "GetExtents"; +static constexpr const char* CURRENT_VALUE = "CurrentValue"; +static constexpr const char* MAXIMUM_VALUE = "MaximumValue"; +static constexpr const char* MINIMUM_VALUE = "MinimumValue"; +static constexpr const char* GET_INTERFACES = "GetInterfaces"; +static constexpr const char* GET_NAVIGABLE_AT_POINT = "GetNavigableAtPoint"; +} +} + +#endif diff --git a/dali/devel-api/adaptor-framework/accessibility.h b/dali/devel-api/adaptor-framework/accessibility.h new file mode 100644 index 0000000..15881b0 --- /dev/null +++ b/dali/devel-api/adaptor-framework/accessibility.h @@ -0,0 +1,248 @@ +#ifndef ATSPI_BRIDGE_HPP +#define ATSPI_BRIDGE_HPP + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace Dali +{ +namespace Accessibility +{ +class Accessible; +class Text; +class Value; +class Component; +class Collection; +class Action; + +using ObjectsMapType = std::unordered_map< unsigned int, Accessible* >; + +struct DALI_IMPORT_API Bridge +{ + enum class Visibility + { + hidden, + thisThreadOnly, + allThreads + }; + enum class ForceUpResult + { + justStarted, + alreadyUp + }; + virtual ~Bridge() = default; + + virtual const std::string& GetBusName() const = 0; + virtual void SetApplicationChild( Accessible* ) = 0; + virtual void SetApplicationName( std::string ) = 0; + virtual Accessible* GetApplication() const = 0; + virtual Accessible* FindByPath( const std::string& ) const = 0; + virtual void ApplicationShown() = 0; + virtual void ApplicationHidden() = 0; + virtual void Initialize() = 0; + virtual ForceUpResult ForceUp() + { + if( data ) + return ForceUpResult::alreadyUp; + data = std::make_shared< Data >(); + data->bridge = this; + return ForceUpResult::justStarted; + } + virtual void ForceDown() + { + data = {}; + } + bool IsUp() const { return bool(data); } + + virtual void EmitStateChanged( Accessible* obj, State state, int val1, int val2 = 0 ) = 0; + virtual void Emit( Accessible* obj, WindowEvent we, unsigned int detail1 = 0 ) = 0; + virtual Consumed Emit( KeyEventType type, unsigned int keyCode, const std::string& keyName, unsigned int timeStamp, bool isText ) = 0; + void MakePublic( Visibility ); + + static Bridge* GetCurrentBridge(); + +protected: + struct Data + { + ObjectsMapType objects; + std::atomic< unsigned int > objectId; + std::string busName; + Accessible* root = nullptr; + Bridge* bridge = nullptr; + }; + std::shared_ptr< Data > data; + friend class Accessible; + + void RegisterOnBridge( Accessible* ); + void SetIsOnRootLevel( Accessible* ); +}; + +inline bool IsUp() +{ + return Bridge::GetCurrentBridge()->IsUp(); +} + +class DALI_IMPORT_API Accessible +{ +protected: + Accessible(); + + Accessible( const Accessible& ) = delete; + Accessible( Accessible&& ) = delete; + + Accessible& operator=( const Accessible& ) = delete; + Accessible& operator=( Accessible&& ) = delete; + +public: + virtual ~Accessible(); + + void EmitShowing( bool showing ); + void EmitVisible( bool visible ); + void EmitHighlighted( bool set ); + void Emit( WindowEvent we, unsigned int detail1 = 0 ); + + virtual std::string GetName() = 0; + virtual std::string GetDescription() = 0; + virtual Accessible* GetParent() = 0; + virtual size_t GetChildCount() = 0; + virtual std::vector< Accessible* > GetChildren(); + virtual Accessible* GetChildAtIndex( size_t index ) = 0; + virtual size_t GetIndexInParent() = 0; + virtual Role GetRole() = 0; + virtual std::string GetRoleName(); + virtual std::string GetLocalizedRoleName(); + virtual States GetStates() = 0; + virtual Attributes GetAttributes() = 0; + virtual bool IsProxy(); + virtual Address GetAddress(); + + std::vector< std::string > GetInterfaces(); + bool GetIsOnRootLevel() const { return isOnRootLevel; } + + static void RegisterControlAccessibilityGetter( std::function< Accessible*( Dali::Actor ) > ); + static Accessible* Get( Dali::Actor actor ); + +protected: + std::shared_ptr< Bridge::Data > GetBridgeData(); + +private: + friend class Bridge; + + std::weak_ptr< Bridge::Data > bridgeData; + ObjectsMapType::iterator it; + bool isOnRootLevel = false; +}; + +class DALI_IMPORT_API Action : public virtual Accessible +{ +public: + virtual std::string GetActionName( size_t index ) = 0; + virtual std::string GetLocalizedActionName( size_t index ) = 0; + virtual std::string GetActionDescription( size_t index ) = 0; + virtual std::string GetActionKeyBinding( size_t index ) = 0; + virtual size_t GetActionCount() = 0; + virtual bool DoAction( size_t index ) = 0; +}; + +class DALI_IMPORT_API Collection : public virtual Accessible +{ +public: +}; + +class DALI_IMPORT_API Component : public virtual Accessible +{ +public: + virtual Rectangle GetExtents( CoordType ctype ) = 0; + virtual ComponentLayer GetLayer() = 0; + virtual int GetMdiZOrder() = 0; + virtual bool GrabFocus() = 0; + virtual double GetAlpha() = 0; + virtual bool SetExtents( Rectangle rect, CoordType ctype ) = 0; + virtual bool GrabHighlight() = 0; + virtual bool ClearHighlight() = 0; + virtual int GetHighlightIndex() = 0; + virtual bool IsScrollable(); + virtual Component* GetAccessibleAtPoint( Point p, CoordType ctype ); + virtual bool Contains( Point p, CoordType ctype ); +}; + +class DALI_IMPORT_API Value : public virtual Accessible +{ +public: + virtual double GetMinimum() = 0; + virtual double GetCurrent() = 0; + virtual double GetMaximum() = 0; + virtual bool SetCurrent( double ) = 0; + virtual double GetMinimumIncrement() = 0; +}; + +class DALI_IMPORT_API Text : public virtual Accessible +{ +public: + virtual std::string GetText( size_t startOffset, size_t endOffset ) = 0; + virtual size_t GetCharacterCount() = 0; + virtual Range GetTextAtOffset( size_t offset, TextBoundary boundary ) = 0; + virtual Range GetSelection( size_t selectionNum ) = 0; + virtual bool RemoveSelection( size_t selectionNum ) = 0; + virtual bool SetSelection( size_t selectionNum, size_t startOffset, size_t endOffset ) = 0; +}; + +class DALI_IMPORT_API EditableText : public virtual Accessible +{ +public: + virtual bool CopyText( size_t startPosition, size_t endPosition ) = 0; + virtual bool CutText( size_t startPosition, size_t endPosition ) = 0; +}; + +/** + * @brief minimalistic, always empty Accessible object with settable address + * + * For those situations, where you want to return address in different bridge + * (embedding for example), but the object itself ain't planned to be used otherwise. + * This object has null parent, no children, empty name and so on + */ +class DALI_IMPORT_API EmptyAccessibleWithAddress : public virtual Accessible +{ +public: + EmptyAccessibleWithAddress() = default; + EmptyAccessibleWithAddress( Address address ) : address( std::move( address ) ) {} + + void SetAddress( Address address ) { this->address = std::move( address ); } + + std::string GetName() override { return ""; } + std::string GetDescription() override { return ""; } + Accessible* GetParent() override { return nullptr; } + size_t GetChildCount() override { return 0; } + std::vector< Accessible* > GetChildren() override { return {}; } + Accessible* GetChildAtIndex( size_t index ) override + { + throw AccessibleError{"out of bounds index (" + std::to_string( index ) + ") - no children"}; + } + size_t GetIndexInParent() override { return static_cast< size_t >( -1 ); } + Role GetRole() override { return {}; } + std::string GetRoleName() override; + States GetStates() override { return {}; } + Attributes GetAttributes() override { return {}; } + Address GetAddress() override + { + return address; + } + +private: + Address address; +}; + +std::shared_ptr< Bridge > CreateBridge(); +} +} + +#endif diff --git a/dali/devel-api/file.list b/dali/devel-api/file.list old mode 100644 new mode 100755 index 696291a..e62fef3 --- a/dali/devel-api/file.list +++ b/dali/devel-api/file.list @@ -67,8 +67,9 @@ devel_api_adaptor_framework_header_files = \ $(adaptor_devel_api_dir)/adaptor-framework/key-extension-plugin.h \ $(adaptor_devel_api_dir)/adaptor-framework/virtual-keyboard.h \ $(adaptor_devel_api_dir)/adaptor-framework/physical-keyboard.h \ - $(adaptor_devel_api_dir)/adaptor-framework/key-devel.h - + $(adaptor_devel_api_dir)/adaptor-framework/key-devel.h \ + $(adaptor_devel_api_dir)/adaptor-framework/accessibility.h + devel_api_text_abstraction_src_files = \ $(adaptor_devel_api_dir)/text-abstraction/bidirectional-support.cpp \ $(adaptor_devel_api_dir)/text-abstraction/font-client.cpp \ diff --git a/dali/internal/accessibility/accessibility-impl.cpp b/dali/internal/accessibility/accessibility-impl.cpp new file mode 100644 index 0000000..7e6fb7d --- /dev/null +++ b/dali/internal/accessibility/accessibility-impl.cpp @@ -0,0 +1,646 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace Dali::Accessibility; + +std::string EmptyAccessibleWithAddress::GetRoleName() +{ + return ""; +} + +std::string Accessible::GetLocalizedRoleName() +{ + return GetRoleName(); +} + +std::string Accessible::GetRoleName() +{ + switch( GetRole() ) + { + case Role::Invalid: + { + return "invalid"; + } + case Role::AcceleratorLabel: + { + return "accelerator label"; + } + case Role::Alert: + { + return "alert"; + } + case Role::Animation: + { + return "animation"; + } + case Role::Arrow: + { + return "arrow"; + } + case Role::Calendar: + { + return "calendar"; + } + case Role::Canvas: + { + return "canvas"; + } + case Role::CheckBox: + { + return "check box"; + } + case Role::CheckMenuItem: + { + return "check menu item"; + } + case Role::ColorChooser: + { + return "color chooser"; + } + case Role::ColumnHeader: + { + return "column header"; + } + case Role::ComboBox: + { + return "combo box"; + } + case Role::DateEditor: + { + return "date editor"; + } + case Role::DesktopIcon: + { + return "desktop icon"; + } + case Role::DesktopFrame: + { + return "desktop frame"; + } + case Role::Dial: + { + return "dial"; + } + case Role::Dialog: + { + return "dialog"; + } + case Role::DirectoryPane: + { + return "directory pane"; + } + case Role::DrawingArea: + { + return "drawing area"; + } + case Role::FileChooser: + { + return "file chooser"; + } + case Role::Filler: + { + return "filler"; + } + case Role::FocusTraversable: + { + return "focus traversable"; + } + case Role::FontChooser: + { + return "font chooser"; + } + case Role::Frame: + { + return "frame"; + } + case Role::GlassPane: + { + return "glass pane"; + } + case Role::HtmlContainer: + { + return "html container"; + } + case Role::Icon: + { + return "icon"; + } + case Role::Image: + { + return "image"; + } + case Role::InternalFrame: + { + return "internal frame"; + } + case Role::Label: + { + return "label"; + } + case Role::LayeredPane: + { + return "layered pane"; + } + case Role::List: + { + return "list"; + } + case Role::ListItem: + { + return "list item"; + } + case Role::Menu: + { + return "menu"; + } + case Role::MenuBar: + { + return "menu bar"; + } + case Role::MenuItem: + { + return "menu item"; + } + case Role::OptionPane: + { + return "option pane"; + } + case Role::PageTab: + { + return "page tab"; + } + case Role::PageTabList: + { + return "page tab list"; + } + case Role::Panel: + { + return "panel"; + } + case Role::PasswordText: + { + return "password text"; + } + case Role::PopupMenu: + { + return "popup menu"; + } + case Role::ProgressBar: + { + return "progress bar"; + } + case Role::PushButton: + { + return "push button"; + } + case Role::RadioButton: + { + return "radio button"; + } + case Role::RadioMenuItem: + { + return "radio menu item"; + } + case Role::RootPane: + { + return "root pane"; + } + case Role::RowHeader: + { + return "row header"; + } + case Role::ScrollBar: + { + return "scroll bar"; + } + case Role::ScrollPane: + { + return "scroll pane"; + } + case Role::Separator: + { + return "separator"; + } + case Role::Slider: + { + return "slider"; + } + case Role::SpinButton: + { + return "spin button"; + } + case Role::SplitPane: + { + return "split pane"; + } + case Role::StatusBar: + { + return "status bar"; + } + case Role::Table: + { + return "table"; + } + case Role::TableCell: + { + return "table cell"; + } + case Role::TableColumnHeader: + { + return "table column header"; + } + case Role::TableRowHeader: + { + return "table row header"; + } + case Role::TearoffMenuItem: + { + return "tearoff menu item"; + } + case Role::Terminal: + { + return "terminal"; + } + case Role::Text: + { + return "text"; + } + case Role::ToggleButton: + { + return "toggle button"; + } + case Role::ToolBar: + { + return "tool bar"; + } + case Role::ToolTip: + { + return "tool tip"; + } + case Role::Tree: + { + return "tree"; + } + case Role::TreeTable: + { + return "tree table"; + } + case Role::Unknown: + { + return "unknown"; + } + case Role::Viewport: + { + return "viewport"; + } + case Role::Window: + { + return "window"; + } + case Role::Extended: + { + return "extended"; + } + case Role::Header: + { + return "header"; + } + case Role::Footer: + { + return "footer"; + } + case Role::Paragraph: + { + return "paragraph"; + } + case Role::Ruler: + { + return "ruler"; + } + case Role::Application: + { + return "application"; + } + case Role::Autocomplete: + { + return "autocomplete"; + } + case Role::Editbar: + { + return "edit bar"; + } + case Role::Embedded: + { + return "embedded"; + } + case Role::Entry: + { + return "entry"; + } + case Role::Chart: + { + return "chart"; + } + case Role::Caption: + { + return "caution"; + } + case Role::DocumentFrame: + { + return "document frame"; + } + case Role::Heading: + { + return "heading"; + } + case Role::Page: + { + return "page"; + } + case Role::Section: + { + return "section"; + } + case Role::RedundantObject: + { + return "redundant object"; + } + case Role::Form: + { + return "form"; + } + case Role::Link: + { + return "link"; + } + case Role::InputMethodWindow: + { + return "input method window"; + } + case Role::TableRow: + { + return "table row"; + } + case Role::TreeItem: + { + return "tree item"; + } + case Role::DocumentSpreadsheet: + { + return "document spreadsheet"; + } + case Role::DocumentPresentation: + { + return "document presentation"; + } + case Role::DocumentText: + { + return "document text"; + } + case Role::DocumentWeb: + { + return "document web"; + } + case Role::DocumentEmail: + { + return "document email"; + } + case Role::Comment: + { + return "comment"; + } + case Role::ListBox: + { + return "list box"; + } + case Role::Grouping: + { + return "grouping"; + } + case Role::ImageMap: + { + return "image map"; + } + case Role::Notification: + { + return "notification"; + } + case Role::InfoBar: + { + return "info bar"; + } + case Role::LevelBar: + { + return "level bar"; + } + case Role::TitleBar: + { + return "title bar"; + } + case Role::BlockQuote: + { + return "block quote"; + } + case Role::Audio: + { + return "audio"; + } + case Role::Video: + { + return "video"; + } + case Role::Definition: + { + return "definition"; + } + case Role::Article: + { + return "article"; + } + case Role::Landmark: + { + return "landmark"; + } + case Role::Log: + { + return "log"; + } + case Role::Marquee: + { + return "marquee"; + } + case Role::Math: + { + return "math"; + } + case Role::Rating: + { + return "rating"; + } + case Role::Timer: + { + return "timer"; + } + case Role::Static: + { + return "static"; + } + case Role::MathFraction: + { + return "math fraction"; + } + case Role::MathRoot: + { + return "math root"; + } + case Role::Subscript: + { + return "subscript"; + } + case Role::Superscript: + { + return "superscript"; + } + case Role::_Count: + { + break; + } + } + return ""; +} + +void Bridge::SetIsOnRootLevel( Accessible* o ) +{ + o->isOnRootLevel = true; +} + +class NonControlAccessible : public virtual Accessible, public virtual Collection +{ +public: + Dali::Actor actor; + bool root = false; + + NonControlAccessible( Dali::Actor actor ) : actor( actor ) + { + Dali::Stage stage = Dali::Stage::GetCurrent(); + root = stage.GetRootLayer() == actor; + } + + std::string GetName() override + { + return actor.GetName(); + } + std::string GetDescription() override + { + return ""; + } + Accessible* GetParent() override + { + if( GetIsOnRootLevel() ) + { + auto b = GetBridgeData(); + return b->bridge->GetApplication(); + } + return Get( actor.GetParent() ); + } + size_t GetChildCount() override + { + return static_cast< size_t >( actor.GetChildCount() ); + } + Accessible* GetChildAtIndex( size_t index ) override + { + auto s = static_cast< size_t >( actor.GetChildCount() ); + if( index >= s ) + throw AccessibleError{"invalid index " + std::to_string( index ) + " for object with " + std::to_string( s ) + " children"}; + return Get( actor.GetChildAt( static_cast< unsigned int >( index ) ) ); + } + size_t GetIndexInParent() override + { + auto p = actor.GetParent(); + if( !p ) + return 0; + auto s = static_cast< size_t >( p.GetChildCount() ); + for( auto i = 0u; i < s; ++i ) + { + if( p.GetChildAt( i ) == actor ) + return i; + } + throw AccessibleError{"actor is not a child of it's parent"}; + } + Role GetRole() override + { + return root ? Role::Window : Role::RedundantObject; + } + States GetStates() override + { + States s; + if( root ) + { + s[State::Highlightable] = true; + s[State::Enabled] = true; + s[State::Sensitive] = true; + s[State::Showing] = true; + s[State::Active] = true; + } + else + { + auto t = GetParent()->GetStates(); + s[State::Showing] = t[State::Showing]; + s[State::Visible] = t[State::Visible]; + } + return s; + } + Attributes GetAttributes() override + { + Dali::TypeInfo type; + actor.GetTypeInfo( type ); + return { + {"t", type.GetName()}, + }; + } +}; + +using NonControlAccessiblesType = std::unordered_map< const Dali::RefObject*, std::unique_ptr< NonControlAccessible > >; +static NonControlAccessiblesType nonControlAccessibles; + +static std::function< Accessible*( Dali::Actor ) > convertingFunctor = []( Dali::Actor ) -> Accessible* { + return nullptr; +}; + +void Accessible::RegisterControlAccessibilityGetter( std::function< Accessible*( Dali::Actor ) > functor ) +{ + convertingFunctor = functor; +} + +Accessible* Accessible::Get( Dali::Actor actor ) +{ + if( !actor ) + { + return nullptr; + } + auto p = convertingFunctor( actor ); + if( !p ) + { + if( nonControlAccessibles.empty() ) + { + auto registry = Dali::Stage::GetCurrent().GetObjectRegistry(); + registry.ObjectDestroyedSignal().Connect( []( const Dali::RefObject* obj ) { + nonControlAccessibles.erase( obj ); + } ); + } + auto it = nonControlAccessibles.emplace( &actor.GetBaseObject(), nullptr ); + if( it.second ) + { + it.first->second.reset( new NonControlAccessible( actor ) ); + } + p = it.first->second.get(); + } + return p; +} diff --git a/dali/internal/accessibility/file.list b/dali/internal/accessibility/file.list index 8ccdb83..2278f8e 100644 --- a/dali/internal/accessibility/file.list +++ b/dali/internal/accessibility/file.list @@ -2,6 +2,7 @@ # module: accessibility, backend: common adaptor_accessibility_common_src_files=\ + ${adaptor_accessibility_dir}/accessibility-impl.cpp \ ${adaptor_accessibility_dir}/common/tts-player-factory.cpp \ ${adaptor_accessibility_dir}/common/tts-player-impl.cpp \ ${adaptor_accessibility_dir}/common/accessibility-adaptor-impl.cpp \ diff --git a/dali/internal/adaptor/common/adaptor-impl.cpp b/dali/internal/adaptor/common/adaptor-impl.cpp index 3fa8e3f..f14eb95 100644 --- a/dali/internal/adaptor/common/adaptor-impl.cpp +++ b/dali/internal/adaptor/common/adaptor-impl.cpp @@ -55,11 +55,15 @@ #include #include -#include +#include #include +#include #include +#include +#include + using Dali::TextAbstraction::FontClient; namespace Dali @@ -243,6 +247,38 @@ void Adaptor::Initialize( Dali::Configuration::ContextLoss configuration ) } SetupSystemInformation(); + + Dali::Stage stage = Dali::Stage::GetCurrent(); + + char appname[4096] = {0}; + int pid = getpid(); + aul_app_get_pkgname_bypid( pid, appname, sizeof( appname ) ); + + accessibilityObserver.atspiBridge = Accessibility::CreateBridge(); + auto accessible = Accessibility::Accessible::Get( stage.GetRootLayer() ); + accessibilityObserver.atspiBridge->SetApplicationChild( accessible ); + accessibilityObserver.atspiBridge->SetApplicationName( appname ); + accessibilityObserver.atspiBridge->Initialize(); + + Dali::Stage::GetCurrent().KeyEventSignal().Connect( &accessibilityObserver, &AccessibilityObserver::OnAccessibleKeyEvent ); +} + +void Adaptor::AccessibilityObserver::OnAccessibleKeyEvent( const KeyEvent& event ) +{ + Accessibility::KeyEventType type; + if( event.state == KeyEvent::Down ) + { + type = Accessibility::KeyEventType::KeyPressed; + } + else if( event.state == KeyEvent::Up ) + { + type = Accessibility::KeyEventType::KeyReleased; + } + else + { + return; + } + atspiBridge->Emit( type, event.keyCode, event.keyPressedName, event.time, !event.keyPressed.empty() ); } Adaptor::~Adaptor() @@ -748,6 +784,8 @@ void Adaptor::OnWindowShown() { if ( PAUSED_WHILE_HIDDEN == mState ) { + accessibilityObserver.atspiBridge->ApplicationShown(); + // Adaptor can now be resumed mState = PAUSED; @@ -762,6 +800,8 @@ void Adaptor::OnWindowHidden() { if ( RUNNING == mState ) { + accessibilityObserver.atspiBridge->ApplicationHidden(); + Pause(); // Adaptor cannot be resumed until the window is shown diff --git a/dali/internal/adaptor/common/adaptor-impl.h b/dali/internal/adaptor/common/adaptor-impl.h index 27508e6..2476136 100644 --- a/dali/internal/adaptor/common/adaptor-impl.h +++ b/dali/internal/adaptor/common/adaptor-impl.h @@ -48,6 +48,11 @@ namespace Dali { +namespace Accessibility +{ +class Bridge; +} + class RenderSurface; class Window; @@ -603,8 +608,18 @@ private: // Data SocketFactory mSocketFactory; ///< Socket factory const bool mEnvironmentOptionsOwned:1; ///< Whether we own the EnvironmentOptions (and thus, need to delete it) bool mUseRemoteSurface; ///< whether the remoteSurface is used or not + + class AccessibilityObserver : public ConnectionTracker + { + public: + std::shared_ptr< Dali::Accessibility::Bridge > atspiBridge; + + void OnAccessibleKeyEvent( const KeyEvent& event ); + }; + AccessibilityObserver accessibilityObserver; + public: - inline static Adaptor& GetImplementation(Dali::Adaptor& adaptor) {return *adaptor.mImpl;} + inline static Adaptor& GetImplementation(Dali::Adaptor& adaptor) { return *adaptor.mImpl; } }; } // namespace Internal diff --git a/dali/public-api/adaptor-framework/accessibility.h b/dali/public-api/adaptor-framework/accessibility.h new file mode 100644 index 0000000..6faa6f5 --- /dev/null +++ b/dali/public-api/adaptor-framework/accessibility.h @@ -0,0 +1,503 @@ +#ifndef __DALI_ACCESSIBILITY_H__ +#define __DALI_ACCESSIBILITY_H__ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace Dali +{ +class Actor; + +namespace Accessibility +{ +enum class RelationType +{ + FlowsFrom, + FlowsTo +}; + +enum class MatchType : int32_t +{ + Invalid, + All, + Any, + None, + Empty +}; + +enum class SortOrder : uint32_t +{ + Invalid, + Canonical, + Flow, + Tab, + ReverseCanonical, + ReverseFlow, + ReverseTab, + LastDefined +}; + +enum class CoordType +{ + Screen, + Window +}; + +enum class NeighborSearchDirection +{ + Forward, + Backward +}; + +enum class ComponentLayer +{ + Invalid, + Background, + Canvas, + Widget, + Mdi, + Popup, + Overlay, + Window, + LastDefined, +}; + +enum class Role : uint32_t +{ + Invalid, + AcceleratorLabel, + Alert, + Animation, + Arrow, + Calendar, + Canvas, + CheckBox, + CheckMenuItem, + ColorChooser, + ColumnHeader, + ComboBox, + DateEditor, + DesktopIcon, + DesktopFrame, + Dial, + Dialog, + DirectoryPane, + DrawingArea, + FileChooser, + Filler, + FocusTraversable, + FontChooser, + Frame, + GlassPane, + HtmlContainer, + Icon, + Image, + InternalFrame, + Label, + LayeredPane, + List, + ListItem, + Menu, + MenuBar, + MenuItem, + OptionPane, + PageTab, + PageTabList, + Panel, + PasswordText, + PopupMenu, + ProgressBar, + PushButton, + RadioButton, + RadioMenuItem, + RootPane, + RowHeader, + ScrollBar, + ScrollPane, + Separator, + Slider, + SpinButton, + SplitPane, + StatusBar, + Table, + TableCell, + TableColumnHeader, + TableRowHeader, + TearoffMenuItem, + Terminal, + Text, + ToggleButton, + ToolBar, + ToolTip, + Tree, + TreeTable, + Unknown, + Viewport, + Window, + Extended, + Header, + Footer, + Paragraph, + Ruler, + Application, + Autocomplete, + Editbar, + Embedded, + Entry, + Chart, + Caption, + DocumentFrame, + Heading, + Page, + Section, + RedundantObject, + Form, + Link, + InputMethodWindow, + TableRow, + TreeItem, + DocumentSpreadsheet, + DocumentPresentation, + DocumentText, + DocumentWeb, + DocumentEmail, + Comment, + ListBox, + Grouping, + ImageMap, + Notification, + InfoBar, + LevelBar, + TitleBar, + BlockQuote, + Audio, + Video, + Definition, + Article, + Landmark, + Log, + Marquee, + Math, + Rating, + Timer, + Static, + MathFraction, + MathRoot, + Subscript, + Superscript, + _Count, +}; + +enum class State : uint32_t +{ + Invalid, + Active, + Armed, + Busy, + Checked, + Collapsed, + Defunct, + Editable, + Enabled, + Expandable, + Expanded, + Focusable, + Focused, + HasTooltip, + Horizontal, + Iconified, + Modal, + MultiLine, + MultiSelectable, + Opaque, + Pressed, + Resizeable, + Selectable, + Selected, + Sensitive, + Showing, + SingleLine, + Stale, + Transient, + Vertical, + Visible, + ManagesDescendants, + Indeterminate, + Required, + Truncated, + Animated, + InvalidEntry, + SupportsAutocompletion, + SelectableText, + IsDefault, + Visited, + Checkable, + HasPopup, + ReadOnly, + Highlighted, + Highlightable, + _Count +}; + +enum class WindowEvent +{ + PropertyChange, + Minimize, + Maximize, + Restore, + Close, + Create, + Reparent, + DesktopCreate, + DesktopDestroy, + Destroy, + Activate, + Deactivate, + Raise, + Lower, + Move, + Resize, + Shade, + UuShade, + Restyle, +}; + +enum class TextBoundary : uint32_t +{ + Character, + WordStart, + WordEnd, + SentenceStart, + SentenceEnd, + LineStart, + LineEnd, + _Count +}; + +template < size_t I, typename S > +class BitStates +{ + std::array< uint32_t, I > data; + +public: + BitStates() + { + for( auto& u : data ) + u = 0; + } + explicit BitStates( std::array< uint32_t, I > d ) + { + for( auto i = 0u; i < I; ++i ) + data[i] = d[i]; + } + explicit BitStates( std::array< int32_t, I > d ) + { + for( auto i = 0u; i < I; ++i ) + data[i] = static_cast< uint32_t >( d[i] ); + } + + struct reference + { + std::array< uint32_t, I >& data; + size_t pos; + bool operator=( reference r ) + { + return ( *this ) = static_cast< bool >( r ); + } + bool operator=( bool v ) + { + if( v ) + data[pos / 32] |= 1 << ( pos & 31 ); + else + data[pos / 32] &= ~( 1 << ( pos & 31 ) ); + return v; + } + operator bool() const + { + auto i = static_cast< size_t >( pos ); + return ( data[i / 32] & ( 1 << ( i & 31 ) ) ) != 0; + } + }; + reference operator[]( S index ) { return {data, static_cast< size_t >( index )}; } + bool operator[]( S index ) const + { + auto i = static_cast< size_t >( index ); + return ( data[i / 32] & ( 1 << ( i & 31 ) ) ) != 0; + } + std::array< uint32_t, I > GetRawData() const { return data; } + + BitStates operator|( BitStates b ) const + { + BitStates r; + for( auto i = 0u; i < I; ++i ) + r.data[i] = data[i] | b.data[i]; + return r; + } + BitStates operator&( BitStates b ) const + { + BitStates r; + for( auto i = 0u; i < I; ++i ) + r.data[i] = data[i] & b.data[i]; + return r; + } + bool operator==( BitStates b ) const + { + for( auto i = 0u; i < I; ++i ) + if( data[i] != b.data[i] ) + return false; + return true; + } + explicit operator bool() const + { + for( auto& u : data ) + if( u ) + return true; + return false; + } +}; + +using States = BitStates< 2, State >; +using Attributes = std::unordered_map< std::string, std::string >; + +class DALI_IMPORT_API Address +{ +public: + Address() = default; + Address( std::string bus, std::string path ) : bus( std::move( bus ) ), path( std::move( path ) ) {} + + explicit operator bool() const { return !path.empty(); } + std::string ToString() const + { + return *this ? bus + ":" + path : "::null"; + } + const std::string& GetBus() const { return bus; } + const std::string& GetPath() const { return path; } + +private: + std::string bus, path; +}; + +enum class KeyEventType +{ + KeyPressed, + KeyReleased, +}; +enum class Consumed +{ + No, + Yes +}; +struct DALI_IMPORT_API Point +{ + int x = 0; + int y = 0; + + Point() = default; + Point( int x, int y ) : x( x ), y( y ) {} + + bool operator==( Point p ) const + { + return x == p.x && y == p.y; + } + bool operator!=( Point p ) const + { + return !( *this == p ); + } +}; + +struct DALI_IMPORT_API Size +{ + int width = 0; + int height = 0; + + Size() = default; + Size( int w, int h ) : width( w ), height( h ) {} + + bool operator==( Size p ) const + { + return width == p.width && height == p.height; + } + bool operator!=( Size p ) const + { + return !( *this == p ); + } +}; + +struct DALI_IMPORT_API Rectangle +{ + Point position; + Size size; + + bool operator==( Rectangle p ) const + { + return position == p.position && size == p.size; + } + bool operator!=( Rectangle p ) const + { + return !( *this == p ); + } +}; +struct DALI_IMPORT_API Range +{ + int startOffset = 0; + int endOffset = 0; + std::string content; + + Range() = default; + Range( size_t start, size_t end ) : startOffset( start ), endOffset( end ) + { + } + Range( size_t start, size_t end, std::string content ) : startOffset( start ), endOffset( end ), content( content ) + { + } +}; + +class DALI_IMPORT_API AccessibleError : public std::exception +{ +public: + AccessibleError( std::string msg ) : msg( std::move( msg ) ) {} + const char* what() const noexcept override { return msg.c_str(); } + +private: + std::string msg; +}; +} +} + +namespace std +{ +template <> +struct hash< Dali::Accessibility::Point > +{ + size_t operator()( Dali::Accessibility::Point p ) + { + return static_cast< size_t >( p.x ) ^ ( static_cast< size_t >( p.y ) * 11 ); + } +}; +template <> +struct hash< Dali::Accessibility::Size > +{ + size_t operator()( Dali::Accessibility::Size p ) + { + return static_cast< size_t >( p.width ) ^ ( static_cast< size_t >( p.height ) * 11 ); + } +}; +template <> +struct hash< Dali::Accessibility::Rectangle > +{ + size_t operator()( Dali::Accessibility::Rectangle p ) + { + return hash< Dali::Accessibility::Point >()( p.position ) ^ ( hash< Dali::Accessibility::Size >()( p.size ) * 11 ); + } +}; +} + +#endif diff --git a/dali/public-api/file.list b/dali/public-api/file.list index 2f1471d..fc24b4f 100644 --- a/dali/public-api/file.list +++ b/dali/public-api/file.list @@ -21,6 +21,7 @@ public_api_adaptor_framework_header_files = \ $(adaptor_public_api_dir)/adaptor-framework/device-status.h \ $(adaptor_public_api_dir)/adaptor-framework/input-method.h \ $(adaptor_public_api_dir)/adaptor-framework/key.h \ + $(adaptor_public_api_dir)/adaptor-framework/accessibility.h \ $(adaptor_public_api_dir)/adaptor-framework/key-grab.h \ $(adaptor_public_api_dir)/adaptor-framework/style-change.h \ $(adaptor_public_api_dir)/adaptor-framework/timer.h \ diff --git a/packaging/dali-adaptor.spec b/packaging/dali-adaptor.spec old mode 100644 new mode 100755 -- 2.7.4