diff --git a/src/Makefile_Eldbus.am b/src/Makefile_Eldbus.am index 66ca2bf913..c5113dbd53 100644 --- a/src/Makefile_Eldbus.am +++ b/src/Makefile_Eldbus.am @@ -1,6 +1,31 @@ ### Library +eldbus_eolian_files = \ + lib/eldbus/eldbus_model_connection.eo \ + lib/eldbus/eldbus_model_object.eo \ + lib/eldbus/eldbus_model_proxy.eo \ + lib/eldbus/eldbus_model_method.eo \ + lib/eldbus/eldbus_model_arguments.eo \ + lib/eldbus/eldbus_model_signal.eo + +eldbus_eolian_c = $(eldbus_eolian_files:%.eo=%.eo.c) +eldbus_eolian_h = $(eldbus_eolian_files:%.eo=%.eo.h) + +BUILT_SOURCES += \ + $(eldbus_eolian_c) \ + $(eldbus_eolian_h) + +CLEANFILES += \ + $(eldbus_eolian_c) \ + $(eldbus_eolian_h) + +eldbuseolianfilesdir = $(datadir)/eolian/include/eldbus-@VMAJ@ +eldbuseolianfiles_DATA = $(eldbus_eolian_files) + +EXTRA_DIST += \ + ${eldbuseolianfiles_DATA} + lib_LTLIBRARIES += lib/eldbus/libeldbus.la installed_eldbusmainheadersdir = $(includedir)/eldbus-@VMAJ@ @@ -17,9 +42,20 @@ lib/eldbus/eldbus_signal_handler.h \ lib/eldbus/eldbus_message_helper.h \ lib/eldbus/eldbus_message_eina_value.h +nodist_installed_eldbusmainheaders_DATA = $(eldbus_eolian_h) + lib_eldbus_libeldbus_la_SOURCES = \ lib/eldbus/eldbus_private.h \ lib/eldbus/eldbus_private_types.h \ +lib/eldbus/eldbus_model_private.h \ +lib/eldbus/eldbus_model_proxy_private.h \ +lib/eldbus/eldbus_model_object_private.h \ +lib/eldbus/eldbus_model_arguments_private.h \ +lib/eldbus/eldbus_model_connection_private.h \ +lib/eldbus/eldbus_model_signal_private.h \ +lib/eldbus/eldbus_model_method_private.h \ +lib/eldbus/eldbus_introspection.h \ +lib/eldbus/Eldbus_Model.h \ lib/eldbus/eldbus_proxy.c \ lib/eldbus/eldbus_core.c \ lib/eldbus/eldbus_message.c \ @@ -30,13 +66,20 @@ lib/eldbus/eldbus_service.c \ lib/eldbus/eldbus_signal_handler.c \ lib/eldbus/eldbus_message_helper.c \ lib/eldbus/eldbus_message_to_eina_value.c \ -lib/eldbus/eldbus_message_from_eina_value.c +lib/eldbus/eldbus_message_from_eina_value.c \ +lib/eldbus/eldbus_model_connection.c \ +lib/eldbus/eldbus_model_object.c \ +lib/eldbus/eldbus_model_proxy.c \ +lib/eldbus/eldbus_model_method.c \ +lib/eldbus/eldbus_model_arguments.c \ +lib/eldbus/eldbus_model_signal.c \ +lib/eldbus/eldbus_introspection.c lib_eldbus_libeldbus_la_CPPFLAGS = -I$(top_builddir)/src/lib/efl \ -include $(top_builddir)/config.h \ -@ELDBUS_CFLAGS@ -lib_eldbus_libeldbus_la_LIBADD = @ELDBUS_LIBS@ -lib_eldbus_libeldbus_la_DEPENDENCIES = @ELDBUS_INTERNAL_LIBS@ +@ELDBUS_CFLAGS@ @EFL_CFLAGS@ +lib_eldbus_libeldbus_la_LIBADD = @ELDBUS_LIBS@ @EFL_LIBS@ +lib_eldbus_libeldbus_la_DEPENDENCIES = @ELDBUS_INTERNAL_LIBS@ @EFL_INTERNAL_LIBS@ lib_eldbus_libeldbus_la_LDFLAGS = @EFL_LTLIBRARY_FLAGS@ ### Binary @@ -74,12 +117,22 @@ TESTS += tests/eldbus/eldbus_suite tests_eldbus_eldbus_suite_SOURCES = \ tests/eldbus/eldbus_suite.c \ tests/eldbus/eldbus_test_eldbus_init.c \ +tests/eldbus/eldbus_test_eldbus_model.h \ +tests/eldbus/eldbus_fake_server.h \ +tests/eldbus/eldbus_test_eldbus_model.c \ +tests/eldbus/eldbus_test_eldbus_model_connection.c \ +tests/eldbus/eldbus_test_eldbus_model_object.c \ +tests/eldbus/eldbus_test_eldbus_model_proxy.c \ +tests/eldbus/eldbus_test_eldbus_model_method.c \ +tests/eldbus/eldbus_test_eldbus_model_signal.c \ +tests/eldbus/eldbus_test_fake_server_eldbus_model_proxy.c \ +tests/eldbus/eldbus_fake_server.c \ tests/eldbus/eldbus_suite.h tests_eldbus_eldbus_suite_CPPFLAGS = -I$(top_builddir)/src/lib/efl @CHECK_CFLAGS@ @ELDBUS_CFLAGS@ \ --DTESTS_BUILD_DIR=\"$(top_builddir)/src/tests/eldbus\" +@EFL_CFLAGS@ -DTESTS_BUILD_DIR=\"$(top_builddir)/src/tests/eldbus\" -tests_eldbus_eldbus_suite_LDADD = @CHECK_LIBS@ @USE_ELDBUS_LIBS@ +tests_eldbus_eldbus_suite_LDADD = @CHECK_LIBS@ @USE_ELDBUS_LIBS@ @USE_EFL_LIBS@ tests_eldbus_eldbus_suite_DEPENDENCIES = @USE_ELDBUS_INTERNAL_LIBS@ endif diff --git a/src/Makefile_Eldbus_Cxx.am b/src/Makefile_Eldbus_Cxx.am index ad17184428..d265b2c3f9 100644 --- a/src/Makefile_Eldbus_Cxx.am +++ b/src/Makefile_Eldbus_Cxx.am @@ -1,6 +1,27 @@ if HAVE_CXX11 +### Generated headers + +generated_eldbus_cxx_bindings = $(eldbus_eolian_files:%.eo=%.eo.hh) +generated_eldbus_cxx_impl_bindings = $(eldbus_eolian_files:%.eo=%.eo.impl.hh) + +lib/eldbus/Eldbus_Model.hh: $(generated_eldbus_cxx_bindings) + @echo @ECHO_E@ "#ifndef EFL_CXX_ELDBUS_MODEL_HH\n#define EFL_CXX_ELDBUS_MODEL_HH\n" > $(top_builddir)/src/lib/eldbus/Eldbus_Model.hh + @echo @ECHO_E@ "#include " >> $(top_builddir)/src/lib/eldbus/Eldbus_Model.hh + @echo @ECHO_E@ "#ifdef EFL_BETA_API_SUPPORT" >> $(top_builddir)/src/lib/eldbus/Eldbus_Model.hh + @for i in $(generated_eldbus_cxx_bindings); do echo "#include <$$(basename $$i)>" >> $(top_builddir)/src/lib/eldbus/Eldbus_Model.hh; done + @echo @ECHO_E@ "#endif\n\n#endif\n" >> $(top_builddir)/src/lib/eldbus/Eldbus_Model.hh + +generated_eldbus_cxx_all = \ + $(generated_eldbus_cxx_bindings) \ + $(generated_eldbus_cxx_impl_bindings) \ + lib/eldbus/Eldbus_Model.hh + +CLEANFILES += $(generated_eldbus_cxx_all) + +nodist_installed_eldbuscxxmainheaders_DATA = $(generated_eldbus_cxx_all) + ### Library installed_eldbuscxxmainheadersdir = $(includedir)/eldbus_cxx-@VMAJ@ diff --git a/src/Makefile_Elocation.am b/src/Makefile_Elocation.am index 604b50d512..82ff192724 100644 --- a/src/Makefile_Elocation.am +++ b/src/Makefile_Elocation.am @@ -17,7 +17,7 @@ lib/elocation/gen/eldbus_geo_clue2_client.c \ lib/elocation/gen/eldbus_geo_clue2_location.c \ lib/elocation/gen/eldbus_geo_clue2_manager.c -lib_elocation_libelocation_la_CPPFLAGS = -I$(top_builddir)/src/lib/efl @ELOCATION_CFLAGS@ +lib_elocation_libelocation_la_CPPFLAGS = -I$(top_builddir)/src/lib/efl @ELOCATION_CFLAGS@ @EFL_CFLAGS@ lib_elocation_libelocation_la_LIBADD = @ELOCATION_LIBS@ lib_elocation_libelocation_la_DEPENDENCIES = @ELOCATION_INTERNAL_LIBS@ lib_elocation_libelocation_la_LDFLAGS = @EFL_LTLIBRARY_FLAGS@ diff --git a/src/examples/eldbus/.gitignore b/src/examples/eldbus/.gitignore index db67874f43..70650f40e4 100644 --- a/src/examples/eldbus/.gitignore +++ b/src/examples/eldbus/.gitignore @@ -8,3 +8,4 @@ /ofono-dial /server /simple-signal-emit +/dbusmodel diff --git a/src/lib/.gitignore b/src/lib/.gitignore index df511ac721..190a85e1d6 100644 --- a/src/lib/.gitignore +++ b/src/lib/.gitignore @@ -8,3 +8,4 @@ /edje/Edje.hh /edje/Edje.eo.hh /eio/Eio.hh +/eldbus/Eldbus_Model.hh diff --git a/src/lib/eldbus/Eldbus.h b/src/lib/eldbus/Eldbus.h index ee1195420b..c8f35d8a37 100644 --- a/src/lib/eldbus/Eldbus.h +++ b/src/lib/eldbus/Eldbus.h @@ -79,6 +79,8 @@ #include #include #include +#include +#include #ifdef EAPI # undef EAPI @@ -236,6 +238,18 @@ typedef void (*Eldbus_Signal_Cb)(void *data, const Eldbus_Message *msg); #include "eldbus_proxy.h" #include "eldbus_freedesktop.h" #include "eldbus_service.h" +#include "eldbus_introspection.h" + +#ifdef EFL_BETA_API_SUPPORT + +#include "eldbus_model_arguments.eo.h" +#include "eldbus_model_connection.eo.h" +#include "eldbus_model_method.eo.h" +#include "eldbus_model_object.eo.h" +#include "eldbus_model_proxy.eo.h" +#include "eldbus_model_signal.eo.h" + +#endif #ifdef __cplusplus } diff --git a/src/lib/eldbus/Eldbus_Model.h b/src/lib/eldbus/Eldbus_Model.h new file mode 100644 index 0000000000..9edbd77332 --- /dev/null +++ b/src/lib/eldbus/Eldbus_Model.h @@ -0,0 +1,23 @@ +#ifndef _ELDBUS_MODEL_H +#define _ELDBUS_MODEL_H + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include +#include +#include +#include +#include + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/lib/eldbus/eldbus_core.c b/src/lib/eldbus/eldbus_core.c index cf149cdd70..29899d1564 100644 --- a/src/lib/eldbus/eldbus_core.c +++ b/src/lib/eldbus/eldbus_core.c @@ -73,6 +73,7 @@ EAPI const Eldbus_Version * eldbus_version = &_version; static int _eldbus_init_count = 0; int _eldbus_log_dom = -1; +int eldbus_model_log_dom = -1; /* We don't save ELDBUS_CONNECTION_TYPE_UNKNOWN in here so we need room for * last - 1 elements */ @@ -104,6 +105,16 @@ eldbus_init(void) return 0; } + eldbus_model_log_dom = eina_log_domain_register("eldbus_model", EINA_COLOR_CYAN); + if (eldbus_model_log_dom < 0) + { + EINA_LOG_ERR("Unable to create an 'eldbus_model' log domain"); + eina_log_domain_unregister(_eldbus_log_dom); + _eldbus_log_dom = -1; + eina_shutdown(); + return 0; + } + if (!ecore_init()) { ERR("Unable to initialize ecore"); @@ -144,6 +155,8 @@ signal_handler_failed: eldbus_message_shutdown(); message_failed: ecore_shutdown(); + eina_log_domain_unregister(eldbus_model_log_dom); + eldbus_model_log_dom = -1; eina_log_domain_unregister(_eldbus_log_dom); _eldbus_log_dom = -1; eina_shutdown(); @@ -245,6 +258,8 @@ eldbus_shutdown(void) eldbus_message_shutdown(); ecore_shutdown(); + eina_log_domain_unregister(eldbus_model_log_dom); + eldbus_model_log_dom = -1; eina_log_domain_unregister(_eldbus_log_dom); _eldbus_log_dom = -1; eina_shutdown(); diff --git a/src/lib/eldbus/eldbus_freedesktop.h b/src/lib/eldbus/eldbus_freedesktop.h index b62937911d..838fee0777 100644 --- a/src/lib/eldbus/eldbus_freedesktop.h +++ b/src/lib/eldbus/eldbus_freedesktop.h @@ -239,6 +239,19 @@ EAPI Eldbus_Pending *eldbus_proxy_property_get(Eldbus_Proxy *proxy, const */ EAPI Eldbus_Pending *eldbus_proxy_property_set(Eldbus_Proxy *proxy, const char *name, const char *sig, const void *value, Eldbus_Message_Cb cb, const void *data) EINA_ARG_NONNULL(1, 2, 3, 4); +/** + * Set a property with a Eina_Value. + * + * @param proxy The proxy object on which to do the query. + * @param name The property name to get. + * @param sig + * @param value The value to set. + * @param cb The callback to be called when receiving an answer. + * @param data Data to be passed to the callback. + * @return Eldbus_Pending object corresponding to the message sent. + */ +EAPI Eldbus_Pending *eldbus_proxy_property_value_set(Eldbus_Proxy *proxy, const char *name, const char *sig, const Eina_Value *value, Eldbus_Message_Cb cb, const void *data) EINA_ARG_NONNULL(1, 2, 3, 4); + /** * Get all properties. * diff --git a/src/lib/eldbus/eldbus_introspection.c b/src/lib/eldbus/eldbus_introspection.c new file mode 100644 index 0000000000..dbacf74466 --- /dev/null +++ b/src/lib/eldbus/eldbus_introspection.c @@ -0,0 +1,469 @@ +#include "eldbus_introspection.h" + +typedef struct _Eldbus_Introspection_Element_Parse_Table Eldbus_Introspection_Element_Parse_Table; + +struct _Eldbus_Introspection_Element_Parse_Table +{ + const char *name; + void *(*parse)(Eina_Simple_XML_Node_Tag *tag); + Eina_List **list; +}; + +static const char *_eldbus_introspection_attribute_value_get(Eina_Inlist *, const char *); +static void *_eldbus_introspection_parse_node(Eina_Simple_XML_Node_Tag *); +static void *_eldbus_introspection_parse_interface(Eina_Simple_XML_Node_Tag *); +static void *_eldbus_introspection_parse_method(Eina_Simple_XML_Node_Tag *); +static void *_eldbus_introspection_parse_signal(Eina_Simple_XML_Node_Tag *); +static void *_eldbus_introspection_parse_argument(Eina_Simple_XML_Node_Tag *); +static void *_eldbus_introspection_parse_property(Eina_Simple_XML_Node_Tag *); +static void *_eldbus_introspection_parse_annotation(Eina_Simple_XML_Node_Tag *); +static void _eldbus_introspection_parse_children(Eina_Inlist *, const Eldbus_Introspection_Element_Parse_Table[]); +static void _eldbus_introspection_interface_free(Eldbus_Introspection_Interface *); +static void _eldbus_introspection_method_free(Eldbus_Introspection_Method *); +static void _eldbus_introspection_signal_free(Eldbus_Introspection_Signal *); +static void _eldbus_introspection_argument_free(Eldbus_Introspection_Argument *); +static void _eldbus_introspection_property_free(Eldbus_Introspection_Property *); +static void _eldbus_introspection_annotation_free(Eldbus_Introspection_Annotation *); + +EAPI Eldbus_Introspection_Node * +eldbus_introspection_parse(const char *xml) +{ + Eldbus_Introspection_Node *node; + Eina_Simple_XML_Node_Root *xml_root; + Eina_Simple_XML_Node *xml_node; + + EINA_SAFETY_ON_NULL_RETURN_VAL(xml, NULL); + + node = NULL; + xml_root = eina_simple_xml_node_load(xml, strlen(xml), EINA_TRUE); + xml_node = (Eina_Simple_XML_Node*)eina_inlist_last(xml_root->children); + EINA_SAFETY_ON_FALSE_GOTO(EINA_SIMPLE_XML_NODE_TAG == xml_node->type, free_root); + + node = (Eldbus_Introspection_Node*)_eldbus_introspection_parse_node((Eina_Simple_XML_Node_Tag*)xml_node); + + free_root: + eina_simple_xml_node_root_free(xml_root); + + return node; +} + +static void * +_eldbus_introspection_parse_node(Eina_Simple_XML_Node_Tag *tag) +{ + Eldbus_Introspection_Node *node;; + Eldbus_Introspection_Element_Parse_Table table[] = { + { "node", _eldbus_introspection_parse_node, NULL }, + { "interface", _eldbus_introspection_parse_interface, NULL }, + { NULL, NULL, NULL } + }; + const char *name; + + node = calloc(1, sizeof(Eldbus_Introspection_Node)); + EINA_SAFETY_ON_NULL_RETURN_VAL(node, NULL); + + name = _eldbus_introspection_attribute_value_get(tag->attributes, "name"); + node->name = eina_stringshare_add(name); + table[0].list = &node->nodes; + table[1].list = &node->interfaces; + + _eldbus_introspection_parse_children(tag->children, table); + + return node; +} + +EAPI void +eldbus_introspection_node_free(Eldbus_Introspection_Node *node) +{ + Eldbus_Introspection_Node *child_node; + Eldbus_Introspection_Interface *interface; + + EINA_SAFETY_ON_NULL_RETURN(node); + + EINA_LIST_FREE(node->nodes, child_node) + eldbus_introspection_node_free(child_node); + + EINA_LIST_FREE(node->interfaces, interface) + _eldbus_introspection_interface_free(interface); + + eina_stringshare_del(node->name); + free(node); +} + +static const char * +_eldbus_introspection_attribute_value_get(Eina_Inlist *attributes, const char *key) +{ + Eina_Simple_XML_Attribute *attribute; + + EINA_INLIST_FOREACH(attributes, attribute) + { + if (strcmp(attribute->key, key) == 0) + return attribute->value; + } + + return NULL; +} + +static void * +_eldbus_introspection_parse_interface(Eina_Simple_XML_Node_Tag *tag) +{ + Eldbus_Introspection_Interface *interface; + Eldbus_Introspection_Element_Parse_Table table[] = { + { "method", _eldbus_introspection_parse_method, NULL }, + { "signal", _eldbus_introspection_parse_signal, NULL }, + { "property", _eldbus_introspection_parse_property, NULL }, + { "annotation", _eldbus_introspection_parse_annotation, NULL }, + { NULL, NULL, NULL } + }; + const char *name; + + interface = calloc(1, sizeof(Eldbus_Introspection_Interface)); + EINA_SAFETY_ON_NULL_RETURN_VAL(interface, NULL); + + name = _eldbus_introspection_attribute_value_get(tag->attributes, "name"); + if (!name) goto error; + interface->name = eina_stringshare_add(name); + table[0].list = &interface->methods; + table[1].list = &interface->signals; + table[2].list = &interface->properties; + table[3].list = &interface->annotations; + + _eldbus_introspection_parse_children(tag->children, table); + + return interface; + + error: + _eldbus_introspection_interface_free(interface); + return NULL; +} + +static void +_eldbus_introspection_interface_free(Eldbus_Introspection_Interface *interface) +{ + Eldbus_Introspection_Method *method; + Eldbus_Introspection_Signal *signal; + Eldbus_Introspection_Property *property; + Eldbus_Introspection_Annotation *annotation; + + EINA_SAFETY_ON_NULL_RETURN(interface); + + EINA_LIST_FREE(interface->methods, method) + _eldbus_introspection_method_free(method); + + EINA_LIST_FREE(interface->signals, signal) + _eldbus_introspection_signal_free(signal); + + EINA_LIST_FREE(interface->properties, property) + _eldbus_introspection_property_free(property); + + EINA_LIST_FREE(interface->annotations, annotation) + _eldbus_introspection_annotation_free(annotation); + + eina_stringshare_del(interface->name); + free(interface); +} + +static void * +_eldbus_introspection_parse_method(Eina_Simple_XML_Node_Tag *tag) +{ + Eldbus_Introspection_Method *method; + Eldbus_Introspection_Element_Parse_Table table[] = { + { "arg", _eldbus_introspection_parse_argument, NULL }, + { "annotation", _eldbus_introspection_parse_annotation, NULL }, + { NULL, NULL, NULL } + }; + const char *name; + + method = calloc(1, sizeof(Eldbus_Introspection_Method)); + EINA_SAFETY_ON_NULL_RETURN_VAL(method, NULL); + + name = _eldbus_introspection_attribute_value_get(tag->attributes, "name"); + if (!name) goto error; + method->name = eina_stringshare_add(name); + table[0].list = &method->arguments; + table[1].list = &method->annotations; + + _eldbus_introspection_parse_children(tag->children, table); + + return method; + + error: + _eldbus_introspection_method_free(method); + return NULL; +} + +static void +_eldbus_introspection_method_free(Eldbus_Introspection_Method *method) +{ + Eldbus_Introspection_Argument *argument; + Eldbus_Introspection_Annotation *annotation; + + EINA_SAFETY_ON_NULL_RETURN(method); + + EINA_LIST_FREE(method->arguments, argument) + _eldbus_introspection_argument_free(argument); + + EINA_LIST_FREE(method->annotations, annotation) + _eldbus_introspection_annotation_free(annotation); + + eina_stringshare_del(method->name); + free(method); +} + +static void * +_eldbus_introspection_parse_signal(Eina_Simple_XML_Node_Tag *tag) +{ + Eldbus_Introspection_Signal *signal; + Eldbus_Introspection_Element_Parse_Table table[] = { + { "arg", _eldbus_introspection_parse_argument, NULL }, + { "annotation", _eldbus_introspection_parse_annotation, NULL }, + { NULL, NULL, NULL } + }; + const char *name; + + signal = calloc(1, sizeof(Eldbus_Introspection_Signal)); + EINA_SAFETY_ON_NULL_RETURN_VAL(signal, NULL); + + name = _eldbus_introspection_attribute_value_get(tag->attributes, "name"); + if (!name) goto error; + signal->name = eina_stringshare_add(name); + table[0].list = &signal->arguments; + table[1].list = &signal->annotations; + + _eldbus_introspection_parse_children(tag->children, table); + + return signal; + + error: + _eldbus_introspection_signal_free(signal); + return NULL; +} + +static void +_eldbus_introspection_signal_free(Eldbus_Introspection_Signal *signal) +{ + Eldbus_Introspection_Argument *argument; + Eldbus_Introspection_Annotation *annotation; + + EINA_SAFETY_ON_NULL_RETURN(signal); + + EINA_LIST_FREE(signal->arguments, argument) + _eldbus_introspection_argument_free(argument); + + EINA_LIST_FREE(signal->annotations, annotation) + _eldbus_introspection_annotation_free(annotation); + + eina_stringshare_del(signal->name); + free(signal); +} + +static void * +_eldbus_introspection_parse_argument(Eina_Simple_XML_Node_Tag *tag) +{ + Eldbus_Introspection_Argument *argument; + const char *name; + const char *type; + const char *direction; + + argument = calloc(1, sizeof(Eldbus_Introspection_Argument)); + EINA_SAFETY_ON_NULL_RETURN_VAL(argument, NULL); + + name = _eldbus_introspection_attribute_value_get(tag->attributes, "name"); + argument->name = eina_stringshare_add(name); + + type = _eldbus_introspection_attribute_value_get(tag->attributes, "type"); + if (!type) goto error; + argument->type = eina_stringshare_add(type); + + direction = _eldbus_introspection_attribute_value_get(tag->attributes, "direction"); + if (direction) + { + if (strcmp(direction, "in") == 0) + argument->direction = ELDBUS_INTROSPECTION_ARGUMENT_DIRECTION_IN; + else + if (strcmp(direction, "out") == 0) + argument->direction = ELDBUS_INTROSPECTION_ARGUMENT_DIRECTION_OUT; + else + argument->direction = ELDBUS_INTROSPECTION_ARGUMENT_DIRECTION_NONE; + } + + return argument; + + error: + _eldbus_introspection_argument_free(argument); + return NULL; +} + +static void +_eldbus_introspection_argument_free(Eldbus_Introspection_Argument *argument) +{ + EINA_SAFETY_ON_NULL_RETURN(argument); + eina_stringshare_del(argument->name); + eina_stringshare_del(argument->type); + free(argument); +} + +static void * +_eldbus_introspection_parse_property(Eina_Simple_XML_Node_Tag *tag) +{ + Eldbus_Introspection_Property *property; + Eldbus_Introspection_Element_Parse_Table table[] = { + { "annotation", _eldbus_introspection_parse_annotation, NULL }, + { NULL, NULL, NULL } + }; + const char *name; + const char *type; + const char *access; + + property = calloc(1, sizeof(Eldbus_Introspection_Property)); + EINA_SAFETY_ON_NULL_RETURN_VAL(property, NULL); + + name = _eldbus_introspection_attribute_value_get(tag->attributes, "name"); + if (!name) goto error; + property->name = eina_stringshare_add(name); + + type = _eldbus_introspection_attribute_value_get(tag->attributes, "type"); + if (!type) goto error; + property->type = eina_stringshare_add(type); + + access = _eldbus_introspection_attribute_value_get(tag->attributes, "access"); + if (!access) goto error; + + if (strcmp(access, "read") == 0) + property->access = ELDBUS_INTROSPECTION_PROPERTY_ACCESS_READ; + else if (strcmp(access, "write") == 0) + property->access = ELDBUS_INTROSPECTION_PROPERTY_ACCESS_WRITE; + else if (strcmp(access, "readwrite") == 0) + property->access = ELDBUS_INTROSPECTION_PROPERTY_ACCESS_READWRITE; + else + EINA_SAFETY_ON_TRUE_GOTO(!!"Unknown property access", error); + table[0].list = &property->annotations; + + _eldbus_introspection_parse_children(tag->children, table); + return property; + + error: + _eldbus_introspection_property_free(property); + return NULL; +} + +static void +_eldbus_introspection_property_free(Eldbus_Introspection_Property *property) +{ + Eldbus_Introspection_Annotation *annotation; + + EINA_SAFETY_ON_NULL_RETURN(property); + + EINA_LIST_FREE(property->annotations, annotation) + _eldbus_introspection_annotation_free(annotation); + + eina_stringshare_del(property->name); + eina_stringshare_del(property->type); + free(property); +} + +static void * +_eldbus_introspection_parse_annotation(Eina_Simple_XML_Node_Tag *tag) +{ + Eldbus_Introspection_Annotation *annotation; + const char *name; + const char *value; + + annotation = calloc(1, sizeof(Eldbus_Introspection_Annotation)); + EINA_SAFETY_ON_NULL_RETURN_VAL(annotation, NULL); + + name = _eldbus_introspection_attribute_value_get(tag->attributes, "name"); + if (!name) goto error; + annotation->name = eina_stringshare_add(name); + + value = _eldbus_introspection_attribute_value_get(tag->attributes, "value"); + if (!value) goto error; + annotation->value = eina_stringshare_add(value); + + return annotation; + + error: + _eldbus_introspection_annotation_free(annotation); + return NULL; +} + +static void +_eldbus_introspection_annotation_free(Eldbus_Introspection_Annotation *annotation) +{ + EINA_SAFETY_ON_NULL_RETURN(annotation); + eina_stringshare_del(annotation->name); + eina_stringshare_del(annotation->value); + free(annotation); +} + +static void +_eldbus_introspection_parse_children(Eina_Inlist *children, const Eldbus_Introspection_Element_Parse_Table table[]) +{ + Eina_Simple_XML_Node *child; + + EINA_INLIST_FOREACH(children, child) + { + const Eldbus_Introspection_Element_Parse_Table *it; + Eina_Simple_XML_Node_Tag *tag; + void *item; + + if (EINA_SIMPLE_XML_NODE_TAG != child->type) + continue; + + tag = (Eina_Simple_XML_Node_Tag*) child; + + for (it = table; it->name; ++it) + { + if (strcmp(tag->name, it->name) != 0) + continue; + + item = it->parse(tag); + if (item) + *it->list = eina_list_append(*it->list, item); + } + } +} + +EAPI Eldbus_Introspection_Interface * +eldbus_introspection_interface_find(Eina_List *interfaces, const char *name) +{ + Eina_List *it; + Eldbus_Introspection_Interface *interface; + + EINA_LIST_FOREACH(interfaces, it, interface) + { + if (strcmp(interface->name, name) == 0) + return interface; + } + + return NULL; +} + +EAPI Eldbus_Introspection_Property * +eldbus_introspection_property_find(Eina_List *properties, const char *name) +{ + Eina_List *it; + Eldbus_Introspection_Property *property; + + EINA_LIST_FOREACH(properties, it, property) + { + if (strcmp(property->name, name) == 0) + return property; + } + + return NULL; +} + +EAPI Eldbus_Introspection_Argument * +eldbus_introspection_argument_find(Eina_List *arguments, const char *name) +{ + Eina_List *it; + Eldbus_Introspection_Argument *argument; + + EINA_LIST_FOREACH(arguments, it, argument) + { + if (strcmp(argument->name, name) == 0) + return argument; + } + + return NULL; +} diff --git a/src/lib/eldbus/eldbus_introspection.h b/src/lib/eldbus/eldbus_introspection.h new file mode 100644 index 0000000000..4f3746016c --- /dev/null +++ b/src/lib/eldbus/eldbus_introspection.h @@ -0,0 +1,124 @@ +#ifndef _ELDBUS_INTROSPECTION_INTROSPECTION_H +#define _ELDBUS_INTROSPECTION_INTROSPECTION_H + +#include + +// DTD conversion form: http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd + +typedef struct _Eldbus_Introspection_Node Eldbus_Introspection_Node; +typedef struct _Eldbus_Introspection_Interface Eldbus_Introspection_Interface; +typedef struct _Eldbus_Introspection_Method Eldbus_Introspection_Method; +typedef struct _Eldbus_Introspection_Signal Eldbus_Introspection_Signal; +typedef struct _Eldbus_Introspection_Argument Eldbus_Introspection_Argument; +typedef struct _Eldbus_Introspection_Property Eldbus_Introspection_Property; +typedef struct _Eldbus_Introspection_Annotation Eldbus_Introspection_Annotation; + +typedef enum +{ + ELDBUS_INTROSPECTION_ARGUMENT_DIRECTION_NONE = 0, + ELDBUS_INTROSPECTION_ARGUMENT_DIRECTION_IN, + ELDBUS_INTROSPECTION_ARGUMENT_DIRECTION_OUT, + +} Eldbus_Introspection_Argument_Direction; + +typedef enum +{ + ELDBUS_INTROSPECTION_PROPERTY_ACCESS_READ, + ELDBUS_INTROSPECTION_PROPERTY_ACCESS_WRITE, + ELDBUS_INTROSPECTION_PROPERTY_ACCESS_READWRITE, +} Eldbus_Introspection_Property_Access; + +struct _Eldbus_Introspection_Node +{ + Eina_Stringshare *name; // optional + Eina_List *nodes; + Eina_List *interfaces; +}; + +struct _Eldbus_Introspection_Interface +{ + Eina_Stringshare *name; + Eina_List *methods; + Eina_List *signals; + Eina_List *properties; + Eina_List *annotations; +}; + +struct _Eldbus_Introspection_Method +{ + Eina_Stringshare *name; + Eina_List *arguments; + Eina_List *annotations; +}; + +struct _Eldbus_Introspection_Signal +{ + Eina_Stringshare *name; + Eina_List *arguments; + Eina_List *annotations; +}; + +struct _Eldbus_Introspection_Argument +{ + Eina_Stringshare *name; // optional + Eina_Stringshare *type; + Eldbus_Introspection_Argument_Direction direction; +}; + +struct _Eldbus_Introspection_Property +{ + Eina_Stringshare *name; + Eina_Stringshare *type; + Eldbus_Introspection_Property_Access access; + Eina_List *annotations; +}; + +struct _Eldbus_Introspection_Annotation +{ + Eina_Stringshare *name; + Eina_Stringshare *value; +}; + +/** + * @brief Parses the introspection xml abstracting it to an object tree + * + * @param xml The introspection xml + * @return The introspection object tree + */ +EAPI Eldbus_Introspection_Node *eldbus_introspection_parse(const char *xml); + +/** + * @brief Frees the introspection object tree + * + * @param node The root node of introspection tree + */ +EAPI void eldbus_introspection_node_free(Eldbus_Introspection_Node *node); + +/** + * @brief Finds an interface by name + * + * @param interfaces The list of interfaces of type @c Eldbus_Introspection_Interface + * @param name The interfaces's name to search for + * @return Returns the interface found or @c NULL if not + */ +EAPI Eldbus_Introspection_Interface *eldbus_introspection_interface_find(Eina_List *interfaces, const char *name); + +/** + * @brief Finds a property by name + * + * @param properties The list of properties of type @c Eldbus_Introspection_Property + * @param name The properties's name to search for + * @return Returns the property found or @c NULL if not + */ +EAPI Eldbus_Introspection_Property *eldbus_introspection_property_find(Eina_List *properties, const char *name); + +/** + * @brief Finds an argument by name + * + * @param arguments The list of arguments of type @c Eldbus_Introspection_Property + * @param name The arguments's name to search for + * @return Returns the argument found or @c NULL if not + */ +EAPI Eldbus_Introspection_Argument *eldbus_introspection_argument_find(Eina_List *arguments, const char *name); + +#endif diff --git a/src/lib/eldbus/eldbus_message_from_eina_value.c b/src/lib/eldbus/eldbus_message_from_eina_value.c index 05bb9f5e41..3d594fb6ab 100644 --- a/src/lib/eldbus/eldbus_message_from_eina_value.c +++ b/src/lib/eldbus/eldbus_message_from_eina_value.c @@ -1,6 +1,8 @@ #include "eldbus_private.h" #include "eldbus_private_types.h" +#include + static Eina_Bool _compatible_type(int dbus_type, const Eina_Value_Type *value_type) { @@ -205,7 +207,7 @@ _array_append(const char *type, const Eina_Value *value_array, Eldbus_Message_It } static Eina_Bool -_basic_append(char type, const Eina_Value *value, const Eina_Value_Struct_Desc *desc, unsigned idx, Eldbus_Message_Iter *iter) +_basic_append_value_struct(char type, const Eina_Value *value, const Eina_Value_Struct_Desc *desc, unsigned idx, Eldbus_Message_Iter *iter) { EINA_SAFETY_ON_FALSE_RETURN_VAL( _compatible_type(type, desc->members[idx].type), EINA_FALSE); @@ -305,7 +307,7 @@ _message_iter_from_eina_value_struct(const char *signature, Eldbus_Message_Iter { DBG("type: %s", type); if (type[0] != 'v' && !type[1]) - r = _basic_append(type[0], value, st.desc, i, iter); + r = _basic_append_value_struct(type[0], value, st.desc, i, iter); else if (type[0] == 'a') { Eina_Value value_array; @@ -367,3 +369,121 @@ eldbus_message_from_eina_value(const char *signature, Eldbus_Message *msg, const return _message_iter_from_eina_value_struct(signature, iter, value); } + +static Eina_Bool +_basic_append_value(char type, const Eina_Value *value, Eldbus_Message_Iter *iter) +{ + EINA_SAFETY_ON_NULL_RETURN_VAL(value, EINA_FALSE); + const Eina_Value_Type *value_type = eina_value_type_get(value); + EINA_SAFETY_ON_FALSE_RETURN_VAL(_compatible_type(type, value_type), EINA_FALSE); + switch (type) + { + case 'i'://int + case 'h'://fd + { + int32_t i; + eina_value_get(value, &i); + eldbus_message_iter_basic_append(iter, type, i); + break; + } + case 's': + case 'o'://object path + case 'g'://signature + { + const char *txt; + eina_value_get(value, &txt); + eldbus_message_iter_basic_append(iter, type, txt); + break; + } + case 'b'://boolean + case 'y'://byte + { + unsigned char byte; + eina_value_get(value, &byte); + eldbus_message_iter_basic_append(iter, type, byte); + break; + } + case 'n'://int16 + { + int16_t i; + eina_value_get(value, &i); + eldbus_message_iter_basic_append(iter, type, i); + break; + } + case 'q'://uint16 + { + uint16_t i; + eina_value_get(value, &i); + eldbus_message_iter_basic_append(iter, type, i); + break; + } + case 'u'://uint32 + { + uint32_t i; + eina_value_get(value, &i); + eldbus_message_iter_basic_append(iter, type, i); + break; + } + case 'x'://int64 + { + int64_t i; + eina_value_get(value, &i); + eldbus_message_iter_basic_append(iter, type, i); + break; + } + case 't'://uint64 + { + uint64_t i; + eina_value_get(value, &i); + eldbus_message_iter_basic_append(iter, type, i); + break; + } + case 'd'://double + { + double d; + eina_value_get(value, &d); + eldbus_message_iter_basic_append(iter, type, d); + break; + } + default: + ERR("Unexpected type %c", type); + return EINA_FALSE; + } + return EINA_TRUE; +} + +Eina_Bool +_message_iter_from_eina_value(const char *signature, Eldbus_Message_Iter *iter, const Eina_Value *value) +{ + EINA_SAFETY_ON_NULL_RETURN_VAL(value, EINA_FALSE); + + const Eina_Value_Type *value_type = eina_value_type_get(value); + if (EINA_VALUE_TYPE_STRUCT == value_type || EINA_VALUE_TYPE_ARRAY == value_type) + return _message_iter_from_eina_value_struct(signature, iter, value); + + Eina_Bool result = EINA_TRUE; + DBusSignatureIter signature_iter; + dbus_signature_iter_init(&signature_iter, signature); + char *type; + while ((type = dbus_signature_iter_get_signature(&signature_iter))) + { + DBG("type: %s", type); + if (DBUS_TYPE_VARIANT != type[0] && DBUS_TYPE_INVALID == type[1]) + result = _basic_append_value(type[0], value, iter); + else if (DBUS_TYPE_ARRAY == type[0] || + DBUS_STRUCT_BEGIN_CHAR == type[0] || + DBUS_TYPE_VARIANT == type[0]) + { + ERR("Not a basic type"); + result = EINA_FALSE; + } + else + { + ERR("Unknown type %c", type[0]); + result = EINA_FALSE; + } + dbus_free(type); + if (!result || !dbus_signature_iter_next(&signature_iter)) break; + } + return result; +} diff --git a/src/lib/eldbus/eldbus_message_to_eina_value.c b/src/lib/eldbus/eldbus_message_to_eina_value.c index bac9860b5c..87258bf3d9 100644 --- a/src/lib/eldbus/eldbus_message_to_eina_value.c +++ b/src/lib/eldbus/eldbus_message_to_eina_value.c @@ -3,7 +3,7 @@ static void _message_iter_basic_array_to_eina_value(char type, Eina_Value *value, Eldbus_Message_Iter *iter); -static const Eina_Value_Type * +const Eina_Value_Type * _dbus_type_to_eina_value_type(char type) { switch (type) diff --git a/src/lib/eldbus/eldbus_model_arguments.c b/src/lib/eldbus/eldbus_model_arguments.c new file mode 100644 index 0000000000..f941a72bb2 --- /dev/null +++ b/src/lib/eldbus/eldbus_model_arguments.c @@ -0,0 +1,417 @@ +#ifdef HAVE_CONFIG_H +# include +#endif + +#include "eldbus_model_arguments_private.h" +#include "eldbus_model_private.h" + +#include +#include + +#define MY_CLASS ELDBUS_MODEL_ARGUMENTS_CLASS +#define MY_CLASS_NAME "Eldbus_Model_Arguments" + +#define ARGUMENT_FORMAT "arg%u" + +static void _eldbus_model_arguments_efl_model_base_properties_load(Eo *, Eldbus_Model_Arguments_Data *); +static void _eldbus_model_arguments_efl_model_base_children_load(Eo *, Eldbus_Model_Arguments_Data *); +static void _eldbus_model_arguments_unload(Eldbus_Model_Arguments_Data *); +static Eina_Bool _eldbus_model_arguments_is_input_argument(Eldbus_Model_Arguments_Data *, const char *); +static Eina_Bool _eldbus_model_arguments_is_output_argument(Eldbus_Model_Arguments_Data *, const char *); +static Eina_Bool _eldbus_model_arguments_property_set(Eldbus_Model_Arguments_Data *, Eina_Value *, const char *); +static unsigned int _eldbus_model_arguments_argument_index_get(Eldbus_Model_Arguments_Data *, const char *); + +static void +_eldbus_model_arguments_hash_free(Eina_Value *value) +{ + eina_value_free(value); +} + +static Eo_Base* +_eldbus_model_arguments_eo_base_constructor(Eo *obj, Eldbus_Model_Arguments_Data *pd) +{ + obj = eo_do_super_ret(obj, MY_CLASS, obj, eo_constructor()); + + pd->obj = obj; + pd->load.status = EFL_MODEL_LOAD_STATUS_UNLOADED; + pd->properties_array = NULL; + pd->properties_hash = eina_hash_string_superfast_new(EINA_FREE_CB(_eldbus_model_arguments_hash_free)); + pd->pending_list = NULL; + pd->proxy = NULL; + pd->arguments = NULL; + pd->name = NULL; + return obj; +} + +static void +_eldbus_model_arguments_constructor(Eo *obj EINA_UNUSED, + Eldbus_Model_Arguments_Data *pd, + Eldbus_Proxy *proxy, + const char *name, + const Eina_List *arguments) +{ + EINA_SAFETY_ON_NULL_RETURN(proxy); + EINA_SAFETY_ON_NULL_RETURN(name); + + pd->proxy = eldbus_proxy_ref(proxy); + pd->arguments = arguments; + pd->name = eina_stringshare_add(name); +} + +static void +_eldbus_model_arguments_eo_base_destructor(Eo *obj, Eldbus_Model_Arguments_Data *pd) +{ + _eldbus_model_arguments_unload(pd); + + eina_hash_free(pd->properties_hash); + + eina_stringshare_del(pd->name); + eldbus_proxy_unref(pd->proxy); + + eo_do_super(obj, MY_CLASS, eo_destructor()); +} + +static Efl_Model_Load_Status +_eldbus_model_arguments_efl_model_base_properties_get(Eo *obj EINA_UNUSED, + Eldbus_Model_Arguments_Data *pd, + Eina_Array * const* properties_array) +{ + EINA_SAFETY_ON_NULL_RETURN_VAL(pd, EFL_MODEL_LOAD_STATUS_ERROR); + EINA_SAFETY_ON_NULL_RETURN_VAL(pd->obj, EFL_MODEL_LOAD_STATUS_ERROR); + + if (!(pd->load.status & EFL_MODEL_LOAD_STATUS_LOADED_PROPERTIES)) + { + ERR("%s", "o not loaded."); + return EFL_MODEL_LOAD_STATUS_ERROR; + } + + *(Eina_Array**)properties_array = pd->properties_array; + return pd->load.status; +} + +static void +_eldbus_model_arguments_efl_model_base_properties_load(Eo *obj EINA_UNUSED, Eldbus_Model_Arguments_Data *pd) +{ + unsigned int arguments_count; + unsigned int i; + + if (pd->load.status & EFL_MODEL_LOAD_STATUS_LOADED_PROPERTIES) + return; + + arguments_count = eina_list_count(pd->arguments); + + pd->properties_array = eina_array_new(arguments_count); + EINA_SAFETY_ON_NULL_RETURN(pd->properties_array); + + for (i = 0; i < arguments_count; ++i) + { + Eldbus_Introspection_Argument *arg; + const Eina_Value_Type *type; + Eina_Stringshare *name; + Eina_Value *value; + + name = eina_stringshare_printf(ARGUMENT_FORMAT, i); + if (!name) continue; + + eina_array_push(pd->properties_array, name); + + arg = eina_list_nth(pd->arguments, i); + type = _dbus_type_to_eina_value_type(arg->type[0]); + value = eina_value_new(type); + eina_hash_add(pd->properties_hash, name, value); + } + + efl_model_load_set(pd->obj, &pd->load, EFL_MODEL_LOAD_STATUS_LOADED_PROPERTIES); +} + +static Efl_Model_Load_Status +_eldbus_model_arguments_efl_model_base_property_set(Eo *obj EINA_UNUSED, + Eldbus_Model_Arguments_Data *pd, + const char *property, + Eina_Value const* value) +{ + Eina_Value *prop_value; + Eina_Bool ret; + + EINA_SAFETY_ON_NULL_RETURN_VAL(property, EFL_MODEL_LOAD_STATUS_ERROR); + EINA_SAFETY_ON_NULL_RETURN_VAL(value, EFL_MODEL_LOAD_STATUS_ERROR); + DBG("(%p): property=%s", obj, property); + + if (!(pd->load.status & EFL_MODEL_LOAD_STATUS_LOADED_PROPERTIES)) + return EFL_MODEL_LOAD_STATUS_ERROR; + + if (!_eldbus_model_arguments_is_input_argument(pd, property)) + { + WRN("Property (argument) not found or it is for output only: %s", property); + return EFL_MODEL_LOAD_STATUS_ERROR; + } + + prop_value = eina_hash_find(pd->properties_hash, property); + EINA_SAFETY_ON_NULL_RETURN_VAL(prop_value, EFL_MODEL_LOAD_STATUS_ERROR); + + eina_value_flush(prop_value); + ret = eina_value_copy(value, prop_value); + EINA_SAFETY_ON_FALSE_RETURN_VAL(ret, EFL_MODEL_LOAD_STATUS_ERROR); + + return pd->load.status; +} + +static Efl_Model_Load_Status +_eldbus_model_arguments_efl_model_base_property_get(Eo *obj EINA_UNUSED, + Eldbus_Model_Arguments_Data *pd, + const char *property, + Eina_Value const ** value) +{ + EINA_SAFETY_ON_NULL_RETURN_VAL(property, EFL_MODEL_LOAD_STATUS_ERROR); + EINA_SAFETY_ON_NULL_RETURN_VAL(value, EFL_MODEL_LOAD_STATUS_ERROR); + DBG("(%p): property=%s", obj, property); + + if (!(pd->load.status & EFL_MODEL_LOAD_STATUS_LOADED_PROPERTIES)) + return EFL_MODEL_LOAD_STATUS_ERROR; + + if (!_eldbus_model_arguments_is_output_argument(pd, property)) + { + WRN("Property (argument) not found or it is for input only: %s", property); + return EFL_MODEL_LOAD_STATUS_ERROR; + } + + *value = eina_hash_find(pd->properties_hash, property); + EINA_SAFETY_ON_NULL_RETURN_VAL(*value, EFL_MODEL_LOAD_STATUS_ERROR); + + return pd->load.status; +} + +static void +_eldbus_model_arguments_efl_model_base_load(Eo *obj, Eldbus_Model_Arguments_Data *pd EINA_UNUSED) +{ + eo_do(obj, efl_model_properties_load()); + eo_do(obj, efl_model_children_load()); +} + +static Efl_Model_Load_Status +_eldbus_model_arguments_efl_model_base_load_status_get(Eo *obj EINA_UNUSED, Eldbus_Model_Arguments_Data *pd) +{ + return pd->load.status; +} + +static void +_eldbus_model_arguments_efl_model_base_unload(Eo *obj EINA_UNUSED, Eldbus_Model_Arguments_Data *pd) +{ + _eldbus_model_arguments_unload(pd); + + efl_model_load_set(pd->obj, &pd->load, EFL_MODEL_LOAD_STATUS_UNLOADED); +} + +static Eo * +_eldbus_model_arguments_efl_model_base_child_add(Eo *obj EINA_UNUSED, Eldbus_Model_Arguments_Data *pd EINA_UNUSED) +{ + return NULL; +} + +static Efl_Model_Load_Status +_eldbus_model_arguments_efl_model_base_child_del(Eo *obj EINA_UNUSED, + Eldbus_Model_Arguments_Data *pd EINA_UNUSED, + Eo *child EINA_UNUSED) +{ + return EFL_MODEL_LOAD_STATUS_ERROR; +} + +static Efl_Model_Load_Status +_eldbus_model_arguments_efl_model_base_children_slice_get(Eo *obj EINA_UNUSED, + Eldbus_Model_Arguments_Data *pd, + unsigned start EINA_UNUSED, + unsigned count EINA_UNUSED, + Eina_Accessor **children_accessor) +{ + *children_accessor = NULL; + return pd->load.status; +} + +static Efl_Model_Load_Status +_eldbus_model_arguments_efl_model_base_children_count_get(Eo *obj EINA_UNUSED, + Eldbus_Model_Arguments_Data *pd, + unsigned *children_count) +{ + *children_count = 0; + return pd->load.status; +} + +static void +_eldbus_model_arguments_efl_model_base_children_load(Eo *obj EINA_UNUSED, Eldbus_Model_Arguments_Data *pd) +{ + if (pd->load.status & EFL_MODEL_LOAD_STATUS_LOADED_CHILDREN) + return; + + efl_model_load_set(pd->obj, &pd->load, EFL_MODEL_LOAD_STATUS_LOADED_CHILDREN); +} + +static const char * +_eldbus_model_arguments_name_get(Eo *obj EINA_UNUSED, Eldbus_Model_Arguments_Data *pd) +{ + return pd->name; +} + +static void +_eldbus_model_arguments_unload(Eldbus_Model_Arguments_Data *pd) +{ + Eldbus_Pending *pending; + + EINA_SAFETY_ON_NULL_RETURN(pd); + + EINA_LIST_FREE(pd->pending_list, pending) + eldbus_pending_cancel(pending); + + if (pd->properties_array) + { + Eina_Stringshare *property; + Eina_Array_Iterator it; + unsigned int i; + + EINA_ARRAY_ITER_NEXT(pd->properties_array, i, property, it) + eina_stringshare_del(property); + eina_array_free(pd->properties_array); + pd->properties_array = NULL; + } + + eina_hash_free_buckets(pd->properties_hash); +} + +Eina_Bool +eldbus_model_arguments_process_arguments(Eldbus_Model_Arguments_Data *pd, + const Eldbus_Message *msg, + Eldbus_Pending *pending) +{ + const Eldbus_Introspection_Argument *argument; + const char *error_name, *error_text; + const Eina_List *it; + Eina_Value *value_struct; + Eina_Array *changed_properties; + unsigned int i = 0; + Eina_Bool result = EINA_FALSE; + + pd->pending_list = eina_list_remove(pd->pending_list, pending); + if (eldbus_message_error_get(msg, &error_name, &error_text)) + { + ERR("%s: %s", error_name, error_text); + efl_model_error_notify(pd->obj); + return EINA_FALSE; + } + + value_struct = eldbus_message_to_eina_value(msg); + if (value_struct == NULL) + { + INF("%s", "No output arguments"); + return EINA_TRUE; + } + + changed_properties = eina_array_new(1); + + EINA_LIST_FOREACH(pd->arguments, it, argument) + { + if (ELDBUS_INTROSPECTION_ARGUMENT_DIRECTION_IN != argument->direction) + { + const Eina_Stringshare *property; + Eina_Bool ret; + + property = eina_array_data_get(pd->properties_array, i); + EINA_SAFETY_ON_NULL_GOTO(property, on_error); + + ret = _eldbus_model_arguments_property_set(pd, value_struct, property); + EINA_SAFETY_ON_FALSE_GOTO(ret, on_error); + + ret = eina_array_push(changed_properties, property); + EINA_SAFETY_ON_FALSE_GOTO(ret, on_error); + } + + ++i; + } + + if (eina_array_count(changed_properties)) + { + Efl_Model_Property_Event evt = {.changed_properties = changed_properties}; + eo_do(pd->obj, eo_event_callback_call(EFL_MODEL_BASE_EVENT_PROPERTIES_CHANGED, &evt)); + } + + result = EINA_TRUE; + +on_error: + eina_array_free(changed_properties); + eina_value_free(value_struct); + + return result; +} + +static Eina_Bool +_eldbus_model_arguments_property_set(Eldbus_Model_Arguments_Data *pd, + Eina_Value *value_struct, + const char *property) +{ + Eina_Value *prop_value; + Eina_Value value; + Eina_Bool ret; + + prop_value = eina_hash_find(pd->properties_hash, property); + EINA_SAFETY_ON_NULL_RETURN_VAL(prop_value, EINA_FALSE); + + ret = eina_value_struct_value_get(value_struct, "arg0", &value); + EINA_SAFETY_ON_FALSE_RETURN_VAL(ret, EINA_FALSE); + + eina_value_flush(prop_value); + ret = eina_value_copy(&value, prop_value); + eina_value_flush(&value); + EINA_SAFETY_ON_FALSE_RETURN_VAL(ret, EINA_FALSE); + + return ret; +} + +static Eina_Bool +_eldbus_model_arguments_is(Eldbus_Model_Arguments_Data *pd, + const char *argument, + Eldbus_Introspection_Argument_Direction direction) +{ + Eldbus_Introspection_Argument *argument_introspection; + unsigned int i; + + i = _eldbus_model_arguments_argument_index_get(pd, argument); + if (i >= eina_array_count(pd->properties_array)) + { + WRN("Argument not found: %s", argument); + return false; + } + + argument_introspection = eina_list_nth(pd->arguments, i); + EINA_SAFETY_ON_NULL_RETURN_VAL(argument_introspection, EINA_FALSE); + + return argument_introspection->direction == direction; +} + +static Eina_Bool +_eldbus_model_arguments_is_input_argument(Eldbus_Model_Arguments_Data *pd, const char *argument) +{ + return _eldbus_model_arguments_is(pd, argument, ELDBUS_INTROSPECTION_ARGUMENT_DIRECTION_IN); +} + +static Eina_Bool +_eldbus_model_arguments_is_output_argument(Eldbus_Model_Arguments_Data *pd, const char *argument) +{ + return _eldbus_model_arguments_is(pd, argument, ELDBUS_INTROSPECTION_ARGUMENT_DIRECTION_OUT) || + _eldbus_model_arguments_is(pd, argument, ELDBUS_INTROSPECTION_ARGUMENT_DIRECTION_NONE); +} + +static unsigned int +_eldbus_model_arguments_argument_index_get(Eldbus_Model_Arguments_Data *pd, const char *argument) +{ + const Eina_Stringshare *name; + Eina_Array_Iterator it; + unsigned int i = 0; + + EINA_ARRAY_ITER_NEXT(pd->properties_array, i, name, it) + { + if (strcmp(name, argument) == 0) + return i; + } + + return ++i; +} + +#include "eldbus_model_arguments.eo.c" diff --git a/src/lib/eldbus/eldbus_model_arguments.eo b/src/lib/eldbus/eldbus_model_arguments.eo new file mode 100644 index 0000000000..64f39f9ce2 --- /dev/null +++ b/src/lib/eldbus/eldbus_model_arguments.eo @@ -0,0 +1,40 @@ +class Eldbus.Model_Arguments (Eo.Base, Efl.Model.Base) { + legacy_prefix: null; + methods { + constructor { + [[Custom Eldbus_Model_Arguments constructor. + + @since 1.16]] + params { + @in proxy: Eldbus_Proxy*; [[Eldbus proxy]] + @in name: const(char)*; [[Name]] + @in arguments: const(Eina_List)*; [[The introspected arguments]] + } + } + @property name { + get {} + values { + value: const(char)*; + } + } + } + implements { + Eo.Base.constructor; + Eo.Base.destructor; + Efl.Model.Base.properties.get; + Efl.Model.Base.properties_load; + Efl.Model.Base.property.set; + Efl.Model.Base.property.get; + Efl.Model.Base.load; + Efl.Model.Base.load_status.get; + Efl.Model.Base.unload; + Efl.Model.Base.child_add; + Efl.Model.Base.child_del; + Efl.Model.Base.children_slice.get; + Efl.Model.Base.children_count.get; + Efl.Model.Base.children_load; + } + constructors { + .constructor; + } +} diff --git a/src/lib/eldbus/eldbus_model_arguments_private.h b/src/lib/eldbus/eldbus_model_arguments_private.h new file mode 100644 index 0000000000..08e65d8437 --- /dev/null +++ b/src/lib/eldbus/eldbus_model_arguments_private.h @@ -0,0 +1,28 @@ +#ifndef _ELDBUS_MODEL_ARGUMENTS_PRIVATE_H +#define _ELDBUS_MODEL_ARGUMENTS_PRIVATE_H + +#include "Eldbus_Model.h" + +#include + +typedef struct _Eldbus_Model_Arguments_Data Eldbus_Model_Arguments_Data; + +/** + * eldbus_model_arguments + */ +struct _Eldbus_Model_Arguments_Data +{ + Eo *obj; + Efl_Model_Load load; + Eldbus_Proxy *proxy; + Eina_Array *properties_array; + Eina_Hash *properties_hash; + Eina_Stringshare *name; + Eina_List *pending_list; + const Eina_List *arguments; + Eina_Value tmp_value; +}; + +Eina_Bool eldbus_model_arguments_process_arguments(Eldbus_Model_Arguments_Data *, const Eldbus_Message *, Eldbus_Pending *); + +#endif diff --git a/src/lib/eldbus/eldbus_model_connection.c b/src/lib/eldbus/eldbus_model_connection.c new file mode 100644 index 0000000000..6325a5ac0b --- /dev/null +++ b/src/lib/eldbus/eldbus_model_connection.c @@ -0,0 +1,361 @@ +#ifdef HAVE_CONFIG_H +# include +#endif + +#include "eldbus_model_connection_private.h" +#include "eldbus_model_private.h" + +#define MY_CLASS ELDBUS_MODEL_CONNECTION_CLASS +#define MY_CLASS_NAME "Eldbus_Model_Connection" + +#define UNIQUE_NAME_PROPERTY "unique_name" + +static void _eldbus_model_connection_efl_model_base_properties_load(Eo *, Eldbus_Model_Connection_Data *); +static void _eldbus_model_connection_efl_model_base_children_load(Eo *, Eldbus_Model_Connection_Data *); +static void _eldbus_model_connection_names_list_cb(void *, const Eldbus_Message *, Eldbus_Pending *); +static void _eldbus_model_connection_connect(Eldbus_Model_Connection_Data *); +static void _eldbus_model_connection_disconnect(Eldbus_Model_Connection_Data *); +static void _eldbus_model_connection_clear(Eldbus_Model_Connection_Data *); + +static Eo_Base* +_eldbus_model_connection_eo_base_constructor(Eo *obj, Eldbus_Model_Connection_Data *pd) +{ + obj = eo_do_super_ret(obj, MY_CLASS, obj, eo_constructor()); + + pd->obj = obj; + pd->load.status = EFL_MODEL_LOAD_STATUS_UNLOADED; + pd->connection = NULL; + pd->properties_array = NULL; + pd->children_list = NULL; + pd->type = ELDBUS_CONNECTION_TYPE_UNKNOWN; + pd->address = NULL; + pd->private = false; + pd->unique_name = NULL; + pd->pending_list = NULL; + + return obj; +} + +static void +_eldbus_model_connection_constructor(Eo *obj EINA_UNUSED, + Eldbus_Model_Connection_Data *pd, + int type, + const char* address, + Eina_Bool private) +{ + pd->type = type; + pd->address = eina_stringshare_add(address); + pd->private = private; +} + +static void +_eldbus_model_connection_eo_base_destructor(Eo *obj, Eldbus_Model_Connection_Data *pd) +{ + eina_stringshare_del(pd->address); + + _eldbus_model_connection_clear(pd); + + eo_do_super(obj, MY_CLASS, eo_destructor()); +} + +static Efl_Model_Load_Status +_eldbus_model_connection_efl_model_base_properties_get(Eo *obj EINA_UNUSED, + Eldbus_Model_Connection_Data *pd, + Eina_Array * const* properties_array) +{ + EINA_SAFETY_ON_NULL_RETURN_VAL(pd, EFL_MODEL_LOAD_STATUS_ERROR); + EINA_SAFETY_ON_NULL_RETURN_VAL(pd->obj, EFL_MODEL_LOAD_STATUS_ERROR); + + if (pd->properties_array == NULL) + { + Eina_Bool ret; + + pd->properties_array = eina_array_new(1); + EINA_SAFETY_ON_NULL_RETURN_VAL(pd->properties_array, EFL_MODEL_LOAD_STATUS_ERROR); + + ret = eina_array_push(pd->properties_array, UNIQUE_NAME_PROPERTY); + EINA_SAFETY_ON_FALSE_RETURN_VAL(ret, EFL_MODEL_LOAD_STATUS_ERROR); + } + + *(Eina_Array**)properties_array = pd->properties_array; + return pd->load.status; +} + +static void +_eldbus_model_connection_efl_model_base_properties_load(Eo *obj EINA_UNUSED, Eldbus_Model_Connection_Data *pd) +{ + const char *unique_name; + Eina_Bool ret; + + if (pd->load.status & EFL_MODEL_LOAD_STATUS_LOADED_PROPERTIES) + return; + + if (!pd->connection) + _eldbus_model_connection_connect(pd); + + pd->unique_name = eina_value_new(EINA_VALUE_TYPE_STRING); + EINA_SAFETY_ON_NULL_RETURN(pd->unique_name); + + unique_name = eldbus_connection_unique_name_get(pd->connection); + ret = eina_value_set(pd->unique_name, unique_name); + EINA_SAFETY_ON_FALSE_RETURN(ret); + + efl_model_load_set(pd->obj, &pd->load, EFL_MODEL_LOAD_STATUS_LOADED_PROPERTIES); +} + +static Efl_Model_Load_Status +_eldbus_model_connection_efl_model_base_property_set(Eo *obj EINA_UNUSED, + Eldbus_Model_Connection_Data *pd EINA_UNUSED, + const char *property EINA_UNUSED, + Eina_Value const* value EINA_UNUSED) +{ + EINA_SAFETY_ON_NULL_RETURN_VAL(property, EFL_MODEL_LOAD_STATUS_ERROR); + DBG("(%p): property=%s", obj, property); + return EFL_MODEL_LOAD_STATUS_ERROR; +} + +static Efl_Model_Load_Status +_eldbus_model_connection_efl_model_base_property_get(Eo *obj EINA_UNUSED, + Eldbus_Model_Connection_Data *pd, + const char *property, + Eina_Value const **value) +{ + EINA_SAFETY_ON_NULL_RETURN_VAL(property, EFL_MODEL_LOAD_STATUS_ERROR); + EINA_SAFETY_ON_NULL_RETURN_VAL(value, EFL_MODEL_LOAD_STATUS_ERROR); + DBG("(%p): property=%s", obj, property); + + if (!(pd->load.status & EFL_MODEL_LOAD_STATUS_LOADED_PROPERTIES)) + return EFL_MODEL_LOAD_STATUS_ERROR; + + if (strcmp(property, UNIQUE_NAME_PROPERTY) != 0) + return EFL_MODEL_LOAD_STATUS_ERROR; + + *value = pd->unique_name; + return pd->load.status; +} + +static void +_eldbus_model_connection_efl_model_base_load(Eo *obj EINA_UNUSED, Eldbus_Model_Connection_Data *pd) +{ + if (!pd->connection) + _eldbus_model_connection_connect(pd); + + _eldbus_model_connection_efl_model_base_properties_load(obj, pd); + _eldbus_model_connection_efl_model_base_children_load(obj, pd); +} + +static Efl_Model_Load_Status +_eldbus_model_connection_efl_model_base_load_status_get(Eo *obj EINA_UNUSED, Eldbus_Model_Connection_Data *pd) +{ + return pd->load.status; +} + +static void +_eldbus_model_connection_efl_model_base_unload(Eo *obj EINA_UNUSED, Eldbus_Model_Connection_Data *pd) +{ + _eldbus_model_connection_clear(pd); + + efl_model_load_set(pd->obj, &pd->load, EFL_MODEL_LOAD_STATUS_UNLOADED); +} + +static Eo * +_eldbus_model_connection_efl_model_base_child_add(Eo *obj EINA_UNUSED, Eldbus_Model_Connection_Data *pd EINA_UNUSED) +{ + return NULL; +} + +static Efl_Model_Load_Status +_eldbus_model_connection_efl_model_base_child_del(Eo *obj EINA_UNUSED, + Eldbus_Model_Connection_Data *pd EINA_UNUSED, + Eo *child EINA_UNUSED) +{ + return EFL_MODEL_LOAD_STATUS_ERROR; +} + +static Efl_Model_Load_Status +_eldbus_model_connection_efl_model_base_children_slice_get(Eo *obj EINA_UNUSED, + Eldbus_Model_Connection_Data *pd, + unsigned start, + unsigned count, + Eina_Accessor **children_accessor) +{ + if (!(pd->load.status & EFL_MODEL_LOAD_STATUS_LOADED_CHILDREN)) + { + WRN("(%p): Children not loaded", obj); + *children_accessor = NULL; + return pd->load.status; + } + + *children_accessor = efl_model_list_slice(pd->children_list, start, count); + return pd->load.status; +} + +static Efl_Model_Load_Status +_eldbus_model_connection_efl_model_base_children_count_get(Eo *obj EINA_UNUSED, + Eldbus_Model_Connection_Data *pd, + unsigned *children_count) +{ + *children_count = eina_list_count(pd->children_list); + return pd->load.status; +} + +static void +_eldbus_model_connection_efl_model_base_children_load(Eo *obj EINA_UNUSED, Eldbus_Model_Connection_Data *pd) +{ + Eldbus_Pending *pending; + + if (pd->load.status & EFL_MODEL_LOAD_STATUS_LOADED_CHILDREN) + return; + + if (!pd->connection) + _eldbus_model_connection_connect(pd); + + pending = eldbus_names_list(pd->connection, &_eldbus_model_connection_names_list_cb, pd); + pd->pending_list = eina_list_append(pd->pending_list, pending); + + efl_model_load_set(pd->obj, &pd->load, EFL_MODEL_LOAD_STATUS_LOADING_CHILDREN); +} + +static const char * +_eldbus_model_connection_address_get(Eo *obj EINA_UNUSED, Eldbus_Model_Connection_Data *pd) +{ + return pd->address; +} + +static void +_eldbus_model_connection_address_set(Eo *obj EINA_UNUSED, Eldbus_Model_Connection_Data *pd, const char *value) +{ + eina_stringshare_del(pd->address); + pd->address = eina_stringshare_add(value); +} + +static Eina_Bool +_eldbus_model_connection_private_get(Eo *obj EINA_UNUSED, Eldbus_Model_Connection_Data *pd) +{ + return pd->private; +} + +static void +_eldbus_model_connection_private_set(Eo *obj EINA_UNUSED, Eldbus_Model_Connection_Data *pd, Eina_Bool value) +{ + pd->private = value; +} + +static int +_eldbus_model_connection_type_get(Eo *obj EINA_UNUSED, Eldbus_Model_Connection_Data *pd) +{ + return pd->type; +} + +static void +_eldbus_model_connection_type_set(Eo *obj EINA_UNUSED, Eldbus_Model_Connection_Data *pd, int value) +{ + pd->type = value; +} + +static void +_eldbus_model_connection_connect(Eldbus_Model_Connection_Data *pd) +{ + EINA_SAFETY_ON_NULL_RETURN(pd); + + if (ELDBUS_CONNECTION_TYPE_ADDRESS == pd->type) + { + if (pd->private) + pd->connection = eldbus_address_connection_get(pd->address); + else + pd->connection = eldbus_private_address_connection_get(pd->address); + } + else + { + if (pd->private) + pd->connection = eldbus_private_connection_get(pd->type); + else + pd->connection = eldbus_connection_get(pd->type); + } + + // TODO: Register for disconnection event + + EINA_SAFETY_ON_FALSE_RETURN(pd->connection != NULL); +} + +static void +_eldbus_model_connection_disconnect(Eldbus_Model_Connection_Data *pd) +{ + EINA_SAFETY_ON_NULL_RETURN(pd); + eldbus_connection_unref(pd->connection); + pd->connection = NULL; +} + +static void +_eldbus_model_connection_clear(Eldbus_Model_Connection_Data *pd) +{ + Eldbus_Pending *pending; + Eo *child; + + EINA_SAFETY_ON_NULL_RETURN(pd); + + if (!pd->connection) + return; + + eina_value_free(pd->unique_name); + pd->unique_name = NULL; + + EINA_LIST_FREE(pd->children_list, child) + eo_unref(child); + + EINA_LIST_FREE(pd->pending_list, pending) + eldbus_pending_cancel(pending); + + if (pd->properties_array) + { + eina_array_free(pd->properties_array); + pd->properties_array = NULL; + } + + _eldbus_model_connection_disconnect(pd); +} + +static void +_eldbus_model_connection_names_list_cb(void *data, + const Eldbus_Message *msg, + Eldbus_Pending *pending) +{ + Eldbus_Model_Connection_Data *pd = (Eldbus_Model_Connection_Data*)data; + const char *error_name, *error_text; + Eldbus_Message_Iter *array = NULL; + const char *bus; + unsigned int count; + + pd->pending_list = eina_list_remove(pd->pending_list, pending); + + if (eldbus_message_error_get(msg, &error_name, &error_text)) + { + ERR("%s: %s", error_name, error_text); + efl_model_error_notify(pd->obj); + return; + } + + if (!eldbus_message_arguments_get(msg, "as", &array)) + { + ERR("%s", "Error getting arguments."); + return; + } + + while (eldbus_message_iter_get_and_next(array, 's', &bus)) + { + DBG("(%p): bus = %s", pd->obj, bus); + + Eo *child = eo_add(ELDBUS_MODEL_OBJECT_CLASS, NULL, + eldbus_model_object_connection_constructor(pd->connection, bus, "/")); + + pd->children_list = eina_list_append(pd->children_list, child); + } + + efl_model_load_set(pd->obj, &pd->load, EFL_MODEL_LOAD_STATUS_LOADED_CHILDREN); + + count = eina_list_count(pd->children_list); + if (count) + eo_do(pd->obj, eo_event_callback_call(EFL_MODEL_BASE_EVENT_CHILDREN_COUNT_CHANGED, &count)); +} + +#include "eldbus_model_connection.eo.c" diff --git a/src/lib/eldbus/eldbus_model_connection.eo b/src/lib/eldbus/eldbus_model_connection.eo new file mode 100644 index 0000000000..3f2e5ab389 --- /dev/null +++ b/src/lib/eldbus/eldbus_model_connection.eo @@ -0,0 +1,49 @@ +class Eldbus.Model_Connection (Eo.Base, Efl.Model.Base) { + legacy_prefix: null; + methods { + constructor { + [[Custom Eldbus_Model_Connection constructor. + + @since 1.16]] + params { + @in type: int; [[The connection type]] + @in address: const(char)*; [[Remote address of dbus]] + @in private_: bool; [[Non shared dbus connection]] + } + } + @property type { + values { + value: int; + } + } + @property address { + values { + value: const(char)*; + } + } + @property private { + values { + value: bool; + } + } + } + implements { + Eo.Base.constructor; + Eo.Base.destructor; + Efl.Model.Base.properties.get; + Efl.Model.Base.properties_load; + Efl.Model.Base.property.set; + Efl.Model.Base.property.get; + Efl.Model.Base.load; + Efl.Model.Base.load_status.get; + Efl.Model.Base.unload; + Efl.Model.Base.child_add; + Efl.Model.Base.child_del; + Efl.Model.Base.children_slice.get; + Efl.Model.Base.children_count.get; + Efl.Model.Base.children_load; + } + constructors { + .constructor; + } +} diff --git a/src/lib/eldbus/eldbus_model_connection_private.h b/src/lib/eldbus/eldbus_model_connection_private.h new file mode 100644 index 0000000000..4123f92b07 --- /dev/null +++ b/src/lib/eldbus/eldbus_model_connection_private.h @@ -0,0 +1,28 @@ +#ifndef _ELDBUS_MODEL_CONNECTION_PRIVATE_H +#define _ELDBUS_MODEL_CONNECTION_PRIVATE_H + +#include "Eldbus_Model.h" + +#include + +typedef struct _Eldbus_Model_Connection_Data Eldbus_Model_Connection_Data; + +/** + * eldbus_model_connection + */ +struct _Eldbus_Model_Connection_Data +{ + Eo *obj; + Efl_Model_Load load; + Eldbus_Connection *connection; + Eina_Array *properties_array; + Eina_List *children_list; + Eldbus_Connection_Type type; + Eina_Stringshare *address; + bool private; + Eina_Value *unique_name; + Eina_List *pending_list; +}; + +#endif + diff --git a/src/lib/eldbus/eldbus_model_method.c b/src/lib/eldbus/eldbus_model_method.c new file mode 100644 index 0000000000..69e55cc590 --- /dev/null +++ b/src/lib/eldbus/eldbus_model_method.c @@ -0,0 +1,102 @@ +#ifdef HAVE_CONFIG_H +# include +#endif + +#include "eldbus_model_arguments_private.h" +#include "eldbus_model_method_private.h" +#include "eldbus_model_private.h" +#include "eldbus_proxy.h" + +#include + +#include + +#define MY_CLASS ELDBUS_MODEL_METHOD_CLASS +#define MY_CLASS_NAME "Eldbus_Model_Method" + +static void _eldbus_model_method_call_cb(void *, const Eldbus_Message *, Eldbus_Pending *); + +static Eo_Base* +_eldbus_model_method_eo_base_constructor(Eo *obj, Eldbus_Model_Method_Data *pd) +{ + obj = eo_do_super_ret(obj, MY_CLASS, obj, eo_constructor()); + + pd->obj = obj; + pd->method = NULL; + return obj; +} + +static void +_eldbus_model_method_constructor(Eo *obj EINA_UNUSED, + Eldbus_Model_Method_Data *pd, + Eldbus_Proxy *proxy, + const Eldbus_Introspection_Method *method) +{ + EINA_SAFETY_ON_NULL_RETURN(proxy); + EINA_SAFETY_ON_NULL_RETURN(method); + + eo_do_super(obj, MY_CLASS, eldbus_model_arguments_constructor(proxy, method->name, method->arguments)); + + pd->method = method; +} + +static Efl_Model_Load_Status +_eldbus_model_method_call(Eo *obj EINA_UNUSED, Eldbus_Model_Method_Data *pd EINA_UNUSED) +{ + Eldbus_Model_Arguments_Data *data = eo_data_scope_get(obj, ELDBUS_MODEL_ARGUMENTS_CLASS); + Eldbus_Message *msg = eldbus_proxy_method_call_new(data->proxy, data->name); + Eldbus_Message_Iter *iter = eldbus_message_iter_get(msg); + const Eldbus_Introspection_Argument *argument; + const Eina_List *it; + Eldbus_Pending *pending; + unsigned int i = 0; + + EINA_LIST_FOREACH(data->arguments, it, argument) + { + const Eina_Stringshare *name; + const Eina_Value *value; + const char *signature; + Eina_Bool ret; + + if (ELDBUS_INTROSPECTION_ARGUMENT_DIRECTION_IN != argument->direction) + continue; + + name = eina_array_data_get(data->properties_array, i); + if (!name) continue; + EINA_SAFETY_ON_NULL_GOTO(name, on_error); + + value = eina_hash_find(data->properties_hash, name); + EINA_SAFETY_ON_NULL_GOTO(value, on_error); + + signature = argument->type; + if (dbus_type_is_basic(signature[0])) + ret = _message_iter_from_eina_value(signature, iter, value); + else + ret = _message_iter_from_eina_value_struct(signature, iter, value); + + EINA_SAFETY_ON_FALSE_GOTO(ret, on_error); + + ++i; + } + + pending = eldbus_proxy_send(data->proxy, msg, _eldbus_model_method_call_cb, pd, -1); + data->pending_list = eina_list_append(data->pending_list, pending); + + return data->load.status; + +on_error: + eldbus_message_unref(msg); + return EFL_MODEL_LOAD_STATUS_ERROR; +} + +static void +_eldbus_model_method_call_cb(void *data, const Eldbus_Message *msg, Eldbus_Pending *pending) +{ + Eldbus_Model_Method_Data *pd = (Eldbus_Model_Method_Data*)data; + Eldbus_Model_Arguments_Data *args_data = eo_data_scope_get(pd->obj, ELDBUS_MODEL_ARGUMENTS_CLASS); + + if (eldbus_model_arguments_process_arguments(args_data, msg, pending)) + eo_do(pd->obj, eo_event_callback_call(ELDBUS_MODEL_METHOD_EVENT_SUCCESSFUL_CALL, NULL)); +} + +#include "eldbus_model_method.eo.c" diff --git a/src/lib/eldbus/eldbus_model_method.eo b/src/lib/eldbus/eldbus_model_method.eo new file mode 100644 index 0000000000..8b9d71411f --- /dev/null +++ b/src/lib/eldbus/eldbus_model_method.eo @@ -0,0 +1,35 @@ +class Eldbus.Model_Method (Eldbus.Model_Arguments) { + legacy_prefix: null; + methods { + constructor { + [[Custom Eldbus_Model_Method constructor. + + @since 1.16]] + params { + @in proxy: Eldbus_Proxy*; [[Eldbus proxy]] + @in method: const(Eldbus_Introspection_Method)*; [[The introspected method]] + } + } + call { + [[Calls the method. The properties must have been set. + + The event EMODEL_EVENT_PROPERTIES_CHANGED is raised for output arguments (properties). + + The event ELDBUS_MODEL_METHOD_EVENT_METHOD_CALLED is raised for a successful call. Otherwise + the event EMODEL_EVENT_LOAD_STATUS with the status set to EMODEL_LOAD_STATUS_ERROR is raised. + + @since 1.16]] + return: Efl.Model.Load_Status; [[#Emodel_Load_Status on success, #EMODEL_LOAD_STATUS_ERROR otherwise.]] + } + } + implements { + Eo.Base.constructor; + } + constructors { + .constructor; + } + events { + successful,call; [[Event dispatched for a successful method call.]] + } + +} diff --git a/src/lib/eldbus/eldbus_model_method_private.h b/src/lib/eldbus/eldbus_model_method_private.h new file mode 100644 index 0000000000..5092b50b53 --- /dev/null +++ b/src/lib/eldbus/eldbus_model_method_private.h @@ -0,0 +1,18 @@ +#ifndef _ELDBUS_MODEL_METHOD_PRIVATE_H +#define _ELDBUS_MODEL_METHOD_PRIVATE_H + +#include "Eldbus_Model.h" + +typedef struct _Eldbus_Model_Method_Data Eldbus_Model_Method_Data; + +/** + * eldbus_model_method + */ +struct _Eldbus_Model_Method_Data +{ + Eo *obj; + const Eldbus_Introspection_Method *method; +}; + +#endif + diff --git a/src/lib/eldbus/eldbus_model_object.c b/src/lib/eldbus/eldbus_model_object.c new file mode 100644 index 0000000000..115330e956 --- /dev/null +++ b/src/lib/eldbus/eldbus_model_object.c @@ -0,0 +1,542 @@ +#ifdef HAVE_CONFIG_H +# include +#endif + +#include "eldbus_model_object_private.h" +#include "eldbus_model_private.h" + +#include + +#define MY_CLASS ELDBUS_MODEL_OBJECT_CLASS +#define MY_CLASS_NAME "Eldbus_Model_Object" + +#define UNIQUE_NAME_PROPERTY "unique_name" + +static void _eldbus_model_object_efl_model_base_properties_load(Eo *, Eldbus_Model_Object_Data *); +static void _eldbus_model_object_efl_model_base_children_load(Eo *, Eldbus_Model_Object_Data *); +static bool _eldbus_model_object_introspect(Eldbus_Model_Object_Data *, const char *, const char *); +static void _eldbus_model_object_introspect_cb(void *, const Eldbus_Message *, Eldbus_Pending *); +static void _eldbus_model_object_connect(Eldbus_Model_Object_Data *); +static void _eldbus_model_object_disconnect(Eldbus_Model_Object_Data *); +static void _eldbus_model_object_clear(Eldbus_Model_Object_Data *); +static void _eldbus_model_object_introspect_nodes(Eldbus_Model_Object_Data *, const char *, Eina_List *); +static char *_eldbus_model_object_concatenate_path(const char *, const char *); +static void _eldbus_model_object_create_children(Eldbus_Model_Object_Data *, Eldbus_Object *, Eina_List *); + +static Eo_Base* +_eldbus_model_object_eo_base_constructor(Eo *obj, Eldbus_Model_Object_Data *pd) +{ + obj = eo_do_super_ret(obj, MY_CLASS, obj, eo_constructor()); + + pd->obj = obj; + pd->load.status = EFL_MODEL_LOAD_STATUS_UNLOADED; + pd->connection = NULL; + pd->object_list = NULL; + pd->properties_array = NULL; + pd->children_list = NULL; + pd->type = ELDBUS_CONNECTION_TYPE_UNKNOWN; + pd->address = NULL; + pd->private = false; + pd->bus = NULL; + pd->path = NULL; + pd->unique_name = NULL; + pd->pending_list = NULL; + pd->introspection = NULL; + + return obj; +} + +static void +_eldbus_model_object_constructor(Eo *obj EINA_UNUSED, + Eldbus_Model_Object_Data *pd, + int type, + const char* address, + Eina_Bool private, + const char* bus, + const char* path) +{ + EINA_SAFETY_ON_NULL_RETURN(bus); + EINA_SAFETY_ON_NULL_RETURN(path); + + pd->type = type; + pd->address = eina_stringshare_add(address); + pd->private = private; + pd->bus = eina_stringshare_add(bus); + pd->path = eina_stringshare_add(path); +} + +static void +_eldbus_model_object_connection_constructor(Eo *obj EINA_UNUSED, + Eldbus_Model_Object_Data *pd, + Eldbus_Connection *connection, + const char* bus, + const char* path) +{ + EINA_SAFETY_ON_NULL_RETURN(connection); + EINA_SAFETY_ON_NULL_RETURN(bus); + EINA_SAFETY_ON_NULL_RETURN(path); + + pd->connection = eldbus_connection_ref(connection); + pd->bus = eina_stringshare_add(bus); + pd->path = eina_stringshare_add(path); +} + +static void +_eldbus_model_object_eo_base_destructor(Eo *obj, Eldbus_Model_Object_Data *pd) +{ + eina_stringshare_del(pd->address); + eina_stringshare_del(pd->bus); + eina_stringshare_del(pd->path); + + _eldbus_model_object_clear(pd); + + eo_do_super(obj, MY_CLASS, eo_destructor()); +} + +static Efl_Model_Load_Status +_eldbus_model_object_efl_model_base_properties_get(Eo *obj EINA_UNUSED, + Eldbus_Model_Object_Data *pd, + Eina_Array * const* properties_array) +{ + EINA_SAFETY_ON_NULL_RETURN_VAL(pd, EFL_MODEL_LOAD_STATUS_ERROR); + EINA_SAFETY_ON_NULL_RETURN_VAL(pd->obj, EFL_MODEL_LOAD_STATUS_ERROR); + + if (pd->properties_array == NULL) + { + Eina_Bool ret; + + pd->properties_array = eina_array_new(1); + EINA_SAFETY_ON_NULL_RETURN_VAL(pd->properties_array, EFL_MODEL_LOAD_STATUS_ERROR); + + ret = eina_array_push(pd->properties_array, UNIQUE_NAME_PROPERTY); + EINA_SAFETY_ON_FALSE_RETURN_VAL(ret, EFL_MODEL_LOAD_STATUS_ERROR); + } + + *(Eina_Array**)properties_array = pd->properties_array; + return pd->load.status; +} + +static void +_eldbus_model_object_efl_model_base_properties_load(Eo *obj EINA_UNUSED, Eldbus_Model_Object_Data *pd) +{ + const char *unique_name; + Eina_Bool ret; + + if (pd->load.status & EFL_MODEL_LOAD_STATUS_LOADED_PROPERTIES) + return; + + if (!pd->connection) + _eldbus_model_object_connect(pd); + + pd->unique_name = eina_value_new(EINA_VALUE_TYPE_STRING); + EINA_SAFETY_ON_NULL_RETURN(pd->unique_name); + + unique_name = eldbus_connection_unique_name_get(pd->connection); + ret = eina_value_set(pd->unique_name, unique_name); + EINA_SAFETY_ON_FALSE_RETURN(ret); + + efl_model_load_set(pd->obj, &pd->load, EFL_MODEL_LOAD_STATUS_LOADED_PROPERTIES); +} + +static Efl_Model_Load_Status +_eldbus_model_object_efl_model_base_property_set(Eo *obj EINA_UNUSED, + Eldbus_Model_Object_Data *pd EINA_UNUSED, + const char *property EINA_UNUSED, + Eina_Value const* value EINA_UNUSED) +{ + EINA_SAFETY_ON_NULL_RETURN_VAL(property, EFL_MODEL_LOAD_STATUS_ERROR); + DBG("(%p): property=%s", obj, property); + return EFL_MODEL_LOAD_STATUS_ERROR; +} + +static Efl_Model_Load_Status +_eldbus_model_object_efl_model_base_property_get(Eo *obj EINA_UNUSED, + Eldbus_Model_Object_Data *pd, + const char *property, + Eina_Value const**value) +{ + EINA_SAFETY_ON_NULL_RETURN_VAL(property, EFL_MODEL_LOAD_STATUS_ERROR); + EINA_SAFETY_ON_NULL_RETURN_VAL(value, EFL_MODEL_LOAD_STATUS_ERROR); + DBG("(%p): property=%s", obj, property); + + if (!(pd->load.status & EFL_MODEL_LOAD_STATUS_LOADED_PROPERTIES)) + return EFL_MODEL_LOAD_STATUS_ERROR; + + if (strcmp(property, UNIQUE_NAME_PROPERTY) != 0) + return EFL_MODEL_LOAD_STATUS_ERROR; + + *value = pd->unique_name; + return pd->load.status; +} + +static void +_eldbus_model_object_efl_model_base_load(Eo *obj EINA_UNUSED, Eldbus_Model_Object_Data *pd) +{ + if (!pd->connection) + _eldbus_model_object_connect(pd); + + _eldbus_model_object_efl_model_base_properties_load(obj, pd); + _eldbus_model_object_efl_model_base_children_load(obj, pd); +} + +static Efl_Model_Load_Status +_eldbus_model_object_efl_model_base_load_status_get(Eo *obj EINA_UNUSED, Eldbus_Model_Object_Data *pd) +{ + return pd->load.status; +} + +static void +_eldbus_model_object_efl_model_base_unload(Eo *obj EINA_UNUSED, Eldbus_Model_Object_Data *pd) +{ + _eldbus_model_object_clear(pd); + + efl_model_load_set(pd->obj, &pd->load, EFL_MODEL_LOAD_STATUS_UNLOADED); +} + +static Eo * +_eldbus_model_object_efl_model_base_child_add(Eo *obj EINA_UNUSED, Eldbus_Model_Object_Data *pd EINA_UNUSED) +{ + return NULL; +} + +static Efl_Model_Load_Status +_eldbus_model_object_efl_model_base_child_del(Eo *obj EINA_UNUSED, + Eldbus_Model_Object_Data *pd EINA_UNUSED, + Eo *child EINA_UNUSED) +{ + return EFL_MODEL_LOAD_STATUS_ERROR; +} + +static Efl_Model_Load_Status +_eldbus_model_object_efl_model_base_children_slice_get(Eo *obj EINA_UNUSED, + Eldbus_Model_Object_Data *pd, + unsigned start, + unsigned count, + Eina_Accessor **children_accessor) +{ + if (!(pd->load.status & EFL_MODEL_LOAD_STATUS_LOADED_CHILDREN)) + { + WRN("(%p): Children not loaded", obj); + *children_accessor = NULL; + return pd->load.status; + } + else + WRN("(%p): Children already loaded", obj); + + *children_accessor = efl_model_list_slice(pd->children_list, start, count); + return pd->load.status; +} + +static Efl_Model_Load_Status +_eldbus_model_object_efl_model_base_children_count_get(Eo *obj EINA_UNUSED, + Eldbus_Model_Object_Data *pd, + unsigned *children_count) +{ + *children_count = eina_list_count(pd->children_list); + return pd->load.status; +} + +static void +_eldbus_model_object_efl_model_base_children_load(Eo *obj EINA_UNUSED, Eldbus_Model_Object_Data *pd) +{ + if (pd->load.status & EFL_MODEL_LOAD_STATUS_LOADED_CHILDREN) + return; + + if (!pd->connection) + _eldbus_model_object_connect(pd); + + if (!_eldbus_model_object_introspect(pd, pd->bus, pd->path)) + return; + + efl_model_load_set(pd->obj, &pd->load, EFL_MODEL_LOAD_STATUS_LOADING_CHILDREN); +} + +static const char * +_eldbus_model_object_address_get(Eo *obj EINA_UNUSED, Eldbus_Model_Object_Data *pd) +{ + return pd->address; +} + +static void +_eldbus_model_object_address_set(Eo *obj EINA_UNUSED, Eldbus_Model_Object_Data *pd, const char *value) +{ + eina_stringshare_del(pd->address); + pd->address = eina_stringshare_add(value); +} + +static Eina_Bool +_eldbus_model_object_private_get(Eo *obj EINA_UNUSED, Eldbus_Model_Object_Data *pd) +{ + return pd->private; +} + +static void +_eldbus_model_object_private_set(Eo *obj EINA_UNUSED, Eldbus_Model_Object_Data *pd, Eina_Bool value) +{ + pd->private = value; +} + +static int +_eldbus_model_object_type_get(Eo *obj EINA_UNUSED, Eldbus_Model_Object_Data *pd) +{ + return pd->type; +} + +static void +_eldbus_model_object_type_set(Eo *obj EINA_UNUSED, Eldbus_Model_Object_Data *pd, int value) +{ + pd->type = value; +} + +static const char * +_eldbus_model_object_bus_get(Eo *obj EINA_UNUSED, Eldbus_Model_Object_Data *pd) +{ + return pd->bus; +} + +static void +_eldbus_model_object_bus_set(Eo *obj EINA_UNUSED, Eldbus_Model_Object_Data *pd, const char *value) +{ + eina_stringshare_del(pd->bus); + pd->bus = eina_stringshare_add(value); +} + +static const char * +_eldbus_model_object_path_get(Eo *obj EINA_UNUSED, Eldbus_Model_Object_Data *pd) +{ + return pd->path; +} + +static void +_eldbus_model_object_path_set(Eo *obj EINA_UNUSED, Eldbus_Model_Object_Data *pd, const char *value) +{ + eina_stringshare_del(pd->path); + pd->path = eina_stringshare_add(value); +} + +static void +_eldbus_model_object_connect(Eldbus_Model_Object_Data *pd) +{ + EINA_SAFETY_ON_NULL_RETURN(pd); + + if (ELDBUS_CONNECTION_TYPE_ADDRESS == pd->type) + { + if (pd->private) + pd->connection = eldbus_address_connection_get(pd->address); + else + pd->connection = eldbus_private_address_connection_get(pd->address); + } + else + { + if (pd->private) + pd->connection = eldbus_private_connection_get(pd->type); + else + pd->connection = eldbus_connection_get(pd->type); + } + + // TODO: Register for disconnection event + + EINA_SAFETY_ON_FALSE_RETURN(NULL != pd->connection); +} + +static void +_eldbus_model_object_disconnect(Eldbus_Model_Object_Data *pd) +{ + EINA_SAFETY_ON_NULL_RETURN(pd); + eldbus_connection_unref(pd->connection); + pd->connection = NULL; +} + +static void +_eldbus_model_object_clear(Eldbus_Model_Object_Data *pd) +{ + Eldbus_Pending *pending; + Eldbus_Object *object; + Eo *child; + + EINA_SAFETY_ON_NULL_RETURN(pd); + if (!pd->connection) + return; + + eina_value_free(pd->unique_name); + pd->unique_name = NULL; + + EINA_LIST_FREE(pd->children_list, child) + eo_unref(child); + + EINA_LIST_FREE(pd->pending_list, pending) + eldbus_pending_cancel(pending); + + if (pd->properties_array) + { + eina_array_free(pd->properties_array); + pd->properties_array = NULL; + } + + EINA_LIST_FREE(pd->object_list, object) + eldbus_object_unref(object); + + if (pd->introspection) + { + eldbus_introspection_node_free(pd->introspection); + pd->introspection = NULL; + } + + _eldbus_model_object_disconnect(pd); +} + + +static bool +_eldbus_model_object_introspect(Eldbus_Model_Object_Data *pd, + const char *bus, + const char *path) +{ + Eldbus_Object *object; + Eldbus_Pending *pending; + + EINA_SAFETY_ON_NULL_RETURN_VAL(bus, false); + EINA_SAFETY_ON_NULL_RETURN_VAL(path, false); + + DBG("(%p) Introspecting: bus = %s, path = %s", pd->obj, bus, path); + + object = eldbus_object_get(pd->connection, bus, path); + if (!object) + { + ERR("(%p): Cannot get object: bus=%s, path=%s", pd->obj, bus, path); + return false; + } + pd->object_list = eina_list_append(pd->object_list, object); + + // TODO: Register for interface added/removed event + + pending = eldbus_object_introspect(object, &_eldbus_model_object_introspect_cb, pd); + eldbus_pending_data_set(pending, "object", object); + pd->pending_list = eina_list_append(pd->pending_list, pending); + return true; +} + +static void +_eldbus_model_object_introspect_cb(void *data, + const Eldbus_Message *msg, + Eldbus_Pending *pending) +{ + Eldbus_Model_Object_Data *pd = (Eldbus_Model_Object_Data*)data; + Eldbus_Object *object; + const char *error_name, *error_text; + const char *xml = NULL; + const char *current_path; + + pd->pending_list = eina_list_remove(pd->pending_list, pending); + object = eldbus_pending_data_get(pending, "object"); + + if (eldbus_message_error_get(msg, &error_name, &error_text)) + { + ERR("%s: %s", error_name, error_text); + efl_model_error_notify(pd->obj); + return; + } + + if (!eldbus_message_arguments_get(msg, "s", &xml)) + { + ERR("Error getting arguments."); + return; + } + EINA_SAFETY_ON_NULL_RETURN(xml); + + current_path = eldbus_object_path_get(object); + EINA_SAFETY_ON_NULL_RETURN(current_path); + + DBG("(%p): introspect of bus = %s, path = %s =>\n%s", pd->obj, pd->bus, current_path, xml); + + pd->introspection = eldbus_introspection_parse(xml); + EINA_SAFETY_ON_NULL_RETURN(pd->introspection); + + _eldbus_model_object_introspect_nodes(pd, current_path, pd->introspection->nodes); + _eldbus_model_object_create_children(pd, object, pd->introspection->interfaces); + + if (eina_list_count(pd->pending_list) == 0) + { + unsigned int count; + + efl_model_load_set(pd->obj, &pd->load, EFL_MODEL_LOAD_STATUS_LOADED_CHILDREN); + + count = eina_list_count(pd->children_list); + if (count) + eo_do(pd->obj, eo_event_callback_call(EFL_MODEL_BASE_EVENT_CHILDREN_COUNT_CHANGED, &count)); + } +} + +static void +_eldbus_model_object_introspect_nodes(Eldbus_Model_Object_Data *pd, const char *current_path, Eina_List *nodes) +{ + Eina_List *it; + Eldbus_Introspection_Node *node; + + EINA_SAFETY_ON_NULL_RETURN(pd); + EINA_SAFETY_ON_NULL_RETURN(current_path); + + EINA_LIST_FOREACH(nodes, it, node) + { + const char *relative_path; + char *absolute_path; + + relative_path = node->name; + if (!relative_path) continue; + + absolute_path = _eldbus_model_object_concatenate_path(current_path, relative_path); + if (!absolute_path) continue; + + _eldbus_model_object_introspect(pd, pd->bus, absolute_path); + + free(absolute_path); + } +} + +static char * +_eldbus_model_object_concatenate_path(const char *root_path, const char *relative_path) +{ + Eina_Strbuf *buffer; + char *absolute_path = NULL; + Eina_Bool ret; + + buffer = eina_strbuf_new(); + EINA_SAFETY_ON_NULL_RETURN_VAL(buffer, NULL); + + ret = eina_strbuf_append(buffer, root_path); + if (strcmp(root_path, "/") != 0) + ret = ret && eina_strbuf_append_char(buffer, '/'); + ret = ret && eina_strbuf_append(buffer, relative_path); + EINA_SAFETY_ON_FALSE_GOTO(ret, free_buffer); + + absolute_path = eina_strbuf_string_steal(buffer); + +free_buffer: + eina_strbuf_free(buffer); + return absolute_path; +} + +static void +_eldbus_model_object_create_children(Eldbus_Model_Object_Data *pd, Eldbus_Object *object, Eina_List *interfaces) +{ + Eldbus_Introspection_Interface *interface; + Eina_List *it; + const char *current_path; + + current_path = eldbus_object_path_get(object); + EINA_SAFETY_ON_NULL_RETURN(current_path); + + EINA_LIST_FOREACH(interfaces, it, interface) + { + Eo *child; + + WRN("(%p) Creating child: bus = %s, path = %s, interface = %s", pd->obj, pd->bus, current_path, interface->name); + + // TODO: increment reference to keep 'interface' in memory + child = eo_add_ref(ELDBUS_MODEL_PROXY_CLASS, NULL, + eldbus_model_proxy_constructor(object, interface)); + + pd->children_list = eina_list_append(pd->children_list, child); + } +} + +#include "eldbus_model_object.eo.c" diff --git a/src/lib/eldbus/eldbus_model_object.eo b/src/lib/eldbus/eldbus_model_object.eo new file mode 100644 index 0000000000..68cbc81d0f --- /dev/null +++ b/src/lib/eldbus/eldbus_model_object.eo @@ -0,0 +1,73 @@ +class Eldbus.Model_Object (Eo.Base, Efl.Model.Base) { + legacy_prefix: null; + data: Eldbus_Model_Object_Data; + methods { + constructor { + [[Custom Eldbus_Model_Object constructor. + + @since 1.16]] + params { + @in type: int; [[The connection type]] + @in address: const(char)*; [[Remote address of dbus]] + @in private_: bool; [[Non shared dbus connection]] + @in bus: const(char)*; [[DBus Name or unique-id]] + @in path: const(char)*; [[DBus path]] + } + } + connection_constructor { + [[Custom Eldbus_Model_Object connection_constructor. + + @since 1.16]] + params { + @in connection: Eldbus_Connection*; [[Eldbus connection]] + @in bus: const(char)*; [[DBus Name or unique-id]] + @in path: const(char)*; [[DBus path]] + } + } + @property type { + values { + value: int; + } + } + @property address { + values { + value: const(char)*; + } + } + @property private { + values { + value: bool; + } + } + @property bus { + values { + value: const(char)*; + } + } + @property path { + values { + value: const(char)*; + } + } + } + implements { + Eo.Base.constructor; + Eo.Base.destructor; + Efl.Model.Base.properties.get; + Efl.Model.Base.properties_load; + Efl.Model.Base.property.set; + Efl.Model.Base.property.get; + Efl.Model.Base.load; + Efl.Model.Base.load_status.get; + Efl.Model.Base.unload; + Efl.Model.Base.child_add; + Efl.Model.Base.child_del; + Efl.Model.Base.children_slice.get; + Efl.Model.Base.children_count.get; + Efl.Model.Base.children_load; + } + constructors { + .constructor; + .connection_constructor; + } +} diff --git a/src/lib/eldbus/eldbus_model_object_private.h b/src/lib/eldbus/eldbus_model_object_private.h new file mode 100644 index 0000000000..745492143e --- /dev/null +++ b/src/lib/eldbus/eldbus_model_object_private.h @@ -0,0 +1,32 @@ +#ifndef _ELDBUS_MODEL_OBJECT_PRIVATE_H +#define _ELDBUS_MODEL_OBJECT_PRIVATE_H + +#include "Eldbus_Model.h" + +#include + +typedef struct _Eldbus_Model_Object_Data Eldbus_Model_Object_Data; + +/** + * eldbus_model_object + */ +struct _Eldbus_Model_Object_Data +{ + Eo *obj; + Efl_Model_Load load; + Eldbus_Connection *connection; + Eina_List *object_list; + Eina_Array *properties_array; + Eina_List *children_list; + Eldbus_Connection_Type type; + Eina_Stringshare *address; + bool private; + Eina_Stringshare *bus; + Eina_Stringshare *path; + Eina_Value *unique_name; + Eina_List *pending_list; + Eldbus_Introspection_Node *introspection; +}; + +#endif + diff --git a/src/lib/eldbus/eldbus_model_private.h b/src/lib/eldbus/eldbus_model_private.h new file mode 100644 index 0000000000..784b4226b5 --- /dev/null +++ b/src/lib/eldbus/eldbus_model_private.h @@ -0,0 +1,37 @@ +#ifndef _ELDBUS_MODEL_PRIVATE_H +#define _ELDBUS_MODEL_PRIVATE_H + +#include "eldbus_private.h" + +#include +#include + +/* logging support */ +extern int eldbus_model_log_dom; + +#ifdef CRI +# undef CRI +#endif +#define CRI(...) EINA_LOG_DOM_CRIT(eldbus_model_log_dom, __VA_ARGS__) + +#ifdef ERR +# undef ERR +#endif +#define ERR(...) EINA_LOG_DOM_ERR(eldbus_model_log_dom, __VA_ARGS__) + +#ifdef WRN +# undef WRN +#endif +#define WRN(...) EINA_LOG_DOM_WARN(eldbus_model_log_dom, __VA_ARGS__) + +#ifdef INF +# undef INF +#endif +#define INF(...) EINA_LOG_DOM_INFO(eldbus_model_log_dom, __VA_ARGS__) + +#ifdef DBG +# undef DBG +#endif +#define DBG(...) EINA_LOG_DOM_DBG(eldbus_model_log_dom, __VA_ARGS__) + +#endif diff --git a/src/lib/eldbus/eldbus_model_proxy.c b/src/lib/eldbus/eldbus_model_proxy.c new file mode 100644 index 0000000000..ffae0fc4b5 --- /dev/null +++ b/src/lib/eldbus/eldbus_model_proxy.c @@ -0,0 +1,718 @@ +#ifdef HAVE_CONFIG_H +# include +#endif + +#include "eldbus_model_proxy_private.h" +#include "eldbus_model_private.h" + +#include + +#define MY_CLASS ELDBUS_MODEL_PROXY_CLASS +#define MY_CLASS_NAME "Eldbus_Model_Proxy" + +static void _eldbus_model_proxy_efl_model_base_properties_load(Eo *, Eldbus_Model_Proxy_Data *); +static void _eldbus_model_proxy_efl_model_base_children_load(Eo *, Eldbus_Model_Proxy_Data *); +static bool _eldbus_model_proxy_load(Eldbus_Model_Proxy_Data *); +static void _eldbus_model_proxy_unload(Eldbus_Model_Proxy_Data *); +static void _eldbus_model_proxy_property_get_all_cb(void *, const Eldbus_Message *, Eldbus_Pending *); +static void _eldbus_model_proxy_property_set_cb(void *, const Eldbus_Message *, Eldbus_Pending *); +static void _eldbus_model_proxy_start_monitor(Eldbus_Model_Proxy_Data *); +static void _eldbus_model_proxy_stop_monitor(Eldbus_Model_Proxy_Data *); +static void _eldbus_model_proxy_property_changed_cb(void *, Eldbus_Proxy *, void *); +static void _eldbus_model_proxy_property_invalidated_cb(void *, Eldbus_Proxy *, void *); +static bool _eldbus_model_proxy_is_property_writeable(Eldbus_Model_Proxy_Data *, const char *); +static bool _eldbus_model_proxy_is_property_readable(Eldbus_Model_Proxy_Data *, const char *); +static const char *_eldbus_model_proxy_property_type_get(Eldbus_Model_Proxy_Data *, const char *); +static void _eldbus_model_proxy_create_methods_children(Eldbus_Model_Proxy_Data *); +static void _eldbus_model_proxy_create_signals_children(Eldbus_Model_Proxy_Data *); + +typedef struct _Eldbus_Model_Proxy_Property_Set_Data Eldbus_Model_Proxy_Property_Set_Data; + +struct _Eldbus_Model_Proxy_Property_Set_Data +{ + Eldbus_Model_Proxy_Data *pd; + Eina_Stringshare *property; + Eina_Value value; +}; + +static Eldbus_Model_Proxy_Property_Set_Data * _eldbus_model_proxy_property_set_data_new(Eldbus_Model_Proxy_Data *, const char *, const Eina_Value *); +static void _eldbus_model_proxy_property_set_data_free(Eldbus_Model_Proxy_Property_Set_Data *); + +static void +_eldbus_model_proxy_hash_free(Eina_Value *value) +{ + eina_value_free(value); +} + +static Eo_Base* +_eldbus_model_proxy_eo_base_constructor(Eo *obj, Eldbus_Model_Proxy_Data *pd) +{ + obj = eo_do_super_ret(obj, MY_CLASS, obj, eo_constructor()); + + pd->obj = obj; + pd->load.status = EFL_MODEL_LOAD_STATUS_UNLOADED; + pd->object = NULL; + pd->proxy = NULL; + pd->properties_array = NULL; + pd->properties_hash = eina_hash_string_superfast_new(EINA_FREE_CB(_eldbus_model_proxy_hash_free)); + pd->children_list = NULL; + pd->name = NULL; + pd->pending_list = NULL; + pd->monitoring = false; + pd->interface = NULL; + + return obj; +} + +static void +_eldbus_model_proxy_constructor(Eo *obj EINA_UNUSED, + Eldbus_Model_Proxy_Data *pd, + Eldbus_Object *object, + const Eldbus_Introspection_Interface *interface) +{ + EINA_SAFETY_ON_NULL_RETURN(object); + EINA_SAFETY_ON_NULL_RETURN(interface); + + pd->object = eldbus_object_ref(object); + pd->name = eina_stringshare_add(interface->name); + pd->interface = interface; +} + +static void +_eldbus_model_proxy_eo_base_destructor(Eo *obj, Eldbus_Model_Proxy_Data *pd) +{ + _eldbus_model_proxy_unload(pd); + + eina_hash_free(pd->properties_hash); + + eina_stringshare_del(pd->name); + eldbus_object_unref(pd->object); + + eo_do_super(obj, MY_CLASS, eo_destructor()); +} + +static Efl_Model_Load_Status +_eldbus_model_proxy_efl_model_base_properties_get(Eo *obj EINA_UNUSED, + Eldbus_Model_Proxy_Data *pd, + Eina_Array * const* properties_array) +{ + EINA_SAFETY_ON_NULL_RETURN_VAL(pd, EFL_MODEL_LOAD_STATUS_ERROR); + EINA_SAFETY_ON_NULL_RETURN_VAL(pd->obj, EFL_MODEL_LOAD_STATUS_ERROR); + + if (!(pd->load.status & EFL_MODEL_LOAD_STATUS_LOADED_PROPERTIES)) + { + WRN("%s", "Properties not loaded."); + return EFL_MODEL_LOAD_STATUS_ERROR; + } + + *(Eina_Array**)properties_array = pd->properties_array; + return pd->load.status; +} + +static void +_eldbus_model_proxy_efl_model_base_properties_load(Eo *obj EINA_UNUSED, Eldbus_Model_Proxy_Data *pd) +{ + Eldbus_Introspection_Property *property; + Eina_List *it; + Eldbus_Pending *pending; + + if (pd->load.status & EFL_MODEL_LOAD_STATUS_LOADED_PROPERTIES) + return; + + if (!_eldbus_model_proxy_load(pd)) + return; + + const unsigned int properties_count = eina_list_count(pd->interface->properties); + + pd->properties_array = eina_array_new(properties_count); + EINA_SAFETY_ON_NULL_RETURN(pd->properties_array); + + if (!properties_count) + { + efl_model_load_set(pd->obj, &pd->load, EFL_MODEL_LOAD_STATUS_LOADED_PROPERTIES); + return; + } + + EINA_LIST_FOREACH(pd->interface->properties, it, property) + { + Eina_Stringshare *name; + Eina_Bool ret; + + name = eina_stringshare_add(property->name); + ret = eina_array_push(pd->properties_array, name); + EINA_SAFETY_ON_FALSE_RETURN(ret); + } + + pending = eldbus_proxy_property_get_all(pd->proxy, _eldbus_model_proxy_property_get_all_cb, pd); + pd->pending_list = eina_list_append(pd->pending_list, pending); + + efl_model_load_set(pd->obj, &pd->load, EFL_MODEL_LOAD_STATUS_LOADING_PROPERTIES); +} + +static Efl_Model_Load_Status +_eldbus_model_proxy_efl_model_base_property_set(Eo *obj EINA_UNUSED, + Eldbus_Model_Proxy_Data *pd, + const char *property, + Eina_Value const* value) +{ + Eldbus_Model_Proxy_Property_Set_Data *data; + const char *signature; + Eldbus_Pending *pending; + + EINA_SAFETY_ON_NULL_RETURN_VAL(property, EFL_MODEL_LOAD_STATUS_ERROR); + + DBG("(%p): property=%s", obj, property); + + if (!_eldbus_model_proxy_is_property_writeable(pd, property)) + { + WRN("Property is read-only: %s", property); + return EFL_MODEL_LOAD_STATUS_ERROR; + } + + data = _eldbus_model_proxy_property_set_data_new(pd, property, value); + EINA_SAFETY_ON_NULL_RETURN_VAL(data, EFL_MODEL_LOAD_STATUS_ERROR); + + signature = _eldbus_model_proxy_property_type_get(pd, property); + EINA_SAFETY_ON_NULL_RETURN_VAL(signature, EFL_MODEL_LOAD_STATUS_ERROR); + + pending = eldbus_proxy_property_value_set + (pd->proxy, property, signature, (Eina_Value*)value, _eldbus_model_proxy_property_set_cb, data); + pd->pending_list = eina_list_append(pd->pending_list, pending); + + return pd->load.status; +} + +static Efl_Model_Load_Status +_eldbus_model_proxy_efl_model_base_property_get(Eo *obj EINA_UNUSED, + Eldbus_Model_Proxy_Data *pd, + const char *property, + Eina_Value const **value) +{ + EINA_SAFETY_ON_NULL_RETURN_VAL(property, EFL_MODEL_LOAD_STATUS_ERROR); + EINA_SAFETY_ON_NULL_RETURN_VAL(value, EFL_MODEL_LOAD_STATUS_ERROR); + + if (!(pd->load.status & EFL_MODEL_LOAD_STATUS_LOADED_PROPERTIES)) + return EFL_MODEL_LOAD_STATUS_ERROR; + + if (!_eldbus_model_proxy_is_property_readable(pd, property)) + { + WRN("Property is write-only: %s", property); + return EFL_MODEL_LOAD_STATUS_ERROR; + } + + *value = eina_hash_find(pd->properties_hash, property); + + if(!*value) + { + *value = &pd->tmp_value; + return EFL_MODEL_LOAD_STATUS_ERROR; + } + + return pd->load.status; +} + +static void +_eldbus_model_proxy_efl_model_base_load(Eo *obj EINA_UNUSED, Eldbus_Model_Proxy_Data *pd) +{ + if (!_eldbus_model_proxy_load(pd)) + return; + + _eldbus_model_proxy_efl_model_base_properties_load(obj, pd); + _eldbus_model_proxy_efl_model_base_children_load(obj, pd); +} + +static Efl_Model_Load_Status +_eldbus_model_proxy_efl_model_base_load_status_get(Eo *obj EINA_UNUSED, Eldbus_Model_Proxy_Data *pd) +{ + return pd->load.status; +} + +static void +_eldbus_model_proxy_efl_model_base_unload(Eo *obj EINA_UNUSED, Eldbus_Model_Proxy_Data *pd) +{ + _eldbus_model_proxy_unload(pd); + + efl_model_load_set(pd->obj, &pd->load, EFL_MODEL_LOAD_STATUS_UNLOADED); +} + +static Eo * +_eldbus_model_proxy_efl_model_base_child_add(Eo *obj EINA_UNUSED, Eldbus_Model_Proxy_Data *pd EINA_UNUSED) +{ + return NULL; +} + +static Efl_Model_Load_Status +_eldbus_model_proxy_efl_model_base_child_del(Eo *obj EINA_UNUSED, + Eldbus_Model_Proxy_Data *pd EINA_UNUSED, + Eo *child EINA_UNUSED) +{ + return EFL_MODEL_LOAD_STATUS_ERROR; +} + +static Efl_Model_Load_Status +_eldbus_model_proxy_efl_model_base_children_slice_get(Eo *obj EINA_UNUSED, + Eldbus_Model_Proxy_Data *pd, + unsigned start, + unsigned count, + Eina_Accessor **children_accessor) +{ + if (!(pd->load.status & EFL_MODEL_LOAD_STATUS_LOADED_CHILDREN)) + { + WRN("(%p): Children not loaded", obj); + *children_accessor = NULL; + return pd->load.status; + } + + *children_accessor = efl_model_list_slice(pd->children_list, start, count); + return pd->load.status; +} + +static Efl_Model_Load_Status +_eldbus_model_proxy_efl_model_base_children_count_get(Eo *obj EINA_UNUSED, + Eldbus_Model_Proxy_Data *pd, + unsigned *children_count) +{ + *children_count = eina_list_count(pd->children_list); + return pd->load.status; +} + +static void +_eldbus_model_proxy_efl_model_base_children_load(Eo *obj EINA_UNUSED, Eldbus_Model_Proxy_Data *pd) +{ + unsigned int count; + + if (pd->load.status & EFL_MODEL_LOAD_STATUS_LOADED_CHILDREN) + return; + + if (!_eldbus_model_proxy_load(pd)) + return; + + _eldbus_model_proxy_create_methods_children(pd); + _eldbus_model_proxy_create_signals_children(pd); + + efl_model_load_set(pd->obj, &pd->load, EFL_MODEL_LOAD_STATUS_LOADED_CHILDREN); + + count = eina_list_count(pd->children_list); + if (count) + eo_do(pd->obj, eo_event_callback_call(EFL_MODEL_BASE_EVENT_CHILDREN_COUNT_CHANGED, &count)); +} + +static void +_eldbus_model_proxy_create_methods_children(Eldbus_Model_Proxy_Data *pd) +{ + Eina_List *it; + Eldbus_Introspection_Method *method; + + EINA_LIST_FOREACH(pd->interface->methods, it, method) + { + const char *bus; + const char *path; + const char *interface_name; + const char *method_name; + Eo *child; + + bus = eldbus_object_bus_name_get(pd->object); + if (!bus) continue; + + path = eldbus_object_path_get(pd->object); + if (!path) continue; + + interface_name = pd->interface->name; + if (!interface_name) continue; + + method_name = method->name; + if (!method_name) continue; + + INF("(%p) Creating method child: bus = %s, path = %s, method = %s::%s", pd->obj, bus, path, interface_name, method_name); + + child = eo_add(ELDBUS_MODEL_METHOD_CLASS, NULL, + eldbus_model_method_constructor(pd->proxy, method)); + + pd->children_list = eina_list_append(pd->children_list, child); + } +} + +static void +_eldbus_model_proxy_create_signals_children(Eldbus_Model_Proxy_Data *pd) +{ + Eina_List *it; + Eldbus_Introspection_Signal *signal; + + EINA_LIST_FOREACH(pd->interface->signals, it, signal) + { + const char *bus; + const char *path; + const char *interface_name; + const char *signal_name; + Eo *child; + + bus = eldbus_object_bus_name_get(pd->object); + if (!bus) continue; + + path = eldbus_object_path_get(pd->object); + if (!path) continue; + + interface_name = pd->interface->name; + if (!interface_name) continue; + + signal_name = signal->name; + if (!signal_name) continue; + + DBG("(%p) Creating signal child: bus = %s, path = %s, signal = %s::%s", pd->obj, bus, path, interface_name, signal_name); + + child = eo_add(ELDBUS_MODEL_SIGNAL_CLASS, NULL, + eldbus_model_signal_constructor(pd->proxy, signal)); + + pd->children_list = eina_list_append(pd->children_list, child); + } +} + +static const char * +_eldbus_model_proxy_name_get(Eo *obj EINA_UNUSED, Eldbus_Model_Proxy_Data *pd) +{ + return pd->name; +} + +static bool +_eldbus_model_proxy_load(Eldbus_Model_Proxy_Data *pd) +{ + EINA_SAFETY_ON_NULL_RETURN_VAL(pd, false); + + if (pd->proxy) + return true; + + pd->proxy = eldbus_proxy_get(pd->object, pd->name); + if (!pd->proxy) + { + ERR("Cannot get proxy for interface: %s", pd->name); + return false; + } + + return true; +} + +static void +_eldbus_model_proxy_unload(Eldbus_Model_Proxy_Data *pd) +{ + Eldbus_Pending *pending; + Eo *child; + + EINA_SAFETY_ON_NULL_RETURN(pd); + + EINA_LIST_FREE(pd->children_list, child) + eo_unref(child); + + EINA_LIST_FREE(pd->pending_list, pending) + eldbus_pending_cancel(pending); + + if (pd->properties_array) + { + unsigned int i; + Eina_Stringshare *property; + Eina_Array_Iterator it; + + EINA_ARRAY_ITER_NEXT(pd->properties_array, i, property, it) + eina_stringshare_del(property); + eina_array_free(pd->properties_array); + pd->properties_array = NULL; + } + + eina_hash_free_buckets(pd->properties_hash); + + _eldbus_model_proxy_stop_monitor(pd); + + if (pd->proxy) + { + eldbus_proxy_unref(pd->proxy); + pd->proxy = NULL; + } +} + +static void +_eldbus_model_proxy_start_monitor(Eldbus_Model_Proxy_Data *pd) +{ + if (pd->monitoring) + return; + + pd->monitoring = true; + + eldbus_proxy_event_callback_add(pd->proxy, + ELDBUS_PROXY_EVENT_PROPERTY_CHANGED, + _eldbus_model_proxy_property_changed_cb, + pd); + + eldbus_proxy_event_callback_add(pd->proxy, + ELDBUS_PROXY_EVENT_PROPERTY_REMOVED, + _eldbus_model_proxy_property_invalidated_cb, + pd); +} + +static void +_eldbus_model_proxy_stop_monitor(Eldbus_Model_Proxy_Data *pd) +{ + if (!pd->monitoring) + return; + + pd->monitoring = false; + + eldbus_proxy_event_callback_del(pd->proxy, + ELDBUS_PROXY_EVENT_PROPERTY_CHANGED, + _eldbus_model_proxy_property_changed_cb, + pd); + + eldbus_proxy_event_callback_del(pd->proxy, + ELDBUS_PROXY_EVENT_PROPERTY_REMOVED, + _eldbus_model_proxy_property_invalidated_cb, + pd); +} + +static void +_eldbus_model_proxy_property_changed_cb(void *data, + Eldbus_Proxy *proxy EINA_UNUSED, + void *event_info) +{ + Eldbus_Model_Proxy_Data *pd = (Eldbus_Model_Proxy_Data*)data; + Eldbus_Object_Event_Property_Changed *event = (Eldbus_Object_Event_Property_Changed*)event_info; + Eina_Value *prop_value; + Eina_Bool ret; + + prop_value = eina_hash_find(pd->properties_hash, event->name); + if (!prop_value) return ; + + ret = eina_value_copy(event->value, prop_value); + if (!ret) return ; + + efl_model_property_changed_notify(pd->obj, event->name); +} + +static void +_eldbus_model_proxy_property_invalidated_cb(void *data, + Eldbus_Proxy *proxy EINA_UNUSED, + void *event_info) +{ + Eldbus_Model_Proxy_Data *pd = (Eldbus_Model_Proxy_Data*)data; + Eldbus_Proxy_Event_Property_Changed *event = (Eldbus_Proxy_Event_Property_Changed*)event_info; + +#if 0 + Efl_Model_Property_Event evt = {0}; + + // TODO: eldbus_proxy_property_get(event->name) ? + + evt.invalidated_properties = eina_array_new(1); + EINA_SAFETY_ON_NULL_RETURN(evt.invalidated_properties); + + eina_array_push(evt.invalidated_properties, event->name); + eina_array_free(evt.invalidated_properties); +#endif + + efl_model_property_invalidated_notify(pd->obj, event->name); +} + +static void +_eldbus_model_proxy_property_get_all_cb(void *data, + const Eldbus_Message *msg, + Eldbus_Pending *pending) +{ + Eldbus_Model_Proxy_Data *pd = (Eldbus_Model_Proxy_Data*)data; + Eldbus_Introspection_Property *prop; + Eina_Array *changed_properties; + Eldbus_Message_Iter *values = NULL; + Eldbus_Message_Iter *entry; + Eina_List *it; + const char *error_name, *error_text; + + pd->pending_list = eina_list_remove(pd->pending_list, pending); + + if (eldbus_message_error_get(msg, &error_name, &error_text)) + { + ERR("%s: %s", error_name, error_text); + efl_model_error_notify(pd->obj); + return; + } + + if (!eldbus_message_arguments_get(msg, "a{sv}", &values)) + { + ERR("%s", "Error getting arguments."); + return; + } + + EINA_LIST_FOREACH(pd->interface->properties, it, prop) + { + const Eina_Value_Type *type; + Eina_Value *value; + + type = _dbus_type_to_eina_value_type(prop->type[0]); + value = eina_value_new(type); + + eina_hash_add(pd->properties_hash, prop->name, value); + } + + changed_properties = eina_array_new(1); + while (eldbus_message_iter_get_and_next(values, 'e', &entry)) + { + const char *property; + Eldbus_Message_Iter *variant; + Eina_Value *struct_value; + Eina_Value *prop_value; + Eina_Value arg0; + Eina_Bool ret; + + if (!eldbus_message_iter_arguments_get(entry, "sv", &property, &variant)) + continue; + + struct_value = eldbus_message_iter_struct_like_to_eina_value(variant); + EINA_SAFETY_ON_NULL_GOTO(struct_value, on_error); + + ret = eina_value_struct_value_get(struct_value, "arg0", &arg0); + eina_value_free(struct_value); + EINA_SAFETY_ON_FALSE_GOTO(ret, on_error); + + prop_value = eina_hash_find(pd->properties_hash, property); + EINA_SAFETY_ON_NULL_GOTO(prop_value, on_error); + + ret = eina_value_copy(&arg0, prop_value); + EINA_SAFETY_ON_FALSE_GOTO(ret, on_error); + + eina_value_flush(&arg0); + + ret = eina_array_push(changed_properties, property); + EINA_SAFETY_ON_FALSE_GOTO(ret, on_error); + } + + _eldbus_model_proxy_start_monitor(pd); + + if (eina_array_count(changed_properties)) + { + Efl_Model_Property_Event evt = { + .changed_properties = changed_properties + }; + + eo_do(pd->obj, eo_event_callback_call(EFL_MODEL_BASE_EVENT_PROPERTIES_CHANGED, &evt)); + } + + efl_model_load_set(pd->obj, &pd->load, EFL_MODEL_LOAD_STATUS_LOADED_PROPERTIES); + + on_error: + eina_array_free(changed_properties); +} + + +static void +_eldbus_model_proxy_property_set_cb(void *data, + const Eldbus_Message *msg, + Eldbus_Pending *pending) +{ + Eldbus_Model_Proxy_Property_Set_Data *property_set_data = (Eldbus_Model_Proxy_Property_Set_Data *)data; + Eldbus_Model_Proxy_Data *pd = property_set_data->pd; + const char *error_name, *error_text; + Eina_Value *prop_value; + Eina_Bool ret; + + pd->pending_list = eina_list_remove(pd->pending_list, pending); + + if (eldbus_message_error_get(msg, &error_name, &error_text)) + { + ERR("%s: %s", error_name, error_text); + efl_model_error_notify(pd->obj); + goto on_error; + } + + prop_value = eina_hash_find(pd->properties_hash, + property_set_data->property); + EINA_SAFETY_ON_NULL_GOTO(prop_value, on_error); + + ret = eina_value_copy(&property_set_data->value, prop_value); + EINA_SAFETY_ON_FALSE_GOTO(ret, on_error); + + { + Efl_Model_Property_Event evt = { + .changed_properties = pd->properties_array + }; + eo_do(pd->obj, eo_event_callback_call(EFL_MODEL_BASE_EVENT_PROPERTIES_CHANGED, &evt)); + + efl_model_property_changed_notify(pd->obj, property_set_data->property); + } + + on_error: + _eldbus_model_proxy_property_set_data_free(property_set_data); +} + +static bool +_eldbus_model_proxy_is_property_writeable(Eldbus_Model_Proxy_Data *pd, const char *property) +{ + Eldbus_Introspection_Property *property_introspection = + eldbus_introspection_property_find(pd->interface->properties, property); + + if (property_introspection == NULL) + { + WRN("Property not found: %s", property); + return false; + } + + return ELDBUS_INTROSPECTION_PROPERTY_ACCESS_WRITE == property_introspection->access + || ELDBUS_INTROSPECTION_PROPERTY_ACCESS_READWRITE == property_introspection->access; +} + +static bool +_eldbus_model_proxy_is_property_readable(Eldbus_Model_Proxy_Data *pd, const char *property) +{ + Eldbus_Introspection_Property *property_introspection = + eldbus_introspection_property_find(pd->interface->properties, property); + + if (property_introspection == NULL) + { + WRN("Property not found: %s", property); + return false; + } + + return ELDBUS_INTROSPECTION_PROPERTY_ACCESS_READ == property_introspection->access + || ELDBUS_INTROSPECTION_PROPERTY_ACCESS_READWRITE == property_introspection->access; +} + +static const char * +_eldbus_model_proxy_property_type_get(Eldbus_Model_Proxy_Data *pd, const char *property) +{ + Eldbus_Introspection_Property *property_introspection = + eldbus_introspection_property_find(pd->interface->properties, property); + + if (property_introspection == NULL) + { + WRN("Property not found: %s", property); + return NULL; + } + + return property_introspection->type; + +} + +static Eldbus_Model_Proxy_Property_Set_Data * +_eldbus_model_proxy_property_set_data_new(Eldbus_Model_Proxy_Data *pd, + const char *property, + const Eina_Value *value) +{ + Eldbus_Model_Proxy_Property_Set_Data *data = calloc(1, sizeof(Eldbus_Model_Proxy_Property_Set_Data)); + EINA_SAFETY_ON_NULL_RETURN_VAL(data, NULL); + + data->pd = pd; + data->property = eina_stringshare_add(property); + if (!eina_value_copy(value, &data->value)) + goto error; + + return data; + + error: + eina_stringshare_del(data->property); + free(data); + return NULL; +} + +static void +_eldbus_model_proxy_property_set_data_free(Eldbus_Model_Proxy_Property_Set_Data *data) +{ + EINA_SAFETY_ON_NULL_RETURN(data); + eina_stringshare_del(data->property); + eina_value_flush(&data->value); + free(data); +} + +#include "eldbus_model_proxy.eo.c" diff --git a/src/lib/eldbus/eldbus_model_proxy.eo b/src/lib/eldbus/eldbus_model_proxy.eo new file mode 100644 index 0000000000..2bab11937d --- /dev/null +++ b/src/lib/eldbus/eldbus_model_proxy.eo @@ -0,0 +1,39 @@ +class Eldbus.Model_Proxy (Eo.Base, Efl.Model.Base) { + legacy_prefix: null; + methods { + constructor { + [[Custom Eldbus_Model_Proxy constructor. + + @since 1.16]] + params { + @in object: Eldbus_Object *; [[Eldbus object]] + @in interface: const(Eldbus_Introspection_Interface)*; [[The introspected interface]] + } + } + @property name { + get {} + values { + value: const(char)*; + } + } + } + implements { + Eo.Base.constructor; + Eo.Base.destructor; + Efl.Model.Base.properties.get; + Efl.Model.Base.properties_load; + Efl.Model.Base.property.set; + Efl.Model.Base.property.get; + Efl.Model.Base.load; + Efl.Model.Base.load_status.get; + Efl.Model.Base.unload; + Efl.Model.Base.child_add; + Efl.Model.Base.child_del; + Efl.Model.Base.children_slice.get; + Efl.Model.Base.children_count.get; + Efl.Model.Base.children_load; + } + constructors { + .constructor; + } +} diff --git a/src/lib/eldbus/eldbus_model_proxy_private.h b/src/lib/eldbus/eldbus_model_proxy_private.h new file mode 100644 index 0000000000..02153fc2b9 --- /dev/null +++ b/src/lib/eldbus/eldbus_model_proxy_private.h @@ -0,0 +1,30 @@ +#ifndef _ELDBUS_MODEL_PROXY_PRIVATE_H +#define _ELDBUS_MODEL_PROXY_PRIVATE_H + +#include "Eldbus_Model.h" + +#include + +typedef struct _Eldbus_Model_Proxy_Data Eldbus_Model_Proxy_Data; + +/** + * eldbus_model_proxy + */ +struct _Eldbus_Model_Proxy_Data +{ + Eo *obj; + Efl_Model_Load load; + Eldbus_Object *object; + Eldbus_Proxy *proxy; + Eina_Array *properties_array; + Eina_Hash *properties_hash; + Eina_List *children_list; + Eina_Stringshare *name; + Eina_List *pending_list; + bool monitoring; + const Eldbus_Introspection_Interface *interface; + Eina_Value tmp_value; +}; + +#endif + diff --git a/src/lib/eldbus/eldbus_model_signal.c b/src/lib/eldbus/eldbus_model_signal.c new file mode 100644 index 0000000000..4ef32bb906 --- /dev/null +++ b/src/lib/eldbus/eldbus_model_signal.c @@ -0,0 +1,106 @@ +#ifdef HAVE_CONFIG_H +# include +#endif + +#include "eldbus_model_arguments_private.h" +#include "eldbus_model_signal_private.h" +#include "eldbus_model_private.h" + +#include + +#define MY_CLASS ELDBUS_MODEL_SIGNAL_CLASS +#define MY_CLASS_NAME "Eldbus_Model_Signal" + +static void _eldbus_model_signal_handler_cb(void *, const Eldbus_Message *); +static void _eldbus_model_signal_callback_add(Eldbus_Model_Signal_Data *); +static void _eldbus_model_signal_callback_del(Eldbus_Model_Signal_Data *); + +static Eo_Base* +_eldbus_model_signal_eo_base_constructor(Eo *obj, Eldbus_Model_Signal_Data *pd) +{ + eo_do_super(obj, MY_CLASS, eo_constructor()); + + pd->obj = obj; + pd->handler = NULL; + pd->signal = NULL; + return obj; +} + +static void +_eldbus_model_signal_constructor(Eo *obj EINA_UNUSED, + Eldbus_Model_Signal_Data *pd, + Eldbus_Proxy *proxy, + const Eldbus_Introspection_Signal *signal) +{ + EINA_SAFETY_ON_NULL_RETURN(proxy); + EINA_SAFETY_ON_NULL_RETURN(signal); + + eo_do_super(obj, MY_CLASS, eldbus_model_arguments_constructor(proxy, signal->name, signal->arguments)); + + pd->signal = signal; +} + +static void +_eldbus_model_signal_eo_base_destructor(Eo *obj, Eldbus_Model_Signal_Data *pd) +{ + _eldbus_model_signal_callback_del(pd); + + eo_do_super(obj, MY_CLASS, eo_destructor()); +} + +static void +_eldbus_model_signal_efl_model_base_properties_load(Eo *obj, Eldbus_Model_Signal_Data *pd) +{ + Eldbus_Model_Arguments_Data *args_data = eo_data_scope_get(pd->obj, ELDBUS_MODEL_ARGUMENTS_CLASS); + EINA_SAFETY_ON_NULL_RETURN(args_data); + + if (args_data->load.status & EFL_MODEL_LOAD_STATUS_LOADED_PROPERTIES) + return; + + _eldbus_model_signal_callback_add(pd); + + eo_do_super(obj, MY_CLASS, efl_model_properties_load()); +} + +static void +_eldbus_model_signal_efl_model_base_unload(Eo *obj EINA_UNUSED, Eldbus_Model_Signal_Data *pd) +{ + _eldbus_model_signal_callback_del(pd); + eo_do_super(obj, MY_CLASS, efl_model_unload()); +} + +static void +_eldbus_model_signal_callback_add(Eldbus_Model_Signal_Data *pd) +{ + EINA_SAFETY_ON_NULL_RETURN(pd); + EINA_SAFETY_ON_FALSE_RETURN(NULL == pd->handler); + + Eldbus_Model_Arguments_Data *args_data = eo_data_scope_get(pd->obj, ELDBUS_MODEL_ARGUMENTS_CLASS); + EINA_SAFETY_ON_NULL_RETURN(args_data); + + pd->handler = eldbus_proxy_signal_handler_add(args_data->proxy, pd->signal->name, _eldbus_model_signal_handler_cb, pd); +} + +static void +_eldbus_model_signal_callback_del(Eldbus_Model_Signal_Data *pd) +{ + EINA_SAFETY_ON_NULL_RETURN(pd); + + if (pd->handler) + { + eldbus_signal_handler_unref(pd->handler); + pd->handler = NULL; + } +} + +static void +_eldbus_model_signal_handler_cb(void *data, const Eldbus_Message *msg) +{ + Eldbus_Model_Signal_Data *pd = (Eldbus_Model_Signal_Data*)data; + + Eldbus_Model_Arguments_Data *args_data = eo_data_scope_get(pd->obj, ELDBUS_MODEL_ARGUMENTS_CLASS); + + eldbus_model_arguments_process_arguments(args_data, msg, NULL); +} + +#include "eldbus_model_signal.eo.c" diff --git a/src/lib/eldbus/eldbus_model_signal.eo b/src/lib/eldbus/eldbus_model_signal.eo new file mode 100644 index 0000000000..237b149dbe --- /dev/null +++ b/src/lib/eldbus/eldbus_model_signal.eo @@ -0,0 +1,23 @@ +class Eldbus.Model_Signal (Eldbus.Model_Arguments) { + legacy_prefix: null; + methods { + constructor { + [[Custom Eldbus_Model_Signal constructor. + + @since 1.16]] + params { + @in proxy: Eldbus_Proxy*; [[Eldbus proxy]] + @in signal: const(Eldbus_Introspection_Signal)*; [[The introspected method]] + } + } + } + implements { + Eo.Base.constructor; + Eo.Base.destructor; + Efl.Model.Base.properties_load; + Efl.Model.Base.unload; + } + constructors { + .constructor; + } +} diff --git a/src/lib/eldbus/eldbus_model_signal_private.h b/src/lib/eldbus/eldbus_model_signal_private.h new file mode 100644 index 0000000000..5bef898f2c --- /dev/null +++ b/src/lib/eldbus/eldbus_model_signal_private.h @@ -0,0 +1,19 @@ +#ifndef _ELDBUS_MODEL_SIGNAL_PRIVATE_H +#define _ELDBUS_MODEL_SIGNAL_PRIVATE_H + +#include "Eldbus_Model.h" + +typedef struct _Eldbus_Model_Signal_Data Eldbus_Model_Signal_Data; + +/** + * eldbus_model_signal + */ +struct _Eldbus_Model_Signal_Data +{ + Eo *obj; + Eldbus_Signal_Handler *handler; + const Eldbus_Introspection_Signal *signal; +}; + +#endif + diff --git a/src/lib/eldbus/eldbus_private.h b/src/lib/eldbus/eldbus_private.h index 6593299050..0839d74f54 100644 --- a/src/lib/eldbus/eldbus_private.h +++ b/src/lib/eldbus/eldbus_private.h @@ -76,10 +76,15 @@ Eldbus_Pending *_eldbus_connection_send(Eldbus_Connection *conn, Eldbus_ Eldbus_Message *_eldbus_connection_send_and_block(Eldbus_Connection *conn, Eldbus_Message *msg, double timeout); Eldbus_Message_Iter *eldbus_message_iter_sub_iter_get(Eldbus_Message_Iter *iter); -Eina_Value *_message_iter_struct_to_eina_value(Eldbus_Message_Iter *iter); -Eina_Bool _message_iter_from_eina_value_struct(const char *signature, Eldbus_Message_Iter *iter, const Eina_Value *value); +Eina_Value *_message_iter_struct_to_eina_value(Eldbus_Message_Iter *iter); +Eina_Bool _message_iter_from_eina_value(const char *signature, Eldbus_Message_Iter *iter, const Eina_Value *value); +Eina_Bool _message_iter_from_eina_value_struct(const char *signature, Eldbus_Message_Iter *iter, const Eina_Value *value); void eldbus_connection_name_ref(Eldbus_Connection_Name *cn); void eldbus_connection_name_unref(Eldbus_Connection *conn, Eldbus_Connection_Name *cn); Eldbus_Signal_Handler *_eldbus_signal_handler_add(Eldbus_Connection *conn, const char *sender, const char *path, const char *interface, const char *member, Eldbus_Signal_Cb cb, const void *cb_data); +Eldbus_Message *eldbus_message_signal_new(const char *path, const char *interface, const char *name) EINA_ARG_NONNULL(1, 2, 3) EINA_WARN_UNUSED_RESULT; + +const Eina_Value_Type *_dbus_type_to_eina_value_type(char type); + #endif diff --git a/src/lib/eldbus/eldbus_proxy.c b/src/lib/eldbus/eldbus_proxy.c index e543bb6bfb..d8d3cbfbd6 100644 --- a/src/lib/eldbus/eldbus_proxy.c +++ b/src/lib/eldbus/eldbus_proxy.c @@ -727,6 +727,42 @@ eldbus_proxy_property_set(Eldbus_Proxy *proxy, const char *name, const char *sig return eldbus_proxy_send(proxy->obj->properties, msg, cb, data, -1); } +EAPI Eldbus_Pending * +eldbus_proxy_property_value_set(Eldbus_Proxy *proxy, const char *name, const char *sig, const Eina_Value *value, Eldbus_Message_Cb cb, const void *data) +{ + Eldbus_Message *msg; + Eldbus_Message_Iter *iter, *variant; + + ELDBUS_PROXY_CHECK_RETVAL(proxy, NULL); + EINA_SAFETY_ON_NULL_RETURN_VAL(name, NULL); + EINA_SAFETY_ON_NULL_RETURN_VAL(sig, NULL); + EINA_SAFETY_ON_FALSE_RETURN_VAL(dbus_signature_validate_single(sig, NULL), NULL); + EINA_SAFETY_ON_FALSE_RETURN_VAL((_type_is_number(sig[0]) || value), NULL); + + msg = eldbus_proxy_method_call_new(proxy->obj->properties, "Set"); + iter = eldbus_message_iter_get(msg); + eldbus_message_iter_basic_append(iter, 's', proxy->interface); + eldbus_message_iter_basic_append(iter, 's', name); + variant = eldbus_message_iter_container_new(iter, 'v', sig); + if (dbus_type_is_basic(sig[0])) + { + if (!_message_iter_from_eina_value(sig, variant, value)) + goto error; + } + else + { + if (!_message_iter_from_eina_value_struct(sig, variant, value)) + goto error; + } + eldbus_message_iter_container_close(iter, variant); + + return eldbus_proxy_send(proxy->obj->properties, msg, cb, data, -1); + +error: + eldbus_message_unref(msg); + return NULL; +} + EAPI Eldbus_Pending * eldbus_proxy_property_get_all(Eldbus_Proxy *proxy, Eldbus_Message_Cb cb, const void *data) { diff --git a/src/tests/eldbus/eldbus_fake_server.c b/src/tests/eldbus/eldbus_fake_server.c new file mode 100644 index 0000000000..da1f4022da --- /dev/null +++ b/src/tests/eldbus/eldbus_fake_server.c @@ -0,0 +1,182 @@ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "eldbus_fake_server.h" + +#include + +#include + +#define FAKE_SERVER_DATA_KEY "data" + +static void _fake_server_name_request_cb(void *, const Eldbus_Message *, Eldbus_Pending *); +static Eina_Bool _fakse_server_property_get(const Eldbus_Service_Interface *, const char *, Eldbus_Message_Iter *, const Eldbus_Message *, Eldbus_Message **); +static Eldbus_Message *_fake_server_property_set(const Eldbus_Service_Interface *, const char *, Eldbus_Message_Iter *, const Eldbus_Message *); +static Eldbus_Message *_fake_server_sum(const Eldbus_Service_Interface *, const Eldbus_Message *); +static Eldbus_Message *_fake_server_ping(const Eldbus_Service_Interface *, const Eldbus_Message *); +static Eina_Bool _fake_server_send_pong_signal(void *); + +static Eldbus_Connection *conn; + +static const Eldbus_Method methods[] = { + { + FAKE_SERVER_SUM_METHOD_NAME, ELDBUS_ARGS({"i", "a"}, {"i", "b"}), ELDBUS_ARGS({"i", "result"}), + _fake_server_sum + }, + { + FAKE_SERVER_PING_METHOD_NAME, ELDBUS_ARGS({"i", "a"}), NULL, + _fake_server_ping + }, + { 0 } +}; + +enum +{ + FAKE_SERVER_PONG_SIGNAL = 0 +}; + +static const Eldbus_Signal signals[] = { + [FAKE_SERVER_PONG_SIGNAL] = {FAKE_SERVER_PONG_SIGNAL_NAME, ELDBUS_ARGS({ "i", NULL }), 0}, + { 0 } +}; + +static const Eldbus_Property properties[] = { + { FAKE_SERVER_READONLY_PROPERTY, "i", _fakse_server_property_get, NULL, 0 }, + { FAKE_SERVER_WRITEONLY_PROPERTY, "i", NULL, _fake_server_property_set, 0 }, + { FAKE_SERVER_READWRITE_PROPERTY, "i", _fakse_server_property_get, _fake_server_property_set, 0 }, + { 0 } +}; + +static const Eldbus_Service_Interface_Desc test_interface_desc = { + FAKE_SERVER_INTERFACE, methods, signals, properties, NULL, NULL +}; + + +Eldbus_Service_Interface * +fake_server_start(Fake_Server_Data *data) +{ + ck_assert_ptr_ne(NULL, data); + + conn = eldbus_connection_get(ELDBUS_CONNECTION_TYPE_SESSION); + + Eldbus_Service_Interface *interface = eldbus_service_interface_register(conn, FAKE_SERVER_PATH, &test_interface_desc); + eldbus_service_object_data_set(interface, FAKE_SERVER_DATA_KEY, data); + + eldbus_name_request(conn, FAKE_SERVER_BUS, ELDBUS_NAME_REQUEST_FLAG_DO_NOT_QUEUE, + _fake_server_name_request_cb, interface); + + ecore_main_loop_begin(); + + return interface; +} + +void +fake_server_stop(Eldbus_Service_Interface *interface) +{ + eldbus_service_object_unregister(interface); + eldbus_connection_unref(conn); +} + +static Eldbus_Message * +_fake_server_sum(const Eldbus_Service_Interface *iface EINA_UNUSED, const Eldbus_Message *msg) +{ + int a, b; + if (!eldbus_message_arguments_get(msg, "ii", &a, &b)) + return eldbus_message_error_new(msg, "Invalid arguments", "Error getting arguments.."); + + int sum = a + b; + + Eldbus_Message *reply = eldbus_message_method_return_new(msg); + eldbus_message_arguments_append(reply, "i", sum); + return reply; +} + +static Eldbus_Message * +_fake_server_ping(const Eldbus_Service_Interface *iface , const Eldbus_Message *msg) +{ + int a; + if (!eldbus_message_arguments_get(msg, "i", &a)) + return eldbus_message_error_new(msg, "Invalid arguments", "Error getting arguments.."); + + Fake_Server_Data *pd = eldbus_service_object_data_get(iface, FAKE_SERVER_DATA_KEY); + ck_assert_ptr_ne(NULL, pd); + + pd->pong_response = a + 1; + Ecore_Timer *timer = ecore_timer_add(0.1, _fake_server_send_pong_signal, iface); + ck_assert_ptr_ne(NULL, timer); + + return NULL; +} + +static Eina_Bool +_fake_server_send_pong_signal(void *data) +{ + Eldbus_Service_Interface *iface = (Eldbus_Service_Interface*)data; + + Fake_Server_Data *pd = eldbus_service_object_data_get(iface, FAKE_SERVER_DATA_KEY); + ck_assert_ptr_ne(NULL, pd); + + eldbus_service_signal_emit(iface, FAKE_SERVER_PONG_SIGNAL, pd->pong_response); + return ECORE_CALLBACK_CANCEL; +} + +Eina_Bool +_fakse_server_property_get(const Eldbus_Service_Interface *iface, + const char *propname, + Eldbus_Message_Iter *iter, + const Eldbus_Message *request_msg EINA_UNUSED, + Eldbus_Message **error EINA_UNUSED) +{ + fprintf(stderr, "%s:%d _fake_server_property_get %s\n", __FILE__, __LINE__, propname); fflush(stderr); + Fake_Server_Data *data = eldbus_service_object_data_get(iface, FAKE_SERVER_DATA_KEY); + ck_assert_ptr_ne(NULL, data); + + if (strcmp(propname, FAKE_SERVER_READONLY_PROPERTY) == 0) + eldbus_message_iter_arguments_append(iter, "i", data->readonly_property); + else + if (strcmp(propname, FAKE_SERVER_READWRITE_PROPERTY) == 0) + eldbus_message_iter_arguments_append(iter, "i", data->readwrite_property); + else + return EINA_FALSE; + + return EINA_TRUE; +} + +Eldbus_Message * +_fake_server_property_set(const Eldbus_Service_Interface *iface, + const char *propname, + Eldbus_Message_Iter *iter, + const Eldbus_Message *msg) +{ + Fake_Server_Data *data = eldbus_service_object_data_get(iface, FAKE_SERVER_DATA_KEY); + ck_assert_ptr_ne(NULL, data); + + if (strcmp(propname, FAKE_SERVER_WRITEONLY_PROPERTY) == 0) + eldbus_message_iter_arguments_get(iter, "i", &data->writeonly_property); + else + if (strcmp(propname, FAKE_SERVER_READWRITE_PROPERTY) == 0) + eldbus_message_iter_arguments_get(iter, "i", &data->readwrite_property); + else + return eldbus_message_error_new(msg, "Invalid property", "Invalid property."); + + return eldbus_message_method_return_new(msg); +} + +static void +_fake_server_name_request_cb(void *data EINA_UNUSED, + const Eldbus_Message *msg, + Eldbus_Pending *pending EINA_UNUSED) +{ + if (eldbus_message_error_get(msg, NULL, NULL)) + ck_abort_msg("error on _fake_server_name_request_cb"); + + unsigned int reply; + if (!eldbus_message_arguments_get(msg, "u", &reply)) + ck_abort_msg("error geting arguments on _fake_server_name_request_cb"); + + if (ELDBUS_NAME_REQUEST_REPLY_PRIMARY_OWNER != reply) + ck_abort_msg("error name already in use"); + + ecore_main_loop_quit(); +} diff --git a/src/tests/eldbus/eldbus_fake_server.h b/src/tests/eldbus/eldbus_fake_server.h new file mode 100644 index 0000000000..00b4c86573 --- /dev/null +++ b/src/tests/eldbus/eldbus_fake_server.h @@ -0,0 +1,29 @@ +#ifndef _ELDBUS_FAKE_SERVER_H +#define _ELDBUS_FAKE_SERVER_H + +#include + +#define FAKE_SERVER_BUS "org.Enlightenment" +#define FAKE_SERVER_PATH "/org/enlightenment" +#define FAKE_SERVER_INTERFACE "org.enlightenment.FakeServer" +#define FAKE_SERVER_READONLY_PROPERTY "r" +#define FAKE_SERVER_WRITEONLY_PROPERTY "w" +#define FAKE_SERVER_READWRITE_PROPERTY "rw" +#define FAKE_SERVER_SUM_METHOD_NAME "Sum" +#define FAKE_SERVER_PING_METHOD_NAME "Ping" +#define FAKE_SERVER_PONG_SIGNAL_NAME "Pong" + +typedef struct _Fake_Server_Data Fake_Server_Data; + +struct _Fake_Server_Data +{ + int readonly_property; + int writeonly_property; + int readwrite_property; + int pong_response; +}; + +Eldbus_Service_Interface *fake_server_start(Fake_Server_Data *data); +void fake_server_stop(Eldbus_Service_Interface *interface); + +#endif diff --git a/src/tests/eldbus/eldbus_suite.c b/src/tests/eldbus/eldbus_suite.c index 5c6743decf..8b6a181e1e 100644 --- a/src/tests/eldbus/eldbus_suite.c +++ b/src/tests/eldbus/eldbus_suite.c @@ -19,7 +19,14 @@ struct _Eldbus_Test_Case static const Eldbus_Test_Case etc[] = { { "eldbus_init", eldbus_test_eldbus_init }, - { } + { "eldbus_model", eldbus_test_eldbus_model }, + { "eldbus_model_connection", eldbus_test_eldbus_model_connection }, + { "eldbus_model_object", eldbus_test_eldbus_model_object }, + { "eldbus_model_proxy", eldbus_test_eldbus_model_proxy }, + { "eldbus_test_fake_server_eldbus_model_proxy", eldbus_test_fake_server_eldbus_model_proxy }, + { "eldbus_model_method", eldbus_test_eldbus_model_method }, + { "eldbus_model_signal", eldbus_test_eldbus_model_signal }, + { NULL, NULL } }; static void diff --git a/src/tests/eldbus/eldbus_suite.h b/src/tests/eldbus/eldbus_suite.h index 400d2278ae..5410fe4e90 100644 --- a/src/tests/eldbus/eldbus_suite.h +++ b/src/tests/eldbus/eldbus_suite.h @@ -4,5 +4,12 @@ #include void eldbus_test_eldbus_init(TCase *tc); +void eldbus_test_eldbus_model(TCase *tc); +void eldbus_test_eldbus_model_connection(TCase *tc); +void eldbus_test_eldbus_model_object(TCase *tc); +void eldbus_test_eldbus_model_proxy(TCase *tc); +void eldbus_test_fake_server_eldbus_model_proxy(TCase *tc); +void eldbus_test_eldbus_model_method(TCase *tc); +void eldbus_test_eldbus_model_signal(TCase *tc); #endif diff --git a/src/tests/eldbus/eldbus_test_eldbus_model.c b/src/tests/eldbus/eldbus_test_eldbus_model.c new file mode 100644 index 0000000000..ebf91d7108 --- /dev/null +++ b/src/tests/eldbus/eldbus_test_eldbus_model.c @@ -0,0 +1,412 @@ +#ifdef HAVE_CONFIG_H +# include +#endif + +#include "eldbus_suite.h" +#include "eldbus_test_eldbus_model.h" + +#include +#include +#include +#include + +#include +#include +#include + +static Eina_Bool +_eo_event_quit_cb(void *data EINA_UNUSED, + Eo *obj EINA_UNUSED, + const Eo_Event_Description *desc EINA_UNUSED, + void *event_info EINA_UNUSED) +{ + ecore_main_loop_quit(); + return EINA_FALSE; +} + +void +efl_model_wait_for_event(Eo *obj, const Eo_Event_Description* event) +{ + eo_do(obj, eo_event_callback_add(event, _eo_event_quit_cb, NULL)); + ecore_main_loop_begin(); + eo_do(obj, eo_event_callback_del(event, _eo_event_quit_cb, NULL)); +} + +static Eina_Bool +_event_load_status_quit_cb(void *data, Eo *obj EINA_UNUSED, + const Eo_Event_Description *desc EINA_UNUSED, + void *event_info EINA_UNUSED) +{ + printf("_event_load_status_quit_cb\n"); + Efl_Model_Load_Status expected_status = (Efl_Model_Load_Status)data; + Efl_Model_Load *actual_load = (Efl_Model_Load*)event_info; + + if (expected_status == actual_load->status) + { + ecore_main_loop_quit(); + return EINA_FALSE; + } + + return EINA_TRUE; +} + +void +efl_model_wait_for_load_status(Efl_Model_Base *efl_model, Efl_Model_Load_Status expected_status) +{ + Efl_Model_Load_Status actual_status; + eo_do(efl_model, actual_status = efl_model_load_status_get()); + if (expected_status == actual_status) + return; + + eo_do(efl_model, eo_event_callback_add(EFL_MODEL_BASE_EVENT_LOAD_STATUS, _event_load_status_quit_cb, (void*)expected_status)); + ecore_main_loop_begin(); + eo_do(efl_model, eo_event_callback_del(EFL_MODEL_BASE_EVENT_LOAD_STATUS, _event_load_status_quit_cb, (void*)expected_status)); +} + +Efl_Model_Base * +efl_model_nth_child_get(Efl_Model_Base *efl_model, unsigned int n) +{ + Eina_Accessor *accessor; + Efl_Model_Load_Status status; + eo_do(efl_model, status = efl_model_children_slice_get(n, 1, &accessor)); + ck_assert_int_eq(EFL_MODEL_LOAD_STATUS_LOADED, status); + ck_assert_ptr_ne(NULL, accessor); + Eo *child = NULL; + Eina_Bool ret = eina_accessor_data_get(accessor, 0, (void**)&child); + eina_accessor_free(accessor); + ck_assert(ret); + ck_assert_ptr_ne(NULL, child); + return child; +} + +Efl_Model_Base * +efl_model_first_child_get(Efl_Model_Base *efl_model) +{ + return efl_model_nth_child_get(efl_model, 1); +} + +void +efl_model_load_and_wait_for_load_status(Eo *obj, Efl_Model_Load_Status expected_status) +{ + eo_do(obj, efl_model_load()); + efl_model_wait_for_load_status(obj, expected_status); +} + +void +check_init(void) +{ + int ret = eldbus_init(); + ck_assert_int_ge(ret, 1); +} + +void +check_shutdown(void) +{ + int ret = eldbus_shutdown(); + ck_assert_int_eq(ret, 0); +} + +void +check_property(Eo *object, const char *property_name, const char *expected_value) +{ + Eina_Value const* property_value; + Efl_Model_Load_Status status; + eo_do(object, status = efl_model_property_get(property_name, &property_value)); + ck_assert_msg(EFL_MODEL_LOAD_STATUS_ERROR != status, "Nonexistent property: %s", property_name); + ck_assert_ptr_ne(NULL, property_value); + char *actual_value = eina_value_to_string(property_value); + if (!actual_value) + ck_assert_ptr_eq(expected_value, actual_value); + else + { + bool is_property_equal = strcmp(expected_value, actual_value) == 0; + ck_assert_msg(is_property_equal, "'%s' != '%s'", expected_value, actual_value); + free(actual_value); + } +} + +Eo * +create_connection(void) +{ + Eo *connection = eo_add_ref(ELDBUS_MODEL_CONNECTION_CLASS, NULL, + eldbus_model_connection_constructor(ELDBUS_CONNECTION_TYPE_SESSION, + NULL, + EINA_FALSE)); + ck_assert_ptr_ne(NULL, connection); + return connection; +} + +Eo * +create_and_load_connection(void) +{ + Eo *connection = create_connection(); + efl_model_load_and_wait_for_load_status(connection, EFL_MODEL_LOAD_STATUS_LOADED); + return connection; +} + +Eo * +create_object(void) +{ + Eo *object = eo_add_ref(ELDBUS_MODEL_OBJECT_CLASS, NULL, + eldbus_model_object_constructor(ELDBUS_CONNECTION_TYPE_SESSION, + NULL, + EINA_FALSE, + ELDBUS_FDO_BUS, + ELDBUS_FDO_PATH)); + ck_assert_ptr_ne(NULL, object); + return object; +} + +Eo * +create_and_load_object(void) +{ + Eo *object = create_object(); + efl_model_load_and_wait_for_load_status(object, EFL_MODEL_LOAD_STATUS_LOADED); + return object; +} + +void +check_efl_model_load_status_get(Efl_Model_Base *efl_model, Efl_Model_Load_Status expected_load_status) +{ + Efl_Model_Load_Status actual_load_status; + eo_do(efl_model, actual_load_status = efl_model_load_status_get()); + ck_assert_int_eq(expected_load_status, actual_load_status); +} + +void +check_efl_model_children_count_eq(Efl_Model_Base *efl_model, unsigned int expected_children_count) +{ + unsigned int actual_children_count = 0; + eo_do(efl_model, efl_model_children_count_get(&actual_children_count)); + ck_assert_int_eq(expected_children_count, actual_children_count); +} + +void +check_efl_model_children_count_ge(Efl_Model_Base *efl_model, unsigned int minimum_children_count) +{ + unsigned int actual_children_count = 0; + Efl_Model_Load_Status status; + eo_do(efl_model, status = efl_model_children_count_get(&actual_children_count)); + // A minimum count only exists if model have EFL_MODEL_LOAD_STATUS_LOADED_CHILDREN + ck_assert((EFL_MODEL_LOAD_STATUS_LOADED_CHILDREN & status) == EFL_MODEL_LOAD_STATUS_LOADED_CHILDREN); + + ck_assert_int_ge(actual_children_count, minimum_children_count); +} + +void +check_efl_model_children_slice_get(Efl_Model_Base *efl_model) +{ + unsigned int count = 0; + Efl_Model_Load_Status status; + eo_do(efl_model, status = efl_model_children_count_get(&count)); + ck_assert_int_eq(EFL_MODEL_LOAD_STATUS_LOADED, status); + ck_assert_msg(count, "There must be at least 1 child to test"); + + // Test slice all + Eina_Accessor *accessor; + eo_do(efl_model, status = efl_model_children_slice_get(0, 0, &accessor)); + ck_assert_int_eq(EFL_MODEL_LOAD_STATUS_LOADED, status); + ck_assert_ptr_ne(NULL, accessor); + // Get first child + Eo *first_child = NULL; + Eina_Bool ret = eina_accessor_data_get(accessor, 0, (void**)&first_child); + ck_assert(ret); + ck_assert_ptr_ne(NULL, first_child); + // get last child + Eo *last_child = NULL; + ret = eina_accessor_data_get(accessor, count - 1, (void**)&last_child); + ck_assert(ret); + ck_assert_ptr_ne(NULL, last_child); + // Test nonexistent child + Eo *nonexistent_child = NULL; + ret = eina_accessor_data_get(accessor, count, (void**)&nonexistent_child); + ck_assert(!ret); + ck_assert_ptr_eq(NULL, nonexistent_child); + eina_accessor_free(accessor); + + // Test slice first child + Eo *child = NULL; + eo_do(efl_model, status = efl_model_children_slice_get(1, 1, &accessor)); + ck_assert_int_eq(EFL_MODEL_LOAD_STATUS_LOADED, status); + ck_assert_ptr_ne(NULL, accessor); + ret = eina_accessor_data_get(accessor, 0, (void**)&child); + ck_assert(ret); + ck_assert_ptr_ne(NULL, child); + ret = eina_accessor_data_get(accessor, 1, (void**)&child); + ck_assert(!ret); + ck_assert_ptr_eq(first_child, child); + eina_accessor_free(accessor); + + // Test slice last child + eo_do(efl_model, status = efl_model_children_slice_get(count, 1, &accessor)); + ck_assert_int_eq(EFL_MODEL_LOAD_STATUS_LOADED, status); + ck_assert_ptr_ne(NULL, accessor); + ret = eina_accessor_data_get(accessor, 0, (void**)&child); + ck_assert(ret); + ck_assert_ptr_ne(NULL, child); + ret = eina_accessor_data_get(accessor, 1, (void**)&child); + ck_assert(!ret); + ck_assert_ptr_eq(last_child, child); + eina_accessor_free(accessor); + + // Test slice nonexistent element + eo_do(efl_model, status = efl_model_children_slice_get(count + 1, 1, &accessor)); + ck_assert_int_eq(EFL_MODEL_LOAD_STATUS_LOADED, status); + ck_assert_ptr_eq(NULL, accessor); +} + +START_TEST(smoke) +{ + check_init(); + + Eo *connection = create_and_load_connection(); + eo_unref(connection); + + check_shutdown(); +} +END_TEST + +START_TEST(object) +{ + check_init(); + + Eo *root = create_object(); + + efl_model_load_and_wait_for_load_status(root, EFL_MODEL_LOAD_STATUS_LOADED); + + eo_unref(root); + + check_shutdown(); +} +END_TEST + +START_TEST(proxy) +{ + check_init(); + + Eo *root = create_object(); + + efl_model_load_and_wait_for_load_status(root, EFL_MODEL_LOAD_STATUS_LOADED); + + Eina_Accessor *accessor = NULL; + eo_do(root, efl_model_children_slice_get(0, 0, &accessor)); + ck_assert_ptr_ne(NULL, accessor); + + unsigned int i; + Eo *proxy; + EINA_ACCESSOR_FOREACH(accessor, i, proxy) + { + efl_model_load_and_wait_for_load_status(proxy, EFL_MODEL_LOAD_STATUS_LOADED); + } + eina_accessor_free(accessor); + + eo_unref(root); + + check_shutdown(); +} +END_TEST + +void +eldbus_test_eldbus_model(TCase *tc) +{ + tcase_add_test(tc, smoke); + tcase_add_test(tc, object); + tcase_add_test(tc, proxy); +} + +Eldbus_Model_Proxy * +eldbus_model_proxy_from_object_get(Eldbus_Model_Object *object, const char *interface_name) +{ + Eina_Accessor *accessor; + Efl_Model_Load_Status status; + eo_do(object, status = efl_model_children_slice_get(0, 0, &accessor)); + ck_assert_int_eq(EFL_MODEL_LOAD_STATUS_LOADED, status); + ck_assert_ptr_ne(NULL, accessor); + + Eo *proxy = NULL; + unsigned int i; + EINA_ACCESSOR_FOREACH(accessor, i, proxy) + { + const char *name; + eo_do(proxy, name = eldbus_model_proxy_name_get()); + ck_assert_ptr_ne(NULL, name); + if (strcmp(name, interface_name) == 0) + goto end; + } + proxy = NULL; + +end: + eina_accessor_free(accessor); + return proxy; +} + +static Eldbus_Model_Arguments * +_eldbus_model_arguments_from_proxy_get(Eldbus_Model_Proxy *proxy, const char *method_name, const Eo_Class *klass) +{ + Eina_Accessor *accessor; + Efl_Model_Load_Status status; + eo_do(proxy, status = efl_model_children_slice_get(0, 0, &accessor)); + ck_assert_int_eq(EFL_MODEL_LOAD_STATUS_LOADED, status); + ck_assert_ptr_ne(NULL, accessor); + + Eo *child = NULL; + unsigned int i; + EINA_ACCESSOR_FOREACH(accessor, i, child) + { + if (!eo_isa(child, klass)) + continue; + + const char *name; + eo_do(child, name = eldbus_model_arguments_name_get()); + ck_assert_ptr_ne(NULL, name); + if (strcmp(name, method_name) == 0) + goto end; + } + child = NULL; + +end: + eina_accessor_free(accessor); + return child; +} + +Eldbus_Model_Method * +eldbus_model_method_from_proxy_get(Eldbus_Model_Proxy *proxy, const char *method_name) +{ + return _eldbus_model_arguments_from_proxy_get(proxy, method_name, ELDBUS_MODEL_METHOD_CLASS); +} + +Eldbus_Model_Signal * +eldbus_model_signal_from_proxy_get(Eldbus_Model_Proxy *proxy, const char *signal_name) +{ + return _eldbus_model_arguments_from_proxy_get(proxy, signal_name, ELDBUS_MODEL_SIGNAL_CLASS); +} + +void +check_efl_model_property_int_eq(Efl_Model_Base *efl_model, const char *property, int expected_value) +{ + Eina_Value const* property_value; + Efl_Model_Load_Status status; + eo_do(efl_model, status = efl_model_property_get(property, &property_value)); + ck_assert_int_eq(EFL_MODEL_LOAD_STATUS_LOADED, status); + ck_assert_ptr_ne(NULL, property_value); + + const Eina_Value_Type *property_type = eina_value_type_get(property_value); + ck_assert_ptr_eq(EINA_VALUE_TYPE_INT, property_type); + + int actual_value = 0; + eina_value_get(property_value, &actual_value); + ck_assert_int_eq(expected_value, actual_value); +} + +void +check_efl_model_property_int_set(Efl_Model_Base *efl_model, const char *property, int value) +{ + Eina_Value eina_value; + eina_value_setup(&eina_value, EINA_VALUE_TYPE_INT); + eina_value_set(&eina_value, value); + Efl_Model_Load_Status status; + eo_do(efl_model, status = efl_model_property_set(property, &eina_value)); + eina_value_flush(&eina_value); + ck_assert_int_eq(EFL_MODEL_LOAD_STATUS_LOADED, status); +} + diff --git a/src/tests/eldbus/eldbus_test_eldbus_model.h b/src/tests/eldbus/eldbus_test_eldbus_model.h new file mode 100644 index 0000000000..2d8603a72d --- /dev/null +++ b/src/tests/eldbus/eldbus_test_eldbus_model.h @@ -0,0 +1,32 @@ +#ifndef _ELDBUS_TEST_ELDBUS_MODEL_H +#define _ELDBUS_TEST_ELDBUS_MODEL_H + +#include +#include +#include + +void check_init(void); +void check_shutdown(void); +Eo *create_connection(void); +Eo *create_and_load_connection(void); +Eo *create_object(void); +Eo *create_and_load_object(void); + +void efl_model_wait_for_event(Eo *obj, const Eo_Event_Description *event); +void efl_model_wait_for_load_status(Eo *obj, Efl_Model_Load_Status expected_status); +void efl_model_load_and_wait_for_load_status(Eo *obj, Efl_Model_Load_Status expected_status); +Efl_Model_Base *efl_model_nth_child_get(Efl_Model_Base *obj, unsigned int n); +Efl_Model_Base *efl_model_first_child_get(Efl_Model_Base *efl_model); + +void check_efl_model_load_status_get(Efl_Model_Base *obj, Efl_Model_Load_Status expected_load_status); +void check_efl_model_children_count_eq(Efl_Model_Base *obj, unsigned int expected_children_count); +void check_efl_model_children_count_ge(Efl_Model_Base *obj, unsigned int minimum_children_count); +void check_efl_model_children_slice_get(Efl_Model_Base *efl_model); +void check_efl_model_property_int_eq(Efl_Model_Base *obj, const char *property, int expected_value); +void check_efl_model_property_int_set(Efl_Model_Base *obj, const char *property, int value); + +Eldbus_Model_Proxy *eldbus_model_proxy_from_object_get(Eldbus_Model_Object *object, const char *interface_name); +Eldbus_Model_Method *eldbus_model_method_from_proxy_get(Eldbus_Model_Proxy *proxy, const char *method_name); +Eldbus_Model_Signal *eldbus_model_signal_from_proxy_get(Eldbus_Model_Proxy *proxy, const char *signal_name); + +#endif diff --git a/src/tests/eldbus/eldbus_test_eldbus_model_connection.c b/src/tests/eldbus/eldbus_test_eldbus_model_connection.c new file mode 100644 index 0000000000..4dc9f3decd --- /dev/null +++ b/src/tests/eldbus/eldbus_test_eldbus_model_connection.c @@ -0,0 +1,201 @@ +#ifdef HAVE_CONFIG_H +# include +#endif + +#include "eldbus_suite.h" +#include "eldbus_test_eldbus_model.h" + +#include +#include +#include + +#include + +static Eo *connection = NULL; +static Eo *unloaded_connection = NULL; + +#define UNIQUE_NAME_PROPERTY "unique_name" + +static void +_setup(void) +{ + check_init(); + connection = create_and_load_connection(); + unloaded_connection = create_connection(); +} + +static void +_teardown(void) +{ + eo_unref(unloaded_connection); + eo_unref(connection); + check_shutdown(); +} + +START_TEST(load_status_get) +{ + check_efl_model_load_status_get(connection, EFL_MODEL_LOAD_STATUS_LOADED); + check_efl_model_load_status_get(unloaded_connection, EFL_MODEL_LOAD_STATUS_UNLOADED); +} +END_TEST + +START_TEST(properties_get) +{ + Eina_Array *properties = NULL; + Efl_Model_Load_Status status; + eo_do(connection, status = efl_model_properties_get(&properties)); + ck_assert_int_eq(EFL_MODEL_LOAD_STATUS_LOADED, status); + ck_assert_ptr_ne(NULL, properties); + + const unsigned int expected_properties_count = 1; + unsigned int actual_properties_count = eina_array_count(properties); + ck_assert_int_eq(expected_properties_count, actual_properties_count); + + // Unloaded connection populates its properties + eo_do(unloaded_connection, status = efl_model_properties_get(&properties)); + ck_assert_int_eq(EFL_MODEL_LOAD_STATUS_UNLOADED, status); + ck_assert_ptr_ne(NULL, properties); + + actual_properties_count = eina_array_count(properties); + ck_assert_int_eq(expected_properties_count, actual_properties_count); +} +END_TEST + +START_TEST(property_get) +{ + Eina_Value const* property_value; + Efl_Model_Load_Status status; + eo_do(connection, status = efl_model_property_get(UNIQUE_NAME_PROPERTY, &property_value)); + ck_assert_int_eq(EFL_MODEL_LOAD_STATUS_LOADED, status); + ck_assert_ptr_ne(NULL, property_value); + + // Nonexistent property must return EFL_MODEL_LOAD_STATUS_ERROR + eo_do(connection, status = efl_model_property_get("nonexistent", &property_value)); + ck_assert_int_eq(EFL_MODEL_LOAD_STATUS_ERROR, status); +} +END_TEST + +START_TEST(property_set) +{ + // Nonexistent property must return EFL_MODEL_LOAD_STATUS_ERROR + Eina_Value value; + eina_value_setup(&value, EINA_VALUE_TYPE_INT); + eina_value_set(&value, 1); + Efl_Model_Load_Status status; + eo_do(connection, status = efl_model_property_set("nonexistent", &value)); + eina_value_flush(&value); + ck_assert_int_eq(EFL_MODEL_LOAD_STATUS_ERROR, status); + + // UNIQUE_NAME_PROPERTY is read-only + eo_do(connection, status = efl_model_property_set(UNIQUE_NAME_PROPERTY, &value)); + ck_assert_int_eq(EFL_MODEL_LOAD_STATUS_ERROR, status); + + // The model must be loaded to be able to set its properties + const char *expected_value = "unloaded"; + eina_value_setup(&value, EINA_VALUE_TYPE_STRING); + eina_value_set(&value, expected_value); + eo_do(unloaded_connection, status = efl_model_property_set(UNIQUE_NAME_PROPERTY, &value)); + eina_value_flush(&value); + ck_assert_int_eq(EFL_MODEL_LOAD_STATUS_ERROR, status); +} +END_TEST + +static void +_test_children_count(Eo *efl_model) +{ + // At least this connection and 'org.freedesktop.DBus' must exist + check_efl_model_children_count_ge(efl_model, 2); +} + +START_TEST(children_count) +{ + _test_children_count(connection); +} +END_TEST + +START_TEST(children_slice_get) +{ + check_efl_model_children_slice_get(connection); + + // Unloaded connection must return EFL_MODEL_LOAD_STATUS_UNLOADED + Eina_Accessor *accessor; + Efl_Model_Load_Status status; + eo_do(unloaded_connection, status = efl_model_children_slice_get(0, 0, &accessor)); + ck_assert_int_eq(EFL_MODEL_LOAD_STATUS_UNLOADED, status); + ck_assert_ptr_eq(NULL, accessor); +} +END_TEST + +START_TEST(unload) +{ + check_efl_model_load_status_get(connection, EFL_MODEL_LOAD_STATUS_LOADED); + eo_do(connection, efl_model_unload()); + check_efl_model_load_status_get(connection, EFL_MODEL_LOAD_STATUS_UNLOADED); + + check_efl_model_children_count_eq(connection, 0); +} +END_TEST + +START_TEST(properties_load) +{ + eo_do(unloaded_connection, efl_model_properties_load()); + check_efl_model_load_status_get(unloaded_connection, EFL_MODEL_LOAD_STATUS_LOADED_PROPERTIES); +} +END_TEST + +START_TEST(children_load) +{ + eo_do(unloaded_connection, efl_model_children_load()); + + check_efl_model_load_status_get(unloaded_connection, EFL_MODEL_LOAD_STATUS_LOADING_CHILDREN); + + efl_model_wait_for_load_status(unloaded_connection, EFL_MODEL_LOAD_STATUS_LOADED_CHILDREN); + + check_efl_model_load_status_get(unloaded_connection, EFL_MODEL_LOAD_STATUS_LOADED_CHILDREN); + + _test_children_count(unloaded_connection); +} +END_TEST + +START_TEST(child_add) +{ + Eo *child; + eo_do(connection, child = efl_model_child_add()); + ck_assert_ptr_eq(NULL, child); +} +END_TEST + +START_TEST(child_del) +{ + unsigned int expected_children_count = 0; + Efl_Model_Load_Status status; + eo_do(connection, status = efl_model_children_count_get(&expected_children_count)); + ck_assert_int_eq(EFL_MODEL_LOAD_STATUS_LOADED, status); + + Eo *child = efl_model_first_child_get(connection); + eo_do(connection, status = efl_model_child_del(child)); + ck_assert_int_eq(EFL_MODEL_LOAD_STATUS_ERROR, status); + + unsigned int actual_children_count = 0; + eo_do(connection, status = efl_model_children_count_get(&actual_children_count)); + ck_assert_int_eq(EFL_MODEL_LOAD_STATUS_LOADED, status); + + ck_assert_int_le(expected_children_count, actual_children_count); +} +END_TEST + +void eldbus_test_eldbus_model_connection(TCase *tc) +{ + tcase_add_checked_fixture(tc, _setup, _teardown); + tcase_add_test(tc, load_status_get); + tcase_add_test(tc, properties_get); + tcase_add_test(tc, property_get); + tcase_add_test(tc, property_set); + tcase_add_test(tc, children_count); + tcase_add_test(tc, children_slice_get); + tcase_add_test(tc, unload); + tcase_add_test(tc, properties_load); + tcase_add_test(tc, children_load); + tcase_add_test(tc, child_add); + tcase_add_test(tc, child_del); +} diff --git a/src/tests/eldbus/eldbus_test_eldbus_model_method.c b/src/tests/eldbus/eldbus_test_eldbus_model_method.c new file mode 100644 index 0000000000..6c9dc5b008 --- /dev/null +++ b/src/tests/eldbus/eldbus_test_eldbus_model_method.c @@ -0,0 +1,220 @@ +#ifdef HAVE_CONFIG_H +# include +#endif + +#include "eldbus_fake_server.h" +#include "eldbus_suite.h" +#include "eldbus_test_eldbus_model.h" + +#include +#include +#include + +#include + +#define ARGUMENT_A "arg0" +#define ARGUMENT_B "arg1" +#define ARGUMENT_RESULT "arg2" + +static Eo *fake_server_object = NULL; +static Eo *fake_server_proxy = NULL; +static Eldbus_Service_Interface *fake_server = NULL; +static Fake_Server_Data fake_server_data = {0}; +static Eo *method = NULL; + +static void +_setup(void) +{ + check_init(); + + fake_server = fake_server_start(&fake_server_data); + + fake_server_object = eo_add(ELDBUS_MODEL_OBJECT_CLASS, NULL, + eldbus_model_object_constructor(ELDBUS_CONNECTION_TYPE_SESSION, + NULL, + EINA_FALSE, + FAKE_SERVER_BUS, + FAKE_SERVER_PATH)); + ck_assert_ptr_ne(NULL, fake_server_object); + + efl_model_load_and_wait_for_load_status(fake_server_object, EFL_MODEL_LOAD_STATUS_LOADED); + + fake_server_proxy = eldbus_model_proxy_from_object_get(fake_server_object, FAKE_SERVER_INTERFACE); + ck_assert_ptr_ne(NULL, fake_server_proxy); + + efl_model_load_and_wait_for_load_status(fake_server_proxy, EFL_MODEL_LOAD_STATUS_LOADED); + + method = eldbus_model_method_from_proxy_get(fake_server_proxy, FAKE_SERVER_SUM_METHOD_NAME); + ck_assert_ptr_ne(NULL, method); + + efl_model_load_and_wait_for_load_status(method, EFL_MODEL_LOAD_STATUS_LOADED); +} + +static void +_teardown(void) +{ + eo_unref(fake_server_object); + + fake_server_stop(fake_server); + + check_shutdown(); +} + +START_TEST(load_status_get) +{ + check_efl_model_load_status_get(method, EFL_MODEL_LOAD_STATUS_LOADED); +} +END_TEST + +START_TEST(properties_get) +{ + Eina_Array *properties = NULL; + Efl_Model_Load_Status status; + eo_do(method, status = efl_model_properties_get(&properties)); + ck_assert_int_eq(EFL_MODEL_LOAD_STATUS_LOADED, status); + ck_assert_ptr_ne(NULL, properties); + + const unsigned int expected_properties_count = 3; // a, b and result arguments of 'sum' method + const unsigned int actual_properties_count = eina_array_count(properties); + ck_assert_int_eq(expected_properties_count, actual_properties_count); +} +END_TEST + +START_TEST(property_get) +{ + // Input only property returns error + Eina_Value const* dummy; + Efl_Model_Load_Status status; + eo_do(method, status = efl_model_property_get(ARGUMENT_A, &dummy)); + ck_assert_int_eq(EFL_MODEL_LOAD_STATUS_ERROR, status); + + eo_do(method, status = efl_model_property_get(ARGUMENT_RESULT, &dummy)); + ck_assert_int_eq(EFL_MODEL_LOAD_STATUS_LOADED, status); + + // Nonexistent property returns error + eo_do(method, status = efl_model_property_get("nonexistent", &dummy)); + ck_assert_int_eq(EFL_MODEL_LOAD_STATUS_ERROR, status); +} +END_TEST + +START_TEST(property_set) +{ + // Output argument returns error + Eina_Value dummy = {0}; + Efl_Model_Load_Status status; + eo_do(method, status = efl_model_property_set(ARGUMENT_RESULT, &dummy)); + ck_assert_int_eq(EFL_MODEL_LOAD_STATUS_ERROR, status); +} +END_TEST + +static void +_test_method_children_count(Eo *efl_model) +{ + check_efl_model_children_count_eq(efl_model, 0); +} + +START_TEST(children_count) +{ + _test_method_children_count(method); +} +END_TEST + +START_TEST(children_slice_get) +{ + Eina_Accessor *accessor; + Efl_Model_Load_Status status; + eo_do(method, status = efl_model_children_slice_get(1, 1, &accessor)); + ck_assert_int_eq(EFL_MODEL_LOAD_STATUS_LOADED, status); + ck_assert_ptr_eq(NULL, accessor); +} +END_TEST + +static void +_check_unload(void) +{ + check_efl_model_load_status_get(method, EFL_MODEL_LOAD_STATUS_LOADED); + eo_do(method, efl_model_unload()); + check_efl_model_load_status_get(method, EFL_MODEL_LOAD_STATUS_UNLOADED); + + check_efl_model_children_count_eq(method, 0); +} + +START_TEST(unload) +{ + _check_unload(); +} +END_TEST + +START_TEST(properties_load) +{ + _check_unload(); + + eo_do(method, efl_model_properties_load()); + efl_model_wait_for_load_status(method, EFL_MODEL_LOAD_STATUS_LOADED_PROPERTIES); + + check_efl_model_load_status_get(method, EFL_MODEL_LOAD_STATUS_LOADED_PROPERTIES); +} +END_TEST + +START_TEST(children_load) +{ + _check_unload(); + + eo_do(method, efl_model_children_load()); + efl_model_wait_for_load_status(method, EFL_MODEL_LOAD_STATUS_LOADED_CHILDREN); + + check_efl_model_load_status_get(method, EFL_MODEL_LOAD_STATUS_LOADED_CHILDREN); + + _test_method_children_count(method); +} +END_TEST + +START_TEST(child_add) +{ + Eo *child; + eo_do(method, child = efl_model_child_add()); + ck_assert_ptr_eq(NULL, child); +} +END_TEST + +START_TEST(child_del) +{ + // efl_model_child_del always returns ERROR + Eo *child = NULL; + Efl_Model_Load_Status status; + eo_do(method, status = efl_model_child_del(child)); + ck_assert_int_eq(EFL_MODEL_LOAD_STATUS_ERROR, status); +} +END_TEST + +START_TEST(call) +{ + check_efl_model_property_int_set(method, ARGUMENT_A, 12345678); + check_efl_model_property_int_set(method, ARGUMENT_B, 87654321); + + Efl_Model_Load_Status status; + eo_do(method, status = eldbus_model_method_call()); + ck_assert_int_eq(EFL_MODEL_LOAD_STATUS_LOADED, status); + + efl_model_wait_for_event(method, ELDBUS_MODEL_METHOD_EVENT_SUCCESSFUL_CALL); + + check_efl_model_property_int_eq(method, ARGUMENT_RESULT, 99999999); +} +END_TEST + +void eldbus_test_eldbus_model_method(TCase *tc) +{ + tcase_add_checked_fixture(tc, _setup, _teardown); + tcase_add_test(tc, load_status_get); + tcase_add_test(tc, properties_get); + tcase_add_test(tc, property_get); + tcase_add_test(tc, property_set); + tcase_add_test(tc, children_count); + tcase_add_test(tc, children_slice_get); + tcase_add_test(tc, unload); + tcase_add_test(tc, properties_load); + tcase_add_test(tc, children_load); + tcase_add_test(tc, child_add); + tcase_add_test(tc, child_del); + tcase_add_test(tc, call); +} diff --git a/src/tests/eldbus/eldbus_test_eldbus_model_object.c b/src/tests/eldbus/eldbus_test_eldbus_model_object.c new file mode 100644 index 0000000000..a03edccd4b --- /dev/null +++ b/src/tests/eldbus/eldbus_test_eldbus_model_object.c @@ -0,0 +1,200 @@ +#ifdef HAVE_CONFIG_H +# include +#endif + +#include "eldbus_suite.h" +#include "eldbus_test_eldbus_model.h" + +#include +#include +#include + +#include + +static Eo *object = NULL; +static Eo *unloaded_object = NULL; + +#define UNIQUE_NAME_PROPERTY "unique_name" + +static void +_setup(void) +{ + check_init(); + object = create_and_load_object(); + unloaded_object = create_object(); +} + +static void +_teardown(void) +{ + eo_unref(unloaded_object); + eo_unref(object); + check_shutdown(); +} + +START_TEST(load_status_get) +{ + check_efl_model_load_status_get(object, EFL_MODEL_LOAD_STATUS_LOADED); + check_efl_model_load_status_get(unloaded_object, EFL_MODEL_LOAD_STATUS_UNLOADED); +} +END_TEST + +START_TEST(properties_get) +{ + Eina_Array *properties = NULL; + Efl_Model_Load_Status status; + eo_do(object, status = efl_model_properties_get(&properties)); + ck_assert_int_eq(EFL_MODEL_LOAD_STATUS_LOADED, status); + ck_assert_ptr_ne(NULL, properties); + + const unsigned int expected_properties_count = 1; + unsigned int actual_properties_count = eina_array_count(properties); + ck_assert_int_eq(expected_properties_count, actual_properties_count); + + // Unloaded object populates its properties + eo_do(unloaded_object, status = efl_model_properties_get(&properties)); + ck_assert_int_eq(EFL_MODEL_LOAD_STATUS_UNLOADED, status); + ck_assert_ptr_ne(NULL, properties); + + actual_properties_count = eina_array_count(properties); + ck_assert_int_eq(expected_properties_count, actual_properties_count); +} +END_TEST + +START_TEST(property_get) +{ + const Eina_Value* property_value; + Efl_Model_Load_Status status; + eo_do(object, status = efl_model_property_get(UNIQUE_NAME_PROPERTY, &property_value)); + ck_assert_int_eq(EFL_MODEL_LOAD_STATUS_LOADED, status); + + // Nonexistent property must return EFL_MODEL_LOAD_STATUS_ERROR + eo_do(object, status = efl_model_property_get("nonexistent", &property_value)); + ck_assert_int_eq(EFL_MODEL_LOAD_STATUS_ERROR, status); +} +END_TEST + +START_TEST(property_set) +{ + // Nonexistent property must return EFL_MODEL_LOAD_STATUS_ERROR + Eina_Value value; + eina_value_setup(&value, EINA_VALUE_TYPE_INT); + eina_value_set(&value, 1); + Efl_Model_Load_Status status; + eo_do(object, status = efl_model_property_set("nonexistent", &value)); + eina_value_flush(&value); + ck_assert_int_eq(EFL_MODEL_LOAD_STATUS_ERROR, status); + + // UNIQUE_NAME_PROPERTY is read-only + eo_do(object, status = efl_model_property_set(UNIQUE_NAME_PROPERTY, &value)); + ck_assert_int_eq(EFL_MODEL_LOAD_STATUS_ERROR, status); + + // The model must be loaded to be able to set its properties + const char *expected_value = "unloaded"; + eina_value_setup(&value, EINA_VALUE_TYPE_STRING); + eina_value_set(&value, expected_value); + eo_do(unloaded_object, status = efl_model_property_set(UNIQUE_NAME_PROPERTY, &value)); + eina_value_flush(&value); + ck_assert_int_eq(EFL_MODEL_LOAD_STATUS_ERROR, status); +} +END_TEST + +static void +_test_children_count(Eo *efl_model) +{ + // 'org.freedesktop.DBus' and 'org.freedesktop.DBus.Introspectable' + check_efl_model_children_count_ge(efl_model, 2); +} + +START_TEST(children_count) +{ + _test_children_count(object); +} +END_TEST + +START_TEST(children_slice_get) +{ + check_efl_model_children_slice_get(object); + + // Unloaded object must return EFL_MODEL_LOAD_STATUS_UNLOADED + Eina_Accessor *accessor; + Efl_Model_Load_Status status; + eo_do(unloaded_object, status = efl_model_children_slice_get(0, 0, &accessor)); + ck_assert_int_eq(EFL_MODEL_LOAD_STATUS_UNLOADED, status); + ck_assert_ptr_eq(NULL, accessor); +} +END_TEST + +START_TEST(unload) +{ + check_efl_model_load_status_get(object, EFL_MODEL_LOAD_STATUS_LOADED); + eo_do(object, efl_model_unload()); + check_efl_model_load_status_get(object, EFL_MODEL_LOAD_STATUS_UNLOADED); + + check_efl_model_children_count_eq(object, 0); +} +END_TEST + +START_TEST(properties_load) +{ + eo_do(unloaded_object, efl_model_properties_load()); + check_efl_model_load_status_get(unloaded_object, EFL_MODEL_LOAD_STATUS_LOADED_PROPERTIES); +} +END_TEST + +START_TEST(children_load) +{ + eo_do(unloaded_object, efl_model_children_load()); + + check_efl_model_load_status_get(unloaded_object, EFL_MODEL_LOAD_STATUS_LOADING_CHILDREN); + + efl_model_wait_for_load_status(unloaded_object, EFL_MODEL_LOAD_STATUS_LOADED_CHILDREN); + + check_efl_model_load_status_get(unloaded_object, EFL_MODEL_LOAD_STATUS_LOADED_CHILDREN); + + _test_children_count(unloaded_object); +} +END_TEST + +START_TEST(child_add) +{ + Eo *child; + eo_do(object, child = efl_model_child_add()); + ck_assert_ptr_eq(NULL, child); +} +END_TEST + +START_TEST(child_del) +{ + unsigned int expected_children_count = 0; + Efl_Model_Load_Status status; + eo_do(object, status = efl_model_children_count_get(&expected_children_count)); + ck_assert_int_eq(EFL_MODEL_LOAD_STATUS_LOADED, status); + + Eo *child = efl_model_first_child_get(object); + eo_do(object, status = efl_model_child_del(child)); + ck_assert_int_eq(EFL_MODEL_LOAD_STATUS_ERROR, status); + + unsigned int actual_children_count = 0; + eo_do(object, status = efl_model_children_count_get(&actual_children_count)); + ck_assert_int_eq(EFL_MODEL_LOAD_STATUS_LOADED, status); + + ck_assert_int_le(expected_children_count, actual_children_count); +} +END_TEST + +void eldbus_test_eldbus_model_object(TCase *tc) +{ + tcase_add_checked_fixture(tc, _setup, _teardown); + tcase_add_test(tc, load_status_get); + tcase_add_test(tc, properties_get); + tcase_add_test(tc, property_get); + tcase_add_test(tc, property_set); + tcase_add_test(tc, children_count); + tcase_add_test(tc, children_slice_get); + tcase_add_test(tc, unload); + tcase_add_test(tc, properties_load); + tcase_add_test(tc, children_load); + tcase_add_test(tc, child_add); + tcase_add_test(tc, child_del); +} diff --git a/src/tests/eldbus/eldbus_test_eldbus_model_proxy.c b/src/tests/eldbus/eldbus_test_eldbus_model_proxy.c new file mode 100644 index 0000000000..e0fca2a1fc --- /dev/null +++ b/src/tests/eldbus/eldbus_test_eldbus_model_proxy.c @@ -0,0 +1,184 @@ +#ifdef HAVE_CONFIG_H +# include +#endif + +#include "eldbus_suite.h" +#include "eldbus_test_eldbus_model.h" + +#include +#include +#include + +#include + +static Eo *dbus_object1 = NULL; +static Eo *dbus_object2 = NULL; +static Eo *dbus_proxy = NULL; +static Eo *unloaded_dbus_proxy = NULL; + +static void +_setup(void) +{ + check_init(); + dbus_object1 = create_and_load_object(); + dbus_object2 = create_and_load_object(); + + dbus_proxy = eldbus_model_proxy_from_object_get(dbus_object1, ELDBUS_FDO_INTERFACE); + ck_assert_ptr_ne(NULL, dbus_proxy); + efl_model_load_and_wait_for_load_status(dbus_proxy, EFL_MODEL_LOAD_STATUS_LOADED); + + unloaded_dbus_proxy = eldbus_model_proxy_from_object_get(dbus_object2, ELDBUS_FDO_INTERFACE); + ck_assert_ptr_ne(NULL, dbus_proxy); +} + +static void +_teardown(void) +{ + eo_unref(dbus_object2); + eo_unref(dbus_object1); + check_shutdown(); +} + +START_TEST(load_status_get) +{ + check_efl_model_load_status_get(dbus_proxy, EFL_MODEL_LOAD_STATUS_LOADED); + check_efl_model_load_status_get(unloaded_dbus_proxy, EFL_MODEL_LOAD_STATUS_UNLOADED); +} +END_TEST + +START_TEST(properties_get) +{ + // ELDBUS_FDO_INTERFACE have no properties + Eina_Array *properties = NULL; + Efl_Model_Load_Status status; + eo_do(dbus_proxy, status = efl_model_properties_get(&properties)); + ck_assert_int_eq(EFL_MODEL_LOAD_STATUS_LOADED, status); + ck_assert_ptr_ne(NULL, properties); + ck_assert_int_eq(0, eina_array_count(properties)); + + // Must be loaded to get the properties + eo_do(unloaded_dbus_proxy, status = efl_model_properties_get(&properties)); + ck_assert_int_eq(EFL_MODEL_LOAD_STATUS_ERROR, status); +} +END_TEST + +START_TEST(property_get) +{ + // Nonexistent property must return EFL_MODEL_LOAD_STATUS_ERROR + const Eina_Value* property_value; + Efl_Model_Load_Status status; + eo_do(dbus_proxy, status = efl_model_property_get("nonexistent", &property_value)); + ck_assert_int_eq(EFL_MODEL_LOAD_STATUS_ERROR, status); +} +END_TEST + +START_TEST(property_set) +{ + // Nonexistent property must return EFL_MODEL_LOAD_STATUS_ERROR + Eina_Value value; + eina_value_setup(&value, EINA_VALUE_TYPE_INT); + eina_value_set(&value, 1); + Efl_Model_Load_Status status; + eo_do(dbus_proxy, status = efl_model_property_set("nonexistent", &value)); + eina_value_flush(&value); + ck_assert_int_eq(EFL_MODEL_LOAD_STATUS_ERROR, status); +} +END_TEST + +static void +_test_dbus_proxy_children_count(Eo *efl_model) +{ + // 'org.freedesktop.DBus::AddMatch' and 'org.freedesktop.DBus::ListNames' at least + check_efl_model_children_count_ge(efl_model, 2); +} + +START_TEST(children_count) +{ + _test_dbus_proxy_children_count(dbus_proxy); +} +END_TEST + +START_TEST(children_slice_get) +{ + check_efl_model_children_slice_get(dbus_proxy); + + // Unloaded dbus_proxy must return EFL_MODEL_LOAD_STATUS_UNLOADED + Eina_Accessor *accessor; + Efl_Model_Load_Status status; + eo_do(unloaded_dbus_proxy, status = efl_model_children_slice_get(0, 0, &accessor)); + ck_assert_int_eq(EFL_MODEL_LOAD_STATUS_UNLOADED, status); + ck_assert_ptr_eq(NULL, accessor); +} +END_TEST + +START_TEST(unload) +{ + check_efl_model_load_status_get(dbus_proxy, EFL_MODEL_LOAD_STATUS_LOADED); + eo_do(dbus_proxy, efl_model_unload()); + check_efl_model_load_status_get(dbus_proxy, EFL_MODEL_LOAD_STATUS_UNLOADED); + + check_efl_model_children_count_eq(dbus_proxy, 0); +} +END_TEST + +START_TEST(properties_load) +{ + eo_do(unloaded_dbus_proxy, efl_model_properties_load()); + check_efl_model_load_status_get(unloaded_dbus_proxy, EFL_MODEL_LOAD_STATUS_LOADED_PROPERTIES); +} +END_TEST + +START_TEST(children_load) +{ + eo_do(unloaded_dbus_proxy, efl_model_children_load()); + + efl_model_wait_for_load_status(unloaded_dbus_proxy, EFL_MODEL_LOAD_STATUS_LOADED_CHILDREN); + + check_efl_model_load_status_get(unloaded_dbus_proxy, EFL_MODEL_LOAD_STATUS_LOADED_CHILDREN); + + _test_dbus_proxy_children_count(unloaded_dbus_proxy); +} +END_TEST + +START_TEST(child_add) +{ + Eo *child; + eo_do(dbus_proxy, child = efl_model_child_add()); + ck_assert_ptr_eq(NULL, child); +} +END_TEST + +START_TEST(child_del) +{ + unsigned int expected_children_count = 0; + Efl_Model_Load_Status status; + eo_do(dbus_proxy, status = efl_model_children_count_get(&expected_children_count)); + ck_assert_int_eq(EFL_MODEL_LOAD_STATUS_LOADED, status); + + Eo *child = efl_model_first_child_get(dbus_proxy); + eo_do(dbus_proxy, status = efl_model_child_del(child)); + ck_assert_int_eq(EFL_MODEL_LOAD_STATUS_ERROR, status); + + unsigned int actual_children_count = 0; + eo_do(dbus_proxy, status = efl_model_children_count_get(&actual_children_count)); + ck_assert_int_eq(EFL_MODEL_LOAD_STATUS_LOADED, status); + + ck_assert_int_le(expected_children_count, actual_children_count); +} +END_TEST + +void eldbus_test_eldbus_model_proxy(TCase *tc) +{ + tcase_add_checked_fixture(tc, _setup, _teardown); + tcase_add_test(tc, load_status_get); + tcase_add_test(tc, properties_get); + tcase_add_test(tc, property_get); + tcase_add_test(tc, property_set); + tcase_add_test(tc, children_count); + tcase_add_test(tc, children_slice_get); + tcase_add_test(tc, unload); + tcase_add_test(tc, properties_load); + tcase_add_test(tc, children_load); + tcase_add_test(tc, child_add); + tcase_add_test(tc, child_del); +} diff --git a/src/tests/eldbus/eldbus_test_eldbus_model_signal.c b/src/tests/eldbus/eldbus_test_eldbus_model_signal.c new file mode 100644 index 0000000000..1a7e907905 --- /dev/null +++ b/src/tests/eldbus/eldbus_test_eldbus_model_signal.c @@ -0,0 +1,220 @@ +#ifdef HAVE_CONFIG_H +# include +#endif + +#include "eldbus_fake_server.h" +#include "eldbus_suite.h" +#include "eldbus_test_eldbus_model.h" + +#include +#include +#include + +#include + +#define ARGUMENT_A "arg0" + +static Eo *fake_server_object = NULL; +static Eo *fake_server_proxy = NULL; +static Eldbus_Service_Interface *fake_server = NULL; +static Fake_Server_Data fake_server_data = {0}; +static Eo *pong_signal = NULL; + +static void +_setup(void) +{ + check_init(); + + fake_server = fake_server_start(&fake_server_data); + + fake_server_object = eo_add(ELDBUS_MODEL_OBJECT_CLASS, NULL, + eldbus_model_object_constructor(ELDBUS_CONNECTION_TYPE_SESSION, + NULL, + EINA_FALSE, + FAKE_SERVER_BUS, + FAKE_SERVER_PATH)); + ck_assert_ptr_ne(NULL, fake_server_object); + + efl_model_load_and_wait_for_load_status(fake_server_object, EFL_MODEL_LOAD_STATUS_LOADED); + + fake_server_proxy = eldbus_model_proxy_from_object_get(fake_server_object, FAKE_SERVER_INTERFACE); + ck_assert_ptr_ne(NULL, fake_server_proxy); + + efl_model_load_and_wait_for_load_status(fake_server_proxy, EFL_MODEL_LOAD_STATUS_LOADED); + + pong_signal = eldbus_model_signal_from_proxy_get(fake_server_proxy, FAKE_SERVER_PONG_SIGNAL_NAME); + ck_assert_ptr_ne(NULL, pong_signal); + + efl_model_load_and_wait_for_load_status(pong_signal, EFL_MODEL_LOAD_STATUS_LOADED); +} + +static void +_teardown(void) +{ + eo_unref(fake_server_object); + + fake_server_stop(fake_server); + + check_shutdown(); +} + +START_TEST(load_status_get) +{ + check_efl_model_load_status_get(pong_signal, EFL_MODEL_LOAD_STATUS_LOADED); +} +END_TEST + +START_TEST(properties_get) +{ + Eina_Array *properties = NULL; + Efl_Model_Load_Status status; + eo_do(pong_signal, status = efl_model_properties_get(&properties)); + ck_assert_int_eq(EFL_MODEL_LOAD_STATUS_LOADED, status); + ck_assert_ptr_ne(NULL, properties); + + const unsigned int expected_properties_count = 1; // 'response' only + const unsigned int actual_properties_count = eina_array_count(properties); + ck_assert_int_eq(expected_properties_count, actual_properties_count); +} +END_TEST + +START_TEST(property_get) +{ + // Signal properties always have output direction + Eina_Value const* dummy; + Efl_Model_Load_Status status; + eo_do(pong_signal, status = efl_model_property_get(ARGUMENT_A, &dummy)); + ck_assert_int_eq(EFL_MODEL_LOAD_STATUS_LOADED, status); + ck_assert_ptr_ne(NULL, dummy); + + // Nonexistent property must return EFL_MODEL_LOAD_STATUS_ERROR + eo_do(pong_signal, status = efl_model_property_get("nonexistent", &dummy)); + ck_assert_int_eq(EFL_MODEL_LOAD_STATUS_ERROR, status); +} +END_TEST + +START_TEST(property_set) +{ + // Signals have output arguments only. All returns error + Eina_Value dummy = {0}; + Efl_Model_Load_Status status; + eo_do(pong_signal, status = efl_model_property_set(ARGUMENT_A, &dummy)); + ck_assert_int_eq(EFL_MODEL_LOAD_STATUS_ERROR, status); +} +END_TEST + +static void +_test_signal_children_count(Eo *efl_model) +{ + check_efl_model_children_count_eq(efl_model, 0); +} + +START_TEST(children_count) +{ + _test_signal_children_count(pong_signal); +} +END_TEST + +START_TEST(children_slice_get) +{ + Eina_Accessor *accessor; + Efl_Model_Load_Status status; + eo_do(pong_signal, status = efl_model_children_slice_get(1, 1, &accessor)); + ck_assert_int_eq(EFL_MODEL_LOAD_STATUS_LOADED, status); + ck_assert_ptr_eq(NULL, accessor); +} +END_TEST + +static void +_check_unload(void) +{ + check_efl_model_load_status_get(pong_signal, EFL_MODEL_LOAD_STATUS_LOADED); + eo_do(pong_signal, efl_model_unload()); + check_efl_model_load_status_get(pong_signal, EFL_MODEL_LOAD_STATUS_UNLOADED); + + check_efl_model_children_count_eq(pong_signal, 0); +} + +START_TEST(unload) +{ + _check_unload(); +} +END_TEST + +START_TEST(properties_load) +{ + _check_unload(); + + eo_do(pong_signal, efl_model_properties_load()); + efl_model_wait_for_load_status(pong_signal, EFL_MODEL_LOAD_STATUS_LOADED_PROPERTIES); + + check_efl_model_load_status_get(pong_signal, EFL_MODEL_LOAD_STATUS_LOADED_PROPERTIES); +} +END_TEST + +START_TEST(children_load) +{ + _check_unload(); + + eo_do(pong_signal, efl_model_children_load()); + efl_model_wait_for_load_status(pong_signal, EFL_MODEL_LOAD_STATUS_LOADED_CHILDREN); + + check_efl_model_load_status_get(pong_signal, EFL_MODEL_LOAD_STATUS_LOADED_CHILDREN); + + _test_signal_children_count(pong_signal); +} +END_TEST + +START_TEST(child_add) +{ + Eo *child; + eo_do(pong_signal, child = efl_model_child_add()); + ck_assert_ptr_eq(NULL, child); +} +END_TEST + +START_TEST(child_del) +{ + // efl_model_child_del always returns ERROR + Eo *child = NULL; + Efl_Model_Load_Status status; + eo_do(pong_signal, status = efl_model_child_del(child)); + ck_assert_int_eq(EFL_MODEL_LOAD_STATUS_ERROR, status); +} +END_TEST + +START_TEST(signals) +{ + Eldbus_Model_Method *ping_method = eldbus_model_method_from_proxy_get(fake_server_proxy, FAKE_SERVER_PING_METHOD_NAME); + ck_assert_ptr_ne(NULL, ping_method); + + efl_model_load_and_wait_for_load_status(ping_method, EFL_MODEL_LOAD_STATUS_LOADED); + + check_efl_model_property_int_set(ping_method, ARGUMENT_A, 99); + + Efl_Model_Load_Status status; + eo_do(ping_method, status = eldbus_model_method_call()); + ck_assert_int_eq(EFL_MODEL_LOAD_STATUS_LOADED, status); + + efl_model_wait_for_event(pong_signal, EFL_MODEL_BASE_EVENT_PROPERTIES_CHANGED); + + check_efl_model_property_int_eq(pong_signal, ARGUMENT_A, 100); +} +END_TEST + +void eldbus_test_eldbus_model_signal(TCase *tc) +{ + tcase_add_checked_fixture(tc, _setup, _teardown); + tcase_add_test(tc, load_status_get); + tcase_add_test(tc, properties_get); + tcase_add_test(tc, property_get); + tcase_add_test(tc, property_set); + tcase_add_test(tc, children_count); + tcase_add_test(tc, children_slice_get); + tcase_add_test(tc, unload); + tcase_add_test(tc, properties_load); + tcase_add_test(tc, children_load); + tcase_add_test(tc, child_add); + tcase_add_test(tc, child_del); + tcase_add_test(tc, signals); +} diff --git a/src/tests/eldbus/eldbus_test_fake_server_eldbus_model_proxy.c b/src/tests/eldbus/eldbus_test_fake_server_eldbus_model_proxy.c new file mode 100644 index 0000000000..d4161b6b99 --- /dev/null +++ b/src/tests/eldbus/eldbus_test_fake_server_eldbus_model_proxy.c @@ -0,0 +1,264 @@ +#ifdef HAVE_CONFIG_H +# include +#endif + +#include "eldbus_fake_server.h" +#include "eldbus_suite.h" +#include "eldbus_test_eldbus_model.h" + +#include +#include +#include + +#include + +static Eo *fake_server_object = NULL; +static Eo *fake_server_proxy = NULL; +static Eldbus_Service_Interface *fake_server = NULL; +static Fake_Server_Data fake_server_data = {0}; + +#define FAKE_SERVER_READONLY_PROPERTY_VALUE 1111 +#define FAKE_SERVER_WRITEONLY_PROPERTY_VALUE 2222 +#define FAKE_SERVER_READWRITE_PROPERTY_VALUE 3333 + +static void +_setup(void) +{ + check_init(); + + fake_server_data = (Fake_Server_Data){ + .readonly_property = FAKE_SERVER_READONLY_PROPERTY_VALUE, + .writeonly_property = FAKE_SERVER_WRITEONLY_PROPERTY_VALUE, + .readwrite_property = FAKE_SERVER_READWRITE_PROPERTY_VALUE + }; + fake_server = fake_server_start(&fake_server_data); + + fake_server_object = eo_add(ELDBUS_MODEL_OBJECT_CLASS, NULL, + eldbus_model_object_constructor(ELDBUS_CONNECTION_TYPE_SESSION, + NULL, + EINA_FALSE, + FAKE_SERVER_BUS, + FAKE_SERVER_PATH)); + ck_assert_ptr_ne(NULL, fake_server_object); + + efl_model_load_and_wait_for_load_status(fake_server_object, EFL_MODEL_LOAD_STATUS_LOADED); + + fake_server_proxy = eldbus_model_proxy_from_object_get(fake_server_object, FAKE_SERVER_INTERFACE); + + efl_model_load_and_wait_for_load_status(fake_server_proxy, EFL_MODEL_LOAD_STATUS_LOADED); +} + +static void +_teardown(void) +{ + eo_unref(fake_server_object); + + fake_server_stop(fake_server); + + check_shutdown(); +} + +START_TEST(load_status_get) +{ + check_efl_model_load_status_get(fake_server_proxy, EFL_MODEL_LOAD_STATUS_LOADED); + + _teardown(); +} +END_TEST + +START_TEST(properties_get) +{ + Eina_Array *properties = NULL; + Efl_Model_Load_Status status; + eo_do(fake_server_proxy, status = efl_model_properties_get(&properties)); + ck_assert_int_eq(EFL_MODEL_LOAD_STATUS_LOADED, status); + ck_assert_ptr_ne(NULL, properties); + + const unsigned int expected_properties_count = 3; // FAKE_SERVER_READONLY_PROPERTY, FAKE_SERVER_WRITEONLY_PROPERTY and FAKE_SERVER_READWRITE_PROPERTY properties + const unsigned int actual_properties_count = eina_array_count(properties); + ck_assert_int_eq(expected_properties_count, actual_properties_count); + + _teardown(); +} +END_TEST + +START_TEST(property_get) +{ + check_efl_model_property_int_eq(fake_server_proxy, FAKE_SERVER_READONLY_PROPERTY, FAKE_SERVER_READONLY_PROPERTY_VALUE); + check_efl_model_property_int_eq(fake_server_proxy, FAKE_SERVER_READWRITE_PROPERTY, FAKE_SERVER_READWRITE_PROPERTY_VALUE); + + // Write-only property returns error + const Eina_Value *dummy; + Efl_Model_Load_Status status; + eo_do(fake_server_proxy, status = efl_model_property_get(FAKE_SERVER_WRITEONLY_PROPERTY, &dummy)); + ck_assert_int_eq(EFL_MODEL_LOAD_STATUS_ERROR, status); + + _teardown(); +} +END_TEST + +static void +_check_property_set(const char *property_name, int expected_property_value, int *actual_property_value) +{ + Eina_Value value; + eina_value_setup(&value, EINA_VALUE_TYPE_INT); + eina_value_set(&value, expected_property_value); + Efl_Model_Load_Status status; + eo_do(fake_server_proxy, status = efl_model_property_set(property_name, &value)); + eina_value_flush(&value); + ck_assert_int_eq(EFL_MODEL_LOAD_STATUS_LOADED, status); + + efl_model_wait_for_event(fake_server_proxy, EFL_MODEL_BASE_EVENT_PROPERTIES_CHANGED); + + ck_assert_int_eq(expected_property_value, *actual_property_value); +} + +START_TEST(property_set) +{ + _check_property_set(FAKE_SERVER_WRITEONLY_PROPERTY, 0x12345678, &fake_server_data.writeonly_property); + _check_property_set(FAKE_SERVER_READWRITE_PROPERTY, 0x76543210, &fake_server_data.readwrite_property); + + // Read-only property returns error + Eina_Value dummy = {0}; + Efl_Model_Load_Status status; + eo_do(fake_server_proxy, status = efl_model_property_set(FAKE_SERVER_READONLY_PROPERTY, &dummy)); + ck_assert_int_eq(EFL_MODEL_LOAD_STATUS_ERROR, status); + + _teardown(); +} +END_TEST + +static void +_test_fake_server_proxy_children_count(Eo *efl_model) +{ + // 'Sum' and 'Ping' methods and 'Pong' signal + check_efl_model_children_count_eq(efl_model, 3); +} + +START_TEST(children_count) +{ + _test_fake_server_proxy_children_count(fake_server_proxy); + + _teardown(); +} +END_TEST + +START_TEST(children_slice_get) +{ + Eldbus_Model_Arguments *method1 = efl_model_nth_child_get(fake_server_proxy, 1); + Eldbus_Model_Arguments *method2 = efl_model_nth_child_get(fake_server_proxy, 2); + Eldbus_Model_Arguments *signal1 = efl_model_nth_child_get(fake_server_proxy, 3); + + const char *actual_method1_name = eo_do_ret(method1, actual_method1_name, eldbus_model_arguments_name_get()); + const char *actual_method2_name = eo_do_ret(method2, actual_method2_name, eldbus_model_arguments_name_get()); + const char *actual_signal1_name = eo_do_ret(signal1, actual_signal1_name, eldbus_model_arguments_name_get()); + + ck_assert_ptr_ne(NULL, actual_method1_name); + ck_assert_ptr_ne(NULL, actual_method2_name); + ck_assert_ptr_ne(NULL, actual_signal1_name); + + // Eldbus doesn't have order for method names. Methods order are determined by Eina_Hash + if (strcmp(FAKE_SERVER_SUM_METHOD_NAME, actual_method1_name) == 0) + ck_assert(strcmp(FAKE_SERVER_PING_METHOD_NAME, actual_method2_name) == 0); + else + ck_assert(strcmp(FAKE_SERVER_SUM_METHOD_NAME, actual_method2_name) == 0); + + ck_assert(strcmp(FAKE_SERVER_PONG_SIGNAL_NAME, actual_signal1_name) == 0); + + _teardown(); +} +END_TEST + +static void +_check_unload(void) +{ + check_efl_model_load_status_get(fake_server_proxy, EFL_MODEL_LOAD_STATUS_LOADED); + eo_do(fake_server_proxy, efl_model_unload()); + check_efl_model_load_status_get(fake_server_proxy, EFL_MODEL_LOAD_STATUS_UNLOADED); + + check_efl_model_children_count_eq(fake_server_proxy, 0); +} + +START_TEST(unload) +{ + _check_unload(); + + _teardown(); +} +END_TEST + +START_TEST(properties_load) +{ + _check_unload(); + + eo_do(fake_server_proxy, efl_model_properties_load()); + efl_model_wait_for_load_status(fake_server_proxy, EFL_MODEL_LOAD_STATUS_LOADED_PROPERTIES); + + check_efl_model_load_status_get(fake_server_proxy, EFL_MODEL_LOAD_STATUS_LOADED_PROPERTIES); + + _teardown(); +} +END_TEST + +START_TEST(children_load) +{ + _check_unload(); + + eo_do(fake_server_proxy, efl_model_children_load()); + efl_model_wait_for_load_status(fake_server_proxy, EFL_MODEL_LOAD_STATUS_LOADED_CHILDREN); + + check_efl_model_load_status_get(fake_server_proxy, EFL_MODEL_LOAD_STATUS_LOADED_CHILDREN); + + _test_fake_server_proxy_children_count(fake_server_proxy); + + _teardown(); +} +END_TEST + +START_TEST(child_add) +{ + Eo *child = eo_do_ret(fake_server_proxy, child, efl_model_child_add()); + ck_assert_ptr_eq(NULL, child); + + _teardown(); +} +END_TEST + +START_TEST(child_del) +{ + // Tests that it is not possible to delete children + unsigned int expected_children_count = 0; + Efl_Model_Load_Status status; + eo_do(fake_server_proxy, status = efl_model_children_count_get(&expected_children_count)); + ck_assert_int_eq(EFL_MODEL_LOAD_STATUS_LOADED, status); + + // efl_model_child_del always returns ERROR + Eo *child = efl_model_first_child_get(fake_server_proxy); + eo_do(fake_server_proxy, status = efl_model_child_del(child)); + ck_assert_int_eq(EFL_MODEL_LOAD_STATUS_ERROR, status); + + unsigned int actual_children_count = 0; + eo_do(fake_server_proxy, status = efl_model_children_count_get(&actual_children_count)); + ck_assert_int_eq(EFL_MODEL_LOAD_STATUS_LOADED, status); + + ck_assert_int_le(expected_children_count, actual_children_count); + + _teardown(); +} +END_TEST + +void eldbus_test_fake_server_eldbus_model_proxy(TCase *tc) +{ + tcase_add_checked_fixture(tc, _setup, NULL); + tcase_add_test(tc, load_status_get); + tcase_add_test(tc, properties_get); + tcase_add_test(tc, property_get); + tcase_add_test(tc, property_set); + tcase_add_test(tc, children_count); + tcase_add_test(tc, children_slice_get); + tcase_add_test(tc, unload); + tcase_add_test(tc, properties_load); + tcase_add_test(tc, children_load); + tcase_add_test(tc, child_add); + tcase_add_test(tc, child_del); +}