edbus: Implement DBus.Properties for services
authorJosé Roberto de Souza <zehortigoza@profusion.mobi>
Fri, 9 Nov 2012 18:35:14 +0000 (18:35 +0000)
committerLucas De Marchi <lucas.demarchi@profusion.mobi>
Fri, 9 Nov 2012 18:35:14 +0000 (18:35 +0000)
Patch by: José Roberto de Souza  <zehortigoza@profusion.mobi>

SVN revision: 79035

src/examples/complex_types_client_eina_value.c
src/examples/complex_types_server.c
src/lib/edbus_private_types.h
src/lib/edbus_service.c
src/lib/edbus_service.h

index f5dcfb4..2e9455f 100644 (file)
@@ -158,7 +158,7 @@ _property_changed(void *data, EDBus_Proxy *proxy, void *event_info)
    name = event->name;
    value = event->value;
 
-   if (!strcmp(name, "text"))
+   if (!strcmp(name, "text") || !strcmp(name, "Resp2"))
      {
         const char *txt;
         eina_value_get(value, &txt);
@@ -189,18 +189,27 @@ _read_cache(void *data)
    Eina_Value *v;
 
    v = edbus_proxy_property_local_get(proxy, "text");
-   eina_value_get(v, &txt);
-   printf("Read cache: [txt] = %s\n", txt);
+   if (v)
+     {
+        eina_value_get(v, &txt);
+        printf("Read cache: [txt] = %s\n", txt);
+     }
 
    v = edbus_proxy_property_local_get(proxy, "int32");
-   eina_value_get(v, &num);
-   printf("Read cache: [int32] = %d\n", num);
+   if (v)
+     {
+        eina_value_get(v, &num);
+        printf("Read cache: [int32] = %d\n", num);
+     }
 
    v = edbus_proxy_property_local_get(proxy, "st");
-   eina_value_struct_get(v, "arg0", &txt);
-   printf("Read cache: [st] %s | ", txt);
-   eina_value_struct_get(v, "arg1", &txt);
-   printf("%s\n", txt);
+   if (v)
+     {
+        eina_value_struct_get(v, "arg0", &txt);
+        printf("Read cache: [st] %s | ", txt);
+        eina_value_struct_get(v, "arg1", &txt);
+        printf("%s\n", txt);
+     }
 
    return EINA_FALSE;
 }
@@ -258,6 +267,7 @@ main(void)
                                   EDBUS_PROXY_EVENT_PROPERTY_CHANGED,
                                   _property_changed, NULL);
 
+   edbus_proxy_properties_monitor(proxy, EINA_TRUE);
    ecore_timer_add(10, _read_cache, proxy);
 
    ecore_main_loop_begin();
index b958a4e..05443a8 100644 (file)
@@ -6,6 +6,9 @@
 #define IFACE "com.profusion.Test"
 
 static char *resp2;
+/* dummy, incremented each time DBus.Properties.Get() is called */
+static int int32 = 35;
+static Ecore_Timer *timer;
 
 static EDBus_Message *
 _receive_array(const EDBus_Service_Interface *iface, const EDBus_Message *msg)
@@ -199,47 +202,31 @@ _double_container(const EDBus_Service_Interface *iface, const EDBus_Message *msg
    return reply;
 }
 
