[vala/tintou/gdbus-properties] dbus: Bind properties with the GDBusProxy directly




commit 9a6cc66c8f8630c7711666ccce57562d1b58b557
Author: Corentin Noël <corentin elementary io>
Date:   Tue Apr 7 23:50:55 2020 +0200

    dbus: Bind properties with the GDBusProxy directly

 codegen/valagdbusclientmodule.vala | 414 ++++++++++++++++++++++++++++++++-----
 codegen/valagdbusservermodule.vala | 111 ++++++++++
 tests/Makefile.am                  |   1 +
 tests/dbus/properties.test         |  98 +++++++++
 vala/valasemanticanalyzer.vala     |   5 -
 5 files changed, 567 insertions(+), 62 deletions(-)
---
diff --git a/codegen/valagdbusclientmodule.vala b/codegen/valagdbusclientmodule.vala
index af833f749..3eb55b6ee 100644
--- a/codegen/valagdbusclientmodule.vala
+++ b/codegen/valagdbusclientmodule.vala
@@ -205,17 +205,51 @@ public class Vala.GDBusClientModule : GDBusModule {
 
                cfile.add_type_member_definition (define_type);
 
+               generate_properties_enums (iface);
+
                var proxy_class_init = new CCodeFunction (lower_cname + "_class_init", "void");
                proxy_class_init.add_parameter (new CCodeParameter ("klass", cname + "Class*"));
                proxy_class_init.modifiers = CCodeModifiers.STATIC;
                push_function (proxy_class_init);
                var proxy_class = new CCodeFunctionCall (new CCodeIdentifier ("G_DBUS_PROXY_CLASS"));
                proxy_class.add_argument (new CCodeIdentifier ("klass"));
-               ccode.add_assignment (new CCodeMemberAccess.pointer (proxy_class, "g_signal"), new 
CCodeIdentifier (lower_cname + "_g_signal"));
+               ccode.add_declaration ("GDBusProxyClass *", new CCodeVariableDeclarator.zero ("proxy_class", 
proxy_class));
+               var proxy_class_identifier = new CCodeIdentifier ("proxy_class");
+
+               var object_class = new CCodeFunctionCall (new CCodeIdentifier ("G_OBJECT_CLASS"));
+               object_class.add_argument (new CCodeIdentifier ("klass"));
+               ccode.add_declaration ("GObjectClass *", new CCodeVariableDeclarator.zero ("object_class", 
object_class));
+               var object_class_identifier = new CCodeIdentifier ("object_class");
+
+               ccode.add_assignment (new CCodeMemberAccess.pointer (proxy_class_identifier, "g_signal"), new 
CCodeIdentifier (lower_cname + "_g_signal"));
+               ccode.add_assignment (new CCodeMemberAccess.pointer (proxy_class_identifier, 
"g_properties_changed"), new CCodeIdentifier (lower_cname + "_g_properties_changed"));
+
+               ccode.add_assignment (new CCodeMemberAccess.pointer (object_class_identifier, 
"get_property"), new CCodeIdentifier (lower_cname + "_get_property"));
+               ccode.add_assignment (new CCodeMemberAccess.pointer (object_class_identifier, 
"set_property"), new CCodeIdentifier (lower_cname + "_set_property"));
+
+               var sym_name = get_ccode_upper_case_name (iface) + "_PROXY";
+               var pspecs = new CCodeIdentifier ("%s_proxy_properties".printf (get_ccode_lower_case_name 
(iface)));
+               foreach (Property prop in iface.get_properties ()) {
+                       var upper_identifier = new CCodeIdentifier ("%s_%s_PROPERTY".printf (sym_name, 
Symbol.camel_case_to_lower_case (prop.name).ascii_up ()));
+                       var override_func = new CCodeFunctionCall (new CCodeIdentifier 
("g_object_class_override_property"));
+                       override_func.add_argument (object_class_identifier);
+                       override_func.add_argument (upper_identifier);
+                       override_func.add_argument (get_property_canonical_cconstant (prop));
+                       ccode.add_expression (override_func);
+                       var find_prop = new CCodeFunctionCall (new CCodeIdentifier 
("g_object_class_find_property"));
+                       find_prop.add_argument (object_class_identifier);
+                       find_prop.add_argument (get_property_canonical_cconstant (prop));
+                       ccode.add_assignment (new CCodeElementAccess (pspecs, upper_identifier), find_prop);
+               }
+
                pop_function ();
                cfile.add_function (proxy_class_init);
 
+               generate_pspec_from_dbus_property (iface);
+               generate_get_property_function (iface);
+               generate_set_property_function (iface);
                generate_signal_handler_function (iface);
+               generate_properties_changed_handler_function (iface);
 
                if (in_plugin) {
                        var proxy_class_finalize = new CCodeFunction (lower_cname + "_class_finalize", 
"void");
@@ -501,6 +535,208 @@ public class Vala.GDBusClientModule : GDBusModule {
                return wrapper_name;
        }
 
+       void generate_properties_enums (ObjectTypeSymbol sym) {
+               var prop_enum = new CCodeEnum ();
+               var sym_name = get_ccode_upper_case_name (sym) + "_PROXY";
+               prop_enum.add_value (new CCodeEnumValue ("%s_0_PROPERTY".printf (sym_name)));
+               var properties = sym.get_properties ();
+               foreach (Property prop in properties) {
+                          prop_enum.add_value (new CCodeEnumValue ("%s_%s_PROPERTY".printf (sym_name, 
Symbol.camel_case_to_lower_case (prop.name).ascii_up ())));
+               }
+
+               var last_prop = "%s_NUM_PROPERTIES".printf (sym_name);
+               prop_enum.add_value (new CCodeEnumValue (last_prop));
+               cfile.add_type_declaration (prop_enum);
+
+               var prop_array_decl = new CCodeDeclaration ("GParamSpec*");
+               prop_array_decl.modifiers |= CCodeModifiers.STATIC;
+               prop_array_decl.add_declarator (new CCodeVariableDeclarator ("%s_proxy_properties".printf 
(get_ccode_lower_case_name (sym)), null, new CCodeDeclaratorSuffix.with_array (new CCodeIdentifier 
(last_prop))));
+               cfile.add_type_declaration (prop_array_decl);
+       }
+
+       void generate_get_property_function (ObjectTypeSymbol sym) {
+               var cfunc = new CCodeFunction (get_ccode_lower_case_prefix (sym) + "proxy_get_property", 
"void");
+               cfunc.add_parameter (new CCodeParameter ("object", "GObject*"));
+               cfunc.add_parameter (new CCodeParameter ("property_id", "guint"));
+               cfunc.add_parameter (new CCodeParameter ("value", "GValue*"));
+               cfunc.add_parameter (new CCodeParameter ("pspec", "GParamSpec*"));
+
+               cfunc.modifiers |= CCodeModifiers.STATIC;
+
+               cfile.add_function_declaration (cfunc);
+
+               push_function (cfunc);
+
+               ccode.add_declaration ("GVariant *", new CCodeVariableDeclarator ("variant", null));
+               ccode.add_declaration ("const gchar *", new CCodeVariableDeclarator.zero 
("dbus_property_name", new CCodeConstant ("NULL")));
+
+               ccode.open_switch (new CCodeIdentifier ("property_id"));
+
+               var sym_name = get_ccode_upper_case_name (sym) + "_PROXY";
+               var dbus_proxy_cast = new CCodeFunctionCall (new CCodeIdentifier ("G_DBUS_PROXY"));
+               dbus_proxy_cast.add_argument (new CCodeIdentifier ("object"));
+               foreach (Property prop in sym.get_properties ()) {
+                       if (prop.access != SymbolAccessibility.PUBLIC) {
+                               continue;
+                       }
+
+                       var upper_identifier = new CCodeIdentifier ("%s_%s_PROPERTY".printf (sym_name, 
Symbol.camel_case_to_lower_case (prop.name).ascii_up ()));
+                       ccode.add_case (upper_identifier);
+                       ccode.add_assignment (new CCodeIdentifier ("dbus_property_name"), new 
CCodeConstant.string ("\"%s\"".printf (get_dbus_name_for_member (prop))));
+                       ccode.add_break ();
+               }
+
+               ccode.add_default ();
+               ccode.add_statement (new CCodeReturnStatement (null));
+               ccode.close ();
+
+               var get_cached_property_function = new CCodeFunctionCall (new CCodeIdentifier 
("g_dbus_proxy_get_cached_property"));
+               get_cached_property_function.add_argument (dbus_proxy_cast);
+               get_cached_property_function.add_argument (new CCodeIdentifier ("dbus_property_name"));
+               ccode.add_assignment (new CCodeIdentifier ("variant"), get_cached_property_function);
+
+               ccode.open_if (new CCodeBinaryExpression (CCodeBinaryOperator.INEQUALITY, new CCodeIdentifier 
("variant"), new CCodeConstant ("NULL")));
+
+               var dbus_gvariant_to_gvalue = new CCodeFunctionCall (new CCodeIdentifier 
("g_dbus_gvariant_to_gvalue"));
+               dbus_gvariant_to_gvalue.add_argument (new CCodeIdentifier ("variant"));
+               dbus_gvariant_to_gvalue.add_argument (new CCodeIdentifier ("value"));
+               ccode.add_expression (dbus_gvariant_to_gvalue);
+
+               var variant_unref = new CCodeFunctionCall (new CCodeIdentifier ("g_variant_unref"));
+               variant_unref.add_argument (new CCodeIdentifier ("variant"));
+               ccode.add_expression (variant_unref);
+
+               ccode.close ();
+
+               pop_function ();
+
+               cfile.add_function (cfunc);
+       }
+
+       void generate_set_property_function (ObjectTypeSymbol sym) {
+               // Create the async callback
+               var cfunc = new CCodeFunction (get_ccode_lower_case_prefix (sym) + "proxy_set_property_cb", 
"void");
+               cfunc.add_parameter (new CCodeParameter ("proxy", "GDBusProxy*"));
+               cfunc.add_parameter (new CCodeParameter ("res", "GAsyncResult*"));
+               cfunc.add_parameter (new CCodeParameter ("user_data", "gpointer"));
+
+               cfunc.modifiers |= CCodeModifiers.STATIC;
+
+               cfile.add_function_declaration (cfunc);
+
+               push_function (cfunc);
+
+               ccode.add_declaration ("GVariant *", new CCodeVariableDeclarator ("_ret", null));
+               ccode.add_declaration ("GError *", new CCodeVariableDeclarator.zero ("error", new 
CCodeConstant ("NULL")));
+               ccode.add_declaration ("const gchar *", new CCodeVariableDeclarator.zero 
("dbus_property_name", new CCodeIdentifier ("user_data")));
+
+               var ret_variant = new CCodeIdentifier ("_ret");
+               var dbus_proxy_call_finish = new CCodeFunctionCall (new CCodeIdentifier 
("g_dbus_proxy_call_finish"));
+               dbus_proxy_call_finish.add_argument (new CCodeIdentifier ("proxy"));
+               dbus_proxy_call_finish.add_argument (new CCodeIdentifier ("res"));
+               dbus_proxy_call_finish.add_argument (new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, 
new CCodeIdentifier ("error")));
+               ccode.add_assignment (ret_variant, dbus_proxy_call_finish);
+
+               ccode.open_if (new CCodeBinaryExpression (CCodeBinaryOperator.EQUALITY, ret_variant, new 
CCodeConstant ("NULL")));
+
+               var quark_to_string = new CCodeFunctionCall (new CCodeIdentifier ("g_quark_to_string"));
+               quark_to_string.add_argument (new CCodeMemberAccess.pointer (new CCodeIdentifier ("error"), 
"domain"));
+
+               var warning_call = new CCodeFunctionCall (new CCodeIdentifier ("g_warning"));
+               warning_call.add_argument (new CCodeConstant.string ("\"Error setting property '%%s' on 
interface %s: %%s (%%s, %%d)\"".printf (get_dbus_name (sym))));
+               warning_call.add_argument (new CCodeIdentifier ("dbus_property_name"));
+               warning_call.add_argument (new CCodeMemberAccess.pointer (new CCodeIdentifier ("error"), 
"message"));
+               warning_call.add_argument (quark_to_string);
+               warning_call.add_argument (new CCodeMemberAccess.pointer (new CCodeIdentifier ("error"), 
"code"));
+               ccode.add_expression (warning_call);
+
+               var error_free = new CCodeFunctionCall (new CCodeIdentifier ("g_error_free"));
+               error_free.add_argument (new CCodeIdentifier ("error"));
+               ccode.add_expression (error_free);
+
+               ccode.add_else ();
+               var variant_unref = new CCodeFunctionCall (new CCodeIdentifier ("g_variant_unref"));
+               variant_unref.add_argument (ret_variant);
+               ccode.add_expression (variant_unref);
+               ccode.close ();
+
+               pop_function ();
+
+               cfile.add_function (cfunc);
+
+               // Create the function
+               cfunc = new CCodeFunction (get_ccode_lower_case_prefix (sym) + "proxy_set_property", "void");
+               cfunc.add_parameter (new CCodeParameter ("object", "GObject*"));
+               cfunc.add_parameter (new CCodeParameter ("property_id", "guint"));
+               cfunc.add_parameter (new CCodeParameter ("value", "const GValue*"));
+               cfunc.add_parameter (new CCodeParameter ("pspec", "GParamSpec*"));
+
+               cfunc.modifiers |= CCodeModifiers.STATIC;
+
+               cfile.add_function_declaration (cfunc);
+
+               push_function (cfunc);
+
+               ccode.add_declaration ("GVariant *", new CCodeVariableDeclarator ("variant", null));
+               ccode.add_declaration ("const gchar *", new CCodeVariableDeclarator.zero 
("dbus_property_name", new CCodeConstant ("NULL")));
+               ccode.add_declaration ("const GVariantType *", new CCodeVariableDeclarator.zero 
("dbus_variant_type", new CCodeConstant ("NULL")));
+
+               ccode.open_switch (new CCodeIdentifier ("property_id"));
+
+               var sym_name = get_ccode_upper_case_name (sym) + "_PROXY";
+               foreach (Property prop in sym.get_properties ()) {
+                       if (prop.access != SymbolAccessibility.PUBLIC) {
+                               continue;
+                       }
+
+                       var upper_identifier = new CCodeIdentifier ("%s_%s_PROPERTY".printf (sym_name, 
Symbol.camel_case_to_lower_case (prop.name).ascii_up ()));
+                       ccode.add_case (upper_identifier);
+                       ccode.add_assignment (new CCodeIdentifier ("dbus_property_name"), new 
CCodeConstant.string ("\"%s\"".printf (get_dbus_name_for_member (prop))));
+                       var variant_type_cast = new CCodeFunctionCall (new CCodeIdentifier 
("G_VARIANT_TYPE"));
+                       variant_type_cast.add_argument (new CCodeConstant.string ("\"%s\"".printf 
(prop.property_type.get_type_signature (prop))));
+                       ccode.add_assignment (new CCodeIdentifier ("dbus_variant_type"), variant_type_cast);
+                       ccode.add_break ();
+               }
+
+               ccode.add_default ();
+               ccode.add_statement (new CCodeReturnStatement (null));
+               ccode.close ();
+
+               var dbus_gvalue_to_gvariant = new CCodeFunctionCall (new CCodeIdentifier 
("g_dbus_gvalue_to_gvariant"));
+               dbus_gvalue_to_gvariant.add_argument (new CCodeIdentifier ("value"));
+               dbus_gvalue_to_gvariant.add_argument (new CCodeIdentifier ("dbus_variant_type"));
+               ccode.add_assignment (new CCodeIdentifier ("variant"), dbus_gvalue_to_gvariant);
+
+               var dbus_proxy_cast = new CCodeFunctionCall (new CCodeIdentifier ("G_DBUS_PROXY"));
+               dbus_proxy_cast.add_argument (new CCodeIdentifier ("object"));
+
+               var arg_variant = new CCodeFunctionCall (new CCodeIdentifier ("g_variant_new"));
+               arg_variant.add_argument (new CCodeConstant.string ("\"(ssv)\""));
+               arg_variant.add_argument (new CCodeConstant.string ("\"%s\"".printf (get_dbus_name (sym))));
+               arg_variant.add_argument (new CCodeIdentifier ("dbus_property_name"));
+               arg_variant.add_argument (new CCodeIdentifier ("variant"));
+
+               var callback_cast = new CCodeCastExpression (new CCodeIdentifier (get_ccode_lower_case_prefix 
(sym) + "proxy_set_property_cb"), "GAsyncReadyCallback");
+               var dbus_proxy_call = new CCodeFunctionCall (new CCodeIdentifier ("g_dbus_proxy_call"));
+               dbus_proxy_call.add_argument (dbus_proxy_cast);
+               dbus_proxy_call.add_argument (new CCodeConstant.string 
("\"org.freedesktop.DBus.Properties.Set\""));
+               dbus_proxy_call.add_argument (arg_variant);
+               dbus_proxy_call.add_argument (new CCodeConstant ("G_DBUS_CALL_FLAGS_NONE"));
+               dbus_proxy_call.add_argument (new CCodeConstant ("-1"));
+               dbus_proxy_call.add_argument (new CCodeConstant ("NULL"));
+               dbus_proxy_call.add_argument (callback_cast);
+               dbus_proxy_call.add_argument (new CCodeCastExpression (new CCodeIdentifier 
("dbus_property_name"), "gpointer"));
+               ccode.add_expression (dbus_proxy_call);
+
+               variant_unref = new CCodeFunctionCall (new CCodeIdentifier ("g_variant_unref"));
+               variant_unref.add_argument (new CCodeIdentifier ("variant"));
+               ccode.add_expression (variant_unref);
+
+               pop_function ();
+
+               cfile.add_function (cfunc);
+       }
+
        void generate_signal_handler_function (ObjectTypeSymbol sym) {
                var cfunc = new CCodeFunction (get_ccode_lower_case_prefix (sym) + "proxy_g_signal", "void");
                cfunc.add_parameter (new CCodeParameter ("proxy", "GDBusProxy*"));
@@ -550,6 +786,118 @@ public class Vala.GDBusClientModule : GDBusModule {
                cfile.add_function (cfunc);
        }
 
+       void generate_pspec_from_dbus_property (ObjectTypeSymbol sym) {
+               var cfunc = new CCodeFunction ("_vala_%sfind_property_from_dbus_name".printf 
(get_ccode_lower_case_prefix (sym)), "GParamSpec *");
+               cfunc.add_parameter (new CCodeParameter ("dbus_property_name", "const gchar *"));
+               cfunc.modifiers |= CCodeModifiers.STATIC;
+               
+               cfile.add_function_declaration (cfunc);
+               
+               push_function (cfunc);
+               
+               bool firstif = true;
+
+               var pspecs = new CCodeIdentifier ("%s_proxy_properties".printf (get_ccode_lower_case_name 
(sym)));
+               var sym_name = get_ccode_upper_case_name (sym) + "_PROXY";
+               foreach (Property prop in sym.get_properties ()) {
+                       if (prop.access != SymbolAccessibility.PUBLIC) {
+                               continue;
+                       }
+
+                       var ccheck = new CCodeFunctionCall (new CCodeIdentifier ("g_strcmp0"));
+                       ccheck.add_argument (new CCodeIdentifier ("dbus_property_name"));
+                       ccheck.add_argument (new CCodeConstant ("\"%s\"".printf (get_dbus_name_for_member 
(prop))));
+
+                       var cond = new CCodeBinaryExpression (CCodeBinaryOperator.EQUALITY, ccheck, new 
CCodeConstant ("0"));
+                       if (firstif) {
+                               ccode.open_if (cond);
+                               firstif = false;
+                       } else {
+                               ccode.else_if (cond);
+                       }
+
+                       var upper_identifier = new CCodeIdentifier ("%s_%s_PROPERTY".printf (sym_name, 
Symbol.camel_case_to_lower_case (prop.name).ascii_up ()));
+                       ccode.add_statement (new CCodeReturnStatement (new CCodeElementAccess (pspecs, 
upper_identifier)));
+               }
+
+               if (!firstif) {
+                       ccode.close ();
+               }
+
+               ccode.add_statement (new CCodeReturnStatement (new CCodeConstant ("NULL")));
+               
+               pop_function ();
+               cfile.add_function (cfunc);
+       }
+
+       void generate_properties_changed_handler_function (ObjectTypeSymbol sym) {
+               var cfunc = new CCodeFunction (get_ccode_lower_case_prefix (sym) + 
"proxy_g_properties_changed", "void");
+               cfunc.add_parameter (new CCodeParameter ("proxy", "GDBusProxy*"));
+               cfunc.add_parameter (new CCodeParameter ("changed_properties", "GVariant*"));
+               cfunc.add_parameter (new CCodeParameter ("invalidated_properties", "const gchar* const*"));
+
+               cfunc.modifiers |= CCodeModifiers.STATIC;
+
+               cfile.add_function_declaration (cfunc);
+
+               push_function (cfunc);
+
+               ccode.add_declaration ("GVariantIter *", new CCodeVariableDeclarator ("iter", null));
+               ccode.add_declaration ("const gchar *", new CCodeVariableDeclarator ("key", null));
+               ccode.add_declaration ("GParamSpec *", new CCodeVariableDeclarator ("pspec", null));
+               ccode.add_declaration ("guint", new CCodeVariableDeclarator ("n", null));
+               var pspec_identifier = new CCodeIdentifier ("pspec");
+               var n_identifier = new CCodeIdentifier ("n");
+               var variant_get = new CCodeFunctionCall (new CCodeIdentifier ("g_variant_get"));
+               variant_get.add_argument (new CCodeIdentifier ("changed_properties"));
+               variant_get.add_argument (new CCodeConstant.string ("\"a{sv}\""));
+               variant_get.add_argument (new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, new 
CCodeIdentifier ("iter")));
+               ccode.add_expression (variant_get);
+
+               var variant_iter_next = new CCodeFunctionCall (new CCodeIdentifier ("g_variant_iter_next"));
+               variant_iter_next.add_argument (new CCodeIdentifier ("iter"));
+               variant_iter_next.add_argument (new CCodeConstant.string ("\"{&sv}\""));
+               variant_iter_next.add_argument (new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, new 
CCodeIdentifier ("key")));
+               variant_iter_next.add_argument (new CCodeConstant ("NULL"));
+
+               ccode.open_while (variant_iter_next);
+               var find_property_from_dbus_name = new CCodeFunctionCall (new CCodeIdentifier 
("_vala_%sfind_property_from_dbus_name".printf (get_ccode_lower_case_prefix (sym))));
+               find_property_from_dbus_name.add_argument (new CCodeIdentifier ("key"));
+               ccode.add_assignment (pspec_identifier, find_property_from_dbus_name);
+
+               ccode.open_if (new CCodeBinaryExpression (CCodeBinaryOperator.INEQUALITY, pspec_identifier, 
new CCodeConstant ("NULL")));
+
+               var notify_by_pspec = new CCodeFunctionCall (new CCodeIdentifier 
("g_object_notify_by_pspec"));
+               notify_by_pspec.add_argument (new CCodeCastExpression (new CCodeIdentifier ("proxy"), 
"GObject *"));
+               notify_by_pspec.add_argument (pspec_identifier);
+               ccode.add_expression (notify_by_pspec);
+
+               ccode.close ();
+               ccode.close ();
+
+               var variant_iter_free = new CCodeFunctionCall (new CCodeIdentifier ("g_variant_iter_free"));
+               variant_iter_free.add_argument (new CCodeIdentifier ("iter"));
+               ccode.add_expression (variant_iter_free);
+
+               var for_init = new CCodeAssignment (n_identifier, new CCodeConstant ("0"));
+               var for_increment = new CCodeUnaryExpression (CCodeUnaryOperator.POSTFIX_INCREMENT, 
n_identifier);
+               var for_condition = new CCodeBinaryExpression (CCodeBinaryOperator.INEQUALITY, new 
CCodeElementAccess (new CCodeIdentifier ("invalidated_properties"), n_identifier), new CCodeConstant 
("NULL"));
+               ccode.open_for (for_init, for_condition, for_increment);
+               var find_invalidated_property_from_dbus_name = new CCodeFunctionCall (new CCodeIdentifier 
("_vala_%sfind_property_from_dbus_name".printf (get_ccode_lower_case_prefix (sym))));
+               find_invalidated_property_from_dbus_name.add_argument (new CCodeElementAccess (new 
CCodeIdentifier ("invalidated_properties"), n_identifier));
+               ccode.add_assignment (pspec_identifier, find_invalidated_property_from_dbus_name);
+
+               ccode.open_if (new CCodeBinaryExpression (CCodeBinaryOperator.INEQUALITY, pspec_identifier, 
new CCodeConstant ("NULL")));
+               ccode.add_expression (notify_by_pspec);
+               ccode.close ();
+               ccode.close ();
+
+               pop_function ();
+
+               cfile.add_function (cfunc);
+       }
+
+
        void generate_marshalling (Method m, CallType call_type, string? iface_name, string? method_name, int 
method_timeout) {
                var gdbusproxy = new CCodeCastExpression (new CCodeIdentifier ("self"), "GDBusProxy *");
 
@@ -1090,8 +1438,6 @@ public class Vala.GDBusClientModule : GDBusModule {
        string generate_dbus_proxy_property_set (Interface main_iface, Interface iface, Property prop) {
                string proxy_name = "%sdbus_proxy_set_%s".printf (get_ccode_lower_case_prefix (main_iface), 
prop.name);
 
-               string dbus_iface_name = get_dbus_name (iface);
-
                var array_type = prop.set_accessor.value_type as ArrayType;
 
                var function = new CCodeFunction (proxy_name);
@@ -1114,60 +1460,14 @@ public class Vala.GDBusClientModule : GDBusModule {
 
                push_function (function);
 
-               ccode.add_declaration ("GVariant", new CCodeVariableDeclarator ("*_arguments"));
-               ccode.add_declaration ("GVariant", new CCodeVariableDeclarator ("*_reply"));
-
-               ccode.add_declaration ("GVariantBuilder", new CCodeVariableDeclarator ("_arguments_builder"));
-
-               var builder_init = new CCodeFunctionCall (new CCodeIdentifier ("g_variant_builder_init"));
-               builder_init.add_argument (new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, new 
CCodeIdentifier ("_arguments_builder")));
-               builder_init.add_argument (new CCodeIdentifier ("G_VARIANT_TYPE_TUPLE"));
-               ccode.add_expression (builder_init);
-
-               // interface name
-               write_expression (string_type, new CCodeIdentifier ("_arguments_builder"), new CCodeConstant 
("\"%s\"".printf (dbus_iface_name)), null);
-               // property name
-               write_expression (string_type, new CCodeIdentifier ("_arguments_builder"), new CCodeConstant 
("\"%s\"".printf (get_dbus_name_for_member (prop))), null);
-
-               // property value (as variant)
-               var builder_open = new CCodeFunctionCall (new CCodeIdentifier ("g_variant_builder_open"));
-               builder_open.add_argument (new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, new 
CCodeIdentifier ("_arguments_builder")));
-               builder_open.add_argument (new CCodeIdentifier ("G_VARIANT_TYPE_VARIANT"));
-               ccode.add_expression (builder_open);
-
-               if (prop.property_type.is_real_non_null_struct_type ()) {
-                       write_expression (prop.set_accessor.value_type, new CCodeIdentifier 
("_arguments_builder"), new CCodeUnaryExpression (CCodeUnaryOperator.POINTER_INDIRECTION, new CCodeIdentifier 
("value")), prop);
-               } else {
-                       write_expression (prop.set_accessor.value_type, new CCodeIdentifier 
("_arguments_builder"), new CCodeIdentifier ("value"), prop);
-               }
-
-               var builder_close = new CCodeFunctionCall (new CCodeIdentifier ("g_variant_builder_close"));
-               builder_close.add_argument (new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, new 
CCodeIdentifier ("_arguments_builder")));
-               ccode.add_expression (builder_close);
-
-               var builder_end = new CCodeFunctionCall (new CCodeIdentifier ("g_variant_builder_end"));
-               builder_end.add_argument (new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, new 
CCodeIdentifier ("_arguments_builder")));
-               ccode.add_assignment (new CCodeIdentifier ("_arguments"), builder_end);
-
-               var ccall = new CCodeFunctionCall (new CCodeIdentifier ("g_dbus_proxy_call_sync"));
-               ccall.add_argument (new CCodeCastExpression (new CCodeIdentifier ("self"), "GDBusProxy *"));
-               ccall.add_argument (new CCodeConstant ("\"org.freedesktop.DBus.Properties.Set\""));
-               ccall.add_argument (new CCodeIdentifier ("_arguments"));
-               ccall.add_argument (new CCodeConstant ("G_DBUS_CALL_FLAGS_NONE"));
-               ccall.add_argument (get_dbus_timeout (prop));
-               ccall.add_argument (new CCodeConstant ("NULL"));
-               ccall.add_argument (new CCodeConstant ("NULL"));
-
-               ccode.add_assignment (new CCodeIdentifier ("_reply"), ccall);
-
-               // return on error
-               ccode.open_if (new CCodeUnaryExpression (CCodeUnaryOperator.LOGICAL_NEGATION, new 
CCodeIdentifier ("_reply")));
-               ccode.add_return ();
-               ccode.close ();
-
-               var unref_reply = new CCodeFunctionCall (new CCodeIdentifier ("g_variant_unref"));
-               unref_reply.add_argument (new CCodeIdentifier ("_reply"));
-               ccode.add_expression (unref_reply);
+               var object_cast = new CCodeFunctionCall (new CCodeIdentifier ("G_OBJECT"));
+               object_cast.add_argument (new CCodeIdentifier ("self"));
+               var setter_call = new CCodeFunctionCall (new CCodeIdentifier ("g_object_set"));
+               setter_call.add_argument (object_cast);
+               setter_call.add_argument (get_property_canonical_cconstant (prop));
+               setter_call.add_argument (new CCodeIdentifier ("value"));
+               setter_call.add_argument (new CCodeConstant ("NULL"));
+               ccode.add_expression (setter_call);
 
                pop_function ();
 
