2005-01-29 Havoc Pennington <hp@redhat.com>
authorHavoc Pennington <hp@redhat.com>
Sat, 29 Jan 2005 19:52:19 +0000 (19:52 +0000)
committerHavoc Pennington <hp@redhat.com>
Sat, 29 Jan 2005 19:52:19 +0000 (19:52 +0000)
* glib/Makefile.am: rename dbus-glib-tool to dbus-binding-tool;
though it uses glib, it could be extended for any binding in
principle

* glib/dbus-gobject.c (gobject_message_function): change to the
new way properties work

* dbus/dbus-protocol.h: add the new interfaces

* doc/dbus-specification.xml: document the introspection format,
Introspectable interface, and add an org.freedesktop.Properties
interface.

* glib/dbus-gparser.c: add support for a <property> element

* glib/dbus-gidl.c: add PropertyInfo

* glib/dbus-gobject.c (handle_introspect): put the outermost
<node> outside the signal and property descriptions.
(introspect_properties): export properties as <property> rather
than as method calls

ChangeLog
dbus/dbus-protocol.h
doc/TODO
doc/dbus-specification.xml
glib/.cvsignore
glib/Makefile.am
glib/dbus-gidl.c
glib/dbus-gidl.h
glib/dbus-glib-tool.c
glib/dbus-gobject.c
glib/dbus-gparser.c

index 23d1f42..e5367ed 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,27 @@
+2005-01-29  Havoc Pennington  <hp@redhat.com>
+
+       * glib/Makefile.am: rename dbus-glib-tool to dbus-binding-tool;
+       though it uses glib, it could be extended for any binding in
+       principle
+
+       * glib/dbus-gobject.c (gobject_message_function): change to the
+       new way properties work
+
+       * dbus/dbus-protocol.h: add the new interfaces
+
+       * doc/dbus-specification.xml: document the introspection format,
+       Introspectable interface, and add an org.freedesktop.Properties
+       interface.
+
+       * glib/dbus-gparser.c: add support for a <property> element
+
+       * glib/dbus-gidl.c: add PropertyInfo
+
+       * glib/dbus-gobject.c (handle_introspect): put the outermost
+       <node> outside the signal and property descriptions.
+       (introspect_properties): export properties as <property> rather
+       than as method calls
+
 2005-01-28  Havoc Pennington  <hp@redhat.com>
 
        * doc/TODO, doc/dbus-specification.xml: spec and TODO tweaks