-static EDBus_Message *
-_properties_get(const EDBus_Service_Interface *iface, const EDBus_Message *msg)
+static Eina_Bool
+_properties_get(const EDBus_Service_Interface *iface, const char *propname, EDBus_Message_Iter *iter, EDBus_Message **error)
 {
-   EDBus_Message *reply;
-   char *interface, *property;
-   EDBus_Message_Iter *variant, *iter;
-
-   if (!edbus_message_arguments_get(msg, "ss", &interface, &property))
+   printf("Properties_get - %s\n", propname);
+   if (!strcmp(propname, "Resp2"))
+     edbus_message_iter_basic_append(iter, 's', resp2);
+   else if (!strcmp(propname, "text"))
+     edbus_message_iter_basic_append(iter, 's', "lalalala");
+   else if (!strcmp(propname, "int32"))
      {
-        printf("Error on edbus_message_arguments_get()\n");
-        return NULL;
-     }
-
-   if (strcmp(interface, IFACE))
-     {
-        reply = edbus_message_error_new(msg,
-                                        "org.freedesktop.DBus.Error.UnknownInterface",
-                                        "Interface not found.");
-        return reply;
+        edbus_message_iter_arguments_set(iter, "i", int32);
+        int32++;
      }
-
-   if (strcmp(property, "Resp2"))
+   else if (!strcmp(propname, "st"))
      {
-        reply = edbus_message_error_new(msg,
-                                        "org.freedesktop.DBus.Error.UnknownProperty",
-                                        "Property not found.");
-        return reply;
+        EDBus_Message_Iter *st;
+        edbus_message_iter_arguments_set(iter, "(ss)", &st);
+        edbus_message_iter_arguments_set(st, "ss", "string1", "string2");
+        edbus_message_iter_container_close(iter, st);
      }
-
-   reply = edbus_message_method_return_new(msg);
-   iter = edbus_message_iter_get(reply);
-   variant = edbus_message_iter_container_new(iter, 'v', "s");
-   edbus_message_iter_basic_append(variant, 's', resp2);
-   printf("get %s\n", resp2);
-   edbus_message_iter_container_close(iter, variant);
-
-   return reply;
+   return EINA_TRUE;
 }
 
 static EDBus_Message *
-_properties_set(const EDBus_Service_Interface *iface, const EDBus_Message *msg)
+_properties_set(const EDBus_Service_Interface *iface, const char *propname, const EDBus_Message *msg)
 {
    EDBus_Message *reply;
    char *interface, *property, *type, *txt;
@@ -251,24 +238,7 @@ _properties_set(const EDBus_Service_Interface *iface, const EDBus_Message *msg)
         return NULL;
      }
 
-   if (strcmp(interface, IFACE))
-     {
-        reply = edbus_message_error_new(msg,
-                                        "org.freedesktop.DBus.Error.UnknownInterface",
-                                        "Interface not found.");
-        return reply;
-     }
-
-   if (strcmp(property, "Resp2"))
-     {
-        reply = edbus_message_error_new(msg,
-                                        "org.freedesktop.DBus.Error.UnknownProperty",
-                                        "Property not found.");
-        return reply;
-     }
-
    type = edbus_message_iter_signature_get(variant);