diff --git a/codegen/valagdbusservermodule.vala b/codegen/valagdbusservermodule.vala
index 06166772e..14ec1dae3 100644
--- a/codegen/valagdbusservermodule.vala
+++ b/codegen/valagdbusservermodule.vala
@@ -710,6 +710,115 @@ public class Vala.GDBusServerModule : GDBusClientModule {
                }
        }
 
+       void generate_property_notify (ObjectTypeSymbol sym, Property prop) {
+               var cfunc = new CCodeFunction ("_%snotify_%s".printf (get_ccode_lower_case_prefix (sym), 
prop.name), "void");
+               cfunc.add_parameter (new CCodeParameter ("gobject", "GObject *"));
+               cfunc.add_parameter (new CCodeParameter ("pspec", "GParamSpec *"));
+               cfunc.add_parameter (new CCodeParameter ("user_data", "gpointer"));
+
+               cfunc.modifiers |= CCodeModifiers.STATIC;
+
+               push_function (cfunc);
+
+               ccode.add_declaration ("gpointer*", new CCodeVariableDeclarator.zero ("data", new 
CCodeIdentifier ("user_data")));
+               ccode.add_declaration ("GError*", new CCodeVariableDeclarator.zero ("error", new 
CCodeConstant ("NULL")));
+               ccode.add_declaration ("GValue", new CCodeVariableDeclarator.zero ("value", new CCodeConstant 
("G_VALUE_INIT")));
+               ccode.add_declaration ("GVariant*", new CCodeVariableDeclarator.zero ("parameters", new 
CCodeConstant ("NULL")));
+               ccode.add_declaration ("GVariant*", new CCodeVariableDeclarator.zero ("variant", new 
CCodeConstant ("NULL")));
+               ccode.add_declaration ("GVariantBuilder", new CCodeVariableDeclarator ("changed_builder", 
null));
+               ccode.add_declaration ("GVariantBuilder", new CCodeVariableDeclarator ("invalidated_builder", 
null));
+
+               var variant_builder_init = new CCodeFunctionCall (new CCodeIdentifier 
("g_variant_builder_init"));
+               variant_builder_init.add_argument (new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, 
new CCodeIdentifier ("changed_builder")));
+               variant_builder_init.add_argument (new CCodeConstant ("G_VARIANT_TYPE_VARDICT"));
+               ccode.add_expression (variant_builder_init);
+
+               var variant_builder_init2 = new CCodeFunctionCall (new CCodeIdentifier 
("g_variant_builder_init"));
+               variant_builder_init2.add_argument (new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, 
new CCodeIdentifier ("invalidated_builder")));
+               variant_builder_init2.add_argument (new CCodeConstant ("G_VARIANT_TYPE_STRING_ARRAY"));
+               ccode.add_expression (variant_builder_init2);
+
+               var object_get_property = new CCodeFunctionCall (new CCodeIdentifier 
("g_object_get_property"));
+               object_get_property.add_argument (new CCodeIdentifier ("gobject"));
+               object_get_property.add_argument (new CCodeConstant.string ("\"%s\"".printf (get_ccode_name 
(prop))));
+               object_get_property.add_argument (new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, 
new CCodeIdentifier ("value")));
+               ccode.add_expression (object_get_property);
+
+               var variant_type_cast = new CCodeFunctionCall (new CCodeIdentifier ("G_VARIANT_TYPE"));
+               variant_type_cast.add_argument (new CCodeConstant.string ("\"%s\"".printf 
(prop.property_type.get_type_signature (prop))));
+               var dbus_gvalue_to_gvariant = new CCodeFunctionCall (new CCodeIdentifier 
("g_dbus_gvalue_to_gvariant"));
+               dbus_gvalue_to_gvariant.add_argument (new CCodeUnaryExpression 
(CCodeUnaryOperator.ADDRESS_OF, new CCodeIdentifier ("value")));
+               dbus_gvalue_to_gvariant.add_argument (variant_type_cast);
+               ccode.add_assignment (new CCodeIdentifier ("variant"), dbus_gvalue_to_gvariant);
+
+               var variant_builder_add = new CCodeFunctionCall (new CCodeIdentifier 
("g_variant_builder_add"));
+               variant_builder_add.add_argument (new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, 
new CCodeIdentifier ("changed_builder")));
+               variant_builder_add.add_argument (new CCodeConstant.string ("\"{sv}\""));
+               variant_builder_add.add_argument (new CCodeConstant.string ("\"%s\"".printf 
(get_dbus_name_for_member (prop))));
+               variant_builder_add.add_argument (new CCodeIdentifier ("variant"));
+               ccode.add_expression (variant_builder_add);
+
+               var variant_unref = new CCodeFunctionCall (new CCodeIdentifier ("g_variant_unref"));
+               variant_unref.add_argument (new CCodeIdentifier ("variant"));
+               ccode.add_expression (variant_unref);
+
+               var variant_new = new CCodeFunctionCall (new CCodeIdentifier ("g_variant_new"));
+               variant_new.add_argument (new CCodeConstant.string ("\"(sa{sv}as)\""));
+               variant_new.add_argument (new CCodeConstant.string ("\"%s\"".printf (get_dbus_name (sym))));
+               variant_new.add_argument (new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, new 
CCodeIdentifier ("changed_builder")));
+               variant_new.add_argument (new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, new 
CCodeIdentifier ("invalidated_builder")));
+               ccode.add_assignment (new CCodeIdentifier ("parameters"), variant_new);
+
+               var disconnect_call = new CCodeFunctionCall (new CCodeIdentifier 
("g_dbus_connection_emit_signal"));
+               disconnect_call.add_argument (new CCodeCastExpression (new CCodeElementAccess (new 
CCodeIdentifier ("data"), new CCodeConstant ("1")), "GDBusConnection *"));
+               disconnect_call.add_argument (new CCodeConstant ("NULL"));
+               disconnect_call.add_argument (new CCodeCastExpression (new CCodeElementAccess (new 
CCodeIdentifier ("data"), new CCodeConstant ("2")), "const gchar *"));
+               disconnect_call.add_argument (new CCodeConstant.string 
("\"org.freedesktop.DBus.Properties\""));
+               disconnect_call.add_argument (new CCodeConstant.string ("\"PropertiesChanged\""));
+               disconnect_call.add_argument (new CCodeIdentifier ("parameters"));
+               disconnect_call.add_argument (new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, new 
CCodeIdentifier ("error")));
+               ccode.add_expression (disconnect_call);
+
+               pop_function ();
+
+               cfile.add_function_declaration (cfunc);
+               cfile.add_function (cfunc);
+       }
+
+       void handle_properties (ObjectTypeSymbol sym, bool connect) {
+               string dbus_iface_name = get_dbus_name (sym);
+               if (dbus_iface_name == null) {
+                       return;
+               }
+
+               foreach (Property prop in sym.get_properties ()) {
+                       if (prop.access != SymbolAccessibility.PUBLIC) {
+                               continue;
+                       }
+                       if (!is_dbus_visible (prop)) {
+                               continue;
+                       }
+
+                       var notify_name = "_%snotify_%s".printf (get_ccode_lower_case_prefix (sym), 
prop.name);
+                       if (connect) {
+                               generate_property_notify (sym, prop);
+                               var connect_call = new CCodeFunctionCall (new CCodeIdentifier 
("g_signal_connect"));
+                               connect_call.add_argument (new CCodeIdentifier ("object"));
+                               connect_call.add_argument (new CCodeConstant.string ("\"notify::%s\"".printf 
(get_ccode_name (prop))));
+                               connect_call.add_argument (new CCodeCastExpression (new CCodeIdentifier 
(notify_name), "GCallback"));
+                               connect_call.add_argument (new CCodeIdentifier ("data"));
+                               ccode.add_expression (connect_call);
+                       } else {
+                               // disconnect the signals
+                               var disconnect_call = new CCodeFunctionCall (new CCodeIdentifier 
("g_signal_handlers_disconnect_by_func"));
+                               disconnect_call.add_argument (new CCodeElementAccess (new CCodeIdentifier 
("data"), new CCodeConstant ("0")));
+                               disconnect_call.add_argument (new CCodeIdentifier (notify_name));
+                               disconnect_call.add_argument (new CCodeIdentifier ("data"));
+                               ccode.add_expression (disconnect_call);
+                       }
+               }
+       }
+
        void generate_interface_method_call_function (ObjectTypeSymbol sym) {
                var cfunc = new CCodeFunction (get_ccode_lower_case_prefix (sym) + 
"dbus_interface_method_call", "void");
                cfunc.add_parameter (new CCodeParameter ("connection", "GDBusConnection*"));
@@ -1160,6 +1269,7 @@ public class Vala.GDBusServerModule : GDBusClientModule {
                ccode.close ();
 
                handle_signals (sym, true);
+               handle_properties (sym, true);
 
                ccode.add_return (new CCodeIdentifier ("result"));
 
@@ -1176,6 +1286,7 @@ public class Vala.GDBusServerModule : GDBusClientModule {
                ccode.add_declaration ("gpointer*", new CCodeVariableDeclarator ("data", new CCodeIdentifier 
("user_data")));
 
                handle_signals (sym, false);
+               handle_properties (sym, false);
 
                var unref_object = new CCodeFunctionCall (new CCodeIdentifier (get_ccode_unref_function 
(sym)));
                unref_object.add_argument (new CCodeElementAccess (new CCodeIdentifier ("data"), new 
CCodeConstant ("0")));
diff --git a/tests/Makefile.am b/tests/Makefile.am
index 47e9a711b..f1aae0dd8 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -655,6 +655,7 @@ TESTS = \
        dbus/connection.test \
        dbus/dynamic-method.test \
        dbus/enum-string-marshalling.vala \
+       dbus/properties.test \
        dbus/signals.test \
        dbus/filedescriptor.test \
        dbus/filedescriptor-async.test \
diff --git a/tests/dbus/properties.test b/tests/dbus/properties.test
new file mode 100644
index 000000000..78b2df521
--- /dev/null
+++ b/tests/dbus/properties.test
@@ -0,0 +1,98 @@
+Packages: gio-2.0
+D-Bus
+
+Program: client
+
+[DBus (name = "org.example.Test")]
+interface Test : Object {
+       public abstract string test_property { owned get; set; }
+       public abstract int test_int_property { get; set; }
+       public abstract void change_everything () throws GLib.Error;
+       public abstract void check_everything () throws GLib.Error;
+}
+
+class Test2 : Object {
+       public string test_property { get; set; }
+       public int test_int_property { get; set; }
+}
+
+void main () {
+       // client
+       Test test = Bus.get_proxy_sync (BusType.SESSION, "org.example.Test", "/org/example/test", 
DBusProxyFlags.NONE);
+       assert (test.test_property == "foo");
+       assert (test.test_int_property == 17);
+
+       MainLoop main_loop = new MainLoop ();
+       Test2 test2 = new Test2 ();
+       test.bind_property ("test-property", test2, "test-property", 
GLib.BindingFlags.SYNC_CREATE|GLib.BindingFlags.BIDIRECTIONAL);
+       test.bind_property ("test-int-property", test2, "test-int-property", 
GLib.BindingFlags.SYNC_CREATE|GLib.BindingFlags.BIDIRECTIONAL);
+
+       assert (test2.test_property == "foo");
+       assert (test2.test_int_property == 17);
+
+       test2.notify["test-int-property"].connect (() => {
+               main_loop.quit ();
+       });
+
+       test.change_everything ();
+       main_loop.run ();
+       assert (test.test_property == "bar");
+       assert (test.test_int_property == 53);
+
+       assert (test2.test_property == "bar");
+       assert (test2.test_int_property == 53);
+
+       test2.test_property = "baz";
+       test2.test_int_property = 765;
+       main_loop = new MainLoop ();
+       main_loop.run ();
+
+       assert (test.test_property == "baz");
+       assert (test.test_int_property == 765);
+
+       test.check_everything ();
+}
+
+Program: server
+
+[DBus (name = "org.example.Test")]
+class Test : Object {
+       public string test_property { owned get; set; default = "foo";}
+       public int test_int_property { get; set; default = 17; }
+
+       public void change_everything () throws GLib.Error {
+               test_property = "bar";
+               test_int_property = 53;
+       }
+
+       public void check_everything () throws GLib.Error {
+               assert (test_property == "baz");
+               assert (test_int_property == 765);
+       }
+}
+
+MainLoop main_loop;
+
+void client_exit (Pid pid, int status) {
+       // client finished, terminate server
+       assert (status == 0);
+       main_loop.quit ();
+}
+
+void main () {
+       var conn = Bus.get_sync (BusType.SESSION);
+       conn.register_object ("/org/example/test", new Test ());
+
+       // try to register service in session bus
+       var request_result = conn.call_sync ("org.freedesktop.DBus", "/org/freedesktop/DBus", 
"org.freedesktop.DBus", "RequestName",
+                                             new Variant ("(su)", "org.example.Test", 0x4), null, 0, -1);
+       assert ((uint) request_result.get_child_value (0) == 1);
+
+       // server ready, spawn client
+       Pid client_pid;
+       Process.spawn_async (null, { "dbus_properties_client" }, null, SpawnFlags.DO_NOT_REAP_CHILD, null, 
out client_pid);
+       ChildWatch.add (client_pid, client_exit);
+
+       main_loop = new MainLoop ();
+       main_loop.run ();
+}
diff --git a/vala/valasemanticanalyzer.vala b/vala/valasemanticanalyzer.vala
index 2c8db80c0..0232e7abf 100644
--- a/vala/valasemanticanalyzer.vala
+++ b/vala/valasemanticanalyzer.vala
@@ -476,11 +476,6 @@ public class Vala.SemanticAnalyzer : CodeVisitor {
                        return false;
                }
 
-               if (type_sym is Interface && type_sym.get_attribute ("DBus") != null) {
-                       // GObject properties not currently supported in D-Bus interfaces
-                       return false;
-               }
-
                return true;
        }
 


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]