index 8191262..f72efb6 100644 (file)
@@ -212,8 +212,10 @@ extern "C" {
 /* Interfaces, these #define don't do much other than
  * catch typos at compile time
  */
-#define DBUS_INTERFACE_ORG_FREEDESKTOP_DBUS  "org.freedesktop.DBus"
+#define DBUS_INTERFACE_ORG_FREEDESKTOP_DBUS           "org.freedesktop.DBus"
 #define DBUS_INTERFACE_ORG_FREEDESKTOP_INTROSPECTABLE "org.freedesktop.Introspectable"
+#define DBUS_INTERFACE_ORG_FREEDESKTOP_PROPERTIES     "org.freedesktop.Properties"
+#define DBUS_INTERFACE_ORG_FREEDESKTOP_PEER           "org.freedesktop.Peer"
 
 /* This is a special interface whose methods can only be invoked
  * by the local implementation (messages from remote apps aren't
index d4ca427..49b0077 100644 (file)
--- a/doc/TODO
+++ b/doc/TODO
@@ -43,6 +43,8 @@ Important for 1.0
    support escaping in the addresses, be sure multiple addresses 
    in one env variable work, etc.
 
+ - Ping isn't handled
+
 Important for 1.0 GLib Bindings
 ===
 
index 6e34be2..973f390 100644 (file)
             exceptions.
           </para>
           <para>
+            In converting from native APIs to D-BUS, it is perhaps nice to 
+            map D-BUS naming conventions ("FooBar") to native conventions 
+            such as "fooBar" or "foo_bar" automatically. This is OK 
+            as long as you can say that the native API is one that 
+            was specifically written for D-BUS. It makes the most sense
+            when writing object implementations that will be exported 
+            over the bus. Object proxies used to invoke remote D-BUS 
+            objects probably need the ability to call any D-BUS method,
+            and thus a magic name mapping like this could be a problem.
+          </para>
+          <para>
             This specification doesn't require anything of native API bindings;
             the preceding is only a suggested convention for consistency 
             among bindings.
           </para>
         </sect4>
-
       </sect3>
 
       <sect3 id="message-protocol-types-signal">
     </para>
   </sect1>
 
-  <sect1 id="standard-messages">
-    <title>Standard One-to-One Messages</title>
+  <sect1 id="naming-conventions">
+    <title>Naming Conventions</title>
+    
+    <para>
+      D-BUS namespaces are all lowercase and correspond to reversed domain
+      names, as with Java. e.g. "org.freedesktop"
+    </para>
+    <para>
+      Interface, signal, method, and property names are "WindowsStyleCaps", note
+      that the first letter is capitalized, unlike Java.
+    </para>
+    <para>
+      Object paths are normally all lowercase with underscores used rather than
+      hyphens.
+    </para>
+  </sect1>
+    
+  <sect1 id="standard-interfaces">
+    <title>Standard Interfaces</title>
     <para>
       See <xref linkend="message-protocol-types-notation"/> for details on 
-       the notation used in this section.
+       the notation used in this section. There are some standard interfaces
+      that may be useful across various D-BUS applications.
     </para>
-    <sect2 id="standard-messages-ping">
-      <title><literal>org.freedesktop.Peer.Ping</literal></title>
-      <para>        
+    <sect2 id="standard-interfaces-peer">
+      <title><literal>org.freedesktop.Peer</literal></title>
+      <para>
+        The <literal>org.freedesktop.Peer</literal> interface 
+        has one method:
         <programlisting>
           org.freedesktop.Peer.Ping ()
         </programlisting>
       </para>
       <para>
-        On receipt of the <literal>METHOD_CALL</literal>
-        message <literal>org.freedesktop.Peer.Ping</literal>, an application
-        should do nothing other than reply with a <literal>METHOD_RETURN</literal> as usual.
+        On receipt of the <literal>METHOD_CALL</literal> message
+        <literal>org.freedesktop.Peer.Ping</literal>, an application should do
+        nothing other than reply with a <literal>METHOD_RETURN</literal> as
+        usual.  It does not matter which object path a ping is sent to.  The
+        reference implementation should simply handle this method on behalf of
+        all objects, though it doesn't yet. (The point is, you're really pinging
+        the peer process, not a specific object.)
       </para>
     </sect2>
 
+    <sect2 id="standard-interfaces-introspectable">
+      <title><literal>org.freedesktop.Introspectable</literal></title>
+      <para>
+        This interface has one method:
+        <programlisting>
+          org.freedesktop.Introspectable.Introspect (out STRING xml_data)
+        </programlisting>
+      </para>
+      <para>
+        Objects instances may implement
+        <literal>Introspect</literal> which returns an XML description of
+        the object, including its interfaces (with signals and methods), objects
+        below it in the object path tree, and its properties.
+      </para>
+      <para>
+        <xref linkend="introspection-format"/> describes the format of this XML string.
+      </para>
+    </sect2>
+    <sect2 id="standard-interfaces-properties">
+      <title><literal>org.freedesktop.Properties</literal></title>
+      <para>
+        Many native APIs will have a concept of object <firstterm>properties</firstterm> 
+        or <firstterm>attributes</firstterm>. These can be exposed via the 
+        <literal>org.freedesktop.Properties</literal> interface.
+      </para>
+      <para>
+        <programlisting>
+              org.freedesktop.Properties.Get (in STRING interface_name,
+                                              in STRING property_name,
+                                              out VARIANT value);
+              org.freedesktop.Properties.Set (in STRING interface_name,
+                                              in STRING property_name,
+                                              in VARIANT value);
+        </programlisting>
+      </para>
+      <para>
+        The available properties and whether they are writable can be determined
+        by calling <literal>org.freedesktop.Introspectable.Introspect</literal>,
+        see <xref linkend="standard-interfaces-introspectable"/>.
+      </para>
+    </sect2>
+  </sect1>
+
+  <sect1 id="introspection-format">
+    <title>Introspection Data Format</title>
+    <para>
+      As described in <xref linkend="standard-interfaces-introspectable"/>, 
+      objects may be introspected at runtime, returning an XML string 
+      that describes the object. The same XML format may be used in 
+      other contexts as well, for example as an "IDL" for generating 
+      static language bindings.
+    </para>
+    <para>
+      Here is an example of introspection data:
+      <programlisting>
+        &lt;!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN"
+         "http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd"&gt;
+        &lt;node name="/org/freedesktop/sample_object"&gt;
+          &lt;interface name="org.freedesktop.SampleInterface"&gt;
+            &lt;method name="Frobate"&gt;
+              &lt;arg name="foo" type="int32" direction="in"/&gt;
+              &lt;arg name="bar" type="string" direction="out"/&gt;
+            &lt;/method&gt;
+            &lt;signal name="Changed"&gt;
+              &lt;arg name="new_value" type="boolean"/&gt;
+            &lt;/signal&gt;
+            &lt;property name="Bar" type="byte" access="readwrite"/&gt;
+          &lt;/interface&gt;
+          &lt;node name="child_of_sample_object"/&gt;
+          &lt;node name="another_child_of_sample_object"/&gt;
+       &lt;/node&gt;
+      </programlisting>
+    </para>
+    <para>
+      A more formal DTD and spec needs writing, but here are some quick notes.
+      <itemizedlist>
+        <listitem>
+          <para>
+            Only the root &lt;node&gt; element can omit the node name, as it's
+            known to be the object that was introspected.  If the root
+            &lt;node&gt; does have a name attribute, it should be an absolute
+            object path. If child &lt;node&gt; have object paths, they should be
+            relative.
+          </para>
+        </listitem>
+        <listitem>
+          <para>
+            If a child &lt;node&gt; has any sub-elements, then they 
+            must represent a complete introspection of the child.
+            If a child &lt;node&gt; is empty, then it may or may 
+            not have sub-elements; the child must be introspected
+            in order to find out. The intent is that if an object 
+            knows that its children are "fast" to introspect
+            it can go ahead and return their information, but 
+            otherwise it can omit it.
+          </para>
+        </listitem>
+        <listitem>
+          <para>
+            The direction element on &lt;arg&gt; may be omitted, 
+            in which case it defaults to "in" for method calls 
+            and "out" for signals. Signals only allow "out" 
+            so while direction may be specified, it's pointless.
+          </para>
+        </listitem>
+        <listitem>
+          <para>
+            The possible directions are "in" and "out", 
+            unlike CORBA there is no "inout"
+          </para>
+        </listitem>
+        <listitem>
+          <para>
+            The possible property access flags are 
+            "readwrite", "read", and "write"
+          </para>
+        </listitem>
+        <listitem>
+          <para>
+            The current type="uint32" stuff is totally broken,
+            instead we have to do full signatures. 
+            However, then this format will suck for human readability. 
+            So, some thinking to do here.
+          </para>
+        </listitem>
+        <listitem>
+          <para>
+            Multiple interfaces can of course be listed for 
+            one &lt;node&gt;.
+          </para>
+        </listitem>
+      </itemizedlist>
+    </para>
+
   </sect1>
 
   <sect1 id="message-bus">
index 881b488..4e197a9 100644 (file)
@@ -5,7 +5,7 @@ Makefile.in
 *.lo
 *.la
 dbus-glib-test
-dbus-glib-tool
+dbus-binding-tool
 *.bb
 *.bbg
 *.da
index e0362b4..4db47de 100644 (file)
@@ -34,12 +34,12 @@ libdbus_gtool_la_SOURCES =                  \
 
 libdbus_gtool_la_LIBADD = libdbus-glib-1.la
 
-bin_PROGRAMS=dbus-glib-tool
+bin_PROGRAMS=dbus-binding-tool
 
-dbus_glib_tool_SOURCES =                       \
+dbus_binding_tool_SOURCES =                    \
        dbus-glib-tool.c
 
-dbus_glib_tool_LDADD= -lexpat libdbus-gtool.la
+dbus_binding_tool_LDADD= -lexpat libdbus-gtool.la
 
 if DBUS_BUILD_TESTS
 
index 6fa8391..cabc406 100644 (file)
@@ -2,7 +2,7 @@
 /* dbus-gidl.c data structure describing an interface, to be generated from IDL
  *             or something
  *
- * Copyright (C) 2003  Red Hat, Inc.
+ * Copyright (C) 2003, 2005  Red Hat, Inc.
  *
  * Licensed under the Academic Free License version 2.1
  *
@@ -46,6 +46,7 @@ struct InterfaceInfo
   /* Since we have BaseInfo now these could be one list */
   GSList *methods;
   GSList *signals;
+  GSList *properties;
 };
 
 struct MethodInfo