-
    if (type[0] != 's')
      {
         reply = edbus_message_error_new(msg, "org.freedesktop.DBus.Error.InvalidSignature",
@@ -279,6 +249,7 @@ _properties_set(const EDBus_Service_Interface *iface, const EDBus_Message *msg)
 
    reply = edbus_message_method_return_new(msg);
    edbus_message_iter_arguments_get(variant, "s", &txt);
+   printf("Resp2 was set to: %s, previously was: %s\n", txt, resp2);
    free(type);
    free(resp2);
    resp2 = strdup(txt);
@@ -319,38 +290,30 @@ static const EDBus_Method methods[] = {
       { }
 };
 
-static const EDBus_Method properties_methods[] = {
-      {
-        "Get", EDBUS_ARGS({"s", "interface"}, {"s", "property"}),
-        EDBUS_ARGS({"v", "value"}), _properties_get, 0
-      },
-      {
-        "Set", EDBUS_ARGS({"s", "interface"}, {"s", "property"}, {"v", "value"}),
-        NULL, _properties_set, 0
-      },
+static const EDBus_Property properties[] = {
+      { "Resp2", "s", NULL, _properties_set },
+      { "text", "s", NULL, NULL },
+      { "int32", "i", NULL, NULL },
+      { "st", "(ss)", NULL, NULL},
       { }
 };
 
-
-/*
- * Temporary way to test the PropertiesChanged signal in FDO Properties
- * interface. TODO: Remove me when service part is done.
- */
-static const EDBus_Signal properties_signals[] = {
-   { "PropertiesChanged",
-     EDBUS_ARGS({"s", "interface"}, {"a{sv}", "data"}, {"as", "invalidate"}), 0
-   },
-   { }
-};
-
 static const EDBus_Service_Interface_Desc iface_desc = {
-   IFACE, methods
+   IFACE, methods, NULL, properties, _properties_get
 };
 
+static Eina_Bool _emit_changed(void *data)
+{
+   EDBus_Service_Interface *iface = data;
+   edbus_service_property_changed(iface, "int32");
+   return EINA_TRUE;
+}
+
 static void
 on_name_request(void *data, const EDBus_Message *msg, EDBus_Pending *pending)
 {
    unsigned int flag;
+   EDBus_Service_Interface *iface = data;
 
    resp2 = malloc(sizeof(char) * 5);
    strcpy(resp2, "test");
@@ -372,24 +335,29 @@ on_name_request(void *data, const EDBus_Message *msg, EDBus_Pending *pending)
         printf("error name already in use\n");
         return;
      }
+
+   timer = ecore_timer_add(3, _emit_changed, iface);
 }
 
 int
 main(void)
 {
    EDBus_Connection *conn;
+   EDBus_Service_Interface *iface;
 
    ecore_init();
    edbus_init();
 
    conn = edbus_connection_get(EDBUS_CONNECTION_TYPE_SESSION);
 
-   edbus_service_interface_register(conn, PATH, &iface_desc);
-   edbus_name_request(conn, BUS, EDBUS_NAME_REQUEST_FLAG_DO_NOT_QUEUE, on_name_request, NULL);
+   iface = edbus_service_interface_register(conn, PATH, &iface_desc);
+   edbus_name_request(conn, BUS, EDBUS_NAME_REQUEST_FLAG_DO_NOT_QUEUE,
+                      on_name_request, iface);
 
    ecore_main_loop_begin();
 
    free(resp2);
+   ecore_timer_del(timer);
    edbus_connection_unref(conn);
 
    edbus_shutdown();
index a6c818f..3e47f64 100644 (file)
@@ -150,6 +150,11 @@ struct _EDBus_Service_Interface
    const EDBus_Signal *signals;
    Eina_Array *sign_of_signals;
    EDBus_Service_Object *obj;
+   Eina_Hash *properties;
+   EDBus_Property_Set_Cb set_func;
+   EDBus_Property_Get_Cb get_func;
+   Ecore_Idler *idler_propschanged;
+   Eina_Array *props_changed;
 };
 
 typedef struct _Signal_Argument
index 278e647..ea41f8c 100644 (file)
@@ -51,6 +51,7 @@ static DBusObjectPathVTable vtable = {
 };
 
 EDBus_Service_Interface *introspectable;
+EDBus_Service_Interface *properties_iface;
 
 static void
 _introspect_append_signal(Eina_Strbuf *buf, const EDBus_Signal *sig)
@@ -77,6 +78,23 @@ _introspect_append_signal(Eina_Strbuf *buf, const EDBus_Signal *sig)
 }
 
 static void
+_instrospect_append_property(Eina_Strbuf *buf, const EDBus_Property *prop, const EDBus_Service_Interface *iface)
+{
+   eina_strbuf_append_printf(buf, "<property name=\"%s\" type=\"%s\" access=\"",
+                             prop->name, prop->type);
+   if (iface->get_func || prop->get_func)
+     eina_strbuf_append(buf, "read");
+   if (iface->set_func || prop->set_func)
+     eina_strbuf_append(buf, "write");
+   eina_strbuf_append(buf, "\">");
+
+   if (prop->flags & EDBUS_PROPERTY_FLAG_DEPRECATED)
+     eina_strbuf_append(buf, DBUS_ANNOTATION_DEPRECATED);
+
+   eina_strbuf_append(buf, "</property>");
+}
+
+static void
 _introspect_append_method(Eina_Strbuf *buf, const EDBus_Method *method)
 {
    int i;
@@ -122,6 +140,7 @@ static void
 _introspect_append_interface(Eina_Strbuf *buf, EDBus_Service_Interface *iface)
 {
    EDBus_Method *method;
+   EDBus_Property *prop;
    Eina_Iterator *iterator;
    unsigned short i;
    unsigned int size;
@@ -136,10 +155,176 @@ _introspect_append_interface(Eina_Strbuf *buf, EDBus_Service_Interface *iface)
    for (i = 0; i < size; i++)
      _introspect_append_signal(buf, &iface->signals[i]);
 
+   iterator = eina_hash_iterator_data_new(iface->properties);
+   EINA_ITERATOR_FOREACH(iterator, prop)
+     _instrospect_append_property(buf, prop, iface);
+   eina_iterator_free(iterator);
+
    eina_strbuf_append(buf, "</interface>");
 }
 
 static EDBus_Message *
+_cb_property_get(const EDBus_Service_Interface *piface, const EDBus_Message *msg)
+{
+   const char *propname, *iface_name;
+   EDBus_Service_Object *obj = piface->obj;
+   EDBus_Service_Interface *iface;
+   EDBus_Property *prop;
+   EDBus_Message *reply, *error_reply = NULL;
+   EDBus_Message_Iter *main_iter, *variant;
+   Eina_Bool ret;
+   EDBus_Property_Get_Cb getter = NULL;
+
+   if (!edbus_message_arguments_get(msg, "ss", &iface_name, &propname))
+     return NULL;
+
+   iface = eina_hash_find(obj->interfaces, iface_name);
+   if (!iface)
+     return edbus_message_error_new(msg, DBUS_ERROR_UNKNOWN_INTERFACE,
+                                    "Interface not found.");
+
+   prop = eina_hash_find(iface->properties, propname);
+   if (!prop) goto not_found;
+
+   if (prop->get_func)
+     getter = prop->get_func;
+   else if (iface->get_func)
+     getter = iface->get_func;
+
+   if (!getter) goto not_found;
+
+   reply = edbus_message_method_return_new(msg);
+   EINA_SAFETY_ON_NULL_RETURN_VAL(reply, NULL);
+
+   main_iter = edbus_message_iter_get(reply);
+   variant = edbus_message_iter_container_new(main_iter, 'v', prop->type);
+
+   ret = getter(iface, propname, variant, &error_reply);
+
+   if (ret)
+     {
+        edbus_message_iter_container_close(main_iter, variant);
+        return reply;
+     }
+
+   edbus_message_unref(reply);
+   return error_reply;
+
+not_found:
+   return edbus_message_error_new(msg, DBUS_ERROR_UNKNOWN_PROPERTY,
+                                  "Property not found.");
+}
+
+static EDBus_Message *
+_cb_property_getall(const EDBus_Service_Interface *piface, const EDBus_Message *msg)
+{
+   const char *iface_name;
+   EDBus_Service_Object *obj = piface->obj;
+   EDBus_Service_Interface *iface;
+   Eina_Iterator *iterator;
+   EDBus_Property *prop;
+   EDBus_Message *reply, *error_reply;
+   EDBus_Message_Iter *main_iter, *dict;
+
+   if (!edbus_message_arguments_get(msg, "s", &iface_name))
+     return NULL;
+
+   iface = eina_hash_find(obj->interfaces, iface_name);
+   if (!iface)
+     return edbus_message_error_new(msg, DBUS_ERROR_UNKNOWN_INTERFACE,
+                                    "Interface not found.");
+
+   reply = edbus_message_method_return_new(msg);
+   EINA_SAFETY_ON_NULL_RETURN_VAL(reply, NULL);
+   main_iter = edbus_message_iter_get(reply);
+   if (!edbus_message_iter_arguments_set(main_iter, "a{sv}", &dict))
+     {
+        edbus_message_unref(reply);
+        return NULL;
+     }
+
+   iterator = eina_hash_iterator_data_new(iface->properties);
+   EINA_ITERATOR_FOREACH(iterator, prop)
+     {
+        EDBus_Message_Iter *entry, *var;
+        Eina_Bool ret;
+        EDBus_Property_Get_Cb getter = NULL;
+
+        if (prop->get_func)
+          getter = prop->get_func;
+        else if (iface->get_func)
+          getter = iface->get_func;
+
+        if (!getter)
+          continue;
+
+        if (!edbus_message_iter_arguments_set(dict, "{sv}", &entry))
+          continue;
+
+        edbus_message_iter_basic_append(entry, 's', prop->name);
+        var = edbus_message_iter_container_new(entry, 'v', prop->type);
+
+        ret = getter(iface, prop->name, var, &error_reply);
+
+        if (!ret)
+          {
+             edbus_message_unref(reply);
+             reply = error_reply;
+             goto end;
+          }
+
+        edbus_message_iter_container_close(entry, var);
+        edbus_message_iter_container_close(dict, entry);
+     }
+   edbus_message_iter_container_close(main_iter, dict);
+
+end:
+   eina_iterator_free(iterator);
+   return reply;
+}
+
+static EDBus_Message *
+_cb_property_set(const EDBus_Service_Interface *piface, const EDBus_Message *msg)
+{
+   const char *propname, *iface_name;
+   EDBus_Service_Object *obj = piface->obj;
+   EDBus_Service_Interface *iface;
+   EDBus_Property *prop;
+   EDBus_Message *reply;
+   EDBus_Message_Iter *main_iter;
+   EDBus_Property_Set_Cb setter = NULL;
+
+   main_iter = edbus_message_iter_get(msg);
+   if (!edbus_message_iter_get_and_next(main_iter, 's', &iface_name) ||
+       !edbus_message_iter_get_and_next(main_iter, 's', &propname))
+     return NULL;
+
+   dbus_message_iter_init(msg->dbus_msg,
+                          &main_iter->dbus_iterator);
+
+   iface = eina_hash_find(obj->interfaces, iface_name);
+   if (!iface)
+     return edbus_message_error_new(msg, DBUS_ERROR_UNKNOWN_INTERFACE,
+                                    "Interface not found.");
+
+   prop = eina_hash_find(iface->properties, propname);
+   if (!prop)
+     return edbus_message_error_new(msg, DBUS_ERROR_UNKNOWN_PROPERTY,
+                                    "Property not found.");
+
+   if (prop->set_func)
+     setter = prop->set_func;
+   else if (iface->set_func)
+     setter = iface->set_func;
+
+   if (!setter)
+     return edbus_message_error_new(msg, DBUS_ERROR_PROPERTY_READ_ONLY,
+                                    "This property is read only");
+   reply = setter(iface, propname, msg);
+   return reply;
+}
+
+static EDBus_Message *
 cb_introspect(const EDBus_Service_Interface *_iface, const EDBus_Message *message)
 {
    EDBus_Service_Object *obj = _iface->obj;
@@ -184,6 +369,7 @@ _introspectable_create(void)
 
    EINA_MAGIC_SET(introspectable, EDBUS_SERVICE_INTERFACE_MAGIC);
    introspectable->sign_of_signals = eina_array_new(1);
+   introspectable->properties = eina_hash_string_small_new(NULL);
    introspectable->name = eina_stringshare_add("org.freedesktop.DBus.Introspectable");
    introspectable->methods = eina_hash_string_small_new(NULL);
 
@@ -191,12 +377,63 @@ _introspectable_create(void)
 }
 
 static void
-_introspectable_free(void)
+_default_interfaces_free(void)
 {
    eina_hash_free(introspectable->methods);
+   eina_hash_free(introspectable->properties);
    eina_stringshare_del(introspectable->name);
    eina_array_free(introspectable->sign_of_signals);
    free(introspectable);
+
+   eina_hash_free(properties_iface->methods);
+   eina_hash_free(properties_iface->properties);
+   eina_array_free(properties_iface->sign_of_signals);
+   free(properties_iface);
+}
+
+static const EDBus_Method _property_methods[] = {
+   {
+    "Get", EDBUS_ARGS({"s", "interface"}, {"s", "property"}),
+    EDBUS_ARGS({"v", "value"}), _cb_property_get
+   },
+   {
+    "Set", EDBUS_ARGS({"s", "interface"}, {"s", "property"}, {"v", "value"}),
+    NULL, _cb_property_set
+   },
+   {
+    "GetAll", EDBUS_ARGS({"s", "interface"}), EDBUS_ARGS({"a{sv}", "props"}),
+    _cb_property_getall
+   }
+};
+
+static const EDBus_Signal _properties_signals[] = {
+   {
+    "PropertiesChanged",
+    EDBUS_ARGS({"s", "interface"}, {"a{sv}", "changed_properties"}, {"as", "invalidated_properties"})
+   }
+};
+
+static void
+_properties_create(void)
+{
+   properties_iface = calloc(1, sizeof(EDBus_Service_Interface));
+   if (!properties_iface) return;
+
+   properties_iface->sign_of_signals = eina_array_new(1);
+   properties_iface->properties =  eina_hash_string_small_new(NULL);
+   properties_iface->name = EDBUS_FDO_INTERFACE_PROPERTIES;
+   properties_iface->methods = eina_hash_string_small_new(NULL);
+   EINA_MAGIC_SET(properties_iface, EDBUS_SERVICE_INTERFACE_MAGIC);
+
+   eina_hash_add(properties_iface->methods, _property_methods[0].member,
+                 &_property_methods[0]);
+   eina_hash_add(properties_iface->methods, _property_methods[1].member,
+                 &_property_methods[1]);
+   eina_hash_add(properties_iface->methods, _property_methods[2].member,
+                 &_property_methods[2]);
+
+   properties_iface->signals = _properties_signals;
+   eina_array_push(properties_iface->sign_of_signals, "sa{sv}as");
 }
 
 Eina_Bool
@@ -204,6 +441,8 @@ edbus_service_init(void)
 {
    _introspectable_create();
    EINA_SAFETY_ON_NULL_RETURN_VAL(introspectable, EINA_FALSE);
+   _properties_create();
+   EINA_SAFETY_ON_NULL_RETURN_VAL(properties_iface, EINA_FALSE);
 
    return EINA_TRUE;
 }
@@ -211,7 +450,7 @@ edbus_service_init(void)
 void
 edbus_service_shutdown(void)
 {
-   _introspectable_free();
+   _default_interfaces_free();
 }
 
 static EDBus_Service_Object *
@@ -235,6 +474,7 @@ _edbus_service_object_add(EDBus_Connection *conn, const char *path)
    edbus_connection_cb_free_add(conn, _on_connection_free, obj);
 
    eina_hash_add(obj->interfaces, introspectable->name, introspectable);
+   eina_hash_add(obj->interfaces, properties_iface->name, properties_iface);
 
    return obj;
 }
@@ -253,6 +493,7 @@ _edbus_service_interface_add(EDBus_Service_Object *obj, const char *interface)
    EINA_MAGIC_SET(iface, EDBUS_SERVICE_INTERFACE_MAGIC);
    iface->name = eina_stringshare_add(interface);
    iface->methods = eina_hash_string_superfast_new(NULL);
+   iface->properties = eina_hash_string_superfast_new(NULL);
    iface->obj = obj;
    eina_hash_add(obj->interfaces, iface->name, iface);
    return iface;
@@ -292,12 +533,25 @@ _edbus_service_method_add(EDBus_Service_Interface *interface, EDBus_Method *meth
    return EINA_TRUE;
 }
 
+static Eina_Bool
+_edbus_service_property_add(EDBus_Service_Interface *interface, EDBus_Property *property)
+{
+   EINA_SAFETY_ON_TRUE_RETURN_VAL(!!eina_hash_find(interface->properties,
+                                  property->name), EINA_FALSE);
+   EINA_SAFETY_ON_NULL_RETURN_VAL(property->type, EINA_FALSE);
+   EINA_SAFETY_ON_FALSE_RETURN_VAL(
+            dbus_signature_validate_single(property->type, NULL), EINA_FALSE);
+
+   return eina_hash_add(interface->properties, property->name, property);
+}
+
 EAPI EDBus_Service_Interface *
 edbus_service_interface_register(EDBus_Connection *conn, const char *path, const EDBus_Service_Interface_Desc *desc)
 {
    EDBus_Service_Object *obj;
    EDBus_Service_Interface *iface;
    EDBus_Method *method;
+   EDBus_Property *property;
    unsigned short i, z;
    Eina_Strbuf *buf = NULL;
 
@@ -352,6 +606,13 @@ edbus_service_interface_register(EDBus_Connection *conn, const char *path, const
      }
    iface->signals = desc->signals;
 
+   for (property = (EDBus_Property *)desc->properties;
+        property && property->name; property++)
+     _edbus_service_property_add(iface, property);
+
+   iface->get_func = desc->default_get;
+   iface->set_func = desc->default_set;
+
    return iface;
 }
 
@@ -359,7 +620,7 @@ static void
 _interface_free(EDBus_Service_Interface *interface)
 {
    unsigned size, i;
-   if (interface == introspectable) return;
+   if (interface == introspectable || interface == properties_iface) return;
 
    eina_hash_free(interface->methods);
    eina_stringshare_del(interface->name);
@@ -367,6 +628,11 @@ _interface_free(EDBus_Service_Interface *interface)
    for (i = 0; i < size; i++)
      eina_stringshare_del(eina_array_data_get(interface->sign_of_signals, i));
    eina_array_free(interface->sign_of_signals);
+   eina_hash_free(interface->properties);
+   if (interface->props_changed)
+     eina_array_free(interface->props_changed);
+   if (interface->idler_propschanged)
+     ecore_idler_del(interface->idler_propschanged);
    free(interface);
 }
 
@@ -402,7 +668,7 @@ edbus_service_interface_unregister(EDBus_Service_Interface *iface)
 {
    EDBUS_SERVICE_INTERFACE_CHECK(iface);
    eina_hash_del(iface->obj->interfaces, NULL, iface);
-   if (eina_hash_population(iface->obj->interfaces) < 2)
+   if (eina_hash_population(iface->obj->interfaces) < 3)
      edbus_service_object_unregister(iface);
    _interface_free(iface);
 }
@@ -585,3 +851,106 @@ edbus_service_object_data_del(EDBus_Service_Interface *iface, const char *key)
    EINA_SAFETY_ON_NULL_RETURN_VAL(key, NULL);
    return edbus_data_del(&(((EDBus_Service_Object *)iface->obj)->data), key);
 }
