[gupnp/wip/phako/new-api: 9/17] wip: service-proxy-action new api



commit b6705c994ac248660da75352e40412b8463e93a7
Author: Jens Georg <mail jensge org>
Date:   Sun Jan 13 16:24:51 2019 +0100

    wip: service-proxy-action new api

 examples/light-client.c                       |  36 +-
 libgupnp/gupnp-service-proxy-action-private.h |  96 +++-
 libgupnp/gupnp-service-proxy-action.c         | 211 ++++++++-
 libgupnp/gupnp-service-proxy.c                | 655 ++++++++++----------------
 libgupnp/gupnp-service-proxy.h                |  45 +-
 5 files changed, 612 insertions(+), 431 deletions(-)
---
diff --git a/examples/light-client.c b/examples/light-client.c
index 82ee54c..359bc3e 100644
--- a/examples/light-client.c
+++ b/examples/light-client.c
@@ -39,16 +39,28 @@ send_cmd (GUPnPServiceProxy *proxy)
   GError *error = NULL;
   gboolean target;
 
+  GUPnPServiceProxyAction *action;
+
   if (mode == TOGGLE) {
     /* We're toggling, so first fetch the current status */
-    if (!gupnp_service_proxy_send_action
-        (proxy, "GetStatus", &error,
-         /* IN args */ NULL,
-         /* OUT args */ "ResultStatus", G_TYPE_BOOLEAN, &target, NULL)) {
+
+    action = gupnp_service_proxy_action_new ("GetStatus", NULL);
+
+    gupnp_service_proxy_call_action (proxy, action, NULL, &error);
+    if (error != NULL)
       goto error;
-    }
+
+    gupnp_service_proxy_action_get_result (action,
+                                           &error,
+                                           "ResultStatus", G_TYPE_BOOLEAN, &target, NULL);
+    g_clear_pointer (&action, gupnp_service_proxy_action_unref);
+
+    if (error != NULL)
+      goto error;
+
     /* And then toggle it */
     target = ! target;
+
   } else {
     /* Mode is a boolean, so the target is the mode thanks to our well chosen
        enumeration values. */
@@ -56,12 +68,13 @@ send_cmd (GUPnPServiceProxy *proxy)
   }
 
   /* Set the target */