@@ -60,6 +61,13 @@ struct SignalInfo
   GSList *args;
 };
 
+struct PropertyInfo
+{
+  BaseInfo base;
+  int type;
+  PropertyAccessFlags access;
+};
+
 struct ArgInfo
 {
   BaseInfo base;
@@ -111,6 +119,9 @@ base_info_unref (BaseInfo *info)
     case INFO_TYPE_METHOD:
       method_info_unref ((MethodInfo*) info);
       break;
+    case INFO_TYPE_PROPERTY:
+      property_info_unref ((PropertyInfo*) info);
+      break;
     case INFO_TYPE_ARG:
       arg_info_unref ((ArgInfo*) info);
       break;
@@ -209,6 +220,20 @@ free_signal_list (GSList **signals_p)
   *signals_p = NULL;
 }
 
+static void
+free_property_list (GSList **props_p)
+{
+  GSList *tmp;
+  tmp = *props_p;
+  while (tmp != NULL)
+    {
+      property_info_unref (tmp->data);
+      tmp = tmp->next;
+    }
+  g_slist_free (*props_p);
+  *props_p = NULL;
+}
+
 NodeInfo*
 node_info_new (const char *name)
 {
@@ -307,6 +332,7 @@ interface_info_unref (InterfaceInfo *info)
     {
       free_method_list (&info->methods);
       free_signal_list (&info->signals);
+      free_property_list (&info->properties);
       base_info_free (info);
     }
 }
@@ -329,6 +355,12 @@ interface_info_get_signals (InterfaceInfo *info)
   return info->signals;
 }
 
+GSList*
+interface_info_get_properties (InterfaceInfo *info)
+{
+  return info->properties;
+}
+
 void
 interface_info_add_method (InterfaceInfo *info,
                            MethodInfo    *method)
@@ -345,6 +377,14 @@ interface_info_add_signal (InterfaceInfo *info,
   info->signals = g_slist_append (info->signals, signal);
 }
 