+
+static Eina_Bool
+_idler_propschanged(void *data)
+{
+   EDBus_Service_Interface *iface = data;
+   EDBus_Message *msg;
+   EDBus_Message_Iter *main_iter, *dict, *array_invalidate;
+   Eina_Hash *added = NULL;
+   EDBus_Property *prop;
+
+   iface->idler_propschanged = NULL;
+
+   added = eina_hash_string_small_new(NULL);
+   msg = edbus_message_signal_new(iface->obj->path, properties_iface->name,
+                                  properties_iface->signals[0].name);
+   EINA_SAFETY_ON_NULL_GOTO(msg, error);
+
+   main_iter = edbus_message_iter_get(msg);
+   if (!edbus_message_iter_arguments_set(main_iter, "sa{sv}", iface->name, &dict))
+     {
+        edbus_message_unref(msg);
+        goto error;
+     }
+
+   if (!iface->props_changed)
+     goto invalidate;
+   while ((prop = eina_array_pop(iface->props_changed)))
+     {
+        EDBus_Message_Iter *entry, *var;
+        EDBus_Message *error_reply;
+        Eina_Bool ret;
+        EDBus_Property_Get_Cb getter = NULL;
+
+        if (eina_hash_find(added, prop->name))
+          continue;
+        eina_hash_add(added, prop->name, prop);
+
+        if (prop->get_func)
+          getter = prop->get_func;
+        else if (iface->get_func)
+          getter = iface->get_func;
+
+        if (!getter)
+          continue;
+
+        EINA_SAFETY_ON_FALSE_GOTO(
+                edbus_message_iter_arguments_set(dict, "{sv}", &entry), error);
+
+        edbus_message_iter_basic_append(entry, 's', prop->name);
+        var = edbus_message_iter_container_new(entry, 'v', prop->type);
+
+        ret = getter(iface, prop->name, var, &error_reply);
+
+        if (!ret)
+          {
+             const char *errorname, *errormsg;
+             if (error_reply &&
+                 edbus_message_error_get(error_reply, &errorname, &errormsg))
+               ERR("%s %s", errorname, errormsg);
+
+             edbus_message_unref(msg);
+             if (error_reply) edbus_message_unref(error_reply);
+             goto error;
+          }
+
+        edbus_message_iter_container_close(entry, var);
+        edbus_message_iter_container_close(dict, entry);
+     }
+invalidate:
+   edbus_message_iter_container_close(main_iter, dict);
+
+   edbus_message_iter_arguments_set(main_iter, "as", &array_invalidate);
+   edbus_message_iter_container_close(main_iter, array_invalidate);
+
+   edbus_service_signal_send(iface, msg);
+   edbus_message_unref(msg);
+error:
+   if (added)
+     eina_hash_free(added);
+   if (iface->props_changed)
+     eina_array_flush(iface->props_changed);
+   return ECORE_CALLBACK_CANCEL;
+}
+
+EAPI Eina_Bool
+edbus_service_property_changed(EDBus_Service_Interface *iface, const char *name)
+{
+   EDBus_Property *prop;
+   EDBUS_SERVICE_INTERFACE_CHECK_RETVAL(iface, EINA_FALSE);
+   EINA_SAFETY_ON_NULL_RETURN_VAL(name, EINA_FALSE);
+
+   prop = eina_hash_find(iface->properties, name);
+   EINA_SAFETY_ON_NULL_RETURN_VAL(prop, EINA_FALSE);
+
+   if (!iface->idler_propschanged)
+     {
+        iface->idler_propschanged = ecore_idler_add(_idler_propschanged, iface);
+        if (!iface->props_changed)
+          iface->props_changed = eina_array_new(1);
+     }
+
+   return eina_array_push(iface->props_changed, prop);
+}
index 4a260cd..761797c 100644 (file)
@@ -11,6 +11,8 @@
 
 #define EDBUS_SIGNAL_FLAG_DEPRECATED 1
 
