Generating code and docs: GIO Reference Manual

## Generating code and docs

### Using gdbus-codegen

dbus-glib comes with dbus-binding-tool, which can produce somewhat nice client- and server-side wrappers for a D-Bus interface. With GDBus, gdbus-codegen is used and like its counterpart, it also takes D-Bus Introspection XML as input:

Example 2. Example D-Bus Introspection XML

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    <node>
      <!-- org.gtk.GDBus.Example.ObjectManager.Animal:
           @short_description: Example docs generated by gdbus-codegen
           @since: 2.30

           This D-Bus interface is used to describe a simple animal.
        -->
      <interface name="org.gtk.GDBus.Example.ObjectManager.Animal">
        <!-- Mood: The mood of the animal.
             @since: 2.30

             Known values for this property include
             <literal>Happy</literal> and <literal>Sad</literal>. Use the
             org.gtk.GDBus.Example.ObjectManager.Animal.Poke() method to
             change this property.

             This property influences how often the animal jumps up and
             down, see the
             #org.gtk.GDBus.Example.ObjectManager.Animal::Jumped signal
             for more details.
        -->
        <property name="Mood" type="s" access="read"/>

        <!--
            Poke:
            @make_sad: Whether to make the animal sad.
            @make_happy: Whether to make the animal happy.
            @since: 2.30

            Method used to changing the mood of the animal. See also the
            #org.gtk.GDBus.Example.ObjectManager.Animal:Mood property.
          -->
        <method name="Poke">
          <arg direction="in" type="b" name="make_sad"/>
          <arg direction="in" type="b" name="make_happy"/>
        </method>

        <!--
            Jumped:
            @height: Height, in meters, that the animal jumped.
            @since: 2.30

            Emitted when the animal decides to jump.
          -->
        <signal name="Jumped">
          <arg type="d" name="height"/>
        </signal>

        <!--
            Foo:
            Property with no <quote>since</quote> annotation (should inherit the 2.30 from its containing interface).
          -->
        <property name="Foo" type="s" access="read"/>

        <!--
            Bar:
            @since: 2.36
            Property with a later <quote>since</quote> annotation.
          -->
        <property name="Bar" type="s" access="read"/>
      </interface>

      <!-- org.gtk.GDBus.Example.ObjectManager.Cat:
           @short_description: More example docs generated by gdbus-codegen

           This D-Bus interface is used to describe a cat. Right now there
           are no properties, methods or signals associated with this
           interface so it is essentially a <ulink
           url="http://en.wikipedia.org/wiki/Marker_interface_pattern">Marker
           Interface</ulink>.

           Note that D-Bus objects implementing this interface also
           implement the #org.gtk.GDBus.Example.ObjectManager.Animal
           interface.
        -->
      <interface name="org.gtk.GDBus.Example.ObjectManager.Cat">
      </interface>
    </node>

If this XML is processed like this

    1
    2
    3
    4
    5
    6
    gdbus-codegen --interface-prefix org.gtk.GDBus.Example.ObjectManager. \
                  --generate-c-code generated-code                        \
                  --c-namespace Example                       \
                  --c-generate-object-manager                 \
                  --generate-docbook generated-docs                       \
                  gdbus-example-objectmanager.xml

then two files `generated-code.h` and `generated-code.c` are generated. Additionally, two XML files `generated-docs-org.gtk.GDBus.Example.ObjectManager.Animal` and `generated-docs-org.gtk.GDBus.Example.ObjectManager.Cat` with Docbook XML are generated. For an example of what the docs look like see the Animal D-Bus interface documentation. and the Cat D-Bus interface documentation.

While the contents of `generated-code.h` and `generated-code.c` are best described by the gdbus-codegen manual page, brief examples of how this generated code can be used can be found in Example 3, “Server-side application using generated code” and Example 4, “Client-side application using generated code”. Additionally, since the generated code has 100% gtk-doc coverage, see ExampleAnimal, ExampleCat, ExampleObject and ExampleObjectManagerClient pages for documentation.