+void
+interface_info_add_property (InterfaceInfo *info,
+                             PropertyInfo  *property)
+{
+  property_info_ref (property);
+  info->properties = g_slist_append (info->properties, property);
+}
+
 static void
 free_arg_list (GSList **args_p)
 {
@@ -352,6 +392,8 @@ free_arg_list (GSList **args_p)
   tmp = *args_p;
   while (tmp != NULL)
     {
+      ArgInfo *ai = tmp->data;
+      g_assert (ai->base.type == INFO_TYPE_ARG);
       arg_info_unref (tmp->data);
       tmp = tmp->next;
     }
@@ -484,10 +526,64 @@ signal_info_add_arg (SignalInfo    *info,
   
   arg_info_ref (arg);
   info->args = g_slist_append (info->args, arg);
-
+  
   /* signal args don't need sorting since only "out" is allowed */
 }
 
+PropertyInfo*
+property_info_new (const char          *name,
+                   int                  type,
+                   PropertyAccessFlags  access)
+{
+  PropertyInfo *info;
+
+  info = g_new0 (PropertyInfo, 1);
+  info->base.refcount = 1;
+  info->base.name = g_strdup (name);
+  info->base.type = INFO_TYPE_PROPERTY;
+
+  info->type = type;
+  info->access = access;
+  
+  return info;
+}
+
+PropertyInfo*
+property_info_ref (PropertyInfo *info)
+{
+  info->base.refcount += 1;
+  
+  return info;
+}
+
+void
+property_info_unref (PropertyInfo *info)
+{
+  info->base.refcount -= 1;
+  if (info->base.refcount == 0)
+    {
+      base_info_free (info);
+    }
+}
+
+const char*
+property_info_get_name (PropertyInfo *info)
+{
+  return info->base.name;
+}
+
+int
+property_info_get_type (PropertyInfo *info)
+{
+  return info->type;
+}
+
+PropertyAccessFlags
+property_info_get_access (PropertyInfo *info)
+{
+  return info->access;
+}
+
 ArgInfo*
 arg_info_new (const char  *name,
               ArgDirection direction,
index 47b4f09..fffbddf 100644 (file)
@@ -36,21 +36,30 @@ typedef struct NodeInfo      NodeInfo;
 typedef struct InterfaceInfo InterfaceInfo;
 typedef struct MethodInfo    MethodInfo;
 typedef struct SignalInfo    SignalInfo;
+typedef struct PropertyInfo  PropertyInfo;
 typedef struct ArgInfo       ArgInfo;
 
 typedef enum
 {
+  ARG_INVALID = -1,
   ARG_IN,
   ARG_OUT
 } ArgDirection;
 
 typedef enum
 {
+  PROPERTY_READ  = 1 << 0,
+  PROPERTY_WRITE = 1 << 1
+} PropertyAccessFlags;
+
+typedef enum
+{
   INFO_TYPE_NODE,
   INFO_TYPE_INTERFACE,
   INFO_TYPE_METHOD,
   INFO_TYPE_SIGNAL,
-  INFO_TYPE_ARG
+  INFO_TYPE_ARG,
+  INFO_TYPE_PROPERTY
 
 } InfoType;
 
@@ -64,54 +73,59 @@ GType          base_info_get_gtype        (void);
 #define        BASE_INFO_TYPE             (base_info_get_gtype ())
 
 
-NodeInfo*      node_info_new              (const char    *name);
-NodeInfo*      node_info_ref              (NodeInfo      *info);
-void           node_info_unref            (NodeInfo      *info);
-const char*    node_info_get_name         (NodeInfo      *info);
-GSList*        node_info_get_interfaces   (NodeInfo      *info);
-GSList*        node_info_get_nodes        (NodeInfo      *info);
-void           node_info_add_interface    (NodeInfo      *info,
-                                           InterfaceInfo *interface);
-void           node_info_add_node         (NodeInfo      *info,
-                                           NodeInfo      *child);
-
-InterfaceInfo* interface_info_new         (const char    *name);
-InterfaceInfo* interface_info_ref         (InterfaceInfo *info);
-void           interface_info_unref       (InterfaceInfo *info);
-const char*    interface_info_get_name    (InterfaceInfo *info);
-GSList*        interface_info_get_methods (InterfaceInfo *info);
-GSList*        interface_info_get_signals (InterfaceInfo *info);
-void           interface_info_add_method  (InterfaceInfo *info,
-                                           MethodInfo    *method);
-void           interface_info_add_signal  (InterfaceInfo *info,
-                                           SignalInfo    *signal);
-
-MethodInfo*    method_info_new            (const char    *name);
-MethodInfo*    method_info_ref            (MethodInfo    *info);
-void           method_info_unref          (MethodInfo    *info);
-
-const char*    method_info_get_name       (MethodInfo    *info);
-GSList*        method_info_get_args       (MethodInfo    *info);
-void           method_info_add_arg        (MethodInfo    *info,
-                                           ArgInfo       *arg);
-
-SignalInfo*    signal_info_new            (const char    *name);
-SignalInfo*    signal_info_ref            (SignalInfo    *info);
-void           signal_info_unref          (SignalInfo    *info);
-
-const char*    signal_info_get_name       (SignalInfo    *info);
-GSList*        signal_info_get_args       (SignalInfo    *info);
-void           signal_info_add_arg        (SignalInfo    *info,
-                                           ArgInfo       *arg);
-
-ArgInfo*       arg_info_new               (const char    *name,
-                                           ArgDirection   direction,
-                                           int            type);
-ArgInfo*       arg_info_ref               (ArgInfo       *info);
-void           arg_info_unref             (ArgInfo       *info);
-const char*    arg_info_get_name          (ArgInfo       *info);
-int            arg_info_get_type          (ArgInfo       *info);
-ArgDirection   arg_info_get_direction     (ArgInfo       *info);
+NodeInfo*           node_info_new                 (const char          *name);
+NodeInfo*           node_info_ref                 (NodeInfo            *info);
+void                node_info_unref               (NodeInfo            *info);
+const char*         node_info_get_name            (NodeInfo            *info);
+GSList*             node_info_get_interfaces      (NodeInfo            *info);
+GSList*             node_info_get_nodes           (NodeInfo            *info);
+void                node_info_add_interface       (NodeInfo            *info,
+                                                   InterfaceInfo       *interface);
+void                node_info_add_node            (NodeInfo            *info,
+                                                   NodeInfo            *child);
+InterfaceInfo*      interface_info_new            (const char          *name);
+InterfaceInfo*      interface_info_ref            (InterfaceInfo       *info);
+void                interface_info_unref          (InterfaceInfo       *info);
+const char*         interface_info_get_name       (InterfaceInfo       *info);
+GSList*             interface_info_get_methods    (InterfaceInfo       *info);
+GSList*             interface_info_get_signals    (InterfaceInfo       *info);
+GSList*             interface_info_get_properties (InterfaceInfo       *info);
+void                interface_info_add_method     (InterfaceInfo       *info,
+                                                   MethodInfo          *method);
+void                interface_info_add_signal     (InterfaceInfo       *info,
+                                                   SignalInfo          *signal);
+void                interface_info_add_property   (InterfaceInfo       *info,
+                                                   PropertyInfo        *property);
+MethodInfo*         method_info_new               (const char          *name);
+MethodInfo*         method_info_ref               (MethodInfo          *info);
+void                method_info_unref             (MethodInfo          *info);
+const char*         method_info_get_name          (MethodInfo          *info);
+GSList*             method_info_get_args          (MethodInfo          *info);
+void                method_info_add_arg           (MethodInfo          *info,
+                                                   ArgInfo             *arg);
+SignalInfo*         signal_info_new               (const char          *name);
+SignalInfo*         signal_info_ref               (SignalInfo          *info);
+void                signal_info_unref             (SignalInfo          *info);
+const char*         signal_info_get_name          (SignalInfo          *info);
+GSList*             signal_info_get_args          (SignalInfo          *info);
+void                signal_info_add_arg           (SignalInfo          *info,
+                                                   ArgInfo             *arg);
+PropertyInfo*       property_info_new             (const char          *name,
+                                                   int                  type,
+                                                   PropertyAccessFlags  access);
+PropertyInfo*       property_info_ref             (PropertyInfo        *info);
+void                property_info_unref           (PropertyInfo        *info);
+const char*         property_info_get_name        (PropertyInfo        *info);
+int                 property_info_get_type        (PropertyInfo        *info);
+PropertyAccessFlags property_info_get_access      (PropertyInfo        *info);
+ArgInfo*            arg_info_new                  (const char          *name,
+                                                   ArgDirection         direction,
+                                                   int                  type);
+ArgInfo*            arg_info_ref                  (ArgInfo             *info);
+void                arg_info_unref                (ArgInfo             *info);
+const char*         arg_info_get_name             (ArgInfo             *info);
+int                 arg_info_get_type             (ArgInfo             *info);
+ArgDirection        arg_info_get_direction        (ArgInfo             *info);
 
 G_END_DECLS
 
index c460a35..c8273af 100644 (file)
@@ -57,7 +57,7 @@ pretty_print_list (GSList *list,
                    int     depth)
 {
   GSList *tmp;
-
+  
   tmp = list;
   while (tmp != NULL)
     {
@@ -106,6 +106,7 @@ pretty_print (BaseInfo *base,
 
         pretty_print_list (interface_info_get_methods (i), depth + 1);
         pretty_print_list (interface_info_get_signals (i), depth + 1);
+        pretty_print_list (interface_info_get_properties (i), depth + 1);
 
         indent (depth);
         printf ("}\n");
@@ -139,6 +140,22 @@ pretty_print (BaseInfo *base,
         printf (")\n");
       }
       break;
+    case INFO_TYPE_PROPERTY:
+      {
+        PropertyInfo *a = (PropertyInfo*) base;
+        int pt = property_info_get_type (a);
+        PropertyAccessFlags acc = property_info_get_access (a);
+
+        printf ("%s%s %s",
+                acc & PROPERTY_READ ? "read" : "",
+                acc & PROPERTY_WRITE ? "write" : "",
+                _dbus_gutils_type_to_string (pt));
+        if (name)
+          printf (" %s\n", name);
+        else
+          printf ("\n");
+      }
+      break;
     case INFO_TYPE_ARG:
       {
         ArgInfo *a = (ArgInfo*) base;
@@ -160,16 +177,16 @@ pretty_print (BaseInfo *base,
 static void
 usage (int ecode)
 {
-  fprintf (stderr, "dbus-glib-tool [--version] [--help]\n");
+  fprintf (stderr, "dbus-binding-tool [--version] [--help] [--pretty-print]\n");
   exit (ecode);
 }
 
 static void
 version (void)
 {
-  printf ("D-BUS GLib Tool %s\n"
-          "Copyright (C) 2003, 2004 Red Hat, Inc.\n"
-          "This is free software; see the source for copying conditions.\n"
+  printf ("D-BUS Binding Tool %s\n"
+          "Copyright (C) 2003-2005 Red Hat, Inc.\n"
+          "This is free software; xsee the source for copying conditions.\n"
           "There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n",
           VERSION);
   exit (0);
@@ -313,11 +330,11 @@ run_all_tests (const char *test_data_dir)
   else
     printf ("No test data!\n");
 
-  printf ("%s: running gtool tests\n", "dbus-glib-tool");
+  printf ("%s: running binding tests\n", "dbus-binding-tool");
   if (!_dbus_gtool_test (test_data_dir))
     test_die ("gtool");
 
-  printf ("%s: completed successfully\n", "dbus-glib-tool");
+  printf ("%s: completed successfully\n", "dbus-binding-tool");
 }
 
 #endif /* DBUS_BUILD_TESTS */
index 1836103..5aab4a6 100644 (file)
@@ -198,28 +198,27 @@ introspect_properties (GObject *object, GString *xml)
 
       s = uscore_to_wincaps (spec->name);
       
-      if (can_set)
+      if (can_set || can_get)
         {
-          g_string_append (xml, "    <method name=\"set_");
+          g_string_append (xml, "    <property name=\"");
           g_string_append (xml, s);
-          g_string_append (xml, "\">\n");
-          
-          g_string_append (xml, "      <arg type=\"");
+          g_string_append (xml, "\" type=\"");
           g_string_append (xml, _dbus_gutils_type_to_string (dbus_type));
-          g_string_append (xml, "\"/>\n");
-        }
+          g_string_append (xml, "\" access=\"\n");
 
-      if (can_get)
-        {
-          g_string_append (xml, "    <method name=\"get_");
-          g_string_append (xml, s);
-          g_string_append (xml, "\">\n");
+          if (can_set && can_get)
+            g_string_append (xml, "readwrite");
+          else if (can_get)
+            g_string_append (xml, "read");
+          else
+            {
+              g_assert (can_set);
+              g_string_append (xml, "write");
+            }
           
-          g_string_append (xml, "      <arg type=\"");
-          g_string_append (xml, _dbus_gutils_type_to_string (dbus_type));
-          g_string_append (xml, "\" direction=\"out\"/>\n");
+          g_string_append (xml, "\">\n");
         }
-
+      
       g_free (s);
     }
 
@@ -290,10 +289,10 @@ handle_introspect (DBusConnection *connection,
   
   xml = g_string_new (NULL);
 
+  g_string_append (xml, "<node>\n");
+  
   introspect_signals (G_OBJECT_TYPE (object), xml);
   introspect_properties (object, xml);
-         
-  g_string_append (xml, "<node>\n");
 
   /* Append child nodes */
   for (i = 0; children[i]; i++)
@@ -322,23 +321,24 @@ handle_introspect (DBusConnection *connection,
 }
 
 static DBusMessage*
-set_object_property (DBusConnection *connection,
-                     DBusMessage    *message,
-                     GObject        *object,
-                     GParamSpec     *pspec)
+set_object_property (DBusConnection  *connection,
+                     DBusMessage     *message,
+                     DBusMessageIter *iter,
+                     GObject         *object,
+                     GParamSpec      *pspec)
 {
   GValue value = { 0, };
   DBusMessage *ret;
-  DBusMessageIter iter;
-
-  dbus_message_iter_init (message, &iter);
+  DBusMessageIter sub;
 
+  dbus_message_iter_recurse (iter, &sub);
+  
   /* The g_object_set_property() will transform some types, e.g. it
    * will let you use a uchar to set an int property etc. Note that
    * any error in value range or value conversion will just
    * g_warning(). These GObject skels are not for secure applications.
    */
-  if (dbus_gvalue_demarshal (&iter, &value))
+  if (dbus_gvalue_demarshal (&sub, &value))
     {
       g_object_set_property (object,
                              pspec->name,
@@ -405,10 +405,12 @@ gobject_message_function (DBusConnection  *connection,
   const DBusGObjectInfo *info;
   GParamSpec *pspec;
   GObject *object;
-  const char *member;
   gboolean setter;
   gboolean getter;
   char *s;
+  const char *wincaps_propname;
+  const char *wincaps_propiface;
+  DBusMessageIter iter;
 
   object = G_OBJECT (user_data);
 
@@ -417,8 +419,6 @@ gobject_message_function (DBusConnection  *connection,
                                    "Introspect"))
     return handle_introspect (connection, message, object);
 
-  member = dbus_message_get_member (message);
-
   /* Try the metainfo, which lets us invoke methods */
 
   g_static_mutex_lock (&info_hash_mutex);
@@ -439,13 +439,39 @@ gobject_message_function (DBusConnection  *connection,
   /* If no metainfo, we can still do properties and signals
    * via standard GLib introspection
    */
-  setter = (member[0] == 's' && member[1] == 'e' && member[2] == 't' && member[3] == '_');
-  getter = (member[0] == 'g' && member[1] == 'e' && member[2] == 't' && member[3] == '_');
+  getter = FALSE;
+  setter = FALSE;
+  if (dbus_message_is_method_call (message,
+                                   DBUS_INTERFACE_ORG_FREEDESKTOP_PROPERTIES,
+                                   "Get"))
+    getter = TRUE;
+  else if (dbus_message_is_method_call (message,
+                                        DBUS_INTERFACE_ORG_FREEDESKTOP_PROPERTIES,
+                                        "Set"))
+    setter = TRUE;
 
   if (!(setter || getter))
     return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
 
-  s = wincaps_to_uscore (&member[4]);
+  dbus_message_iter_init (message, &iter);
+
+  if (dbus_message_iter_get_arg_type (&iter) != DBUS_TYPE_STRING)
+    {
+      g_warning ("Property get or set does not have an interface string as first arg\n");
+      return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+    }
+  dbus_message_iter_get_basic (&iter, &wincaps_propiface);
+  dbus_message_iter_next (&iter);
+
+  if (dbus_message_iter_get_arg_type (&iter) != DBUS_TYPE_STRING)
+    {
+      g_warning ("Property get or set does not have a property name string as second arg\n");
+      return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+    }
+  dbus_message_iter_get_basic (&iter, &wincaps_propname);
+  dbus_message_iter_next (&iter);
+  
+  s = wincaps_to_uscore (wincaps_propname);
 
   pspec = g_object_class_find_property (G_OBJECT_GET_CLASS (object),
                                         s);
@@ -458,11 +484,18 @@ gobject_message_function (DBusConnection  *connection,
 
       if (setter)
         {
-          ret = set_object_property (connection, message,
+          if (dbus_message_iter_get_arg_type (&iter) != DBUS_TYPE_VARIANT)
+            {
+              g_warning ("Property set does not have a variant value as third arg\n");
+              return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+            }
+          
+          ret = set_object_property (connection, message, &iter,
                                      object, pspec);
+          dbus_message_iter_next (&iter);
         }
       else if (getter)
-        {
+        {     
           ret = get_object_property (connection, message,
                                      object, pspec);
         }
@@ -474,6 +507,9 @@ gobject_message_function (DBusConnection  *connection,
 
       g_assert (ret != NULL);
 
+      if (dbus_message_iter_get_arg_type (&iter) != DBUS_TYPE_INVALID)
+        g_warning ("Property get or set had too many arguments\n");
+      
       dbus_connection_send (connection, ret, NULL);
       dbus_message_unref (ret);
       return DBUS_HANDLER_RESULT_HANDLED;
index 478dcdf..83922af 100644 (file)
@@ -1,7 +1,7 @@
 /* -*- mode: C; c-file-style: "gnu" -*- */
 /* dbus-gparser.c parse DBus description files
  *
- * Copyright (C) 2003  Red Hat, Inc.
+ * Copyright (C) 2003, 2005  Red Hat, Inc.
  *
  * Licensed under the Academic Free License version 2.1
  * 
@@ -169,6 +169,7 @@ struct Parser
   InterfaceInfo *interface;
   MethodInfo *method;
   SignalInfo *signal;
+  PropertyInfo *property;
   ArgInfo *arg;
 };
 
@@ -238,11 +239,12 @@ parse_node (Parser      *parser,
   if (parser->interface ||
       parser->method ||
       parser->signal ||
+      parser->property ||
       parser->arg)
     {
       g_set_error (error, G_MARKUP_ERROR,
                    G_MARKUP_ERROR_PARSE,
-                   _("Can't put <%s> element here"),
+                   _("Can't put <%s> element here"),
                    element_name);
       return FALSE;      
     }
@@ -264,6 +266,25 @@ parse_node (Parser      *parser,
       return FALSE;
     }
 
+  /* Root element name must be absolute */
+  if (parser->node_stack == NULL && name && *name != '/')
+    {
+      g_set_error (error, G_MARKUP_ERROR,
+                   G_MARKUP_ERROR_PARSE,
+                   _("\"%s\" attribute on <%s> element must be an absolute object path, \"%s\" not OK"),
+                   "name", element_name, name);
+      return FALSE;
+    }
+
+  /* Other element names must not be absolute */
+  if (parser->node_stack != NULL && name && *name == '/')
+    {
+      g_set_error (error, G_MARKUP_ERROR,
+                   G_MARKUP_ERROR_PARSE,
+                   _("\"%s\" attribute on <%s> element must not be an absolute object path, \"%s\" starts with /"),
+                   "name", element_name, name);
+      return FALSE;
+    }
   
   node = node_info_new (name);
 
@@ -293,12 +314,13 @@ parse_interface (Parser      *parser,
   if (parser->interface ||
       parser->method ||
       parser->signal ||
+      parser->property ||
       parser->arg ||
       (parser->node_stack == NULL))
     {
       g_set_error (error, G_MARKUP_ERROR,
                    G_MARKUP_ERROR_PARSE,
-                   _("Can't put <%s> element here"),
+                   _("Can't put <%s> element here"),
                    element_name);
       return FALSE;      
     }
@@ -345,11 +367,12 @@ parse_method (Parser      *parser,
       parser->node_stack == NULL ||
       parser->method ||
       parser->signal ||
+      parser->property ||
       parser->arg)
     {
       g_set_error (error, G_MARKUP_ERROR,
                    G_MARKUP_ERROR_PARSE,
-                   _("Can't put <%s> element here"),
+                   _("Can't put <%s> element here"),
                    element_name);
       return FALSE;      
     }
@@ -395,12 +418,13 @@ parse_signal (Parser      *parser,
   if (parser->interface == NULL ||
       parser->node_stack == NULL ||
       parser->signal ||
-      parser->signal ||
+      parser->method ||
+      parser->property ||
       parser->arg)
     {
       g_set_error (error, G_MARKUP_ERROR,
                    G_MARKUP_ERROR_PARSE,
-                   _("Can't put <%s> element here"),
+                   _("Can't put <%s> element here"),
                    element_name);
       return FALSE;      
     }
@@ -463,10 +487,122 @@ basic_type_from_string (const char *str)
     return DBUS_TYPE_INVALID;
 }
 
+/* FIXME we have to allow type signatures, not just basic types
+ */
 static int
-type_from_string (const char *str)
+type_from_string (const char *str,
+                  const char *element_name,
+                  GError    **error)
+{
+  int t;
+  
+  t = basic_type_from_string (str);
+
+  if (t == DBUS_TYPE_INVALID)
+    {
+      g_set_error (error, G_MARKUP_ERROR,
+                   G_MARKUP_ERROR_PARSE,
+                   _("Type \"%s\" not understood on <%s> element "),
+                   str, element_name);
+    }
+
+  return t;
+}
+
+static gboolean
+parse_property (Parser      *parser,
+                const char  *element_name,
+                const char **attribute_names,
+                const char **attribute_values,
+                GError     **error)
 {
-  return basic_type_from_string (str);
+  const char *name;
+  const char *access;
+  const char *type;
+  PropertyInfo *property;
+  NodeInfo *top;
+  PropertyAccessFlags access_flags;
+  int t;
+  
+  if (parser->interface == NULL ||
+      parser->node_stack == NULL ||
+      parser->signal ||
+      parser->method ||
+      parser->property ||
+      parser->arg)
+    {
+      g_set_error (error, G_MARKUP_ERROR,
+                   G_MARKUP_ERROR_PARSE,
+                   _("Can't put <%s> element here"),
+                   element_name);
+      return FALSE;      
+    }
+
+  name = NULL;
+  if (!locate_attributes (element_name, attribute_names,
+                          attribute_values, error,
+                          "name", &name,
+                          "access", &access,
+                          "type", &type,
+                          NULL))
+    return FALSE;
+
+  if (name == NULL)
+    {
+      g_set_error (error, G_MARKUP_ERROR,
+                   G_MARKUP_ERROR_PARSE,
+                   _("\"%s\" attribute required on <%s> element "),
+                   "name", element_name);
+      return FALSE;
+    }
+
+  if (access == NULL)
+    {
+      g_set_error (error, G_MARKUP_ERROR,
+                   G_MARKUP_ERROR_PARSE,
+                   _("\"%s\" attribute required on <%s> element "),
+                   "access", element_name);
+      return FALSE;
+    }
+
+  if (type == NULL)
+    {
+      g_set_error (error, G_MARKUP_ERROR,
+                   G_MARKUP_ERROR_PARSE,
+                   _("\"%s\" attribute required on <%s> element "),
+                   "type", element_name);
+      return FALSE;
+    }
+
+  t = type_from_string (type, element_name, error);
+  if (t == DBUS_TYPE_INVALID)
+    return FALSE;
+
+  access_flags = 0;
+  if (strcmp (access, "readwrite") == 0)
+    access_flags = PROPERTY_READ | PROPERTY_WRITE;
+  else if (strcmp (access, "read") == 0)
+    access_flags = PROPERTY_READ;
+  else if (strcmp (access, "write") == 0)
+    access_flags = PROPERTY_WRITE;
+  else
+    {
+      g_set_error (error, G_MARKUP_ERROR,
+                   G_MARKUP_ERROR_PARSE,
+                   _("access=\"%s\" must have value readwrite, read, or write on %s\n"),
+                   access, element_name);
+      return FALSE;
+    }
+  
+  top = parser->node_stack->data;
+  
+  property = property_info_new (name, t, access_flags);
+  interface_info_add_property (parser->interface, property);
+  property_info_unref (property);
+
+  parser->property = property;
+  
+  return TRUE;
 }
 
 static gboolean
@@ -485,11 +621,12 @@ parse_arg (Parser      *parser,
   
   if (!(parser->method || parser->signal) ||
       parser->node_stack == NULL ||
+      parser->property ||
       parser->arg)
     {
       g_set_error (error, G_MARKUP_ERROR,
                    G_MARKUP_ERROR_PARSE,
-                   _("Can't put <%s> element here"),
+                   _("Can't put <%s> element here"),
                    element_name);
       return FALSE;      
     }
@@ -525,20 +662,31 @@ parse_arg (Parser      *parser,
         g_assert_not_reached ();
     }
 
+  dir = ARG_INVALID;
+  
   if (strcmp (direction, "in") == 0)
     dir = ARG_IN;
   else if (strcmp (direction, "out") == 0)
     dir = ARG_OUT;
-  else
+  
+  if (dir == ARG_INVALID ||
+      (parser->signal && dir == ARG_IN))
     {
-      g_set_error (error, G_MARKUP_ERROR,
-                   G_MARKUP_ERROR_PARSE,
-                   _("\"%s\" attribute on <%s> has value \"in\" or \"out\""),
-                   "direction", element_name);
+      if (parser->signal)
+        g_set_error (error, G_MARKUP_ERROR,
+                     G_MARKUP_ERROR_PARSE,
+                     _("Signals must have direction=\"out\" (just omit the direction attribute)"));
+      else
+        g_set_error (error, G_MARKUP_ERROR,
+                     G_MARKUP_ERROR_PARSE,
+                     _("\"%s\" attribute on <%s> has value \"in\" or \"out\""),
+                     "direction", element_name);
       return FALSE;
     }
 
-  t = type_from_string (type);
+  t = type_from_string (type, element_name, error);
+  if (t == DBUS_TYPE_INVALID)
+    return FALSE;
   
   arg = arg_info_new (name, dir, t);
   if (parser->method)
@@ -588,6 +736,12 @@ parser_start_element (Parser      *parser,
                          attribute_values, error))
         return FALSE;
     }
+  else if (ELEMENT_IS ("property"))
+    {
+      if (!parse_property (parser, element_name, attribute_names,
+                           attribute_values, error))
+        return FALSE;
+    }
   else if (ELEMENT_IS ("arg"))
     {
       if (!parse_arg (parser, element_name, attribute_names,
@@ -624,6 +778,10 @@ parser_end_element (Parser      *parser,
     {
       parser->signal = NULL;
     }
+  else if (ELEMENT_IS ("property"))
+    {
+      parser->property = NULL;
+    }
   else if (ELEMENT_IS ("arg"))
     {
       parser->arg = NULL;
@@ -655,6 +813,8 @@ parser_content (Parser      *parser,
 {
   g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
 
+  /* FIXME check that it's all whitespace */
+  
   return TRUE;
 }