#ifdef HAVE_CONFIG_H # include #endif #include "eldbus_model_object_private.h" #include "eldbus_model_private.h" #include #include #define MY_CLASS ELDBUS_MODEL_OBJECT_CLASS #define MY_CLASS_NAME "Eldbus_Model_Object" #define UNIQUE_NAME_PROPERTY "unique_name" 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 Efl_Object* _eldbus_model_object_efl_object_constructor(Eo *obj, Eldbus_Model_Object_Data *pd) { obj = efl_constructor(efl_super(obj, MY_CLASS)); pd->obj = obj; pd->is_listed = EINA_FALSE; 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, Eldbus_Connection_Type 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_efl_object_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); efl_destructor(efl_super(obj, MY_CLASS)); } static Eina_Array const * _eldbus_model_object_efl_model_properties_get(Eo *obj EINA_UNUSED, Eldbus_Model_Object_Data *pd) { EINA_SAFETY_ON_NULL_RETURN_VAL(pd, NULL); EINA_SAFETY_ON_NULL_RETURN_VAL(pd->obj, NULL); if (pd->properties_array == NULL) { Eina_Bool ret; pd->properties_array = eina_array_new(1); EINA_SAFETY_ON_NULL_RETURN_VAL(pd->properties_array, NULL); ret = eina_array_push(pd->properties_array, UNIQUE_NAME_PROPERTY); EINA_SAFETY_ON_FALSE_RETURN_VAL(ret, NULL); } return pd->properties_array; } static Efl_Future* _eldbus_model_object_efl_model_property_set(Eo *obj EINA_UNUSED, Eldbus_Model_Object_Data *pd EINA_UNUSED, const char *property, const Eina_Value *value EINA_UNUSED) { Efl_Promise* promise = efl_add(EFL_PROMISE_CLASS, obj); ELDBUS_MODEL_ON_ERROR_EXIT_PROMISE_SET((strcmp(property, UNIQUE_NAME_PROPERTY) == 0), promise, EFL_MODEL_ERROR_NOT_FOUND, efl_promise_future_get(promise)); efl_promise_failed_set(promise, EFL_MODEL_ERROR_READ_ONLY); return efl_promise_future_get(promise); } static Efl_Future* _eldbus_model_object_efl_model_property_get(Eo *obj EINA_UNUSED, Eldbus_Model_Object_Data *pd, const char *property) { Efl_Promise *promise = efl_add(EFL_PROMISE_CLASS, obj); Efl_Future *future = efl_promise_future_get(promise); ELDBUS_MODEL_ON_ERROR_EXIT_PROMISE_SET(property, promise, EFL_MODEL_ERROR_INCORRECT_VALUE, future); DBG("(%p): property=%s", obj, property); if (!pd->connection) _eldbus_model_object_connect(pd); ELDBUS_MODEL_ON_ERROR_EXIT_PROMISE_SET((strcmp(property, UNIQUE_NAME_PROPERTY) == 0), promise, EFL_MODEL_ERROR_NOT_FOUND, future); if (pd->unique_name == NULL) { const char *unique_name; unique_name = eldbus_connection_unique_name_get(pd->connection); ELDBUS_MODEL_ON_ERROR_EXIT_PROMISE_SET(unique_name, promise, EFL_MODEL_ERROR_NOT_FOUND, future); pd->unique_name = strdup(unique_name); } Eina_Value* v = eina_value_new(EINA_VALUE_TYPE_STRING); eina_value_set(v, pd->unique_name); efl_promise_value_set(promise, v, (Eina_Free_Cb)&eina_value_free); return future; } static Eo * _eldbus_model_object_efl_model_child_add(Eo *obj EINA_UNUSED, Eldbus_Model_Object_Data *pd EINA_UNUSED) { return NULL; } static void _eldbus_model_object_efl_model_child_del(Eo *obj EINA_UNUSED, Eldbus_Model_Object_Data *pd EINA_UNUSED, Eo *child EINA_UNUSED) { } static Efl_Future* _eldbus_model_object_efl_model_children_slice_get(Eo *obj EINA_UNUSED, Eldbus_Model_Object_Data *pd, unsigned start, unsigned count) { _Eldbus_Children_Slice_Promise* p; Efl_Promise *promise = efl_add(EFL_PROMISE_CLASS, obj); Efl_Future *future = efl_promise_future_get(promise); if (!pd->connection) _eldbus_model_object_connect(pd); if (pd->is_listed) { Eina_Accessor* ac = efl_model_list_slice(pd->children_list, start, count); efl_promise_value_set(promise, ac, (Eina_Free_Cb)&eina_accessor_free); return future; } p = calloc(1, sizeof(struct _Eldbus_Children_Slice_Promise)); EINA_SAFETY_ON_NULL_RETURN_VAL(p, future); p->promise = promise; p->start = start; p->count = count; pd->children_promises = eina_list_prepend(pd->children_promises, p); if (pd->pending_list == NULL) _eldbus_model_object_introspect(pd, pd->bus, pd->path); return future; } static Efl_Future* _eldbus_model_object_efl_model_children_count_get(Eo *obj EINA_UNUSED, Eldbus_Model_Object_Data *pd) { Efl_Promise *promise = efl_add(EFL_PROMISE_CLASS, obj); if (!pd->connection) _eldbus_model_object_connect(pd); if (pd->is_listed) { unsigned int *c = calloc(sizeof(unsigned int), 1); *c = eina_list_count(pd->children_list); efl_promise_value_set(promise, c, free); return efl_promise_future_get(promise); } pd->count_promises = eina_list_prepend(pd->count_promises, promise); if (pd->pending_list == NULL) _eldbus_model_object_introspect(pd, pd->bus, pd->path); return efl_promise_future_get(promise); } static const char * _eldbus_model_object_address_get(Eo *obj EINA_UNUSED, Eldbus_Model_Object_Data *pd) { return pd->address; } static Eina_Bool _eldbus_model_object_private_get(Eo *obj EINA_UNUSED, Eldbus_Model_Object_Data *pd) { return pd->private; } static Eldbus_Connection_Type _eldbus_model_object_type_get(Eo *obj EINA_UNUSED, Eldbus_Model_Object_Data *pd) { return pd->type; } static const char * _eldbus_model_object_bus_get(Eo *obj EINA_UNUSED, Eldbus_Model_Object_Data *pd) { return pd->bus; } 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_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; free(pd->unique_name); pd->unique_name = NULL; EINA_LIST_FREE(pd->children_list, child) efl_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) { Eina_List* i; pd->is_listed = EINA_TRUE; _Eldbus_Children_Slice_Promise* p; EINA_LIST_FOREACH(pd->children_promises, i, p) { Eina_Accessor* ac = efl_model_list_slice(pd->children_list, p->start, p->count); efl_promise_value_set(p->promise, ac, (Eina_Free_Cb)&eina_accessor_free); free(p); } pd->children_promises = eina_list_free(pd->children_promises); Efl_Promise *ep; EINA_LIST_FOREACH(pd->count_promises, i, ep) { unsigned *c = calloc(sizeof(unsigned), 1); *c = eina_list_count(pd->children_list); efl_promise_value_set(ep, c, free); } pd->count_promises = eina_list_free(pd->count_promises); } } 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; DBG("(%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 = efl_add_ref(ELDBUS_MODEL_PROXY_CLASS, pd->obj, eldbus_model_proxy_constructor(efl_added, object, interface)); pd->children_list = eina_list_append(pd->children_list, child); } } #include "eldbus_model_object.eo.c"