+#define EDBUS_PROPERTY_FLAG_DEPRECATED 1
+
 typedef struct _EDBus_Arg_Info
 {
    const char *signature;
@@ -29,8 +31,8 @@ typedef struct _EDBus_Arg_Info
 typedef struct _EDBus_Service_Interface EDBus_Service_Interface;
 typedef EDBus_Message * (*EDBus_Method_Cb)(const EDBus_Service_Interface *iface, const EDBus_Message *message);
 
-typedef Eina_Bool (*EDBus_Property_Get_Cb)(EDBus_Service_Interface *iface, const char *propname, EDBus_Message_Iter *iter, EDBus_Message **error);
-typedef EDBus_Message *(*EDBus_Property_Set_Cb)(EDBus_Service_Interface *iface, const char *propname, EDBus_Message *input_msg);
+typedef Eina_Bool (*EDBus_Property_Get_Cb)(const EDBus_Service_Interface *iface, const char *propname, EDBus_Message_Iter *iter, EDBus_Message **error);
+typedef EDBus_Message *(*EDBus_Property_Set_Cb)(const EDBus_Service_Interface *iface, const char *propname, const EDBus_Message *input_msg);
 
 typedef struct _EDBus_Method
 {
@@ -52,19 +54,19 @@ typedef struct _EDBus_Property
 {
    const char *name;
    const char *type;
-   unsigned int flags;
-   EDBus_Property_Set_Cb set_func;
    EDBus_Property_Get_Cb get_func;
+   EDBus_Property_Set_Cb set_func;
+   unsigned int flags;
 } EDBus_Property;
 
 typedef struct _EDBus_Service_Interface_Desc
 {
-   const char *interface;
-   const EDBus_Method *methods;
-   const EDBus_Signal *signals;
-   const EDBus_Property *properties;
-   const EDBus_Property_Set_Cb default_set;
-   const EDBus_Property_Get_Cb default_get;
+   const char *interface; /**< interface name */
+   const EDBus_Method *methods; /**< array of the methods that should be registered in this interface, the last item of array should be filled with NULL */
+   const EDBus_Signal *signals; /**< array of signal that this interface send, the last item of array should be filled with NULL */
+   const EDBus_Property *properties; /**< array of property that this interface have, the last item of array should be filled with NULL  */
+   const EDBus_Property_Get_Cb default_get; /**< default get function, if a property don't have a get function this will be used */
+   const EDBus_Property_Set_Cb default_set; /**< default set function, if a property don't have a set function this will be used */
 } EDBus_Service_Interface_Desc;
 
 /**
@@ -72,13 +74,7 @@ typedef struct _EDBus_Service_Interface_Desc
  *
  * @param conn where the interface should listen
  * @param path object path
- * @param iface interface
- * @param methods array of the methods that should be registered in this
- * interface, the last item of array should be filled with NULL
- * @param signals array of signal that this interface send, the last item
- * of array should be filled with NULL
- *
- * @note methods and signals must be static variables
+ * @param desc description of interface
  *
  * @return Interface
  */
@@ -150,6 +146,13 @@ EAPI void *edbus_service_object_data_get(const EDBus_Service_Interface *iface, c
  * @return pointer to data if found otherwise NULL
  */
 EAPI void *edbus_service_object_data_del(EDBus_Service_Interface *iface, const char *key);
+
+/**
+ * Add property to changed list. *
+ * A PropertiesChanged signal will be send on next idler iteration with all
+ * properties in changed list.
+ */
+EAPI Eina_Bool edbus_service_property_changed(EDBus_Service_Interface *iface, const char *name);
 /**
  * @}
  */