Example 3. Server-side application using generated code

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    #include "gdbus-object-manager-example/gdbus-example-objectmanager-generated.h"

    /* ---------------------------------------------------------------------------------------------------- */

    static GDBusObjectManagerServer *manager = NULL;

    static gboolean
    on_animal_poke (ExampleAnimal          *animal,
                    GDBusMethodInvocation  *invocation,
                    gboolean                make_sad,
                    gboolean                make_happy,
                    gpointer                user_data)
    {
      if ((make_sad && make_happy) || (!make_sad && !make_happy))
        {
          g_dbus_method_invocation_return_dbus_error (invocation,
                                                      "org.gtk.GDBus.Examples.ObjectManager.Error.Failed",
                                                      "Exactly one of make_sad or make_happy must be TRUE");
          goto out;
        }

      if (make_sad)
        {
          if (g_strcmp0 (example_animal_get_mood (animal), "Sad") == 0)
            {
              g_dbus_method_invocation_return_dbus_error (invocation,
                                                          "org.gtk.GDBus.Examples.ObjectManager.Error.SadAnimalIsSad",
                                                          "Sad animal is already sad");
              goto out;
            }

          example_animal_set_mood (animal, "Sad");
          example_animal_complete_poke (animal, invocation);
          goto out;
        }

      if (make_happy)
        {
          if (g_strcmp0 (example_animal_get_mood (animal), "Happy") == 0)
            {
              g_dbus_method_invocation_return_dbus_error (invocation,
                                                          "org.gtk.GDBus.Examples.ObjectManager.Error.HappyAnimalIsHappy",
                                                          "Happy animal is already happy");
              goto out;
            }

          example_animal_set_mood (animal, "Happy");
          example_animal_complete_poke (animal, invocation);
          goto out;
        }

      g_assert_not_reached ();

     out:
      return TRUE; /* to indicate that the method was handled */
    }

    static void
    on_bus_acquired (GDBusConnection *connection,
                     const gchar     *name,
                     gpointer         user_data)
    {
      ExampleObjectSkeleton *object;
      guint n;

      g_print ("Acquired a message bus connection\n");

      /* Create a new org.freedesktop.DBus.ObjectManager rooted at /example/Animals */
      manager = g_dbus_object_manager_server_new ("/example/Animals");

      for (n = 0; n < 10; n++)
        {
          gchar *s;
          ExampleAnimal *animal;

          /* Create a new D-Bus object at the path /example/Animals/N where N is 000..009 */
          s = g_strdup_printf ("/example/Animals/%03d", n);
          object = example_object_skeleton_new (s);
          g_free (s);

          /* Make the newly created object export the interface
           * org.gtk.GDBus.Example.ObjectManager.Animal (note
           * that @object takes its own reference to @animal).
           */
          animal = example_animal_skeleton_new ();
          example_animal_set_mood (animal, "Happy");
          example_object_skeleton_set_animal (object, animal);
          g_object_unref (animal);

          /* Cats are odd animals - so some of our objects implement the
           * org.gtk.GDBus.Example.ObjectManager.Cat interface in addition
           * to the .Animal interface
           */
          if (n % 2 == 1)
            {
              ExampleCat *cat;
              cat = example_cat_skeleton_new ();
              example_object_skeleton_set_cat (object, cat);
              g_object_unref (cat);
            }

          /* Handle Poke() D-Bus method invocations on the .Animal interface */
          g_signal_connect (animal,
                            "handle-poke",
                            G_CALLBACK (on_animal_poke),
                            NULL); /* user_data */

          /* Export the object (@manager takes its own reference to @object) */
          g_dbus_object_manager_server_export (manager, G_DBUS_OBJECT_SKELETON (object));
          g_object_unref (object);
        }

      /* Export all objects */
      g_dbus_object_manager_server_set_connection (manager, connection);
    }

    static void
    on_name_acquired (GDBusConnection *connection,
                      const gchar     *name,
                      gpointer         user_data)
    {
      g_print ("Acquired the name %s\n", name);
    }

    static void
    on_name_lost (GDBusConnection *connection,
                  const gchar     *name,
                  gpointer         user_data)
    {
      g_print ("Lost the name %s\n", name);
    }

    gint
    main (gint argc, gchar *argv[])
    {
      GMainLoop *loop;
      guint id;

      loop = g_main_loop_new (NULL, FALSE);

      id = g_bus_own_name (G_BUS_TYPE_SESSION,
                           "org.gtk.GDBus.Examples.ObjectManager",
                           G_BUS_NAME_OWNER_FLAGS_ALLOW_REPLACEMENT |
                           G_BUS_NAME_OWNER_FLAGS_REPLACE,
                           on_bus_acquired,
                           on_name_acquired,
                           on_name_lost,
                           loop,
                           NULL);

      g_main_loop_run (loop);

      g_bus_unown_name (id);
      g_main_loop_unref (loop);

      return 0;
    }

