#ifdef HAVE_CONFIG_H # include #endif #include #include "eldbus_model_proxy_private.h" #include "eldbus_model_private.h" #define MY_CLASS ELDBUS_MODEL_PROXY_CLASS #define MY_CLASS_NAME "Eldbus_Model_Proxy" 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_property_set_load_cb(void *, const Eldbus_Message *, Eldbus_Pending *); static void _eldbus_model_proxy_start_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 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; typedef struct _Eldbus_Property_Promise Eldbus_Property_Promise; struct _Eldbus_Model_Proxy_Property_Set_Data { Eldbus_Model_Proxy_Data *pd; Eina_Stringshare *property; Eina_Promise *promise; Eina_Value *value; }; struct _Eldbus_Property_Promise { Eina_Promise *promise; Eina_Stringshare *property; }; static Eina_Bool _eldbus_model_proxy_load(Eldbus_Model_Proxy_Data *pd) { Eldbus_Introspection_Property *property; Eina_List *it; if (pd->proxy) return EINA_TRUE; pd->proxy = eldbus_proxy_get(pd->object, pd->name); EINA_SAFETY_ON_NULL_RETURN_VAL(pd->proxy, EINA_FALSE); EINA_LIST_FOREACH(pd->interface->properties, it, property) { const Eina_Value_Type *type; Eina_Stringshare *name; Eina_Value *value; type = _dbus_type_to_eina_value_type(property->type[0]); name = eina_stringshare_add(property->name); value = eina_value_new(type); eina_hash_direct_add(pd->properties, name, value); } return EINA_TRUE; } static void _eldbus_model_proxy_unload(Eldbus_Model_Proxy_Data *pd) { Eldbus_Pending *pending; EINA_LIST_FREE(pd->pendings, pending) eldbus_pending_cancel(pending); if (pd->monitoring) { 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); } pd->monitoring = EINA_FALSE; if (pd->proxy) eldbus_proxy_unref(pd->proxy); pd->proxy = NULL; } static void _eldbus_model_proxy_object_del(void *data, Eldbus_Object *object EINA_UNUSED, void *event_info EINA_UNUSED) { Eldbus_Model_Proxy_Data *pd = data; pd->object = NULL; } static Efl_Object* _eldbus_model_proxy_efl_object_constructor(Eo *obj, Eldbus_Model_Proxy_Data *pd) { obj = efl_constructor(efl_super(obj, MY_CLASS)); pd->obj = obj; pd->properties = eina_hash_stringshared_new(NULL); return obj; } static Efl_Object* _eldbus_model_proxy_efl_object_finalize(Eo *obj, Eldbus_Model_Proxy_Data *pd) { if (!pd->object || !pd->name || !pd->interface) return NULL; if (!_eldbus_model_proxy_load(pd)) return NULL; if (!eldbus_model_connection_get(obj)) eldbus_model_connection_set(obj, eldbus_object_connection_get(pd->object)); eldbus_object_event_callback_add(pd->object, ELDBUS_OBJECT_EVENT_DEL, _eldbus_model_proxy_object_del, pd); return efl_finalize(efl_super(obj, MY_CLASS)); } static void _eldbus_model_proxy_object_set(Eo *obj EINA_UNUSED, Eldbus_Model_Proxy_Data *pd, Eldbus_Object *object) { pd->object = eldbus_object_ref(object); } static void _eldbus_model_proxy_interface_set(Eo *obj EINA_UNUSED, Eldbus_Model_Proxy_Data *pd, const Eldbus_Introspection_Interface *interface) { pd->name = eina_stringshare_add(interface->name); pd->interface = interface; } static void _eldbus_model_proxy_efl_object_invalidate(Eo *obj, Eldbus_Model_Proxy_Data *pd) { Eo *child; EINA_LIST_FREE(pd->childrens, child) efl_unref(child); _eldbus_model_proxy_unload(pd); if (pd->object) { eldbus_object_event_callback_del(pd->object, ELDBUS_OBJECT_EVENT_DEL, _eldbus_model_proxy_object_del, pd); eldbus_object_unref(pd->object); } efl_invalidate(efl_super(obj, MY_CLASS)); } static void _eldbus_model_proxy_efl_object_destructor(Eo *obj, Eldbus_Model_Proxy_Data *pd) { Eina_Hash_Tuple *tuple; Eina_Iterator *it; it = eina_hash_iterator_tuple_new(pd->properties); EINA_ITERATOR_FOREACH(it, tuple) { Eina_Stringshare *property = tuple->key; Eina_Value *value = tuple->data; eina_stringshare_del(property); eina_value_free(value); } eina_iterator_free(it); eina_hash_free(pd->properties); eina_stringshare_del(pd->name); efl_destructor(efl_super(obj, MY_CLASS)); } static Eina_Iterator * _eldbus_model_proxy_efl_model_properties_get(const Eo *obj EINA_UNUSED, Eldbus_Model_Proxy_Data *pd) { return eina_hash_iterator_key_new(pd->properties); } #define PROPERTY_EXIST 1 #define PROPERTY_READ 2 #define PROPERTY_WRITE 4 static unsigned char eldbus_model_proxy_property_check(Eldbus_Model_Proxy_Data *pd, const char *property) { Eldbus_Introspection_Property *property_introspection = eldbus_introspection_property_find(pd->interface->properties, property); unsigned char r = 0; if (property_introspection == NULL) { WRN("Property not found: %s", property); return 0; } r = PROPERTY_EXIST; // Check read access if (property_introspection->access == ELDBUS_INTROSPECTION_PROPERTY_ACCESS_READ || property_introspection->access == ELDBUS_INTROSPECTION_PROPERTY_ACCESS_READWRITE) r |= PROPERTY_READ; // Check write access if (property_introspection->access == ELDBUS_INTROSPECTION_PROPERTY_ACCESS_WRITE || property_introspection->access == ELDBUS_INTROSPECTION_PROPERTY_ACCESS_READWRITE) r |= PROPERTY_WRITE; return r; } static void _eldbus_model_proxy_cancel_cb(Efl_Loop_Consumer *consumer EINA_UNUSED, void *data, const Eina_Future *dead_future EINA_UNUSED) { Eldbus_Model_Proxy_Property_Set_Data *sd = data; sd->promise = NULL; eina_stringshare_del(sd->property); eina_value_free(sd->value); free(sd); } static Eldbus_Pending * _eldbus_model_proxy_load_all(Eldbus_Model_Proxy_Data *pd, Eina_Promise *promise, const char *property, Eldbus_Message_Cb callback, void *data) { Eldbus_Property_Promise *p; Eldbus_Pending *pending = NULL; p = calloc(1, sizeof(Eldbus_Property_Promise)); if (!p) { if (promise) eina_promise_reject(promise, ENOMEM); return NULL; } p->promise = promise; p->property = eina_stringshare_add(property); pd->promises = eina_list_append(pd->promises, p); if (!pd->pendings) { pending = eldbus_proxy_property_get_all(pd->proxy, callback, data); } return pending; } static Eina_Future * _eldbus_model_proxy_efl_model_property_set(Eo *obj EINA_UNUSED, Eldbus_Model_Proxy_Data *pd, const char *property, Eina_Value *value) { Eldbus_Model_Proxy_Property_Set_Data *data = NULL; const char *signature; Eldbus_Pending *pending; unsigned char access; Eina_Error err = 0; DBG("(%p): property=%s", obj, property); access = eldbus_model_proxy_property_check(pd, property); err = EFL_MODEL_ERROR_NOT_FOUND; if (!access) goto on_error; err = EFL_MODEL_ERROR_READ_ONLY; if (!(access & PROPERTY_WRITE)) goto on_error; err = EFL_MODEL_ERROR_UNKNOWN; signature = _eldbus_model_proxy_property_type_get(pd, property); if (!signature) goto on_error; err = ENOMEM; data = calloc(1, sizeof (Eldbus_Model_Proxy_Property_Set_Data)); if (!data) goto on_error; data->pd = pd; data->promise = efl_loop_promise_new(obj); data->property = eina_stringshare_add(property); if (!(data->value = eina_value_dup(value))) goto on_error; if (!pd->is_loaded) { pending = _eldbus_model_proxy_load_all(pd, data->promise, property, _eldbus_model_proxy_property_set_load_cb, data); } else { pending = eldbus_proxy_property_value_set(pd->proxy, property, signature, (Eina_Value*)value, _eldbus_model_proxy_property_set_cb, data); } if (pending) pd->pendings = eina_list_append(pd->pendings, pending); return efl_future_then(obj, eina_future_new(data->promise), .data = data, .free = _eldbus_model_proxy_cancel_cb); on_error: free(data); return efl_loop_future_rejected(obj, err); } static Eina_Value * _eldbus_model_proxy_efl_model_property_get(const Eo *obj EINA_UNUSED, Eldbus_Model_Proxy_Data *pd, const char *property) { Eldbus_Pending *pending; unsigned char access; Eina_Error err = 0; access = eldbus_model_proxy_property_check(pd, property); err = EFL_MODEL_ERROR_NOT_FOUND; if (!access) goto on_error; if (!(access & PROPERTY_READ)) goto on_error; if (pd->is_loaded) { Eina_Stringshare *tmp; Eina_Value *value; err = EFL_MODEL_ERROR_NOT_FOUND; tmp = eina_stringshare_add(property); value = eina_hash_find(pd->properties, tmp); eina_stringshare_del(tmp); if (!value) goto on_error; return eina_value_dup(value); } err = ENOMEM; pending = _eldbus_model_proxy_load_all(pd, NULL, property, _eldbus_model_proxy_property_get_all_cb, pd); if (pending) pd->pendings = eina_list_append(pd->pendings, pending); else goto on_error; return eina_value_error_new(EAGAIN); on_error: return eina_value_error_new(err); } static void _eldbus_model_proxy_listed(Eldbus_Model_Proxy_Data *pd) { if (!pd->is_listed) { _eldbus_model_proxy_create_methods_children(pd); _eldbus_model_proxy_create_signals_children(pd); efl_event_callback_call(pd->obj, EFL_MODEL_EVENT_CHILDREN_COUNT_CHANGED, NULL); pd->is_listed = EINA_TRUE; } } static Eina_Future* _eldbus_model_proxy_efl_model_children_slice_get(Eo *obj EINA_UNUSED, Eldbus_Model_Proxy_Data *pd, unsigned start, unsigned count) { Eina_Value v; _eldbus_model_proxy_listed(pd); v = efl_model_list_value_get(pd->childrens, start, count); return efl_loop_future_resolved(obj, v); } static unsigned int _eldbus_model_proxy_efl_model_children_count_get(const Eo *obj EINA_UNUSED, Eldbus_Model_Proxy_Data *pd) { _eldbus_model_proxy_listed(pd); return eina_list_count(pd->childrens); } static void _eldbus_model_proxy_create_methods_children(Eldbus_Model_Proxy_Data *pd) { Eldbus_Introspection_Method *method; Eina_List *it; 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 = efl_add_ref(ELDBUS_MODEL_METHOD_CLASS, pd->obj, eldbus_model_method_proxy_set(efl_added, pd->proxy), eldbus_model_method_set(efl_added, method)); if (child) pd->childrens = eina_list_append(pd->childrens, child); else ERR("Could not create method child: bus = %s, path = %s method = %s::%s.", bus, path, interface_name, method_name); } } 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 = efl_add_ref(ELDBUS_MODEL_SIGNAL_CLASS, pd->obj, eldbus_model_signal_constructor(efl_added, pd->proxy, signal)); if (child) pd->childrens = eina_list_append(pd->childrens, child); else ERR("Could not create signal child: bus = %s, path = %s signal = %s::%s.", bus, path, interface_name, signal_name); } } static const char * _eldbus_model_proxy_proxy_name_get(const Eo *obj EINA_UNUSED, Eldbus_Model_Proxy_Data *pd) { return pd->name; } static void _eldbus_model_proxy_start_monitor(Eldbus_Model_Proxy_Data *pd) { if (pd->monitoring) return; pd->monitoring = EINA_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_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, event->name); if (!prop_value) return ; ret = eina_value_copy(event->value, prop_value); if (!ret) return ; efl_model_properties_changed(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; efl_model_property_invalidated_notify(pd->obj, event->name); } static Eina_Array * _eldbus_model_proxy_property_get_all_load(const Eldbus_Message *msg, Eldbus_Model_Proxy_Data *pd) { Eldbus_Message_Iter *values = NULL; Eldbus_Message_Iter *entry; Eina_Array *changed_properties; Eina_Stringshare *tmp = NULL; const char *error_name, *error_text; if (eldbus_message_error_get(msg, &error_name, &error_text)) { ERR("%s: %s", error_name, error_text); return NULL; } if (!eldbus_message_arguments_get(msg, "a{sv}", &values)) { ERR("%s", "Error getting arguments."); return NULL; } 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); if (!struct_value) goto on_error; ret = eina_value_struct_value_get(struct_value, "arg0", &arg0); eina_value_free(struct_value); if (!ret) goto on_error; tmp = eina_stringshare_add(property); prop_value = eina_hash_find(pd->properties, tmp); if (!prop_value) goto on_error; ret = eina_value_copy(&arg0, prop_value); if (!ret) goto on_error; eina_value_flush(&arg0); ret = eina_array_push(changed_properties, tmp); if (!ret) goto on_error; // Reset tmp to NULL to avoid double free. tmp = NULL; } pd->is_loaded = EINA_TRUE; return changed_properties; on_error: eina_stringshare_del(tmp); while ((tmp = eina_array_pop(changed_properties))) eina_stringshare_del(tmp); eina_array_free(changed_properties); return NULL; } static void _eldbus_model_proxy_promise_clean(Eldbus_Property_Promise* p, Eina_Error err) { if (p->promise) eina_promise_reject(p->promise, err); eina_stringshare_del(p->property); free(p); } 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_Property_Promise* p; Eina_Stringshare *sp; Eina_Array *properties; Efl_Model_Property_Event evt; pd->pendings = eina_list_remove(pd->pendings, pending); properties = _eldbus_model_proxy_property_get_all_load(msg, pd); if (!properties) { EINA_LIST_FREE(pd->promises, p) _eldbus_model_proxy_promise_clean(p, EFL_MODEL_ERROR_NOT_FOUND); return ; } EINA_LIST_FREE(pd->promises, p) _eldbus_model_proxy_promise_clean(p, EFL_MODEL_ERROR_READ_ONLY); _eldbus_model_proxy_start_monitor(pd); evt.changed_properties = properties; efl_event_callback_call(pd->obj, EFL_MODEL_EVENT_PROPERTIES_CHANGED, &evt); while ((sp = eina_array_pop(properties))) eina_stringshare_del(sp); eina_array_free(properties); } static void _eldbus_model_proxy_property_set_load_cb(void *data, const Eldbus_Message *msg, Eldbus_Pending *pending) { Eldbus_Model_Proxy_Property_Set_Data *set_data = (Eldbus_Model_Proxy_Property_Set_Data *)data; Eldbus_Model_Proxy_Data *pd = set_data->pd; Eina_Array *properties; Eina_Stringshare *sp; const char *signature; pd->pendings = eina_list_remove(pd->pendings, pending); signature = _eldbus_model_proxy_property_type_get(pd, set_data->property); properties = _eldbus_model_proxy_property_get_all_load(msg, pd); if (!signature || !properties) { eina_promise_reject(set_data->promise, EFL_MODEL_ERROR_UNKNOWN); goto end; } pending = eldbus_proxy_property_value_set(pd->proxy, set_data->property, signature, set_data->value, _eldbus_model_proxy_property_set_cb, set_data); pd->pendings = eina_list_append(pd->pendings, pending); end: if (!properties) return; while ((sp = eina_array_pop(properties))) eina_stringshare_del(sp); eina_array_free(properties); } static void _eldbus_model_proxy_property_set_cb(void *data, const Eldbus_Message *msg, Eldbus_Pending *pending) { Eldbus_Model_Proxy_Property_Set_Data *sd = (Eldbus_Model_Proxy_Property_Set_Data *)data; Eldbus_Model_Proxy_Data *pd = sd->pd; const char *error_name, *error_text; Eina_Value *value; pd->pendings = eina_list_remove(pd->pendings, pending); if (eldbus_message_error_get(msg, &error_name, &error_text)) { ERR("%s: %s", error_name, error_text); eina_promise_reject(sd->promise, EFL_MODEL_ERROR_UNKNOWN); return; } value = eina_hash_find(pd->properties, sd->property); if (value) { efl_model_properties_changed(pd->obj, sd->property); if (sd->promise) eina_promise_resolve(sd->promise, eina_value_reference_copy(value)); } else { if (sd->promise) eina_promise_reject(sd->promise, EFL_MODEL_ERROR_NOT_FOUND); } } 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; } #include "eldbus_model_proxy.eo.c"