-  if (!gupnp_service_proxy_send_action (proxy, "SetTarget", &error,
-                                        /* IN args */
-                                        "newTargetValue", G_TYPE_BOOLEAN, target, NULL,
-                                        /* OUT args */
-                                        NULL)) {
-    goto error;
+
+  action = gupnp_service_proxy_action_new ("SetTarget",
+                                           "newTargetValue", G_TYPE_BOOLEAN, target, NULL);
+  gupnp_service_proxy_call_action (proxy, action, NULL, &error);
+  g_clear_pointer (&action, gupnp_service_proxy_action_unref);
+  if (error != NULL) {
+          goto error;
   } else {
     if (!quiet) {
       g_print ("Set switch to %s.\n", target ? "on" : "off");
@@ -76,6 +89,7 @@ send_cmd (GUPnPServiceProxy *proxy)
   return;
 
  error:
+  g_clear_pointer (&action, gupnp_service_proxy_action_unref);
   g_printerr ("Cannot set switch: %s\n", error->message);
   g_error_free (error);
   goto done;
diff --git a/libgupnp/gupnp-service-proxy-action-private.h b/libgupnp/gupnp-service-proxy-action-private.h
index 53de39f..26c78de 100644
--- a/libgupnp/gupnp-service-proxy-action-private.h
+++ b/libgupnp/gupnp-service-proxy-action-private.h
@@ -38,6 +38,92 @@ G_BEGIN_DECLS
                 values = g_list_reverse (values); \
         } G_STMT_END
 
+/* This is a skip variant of G_VALUE_LCOPY, same as there is
+ * G_VALUE_COLLECT_SKIP for G_VALUE_COLLECT.
+ */
+#define VALUE_LCOPY_SKIP(value_type, var_args) \
+        G_STMT_START { \
+                GTypeValueTable *_vtable = g_type_value_table_peek (value_type); \
+                const gchar *_lcopy_format = _vtable->lcopy_format; \
+         \
+                while (*_lcopy_format) { \
+                        switch (*_lcopy_format++) { \
+                        case G_VALUE_COLLECT_INT: \
+                                va_arg ((var_args), gint); \
+                                break; \
+                        case G_VALUE_COLLECT_LONG: \
+                                va_arg ((var_args), glong); \
+                                break; \
+                        case G_VALUE_COLLECT_INT64: \
+                                va_arg ((var_args), gint64); \
+                                break; \
+                        case G_VALUE_COLLECT_DOUBLE: \
+                                va_arg ((var_args), gdouble); \
+                                break; \
+                        case G_VALUE_COLLECT_POINTER: \
+                                va_arg ((var_args), gpointer); \
+                                break; \
+                        default: \
+                                g_assert_not_reached (); \
+                        } \
+                } \
+        } G_STMT_END
+
+/* Initializes hash table to hold arg names as keys and GValues of
+ * given type, but without any specific value. Note that if you are
+ * going to use OUT_HASH_TABLE_TO_VAR_ARGS then you have to store a
+ * copy of var_args with G_VA_COPY before using this macro.
+ */
+#define VAR_ARGS_TO_OUT_HASH_TABLE(var_args, hash) \
+        G_STMT_START { \
+                const gchar *arg_name = va_arg (var_args, const gchar *); \
+         \
+                while (arg_name != NULL) { \
+                        GValue *value = g_new0 (GValue, 1); \
+                        GType type = va_arg (var_args, GType); \
+         \
+                        VALUE_LCOPY_SKIP (type, var_args); \
+                        g_value_init (value, type); \
+                        g_hash_table_insert (hash, g_strdup (arg_name), value); \
+                        arg_name = va_arg (var_args, const gchar *); \
+                } \
+        } G_STMT_END
+
+/* Puts values stored in hash table with GValues into var args.
+ */
+#define OUT_HASH_TABLE_TO_VAR_ARGS(hash, var_args) \
+        G_STMT_START { \
+                const gchar *arg_name = va_arg (var_args, const gchar *); \
+         \
+                while (arg_name != NULL) { \
+                        GValue *value = g_hash_table_lookup (hash, arg_name); \
+                        GType type = va_arg (var_args, GType); \
+         \
+                        if (value == NULL) { \
+                                g_warning ("No value for %s", arg_name); \
+                                G_VALUE_COLLECT_SKIP (type, var_args); \
+                        } else if (G_VALUE_TYPE (value) != type) { \
+                                g_warning ("Different GType in value (%s) and in var args (%s) for %s.", \
+                                           G_VALUE_TYPE_NAME (value), \
+                                           g_type_name (type), \
+                                           arg_name); \
+                        } else { \
+                                gchar *__error = NULL; \
+         \
+                                G_VALUE_LCOPY (value, var_args, 0, &__error); \
+                                if (__error != NULL) { \
+                                        g_warning ("Failed to lcopy the value of type %s for %s: %s", \
+                                                   g_type_name (type), \
+                                                   arg_name, \
+                                                   __error); \
+                                        g_free (__error); \
+                                } \
+                        } \
+                        arg_name = va_arg (var_args, const gchar *); \
+                } \
+        } G_STMT_END
+
+
 struct _GUPnPServiceProxyAction {
         GUPnPServiceProxy *proxy;
         char *name;
@@ -56,12 +142,10 @@ struct _GUPnPServiceProxyAction {
 G_GNUC_INTERNAL GUPnPServiceProxyAction *
 gupnp_service_proxy_action_new_internal (const char *action);
 
-G_GNUC_INTERNAL GUPnPServiceProxyAction *
-gupnp_service_proxy_action_ref (GUPnPServiceProxyAction *action);
-
-G_GNUC_INTERNAL void
-gupnp_service_proxy_action_unref (GUPnPServiceProxyAction *action);
-
+G_GNUC_INTERNAL gboolean
+gupnp_service_proxy_action_get_result_valist (GUPnPServiceProxyAction *action,
+                                              GError                 **error,
+                                              va_list                  var_args);
 
 G_END_DECLS
 
diff --git a/libgupnp/gupnp-service-proxy-action.c b/libgupnp/gupnp-service-proxy-action.c
index 6ba01b9..93880e7 100644
--- a/libgupnp/gupnp-service-proxy-action.c
+++ b/libgupnp/gupnp-service-proxy-action.c
@@ -25,6 +25,25 @@
 #include "gvalue-util.h"
 #include "xml-util.h"
 
+/* Checks an action response for errors and returns the parsed
+ * xmlDoc object. */
+
+G_GNUC_INTERNAL xmlDoc *
+_check_action_response (G_GNUC_UNUSED GUPnPServiceProxy *proxy,
+                       GUPnPServiceProxyAction         *action,
+                       xmlNode                        **params,
+                       GError                         **error);
+
+G_GNUC_INTERNAL void
+_read_out_parameter (const char *arg_name,
+                    GValue     *value,
+                    xmlNode    *params);
+
+/* GDestroyNotify for GHashTable holding GValues.
+ */
+G_GNUC_INTERNAL void
+_value_free (gpointer data);
+
 GUPnPServiceProxyAction *
 gupnp_service_proxy_action_new_internal (const char *action) {
         GUPnPServiceProxyAction *ret;
@@ -41,6 +60,7 @@ GUPnPServiceProxyAction *
 gupnp_service_proxy_action_ref (GUPnPServiceProxyAction *action)
 {
         g_return_val_if_fail (action, NULL);
+        g_debug ("=> action_action_ref");
 
         return g_atomic_rc_box_acquire (action);
 }
@@ -48,12 +68,14 @@ gupnp_service_proxy_action_ref (GUPnPServiceProxyAction *action)
 static void
 action_dispose (GUPnPServiceProxyAction *action)
 {
+        g_debug ("=> action_dispose");
         if (action->proxy != NULL) {
                 g_object_remove_weak_pointer (G_OBJECT (action->proxy),
                                 (gpointer *)&(action->proxy));
                 gupnp_service_proxy_remove_action (action->proxy, action);
         }
 
+        g_clear_error (&action->error);
         g_clear_object (&action->msg);
         g_free (action->name);
 }
@@ -62,6 +84,7 @@ void
 gupnp_service_proxy_action_unref (GUPnPServiceProxyAction *action)
 {
         g_return_if_fail (action);
+        g_debug ("=> action_unref");
 
         g_atomic_rc_box_release_full (action, (GDestroyNotify) action_dispose);
 }
@@ -100,9 +123,10 @@ GUPnPServiceProxyAction *
 gupnp_service_proxy_action_new (const char *action,
                                 ...)
 {
-        va_list var_args;
         GList *in_names = NULL;
         GList *in_values = NULL;
+        GUPnPServiceProxyAction *result = NULL;
+        va_list var_args;
 
         g_return_val_if_fail (action != NULL, NULL);
 
@@ -110,11 +134,14 @@ gupnp_service_proxy_action_new (const char *action,
         VAR_ARGS_TO_IN_LIST (var_args, in_names, in_values);
         va_end (var_args);
 
-        return gupnp_service_proxy_action_new_from_list (action,
-                                                         in_names,
-                                                         in_values);;
-}
+        result = gupnp_service_proxy_action_new_from_list (action,
+                                                           in_names,
+                                                           in_values);;
+        g_list_free_full (in_names, g_free);
+        g_list_free_full (in_values, _value_free);
 
+        return result;
+}
 
 /**
  * gupnp_service_proxy_action_new_from_list:
@@ -160,3 +187,177 @@ gupnp_service_proxy_action_new_from_list (const char *action_name,
 
         return action;
 }
+
+/**
+ * gupnp_service_proxy_action_get_result_list:
+ * @action: A #GUPnPServiceProxyAction handle
+ * @out_names: (element-type utf8) (transfer none): #GList of 'out' parameter
+ * names (as strings)
+ * @out_types: (element-type GType) (transfer none): #GList of types (as #GType)
+ * that line up with @out_names
+ * @out_values: (element-type GValue) (transfer full) (out): #GList of values
+ * (as #GValue) that line up with @out_names and @out_types.
+ * @error: The location where to store any error, or %NULL
+ *
+ * A variant of gupnp_service_proxy_action_get_result() that takes lists of
+ * out-parameter names, types and place-holders for values. The returned list
+ * in @out_values must be freed using #g_list_free and each element in it using
+ * #g_value_unset and #g_free.
+ *
+ * Return value : %TRUE on success.
+ *
+ **/
+gboolean
+gupnp_service_proxy_action_get_result_list (GUPnPServiceProxyAction *action,
+                                            GList                   *out_names,
+                                            GList                   *out_types,
+                                            GList                  **out_values,
+                                            GError                 **error)
+{
+        xmlDoc *response;
+        xmlNode *params;
+        GList *names;
+        GList *types;
+        GList *out_values_list;
+
+        out_values_list = NULL;
+
+        g_return_val_if_fail (action, FALSE);
+
+        /* Check for saved error from begin_action() */
+        if (action->error) {
+                g_propagate_error (error, g_error_copy (action->error));
+
+                return FALSE;
+        }
+
+        /* Check response for errors and do initial parsing */
+        response = _check_action_response (NULL, action, &params, &action->error);
+        if (response == NULL) {
+                g_propagate_error (error, g_error_copy (action->error));
+
+                return FALSE;
+        }
+
+        /* Read arguments */
+        types = out_types;
+        for (names = out_names; names; names=names->next) {
+                GValue *val;
+
+                val = g_new0 (GValue, 1);
+                g_value_init (val, (GType) types->data);
+
+                _read_out_parameter (names->data, val, params);
+
+                out_values_list = g_list_append (out_values_list, val);
+
+                types = types->next;
+        }
+
+        *out_values = out_values_list;
+
+        /* Cleanup */
+        xmlFreeDoc (response);
+
+        return TRUE;
+
+}
+
+/**
+ * gupnp_service_proxy_action_get_result_hash:
+ * @action: A #GUPnPServiceProxyAction handle
+ * @out_hash: (element-type utf8 GValue) (inout) (transfer none): A #GHashTable of
+ * out parameter name and initialised #GValue pairs
+ * @error: The location where to store any error, or %NULL
+ *
+ * See gupnp_service_proxy_action_get_result(); this version takes a #GHashTable for
+ * runtime generated parameter lists.
+ *
+ * Return value: %TRUE on success.
+ *
+ **/
+gboolean
+gupnp_service_proxy_action_get_result_hash (GUPnPServiceProxyAction *action,
+                                            GHashTable              *hash,
+                                            GError                 **error)
+{
+        xmlDoc *response;
+        xmlNode *params;
+
+        g_return_val_if_fail (action, FALSE);
+
+        /* Check for saved error from begin_action() */
+        if (action->error) {
+                g_propagate_error (error, g_error_copy (action->error));
+
+                return FALSE;
+        }
+
+        /* Check response for errors and do initial parsing */
+        response = _check_action_response (NULL, action, &params, &action->error);
+        if (response == NULL) {
+                g_propagate_error (error, g_error_copy (action->error));
+
+                return FALSE;
+        }
+
+        /* Read arguments */
+        g_hash_table_foreach (hash, (GHFunc) _read_out_parameter, params);
+
+        /* Cleanup */
+        xmlFreeDoc (response);
+
+        return TRUE;
+
+}
+
+gboolean
+gupnp_service_proxy_action_get_result (GUPnPServiceProxyAction *action,
+                                       GError                 **error,
+                                       ...)
+{
+        va_list var_args;
+        gboolean ret;
+
+        va_start (var_args, error);
+        ret = gupnp_service_proxy_action_get_result_valist (action,
+                                                            error,
+                                                            var_args);
+        va_end (var_args);
+
+        return ret;
+}
+
+gboolean
+gupnp_service_proxy_action_get_result_valist (GUPnPServiceProxyAction *action,
+                                              GError                 **error,
+                                              va_list                  var_args)
+{
+        GHashTable *out_hash;
+        va_list var_args_copy;
+        gboolean result;
+        GError *local_error;
+
+        g_return_val_if_fail (action, FALSE);
+
+        out_hash = g_hash_table_new_full (g_str_hash,
+                                          g_str_equal,
+                                          g_free,
+                                          _value_free);
+        G_VA_COPY (var_args_copy, var_args);
+        VAR_ARGS_TO_OUT_HASH_TABLE (var_args, out_hash);
+        local_error = NULL;
+        result = gupnp_service_proxy_action_get_result_hash (action,
+                                                             out_hash,
+                                                             &local_error);
+
+        if (local_error == NULL) {
+                OUT_HASH_TABLE_TO_VAR_ARGS (out_hash, var_args_copy);
+        } else {
+                g_propagate_error (error, local_error);
+        }
+        va_end (var_args_copy);
+        g_hash_table_unref (out_hash);
+
+        return result;
+}
diff --git a/libgupnp/gupnp-service-proxy.c b/libgupnp/gupnp-service-proxy.c
index 9805f98..4e03774 100644
--- a/libgupnp/gupnp-service-proxy.c
+++ b/libgupnp/gupnp-service-proxy.c
@@ -411,103 +411,10 @@ gupnp_service_proxy_send_action (GUPnPServiceProxy *proxy,
         return ret;
 }
 
-static void
-stop_main_loop (G_GNUC_UNUSED GUPnPServiceProxy       *proxy,
-                G_GNUC_UNUSED GUPnPServiceProxyAction *action,
-                gpointer                               user_data)
-{
-        g_main_loop_quit ((GMainLoop *) user_data);
-}
-
-/* This is a skip variant of G_VALUE_LCOPY, same as there is
- * G_VALUE_COLLECT_SKIP for G_VALUE_COLLECT.
- */
-#define VALUE_LCOPY_SKIP(value_type, var_args) \
-        G_STMT_START { \
-                GTypeValueTable *_vtable = g_type_value_table_peek (value_type); \
-                const gchar *_lcopy_format = _vtable->lcopy_format; \
-         \
-                while (*_lcopy_format) { \
-                        switch (*_lcopy_format++) { \
-                        case G_VALUE_COLLECT_INT: \
-                                va_arg ((var_args), gint); \
-                                break; \
-                        case G_VALUE_COLLECT_LONG: \
-                                va_arg ((var_args), glong); \
-                                break; \
-                        case G_VALUE_COLLECT_INT64: \
-                                va_arg ((var_args), gint64); \
-                                break; \
-                        case G_VALUE_COLLECT_DOUBLE: \
-                                va_arg ((var_args), gdouble); \
-                                break; \
-                        case G_VALUE_COLLECT_POINTER: \
-                                va_arg ((var_args), gpointer); \
-                                break; \
-                        default: \
-                                g_assert_not_reached (); \
-                        } \
-                } \
-        } G_STMT_END
-
-/* Initializes hash table to hold arg names as keys and GValues of
- * given type, but without any specific value. Note that if you are
- * going to use OUT_HASH_TABLE_TO_VAR_ARGS then you have to store a
- * copy of var_args with G_VA_COPY before using this macro.
- */
-#define VAR_ARGS_TO_OUT_HASH_TABLE(var_args, hash) \
-        G_STMT_START { \
-                const gchar *arg_name = va_arg (var_args, const gchar *); \
-         \
-                while (arg_name != NULL) { \
-                        GValue *value = g_new0 (GValue, 1); \
-                        GType type = va_arg (var_args, GType); \
-         \
-                        VALUE_LCOPY_SKIP (type, var_args); \
-                        g_value_init (value, type); \
-                        g_hash_table_insert (hash, g_strdup (arg_name), value); \
-                        arg_name = va_arg (var_args, const gchar *); \
-                } \
-        } G_STMT_END
-
-/* Puts values stored in hash table with GValues into var args.
- */
-#define OUT_HASH_TABLE_TO_VAR_ARGS(hash, var_args) \
-        G_STMT_START { \
-                const gchar *arg_name = va_arg (var_args, const gchar *); \
-         \
-                while (arg_name != NULL) { \
-                        GValue *value = g_hash_table_lookup (hash, arg_name); \
-                        GType type = va_arg (var_args, GType); \
-         \
-                        if (value == NULL) { \
-                                g_warning ("No value for %s", arg_name); \
-                                G_VALUE_COLLECT_SKIP (type, var_args); \
-                        } else if (G_VALUE_TYPE (value) != type) { \
-                                g_warning ("Different GType in value (%s) and in var args (%s) for %s.", \
-                                           G_VALUE_TYPE_NAME (value), \
-                                           g_type_name (type), \
-                                           arg_name); \
-                        } else { \
-                                gchar *__error = NULL; \
-         \
-                                G_VALUE_LCOPY (value, var_args, 0, &__error); \
-                                if (__error != NULL) { \
-                                        g_warning ("Failed to lcopy the value of type %s for %s: %s", \
-                                                   g_type_name (type), \
-                                                   arg_name, \
-                                                   __error); \
-                                        g_free (__error); \
-                                } \
-                        } \
-                        arg_name = va_arg (var_args, const gchar *); \
-                } \
-        } G_STMT_END
-
 /* GDestroyNotify for GHashTable holding GValues.
  */
-static void
-value_free (gpointer data)
+void
+_value_free (gpointer data)
 {
   GValue *value = (GValue *) data;
 
@@ -530,7 +437,7 @@ value_free (gpointer data)
  **/
 gboolean
 gupnp_service_proxy_send_action_valist (GUPnPServiceProxy *proxy,
-                                        const char        *action,
+                                        const char        *action_name,
                                         GError           **error,
                                         va_list            var_args)
 {
@@ -539,48 +446,30 @@ gupnp_service_proxy_send_action_valist (GUPnPServiceProxy *proxy,
         va_list var_args_copy;
         gboolean result;
         GError *local_error;
-        GMainLoop *main_loop;
         GUPnPServiceProxyAction *handle;
 
         g_return_val_if_fail (GUPNP_IS_SERVICE_PROXY (proxy), FALSE);
-        g_return_val_if_fail (action, FALSE);
-
+        g_return_val_if_fail (action_name, FALSE);
 
-        main_loop = g_main_loop_new (g_main_context_get_thread_default (),
-                                     TRUE);
 
         VAR_ARGS_TO_IN_LIST (var_args, in_names, in_values);
         G_VA_COPY (var_args_copy, var_args);
         out_hash = g_hash_table_new_full (g_str_hash,
                                           g_str_equal,
                                           g_free,
-                                          value_free);
+                                          _value_free);
         VAR_ARGS_TO_OUT_HASH_TABLE (var_args, out_hash);
 
-        local_error = NULL;
-        handle = gupnp_service_proxy_begin_action_list (proxy,
-                                                        action,
-                                                        in_names,
-                                                        in_values,
-                                                        stop_main_loop,
-                                                        main_loop);
-        if (!handle) {
-                g_main_loop_unref (main_loop);
-                result = FALSE;
+        handle = gupnp_service_proxy_action_new_from_list (action_name, in_names, in_values);
 
+        if (gupnp_service_proxy_call_action (proxy, handle, NULL,  error) != NULL) {
+                result = FALSE;
                 goto out;
         }
 
-        /* Loop till we get a reply (or time out) */
-        if (g_main_loop_is_running (main_loop))
-                g_main_loop_run (main_loop);
-
-        g_main_loop_unref (main_loop);
-
-        result = gupnp_service_proxy_end_action_hash (proxy,
-                                                      handle,
-                                                      out_hash,
-                                                      &local_error);
+        result = gupnp_service_proxy_action_get_result_hash (handle,
+                                                             out_hash,
+                                                             &local_error);
 
         if (local_error == NULL) {
                 OUT_HASH_TABLE_TO_VAR_ARGS (out_hash, var_args_copy);
@@ -588,9 +477,10 @@ gupnp_service_proxy_send_action_valist (GUPnPServiceProxy *proxy,
                 g_propagate_error (error, local_error);
         }
 out:
+        gupnp_service_proxy_action_unref (handle);
         va_end (var_args_copy);
         g_list_free_full (in_names, g_free);
-        g_list_free_full (in_values, value_free);
+        g_list_free_full (in_values, _value_free);
         g_hash_table_unref (out_hash);
 
         return result;
@@ -617,6 +507,8 @@ out:
  *
  * Return value: %TRUE if sending the action was succesful.
  *
+ * Deprecated: 1.1.2: Use gupnp_service_proxy_action_new_from_list() and gupnp_service_proxy_call_action()
+ *
  **/
 gboolean
 gupnp_service_proxy_send_action_list (GUPnPServiceProxy *proxy,
@@ -628,42 +520,40 @@ gupnp_service_proxy_send_action_list (GUPnPServiceProxy *proxy,
                                       GList            **out_values,
                                       GError           **error)
 {
-        GMainLoop *main_loop;
         GUPnPServiceProxyAction *handle;
+        gboolean result = FALSE;
 
         g_return_val_if_fail (GUPNP_IS_SERVICE_PROXY (proxy), FALSE);
         g_return_val_if_fail (action, FALSE);
 
-        main_loop = g_main_loop_new (g_main_context_get_thread_default (),
-                                     TRUE);
+        handle = gupnp_service_proxy_action_new_from_list (action, in_names, in_values);
 
-        handle = gupnp_service_proxy_begin_action_list (proxy,
-                                                        action,
-                                                        in_names,
-                                                        in_values,
-                                                        stop_main_loop,
-                                                        main_loop);
-        if (!handle) {
-                g_main_loop_unref (main_loop);
+        if (gupnp_service_proxy_call_action (proxy, handle, NULL, error) != NULL) {
+                result = FALSE;
 
-                return FALSE;
+                goto out;
         }
 
-        /* Loop till we get a reply (or time out) */
-        if (g_main_loop_is_running (main_loop))
-                g_main_loop_run (main_loop);
+        result = gupnp_service_proxy_action_get_result_list (handle,
+                                                             out_names,
+                                                             out_types,
+                                                             out_values,
+                                                             error);
+out:
+        gupnp_service_proxy_action_unref (handle);
 
-        g_main_loop_unref (main_loop);
+        return result;
+}
 
-        if (!gupnp_service_proxy_end_action_list (proxy,
-                                                  handle,
-                                                  out_names,
-                                                  out_types,
-                                                  out_values,
-                                                  error))
-                return FALSE;
+static void
+on_legacy_async_callback (GObject *source, GAsyncResult *res, gpointer user_data)
+{
+        GError *error = NULL;
+        GUPnPServiceProxyAction *action;
 
-        return TRUE;
+        gupnp_service_proxy_call_action_finish (GUPNP_SERVICE_PROXY (source), res, &error);
+        action = (GUPnPServiceProxyAction *) user_data;
+        action->callback (action->proxy, action, action->user_data);
 }
 
 /**
@@ -684,6 +574,8 @@ gupnp_service_proxy_send_action_list (GUPnPServiceProxy *proxy,
  * Returns: (transfer none): A #GUPnPServiceProxyAction handle. This will be freed when
  * gupnp_service_proxy_cancel_action() or
  * gupnp_service_proxy_end_action_valist().
+ *
+ * Deprecated: 1.1.1: Use gupnp_service_proxy_action_new() and gupnp_service_proxy_action_call_action_async()
  **/
 GUPnPServiceProxyAction *
 gupnp_service_proxy_begin_action (GUPnPServiceProxy              *proxy,
@@ -692,18 +584,27 @@ gupnp_service_proxy_begin_action (GUPnPServiceProxy              *proxy,
                                   gpointer                        user_data,
                                   ...)
 {
-        va_list var_args;
         GUPnPServiceProxyAction *ret;
+        GList *in_names = NULL;
+        GList *in_values = NULL;
+        va_list var_args;
 
         va_start (var_args, user_data);
-        ret = gupnp_service_proxy_begin_action_valist (proxy,
-                                                       action,
-                                                       callback,
-                                                       user_data,
-                                                       var_args);
+        VAR_ARGS_TO_IN_LIST (var_args, in_names, in_values);
         va_end (var_args);
 
-        return ret;
+        ret = gupnp_service_proxy_action_new_from_list (action, in_names, in_values);
+        g_list_free_full (in_names, g_free);
+        g_list_free_full (in_values, _value_free);
+
+        ret->callback = callback;
+        ret->user_data = user_data;
+
+        return gupnp_service_proxy_call_action_async (proxy,
+                                                      ret,
+                                                      NULL,
+                                                      on_legacy_async_callback,
+                                                      ret);
 }
 
 /* Begins a basic action message */
@@ -788,168 +689,80 @@ prepare_action_msg (GUPnPServiceProxy              *proxy,
         g_string_insert (ret->msg_str, ret->header_pos, service_type);
         ret->header_pos += strlen (service_type);
         g_string_insert (ret->msg_str, ret->header_pos, "\">");
-}
-
-
-/* Begins a basic action message */
-static GUPnPServiceProxyAction *
-begin_action_msg (GUPnPServiceProxy              *proxy,
-                  const char                     *action,
-                  GUPnPServiceProxyActionCallback callback,
-                  gpointer                        user_data)
-{
-        GUPnPServiceProxyAction *ret;
-        GUPnPServiceProxyPrivate *priv;
-        char *control_url, *full_action;
-        const char *service_type;
-
-        priv = gupnp_service_proxy_get_instance_private (proxy);
-
-        /* Create action structure */
-        ret = gupnp_service_proxy_action_new_internal (action);
-
-        ret->proxy = proxy;
-        g_object_add_weak_pointer (G_OBJECT (proxy), (gpointer *)&(ret->proxy));
-
-        ret->callback  = callback;
-        ret->user_data = user_data;
-
-        ret->msg = NULL;
-
-        ret->error = NULL;
-
-        priv->pending_actions = g_list_prepend (priv->pending_actions, ret);
-
-        /* Make sure we have a service type */
-        service_type = gupnp_service_info_get_service_type
-                                        (GUPNP_SERVICE_INFO (proxy));
-        if (service_type == NULL) {
-                ret->error = g_error_new (GUPNP_SERVER_ERROR,
-                                          GUPNP_SERVER_ERROR_OTHER,
-                                          "No service type defined");
-
-                return ret;
-        }
 
-        /* Create message */
-        control_url = gupnp_service_info_get_control_url
-                                        (GUPNP_SERVICE_INFO (proxy));
-
-        if (control_url != NULL) {
-                GUPnPContext *context = NULL;
-                char *local_control_url = NULL;
-
-                context = gupnp_service_info_get_context
-                                        (GUPNP_SERVICE_INFO (proxy));
+        soup_message_set_request (ret->msg,
+                                  "text/xml; charset=\"utf-8\"",
+                                  SOUP_MEMORY_TAKE,
+                                  ret->msg_str->str,
+                                  ret->msg_str->len);
 
-                local_control_url = gupnp_context_rewrite_uri (context,
-                                                               control_url);
-                g_free (control_url);
+        g_string_free (ret->msg_str, FALSE);
+}
 
-                ret->msg = soup_message_new (SOUP_METHOD_POST, local_control_url);
-                g_free (local_control_url);
-        }
+static void
+update_message_after_not_allowed (SoupMessage *msg)
+{
+        const char *full_action;
 
-        if (ret->msg == NULL) {
-                ret->error = g_error_new (GUPNP_SERVER_ERROR,
-                                          GUPNP_SERVER_ERROR_INVALID_URL,
-                                          "No valid control URL defined");
+        /* Retry with M-POST */
+        msg->method = "M-POST";
 
-                return ret;
-        }
+        soup_message_headers_append
+                        (msg->request_headers,
+                         "Man",
+                         "\"http://schemas.xmlsoap.org/soap/envelope/\";; ns=s");
 
-        /* Specify action */
-        full_action = g_strdup_printf ("\"%s#%s\"", service_type, action);
-        soup_message_headers_append (ret->msg->request_headers,
-                                     "SOAPAction",
+        /* Rename "SOAPAction" to "s-SOAPAction" */
+        full_action = soup_message_headers_get_one
+                        (msg->request_headers,
+                         "SOAPAction");
+        soup_message_headers_append (msg->request_headers,
+                                     "s-SOAPAction",
                                      full_action);
-        g_free (full_action);
-
-        /* Specify language */
-        http_request_set_accept_language (ret->msg);
-
-        /* Accept gzip encoding */
-        soup_message_headers_append (ret->msg->request_headers,
-                                    "Accept-Encoding", "gzip");
-
-        /* Set up envelope */
-        ret->msg_str = xml_util_new_string ();
-
-        g_string_append (ret->msg_str,
-                         "<?xml version=\"1.0\"?>"
-                         "<s:Envelope xmlns:s="
-                                "\"http://schemas.xmlsoap.org/soap/envelope/\"; "
-                          "s:encodingStyle="
-                                "\"http://schemas.xmlsoap.org/soap/encoding/\";>"
-                         "<s:Body>");
-
-        g_string_append (ret->msg_str, "<u:");
-        g_string_append (ret->msg_str, action);
-        g_string_append (ret->msg_str, " xmlns:u=\"");
-        g_string_append (ret->msg_str, service_type);
-        g_string_append (ret->msg_str, "\">");
-
-        return ret;
+        soup_message_headers_remove (msg->request_headers,
+                                     "SOAPAction");
 }
 
-/* Received response to action message */
 static void
-action_got_response (SoupSession             *session,
-                     SoupMessage             *msg,
-                     GUPnPServiceProxyAction *action)
+action_task_got_response (SoupSession *session,
+                          SoupMessage *msg,
+                          gpointer    user_data)
 {
-        const char *full_action;
+        GTask *task = G_TASK (user_data);
 
         switch (msg->status_code) {
         case SOUP_STATUS_CANCELLED:
-                /* Silently return */
+                g_task_return_new_error (task,
+                                         G_IO_ERROR,
+                                         G_IO_ERROR_CANCELLED,
+                                         "Action message was cancelled");
+                g_object_unref (task);
                 break;
 
         case SOUP_STATUS_METHOD_NOT_ALLOWED:
-                /* Retry with M-POST */
-                msg->method = "M-POST";
-
-                soup_message_headers_append
-                        (msg->request_headers,
-                         "Man",
-                         "\"http://schemas.xmlsoap.org/soap/envelope/\";; ns=s");
-
-                /* Rename "SOAPAction" to "s-SOAPAction" */
-                full_action = soup_message_headers_get_one
-                        (msg->request_headers,
-                         "SOAPAction");
-                soup_message_headers_append (msg->request_headers,
-                                             "s-SOAPAction",
-                                             full_action);
-                soup_message_headers_remove (msg->request_headers,
-                                            "SOAPAction");
-
                 /* And re-queue */
+                update_message_after_not_allowed (msg);
                 soup_session_requeue_message (session, msg);
 
                 break;
 
         default:
-                /* Success: Call callback */
-                action->callback (action->proxy, action, action->user_data);
+                g_task_return_pointer (task,
+                                       g_task_get_task_data (task),
+                                       (GDestroyNotify) gupnp_service_proxy_action_unref);
+
+                g_object_unref (task);
 
                 break;
         }
 }
 
 static void
-gupnp_service_proxy_action_queue (GUPnPServiceProxyAction *action)
+gupnp_service_proxy_action_queue_task (GTask *task)
 {
         GUPnPContext *context;
         SoupSession *session;
-
-        soup_message_set_request (action->msg,
-                                  "text/xml; charset=\"utf-8\"",
-                                  SOUP_MEMORY_TAKE,
-                                  action->msg_str->str,
-                                  action->msg_str->len);
-
-        g_string_free (action->msg_str, FALSE);
+        GUPnPServiceProxyAction *action = g_task_get_task_data (task);
 
         /* We need to keep our own reference to the message as well,
          * in order for send_action() to work. */
@@ -962,11 +775,12 @@ gupnp_service_proxy_action_queue (GUPnPServiceProxyAction *action)
 
         soup_session_queue_message (session,
                                     action->msg,
-                                    (SoupSessionCallback) action_got_response,
-                                    action);
+                                    (SoupSessionCallback) action_task_got_response,
+                                    task);
 }
 
 /* Finishes an action message and sends it off */
+#if 0
 static void
 finish_action_msg (GUPnPServiceProxyAction *action,
                    const char              *action_name)
@@ -1005,16 +819,7 @@ finish_action_msg (GUPnPServiceProxyAction *action,
                                     (SoupSessionCallback) action_got_response,
                                     action);
 }
-
-static gboolean
-action_error_idle_cb (gpointer user_data)
-{
-        GUPnPServiceProxyAction *action = (GUPnPServiceProxyAction *) user_data;
-
-        action->callback (action->proxy, action, action->user_data);
-
-        return FALSE;
-}
+#endif
 
 /**
  * gupnp_service_proxy_begin_action_valist:
@@ -1031,6 +836,8 @@ action_error_idle_cb (gpointer user_data)
  * Returns: (transfer none): A #GUPnPServiceProxyAction handle. This will
  * be freed when calling gupnp_service_proxy_cancel_action() or
  * gupnp_service_proxy_end_action_valist().
+ *
+ * Deprecated: 1.1.1: Use ove of
  **/
 GUPnPServiceProxyAction *
 gupnp_service_proxy_begin_action_valist
@@ -1041,23 +848,20 @@ gupnp_service_proxy_begin_action_valist
                                     va_list                         var_args)
 {
         GUPnPServiceProxyAction *ret;
-        GList *in_names = NULL, *in_values = NULL;
-
-        g_return_val_if_fail (GUPNP_IS_SERVICE_PROXY (proxy), NULL);
-        g_return_val_if_fail (action, NULL);
-        g_return_val_if_fail (callback, NULL);
+        GList *in_names = NULL;
+        GList *in_values = NULL;
 
         VAR_ARGS_TO_IN_LIST (var_args, in_names, in_values);
-        ret = gupnp_service_proxy_begin_action_list (proxy,
-                                                     action,
-                                                     in_names,
-                                                     in_values,
-                                                     callback,
-                                                     user_data);
+
+        ret = gupnp_service_proxy_action_new_from_list (action, in_names, in_values);
         g_list_free_full (in_names, g_free);
-        g_list_free_full (in_values, value_free);
+        g_list_free_full (in_values, _value_free);
+
+        ret->callback = callback;
+        ret->user_data = user_data;
+
+        return gupnp_service_proxy_call_action_async (proxy, ret, NULL, on_legacy_async_callback, ret);
 
-        return ret;
 }
 
 /**
@@ -1092,27 +896,12 @@ gupnp_service_proxy_begin_action_list
 {
         GUPnPServiceProxyAction *ret;
 
-        g_return_val_if_fail (GUPNP_IS_SERVICE_PROXY (proxy), NULL);
-        g_return_val_if_fail (action, NULL);
-        g_return_val_if_fail (callback, NULL);
-        g_return_val_if_fail (g_list_length (in_names) ==
-                              g_list_length (in_values),
-                              NULL);
-
-        ret = gupnp_service_proxy_action_new_from_list (action,
-                                                        in_names,
-                                                        in_values);
-        prepare_action_msg (proxy, ret, callback, user_data);
-
-        if (ret->error) {
-                g_idle_add (action_error_idle_cb, ret);
-
-                return ret;
-        }
+        ret = gupnp_service_proxy_action_new_from_list (action, in_names, in_values);
 
-        gupnp_service_proxy_action_queue (ret);
+        ret->callback = callback;
+        ret->user_data = user_data;
 
-        return ret;
+        return gupnp_service_proxy_call_action_async (proxy, ret, NULL, on_legacy_async_callback, ret);
 }
 
 /**
@@ -1139,20 +928,31 @@ gupnp_service_proxy_end_action (GUPnPServiceProxy       *proxy,
         va_list var_args;
         gboolean ret;
 
+        g_return_val_if_fail (GUPNP_IS_SERVICE_PROXY (proxy), FALSE);
+        g_return_val_if_fail (action, FALSE);
+        g_return_val_if_fail (proxy == action->proxy, FALSE);
+
+        if (action->error) {
+                g_propagate_error (error, action->error);
+                gupnp_service_proxy_action_unref (action);
+
+                return FALSE;
+        }
+
+
         va_start (var_args, error);
-        ret = gupnp_service_proxy_end_action_valist (proxy,
-                                                     action,
-                                                     error,
-                                                     var_args);
+        ret = gupnp_service_proxy_action_get_result_valist (action, error, var_args);
         va_end (var_args);
 
+        gupnp_service_proxy_action_unref (action);
+
         return ret;
 }
 
 /* Checks an action response for errors and returns the parsed
  * xmlDoc object. */
-static xmlDoc *
-check_action_response (G_GNUC_UNUSED GUPnPServiceProxy *proxy,
+xmlDoc *
+_check_action_response (G_GNUC_UNUSED GUPnPServiceProxy *proxy,
                        GUPnPServiceProxyAction         *action,
                        xmlNode                        **params,
                        GError                         **error)
@@ -1280,8 +1080,8 @@ check_action_response (G_GNUC_UNUSED GUPnPServiceProxy *proxy,
 
 /* Reads a value into the parameter name and initialised GValue pair
  * from @response */
-static void
-read_out_parameter (const char *arg_name,
+void
+_read_out_parameter (const char *arg_name,
                     GValue     *value,
                     xmlNode    *params)
 {
@@ -1320,34 +1120,23 @@ gupnp_service_proxy_end_action_valist (GUPnPServiceProxy       *proxy,
                                        GError                 **error,
                                        va_list                  var_args)
 {
-        GHashTable *out_hash;
-        va_list var_args_copy;
         gboolean result;
-        GError *local_error;
 
         g_return_val_if_fail (GUPNP_IS_SERVICE_PROXY (proxy), FALSE);
         g_return_val_if_fail (action, FALSE);
         g_return_val_if_fail (proxy == action->proxy, FALSE);
 
-        out_hash = g_hash_table_new_full (g_str_hash,
-                                          g_str_equal,
-                                          g_free,
-                                          value_free);
-        G_VA_COPY (var_args_copy, var_args);
-        VAR_ARGS_TO_OUT_HASH_TABLE (var_args, out_hash);
-        local_error = NULL;
-        result = gupnp_service_proxy_end_action_hash (proxy,
-                                                      action,
-                                                      out_hash,
-                                                      &local_error);
+        if (action->error) {
+                g_propagate_error (error, action->error);
+                gupnp_service_proxy_action_unref (action);
 
-        if (local_error == NULL) {
-                OUT_HASH_TABLE_TO_VAR_ARGS (out_hash, var_args_copy);
-        } else {
-                g_propagate_error (error, local_error);
+                return FALSE;
         }
-        va_end (var_args_copy);
-        g_hash_table_unref (out_hash);
+
+        result = gupnp_service_proxy_action_get_result_valist (action,
+                                                               error,
+                                                               var_args);
+        gupnp_service_proxy_action_unref (action);
 
         return result;
 }
@@ -1380,13 +1169,7 @@ gupnp_service_proxy_end_action_list (GUPnPServiceProxy       *proxy,
                                      GList                  **out_values,
                                      GError                 **error)
 {
-        xmlDoc *response;
-        xmlNode *params;
-        GList *names;
-        GList *types;
-        GList *out_values_list;
-
-        out_values_list = NULL;
+        gboolean result;
 
         g_return_val_if_fail (GUPNP_IS_SERVICE_PROXY (proxy), FALSE);
         g_return_val_if_fail (action, FALSE);
@@ -1400,37 +1183,14 @@ gupnp_service_proxy_end_action_list (GUPnPServiceProxy       *proxy,
                 return FALSE;
         }
 
-        /* Check response for errors and do initial parsing */
-        response = check_action_response (proxy, action, &params, error);
-        if (response == NULL) {
-                gupnp_service_proxy_action_unref (action);
-
-                return FALSE;
-        }
-
-        /* Read arguments */
-        types = out_types;
-        for (names = out_names; names; names=names->next) {
-                GValue *val;
-
-                val = g_slice_new0 (GValue);
-                g_value_init (val, (GType) types->data);
-
-                read_out_parameter (names->data, val, params);
-
-                out_values_list = g_list_append (out_values_list, val);
-
-                types = types->next;
-        }
-
-        *out_values = out_values_list;
-
-        /* Cleanup */
+        result = gupnp_service_proxy_action_get_result_list (action,
+                                                             out_names,
+                                                             out_types,
+                                                             out_values,
+                                                             error);
         gupnp_service_proxy_action_unref (action);
 
-        xmlFreeDoc (response);
-
-        return TRUE;
+        return result;
 }
 
 /**
@@ -1454,8 +1214,7 @@ gupnp_service_proxy_end_action_hash
                                     GHashTable                     *hash,
                                     GError                        **error)
 {
-        xmlDoc *response;
-        xmlNode *params;
+        gboolean result;
 
         g_return_val_if_fail (GUPNP_IS_SERVICE_PROXY (proxy), FALSE);
         g_return_val_if_fail (action, FALSE);
@@ -1469,23 +1228,13 @@ gupnp_service_proxy_end_action_hash
                 return FALSE;
         }
 
-        /* Check response for errors and do initial parsing */
-        response = check_action_response (proxy, action, &params, error);
-        if (response == NULL) {
-                gupnp_service_proxy_action_unref (action);
-
-                return FALSE;
-        }
-
-        /* Read arguments */
-        g_hash_table_foreach (hash, (GHFunc) read_out_parameter, params);
 
-        /* Cleanup */
+        result = gupnp_service_proxy_action_get_result_hash (action,
+                                                             hash,
+                                                             error);
         gupnp_service_proxy_action_unref (action);
 
-        xmlFreeDoc (response);
-
-        return TRUE;
+        return result;
 }
 
 /**
@@ -2463,3 +2212,93 @@ gupnp_service_proxy_get_subscribed (GUPnPServiceProxy *proxy)
 
         return priv->subscribed;
 }
+
+/**
+ * gupnp_service_proxy_call_action_async:
+ */
+GUPnPServiceProxyAction *
+gupnp_service_proxy_call_action_async (GUPnPServiceProxy       *proxy,
+                                       GUPnPServiceProxyAction *action,
+                                       GCancellable            *cancellable,
+                                       GAsyncReadyCallback     callback,
+                                       gpointer                user_data)
+{
+        GTask *task;
+
+        g_return_val_if_fail (GUPNP_IS_SERVICE_PROXY (proxy), NULL);
+
+        task = g_task_new (proxy, cancellable, callback, user_data);
+        g_task_set_task_data (task,
+                              gupnp_service_proxy_action_ref (action),
+                              (GDestroyNotify) gupnp_service_proxy_action_unref);
+
+        prepare_action_msg (proxy, action, NULL, NULL);
+
+        if (action->error != NULL) {
+                g_task_return_error (task, g_error_copy (action->error));
+                g_object_unref (task);
+
+                return NULL;
+        }
+
+        gupnp_service_proxy_action_queue_task (task);
+
+        return action;
+}
+
+/**
+ * gupnp_service_proxy_call_action_finish:
+ */
+GUPnPServiceProxyAction *
+gupnp_service_proxy_call_action_finish (GUPnPServiceProxy *proxy,
+                                        GAsyncResult      *result,
+                                        GError           **error)
+{
+        g_return_val_if_fail (g_task_is_valid (G_TASK (result), proxy), NULL);
+
+        return g_task_propagate_pointer (G_TASK (result), error);
+}
+
+GUPnPServiceProxyAction *
+gupnp_service_proxy_call_action (GUPnPServiceProxy       *proxy,
+                                 GUPnPServiceProxyAction *action,
+                                 GCancellable            *cancellable,
+                                 GError                 **error)
+{
+        GUPnPContext *context = NULL;
+        SoupSession *session = NULL;
+
+        g_return_val_if_fail (GUPNP_IS_SERVICE_PROXY (proxy), NULL);
+
+        prepare_action_msg (proxy, action, NULL, NULL);
+
+        if (action->error != NULL) {
+                g_propagate_error (error, g_error_copy (action->error));
+
+                return NULL;
+        }
+
+        context = gupnp_service_info_get_context (GUPNP_SERVICE_INFO (proxy));
+        session = gupnp_context_get_session (context);
+        soup_session_send_message (session, action->msg);
+
+        /* If not allowed, try again */
+        if (action->msg->status_code == SOUP_STATUS_METHOD_NOT_ALLOWED) {
+                update_message_after_not_allowed (action->msg);
+                soup_session_send_message (session, action->msg);
+        }
+
+        /* prepare_action_msg has queued the message, so remove it */
+        gupnp_service_proxy_remove_action (proxy, action);
+
+        if (action->msg->status_code == SOUP_STATUS_CANCELLED) {
+                action->error = g_error_new (G_IO_ERROR,
+                                             G_IO_ERROR_CANCELLED,
+                                             "Action message was cancelled");
+                g_propagate_error (error, g_error_copy (action->error));
+
+                return NULL;
+        }
+
+        return action;
+}
diff --git a/libgupnp/gupnp-service-proxy.h b/libgupnp/gupnp-service-proxy.h
index fe9f46b..74e574d 100644
--- a/libgupnp/gupnp-service-proxy.h
+++ b/libgupnp/gupnp-service-proxy.h
@@ -210,13 +210,56 @@ gupnp_service_proxy_get_subscribed (GUPnPServiceProxy              *proxy);
 
 GUPnPServiceProxyAction *
 gupnp_service_proxy_action_new (const char *action,
-                                ...);
+                                ...) G_GNUC_NULL_TERMINATED;
 
 GUPnPServiceProxyAction *
 gupnp_service_proxy_action_new_from_list (const char *action,
                                           GList      *in_names,
                                           GList      *in_values);
 
+gboolean
+gupnp_service_proxy_action_get_result (GUPnPServiceProxyAction *action,
+                                       GError                 **error,
+                                       ...) G_GNUC_NULL_TERMINATED;
+
+gboolean
+gupnp_service_proxy_action_get_result_list (GUPnPServiceProxyAction *action,
+                                            GList                   *out_names,
+                                            GList                   *out_types,
+                                            GList                  **out_values,
+                                            GError                 **error);
+gboolean
+gupnp_service_proxy_action_get_result_hash (GUPnPServiceProxyAction *action,
+                                            GHashTable              *out_hash,
+                                            GError                 **error);
+
+
+GUPnPServiceProxyAction *
+gupnp_service_proxy_call_action_async (GUPnPServiceProxy       *proxy,
+                                       GUPnPServiceProxyAction *action,
+                                       GCancellable            *cancellable,
+                                       GAsyncReadyCallback     callback,
+                                       gpointer                user_data);
+
+
+GUPnPServiceProxyAction *
+gupnp_service_proxy_call_action_finish (GUPnPServiceProxy *proxy,
+                                        GAsyncResult      *result,
+                                        GError           **error);
+
+GUPnPServiceProxyAction *
+gupnp_service_proxy_call_action (GUPnPServiceProxy       *proxy,
+                                 GUPnPServiceProxyAction *action,
+                                 GCancellable            *cancellable,
+                                 GError                 **error);
+
+GUPnPServiceProxyAction *
+gupnp_service_proxy_action_ref (GUPnPServiceProxyAction *action);
+
+void
+gupnp_service_proxy_action_unref (GUPnPServiceProxyAction *action);
+
+
 G_END_DECLS
 
 #endif /* GUPNP_SERVICE_PROXY_H */


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