Example 4. Client-side application using generated code

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    #include "gdbus-object-manager-example/gdbus-example-objectmanager-generated.h"

    /* ---------------------------------------------------------------------------------------------------- */

    static void
    print_objects (GDBusObjectManager *manager)
    {
      GList *objects;
      GList *l;

      g_print ("Object manager at %s\n", g_dbus_object_manager_get_object_path (manager));
      objects = g_dbus_object_manager_get_objects (manager);
      for (l = objects; l != NULL; l = l->next)
        {
          ExampleObject *object = EXAMPLE_OBJECT (l->data);
          GList *interfaces;
          GList *ll;
          g_print (" - Object at %s\n", g_dbus_object_get_object_path (G_DBUS_OBJECT (object)));

          interfaces = g_dbus_object_get_interfaces (G_DBUS_OBJECT (object));
          for (ll = interfaces; ll != NULL; ll = ll->next)
            {
              GDBusInterface *interface = G_DBUS_INTERFACE (ll->data);
              g_print ("   - Interface %s\n", g_dbus_interface_get_info (interface)->name);

              /* Note that @interface is really a GDBusProxy instance - and additionally also
               * an ExampleAnimal or ExampleCat instance - either of these can be used to
               * invoke methods on the remote object. For example, the generated function
               *
               *  void example_animal_call_poke_sync (ExampleAnimal  *proxy,
               *                                      gboolean        make_sad,
               *                                      gboolean        make_happy,
               *                                      GCancellable   *cancellable,
               *                                      GError        **error);
               *
               * can be used to call the Poke() D-Bus method on the .Animal interface.
               * Additionally, the generated function
               *
               *  const gchar *example_animal_get_mood (ExampleAnimal *object);
               *
               * can be used to get the value of the :Mood property.
               */
            }
          g_list_free_full (interfaces, g_object_unref);
        }
      g_list_free_full (objects, g_object_unref);
    }

    static void
    on_object_added (GDBusObjectManager *manager,
                     GDBusObject        *object,
                     gpointer            user_data)
    {
      gchar *owner;
      owner = g_dbus_object_manager_client_get_name_owner (G_DBUS_OBJECT_MANAGER_CLIENT (manager));
      g_print ("Added object at %s (owner %s)\n", g_dbus_object_get_object_path (object), owner);
      g_free (owner);
    }

    static void
    on_object_removed (GDBusObjectManager *manager,
                       GDBusObject        *object,
                       gpointer            user_data)
    {
      gchar *owner;
      owner = g_dbus_object_manager_client_get_name_owner (G_DBUS_OBJECT_MANAGER_CLIENT (manager));
      g_print ("Removed object at %s (owner %s)\n", g_dbus_object_get_object_path (object), owner);
      g_free (owner);
    }

    static void
    on_notify_name_owner (GObject    *object,
                          GParamSpec *pspec,
                          gpointer    user_data)
    {
      GDBusObjectManagerClient *manager = G_DBUS_OBJECT_MANAGER_CLIENT (object);
      gchar *name_owner;

      name_owner = g_dbus_object_manager_client_get_name_owner (manager);
      g_print ("name-owner: %s\n", name_owner);
      g_free (name_owner);
    }

    static void
    on_interface_proxy_properties_changed (GDBusObjectManagerClient *manager,
                                           GDBusObjectProxy         *object_proxy,
                                           GDBusProxy               *interface_proxy,
                                           GVariant                 *changed_properties,
                                           const gchar *const       *invalidated_properties,
                                           gpointer                  user_data)
    {
      GVariantIter iter;
      const gchar *key;
      GVariant *value;
      gchar *s;

      g_print ("Properties Changed on %s:\n", g_dbus_object_get_object_path (G_DBUS_OBJECT (object_proxy)));
      g_variant_iter_init (&iter, changed_properties);
      while (g_variant_iter_next (&iter, "{&sv}", &key, &value))
        {
          s = g_variant_print (value, TRUE);
          g_print ("  %s -> %s\n", key, s);
          g_variant_unref (value);
          g_free (s);
        }
    }

    gint
    main (gint argc, gchar *argv[])
    {
      GDBusObjectManager *manager;
      GMainLoop *loop;
      GError *error;
      gchar *name_owner;

      manager = NULL;
      loop = NULL;

      loop = g_main_loop_new (NULL, FALSE);

      error = NULL;
      manager = example_object_manager_client_new_for_bus_sync (G_BUS_TYPE_SESSION,
                                                                G_DBUS_OBJECT_MANAGER_CLIENT_FLAGS_NONE,
                                                                "org.gtk.GDBus.Examples.ObjectManager",
                                                                "/example/Animals",
                                                                NULL, /* GCancellable */
                                                                &error);
      if (manager == NULL)
        {
          g_printerr ("Error getting object manager client: %s", error->message);
          g_error_free (error);
          goto out;
        }

      name_owner = g_dbus_object_manager_client_get_name_owner (G_DBUS_OBJECT_MANAGER_CLIENT (manager));
      g_print ("name-owner: %s\n", name_owner);
      g_free (name_owner);

      print_objects (manager);

      g_signal_connect (manager,
                        "notify::name-owner",
                        G_CALLBACK (on_notify_name_owner),
                        NULL);
      g_signal_connect (manager,
                        "object-added",
                        G_CALLBACK (on_object_added),
                        NULL);
      g_signal_connect (manager,
                        "object-removed",
                        G_CALLBACK (on_object_removed),
                        NULL);
      g_signal_connect (manager,
                        "interface-proxy-properties-changed",
                        G_CALLBACK (on_interface_proxy_properties_changed),
                        NULL);

      g_main_loop_run (loop);

     out:
      if (manager != NULL)
        g_object_unref (manager);
      if (loop != NULL)
        g_main_loop_unref (loop);

      return 0;
    }