Re: [spice-gtk v3 07/16] file-xfer: call user callback once per operation

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

 



On Mon, 2016-05-30 at 11:55 +0200, Victor Toso wrote:
> SpiceFileTransferTask has a callback to be called when operation
> ended. Til this patch, we were setting the user callback which means
> that in multiple file-transfers, we were calling the user callback
> several times.
> 
> Following the same logic pointed from 113093dd00a1cf10f6d3c3589b7 this
> is a SpiceMainChannel operation and it should only call the user
> callback when this operation is over (FileTransferOperation now).
> ---
>  src/channel-main.c                              |  72 ++-
>  src/tmp-introspect325cwcm0/SpiceClientGtk-3.0.c | 628
> ++++++++++++++++++++++++
>  2 files changed, 694 insertions(+), 6 deletions(-)
>  create mode 100644 src/tmp-introspect325cwcm0/SpiceClientGtk-3.0.c
> 
> diff --git a/src/channel-main.c b/src/channel-main.c
> index f36326d..e204a1e 100644
> --- a/src/channel-main.c
> +++ b/src/channel-main.c
> @@ -157,6 +157,10 @@ typedef struct {
>      SpiceMainChannel           *channel;
>      GFileProgressCallback       progress_callback;
>      gpointer                    progress_callback_data;
> +    GAsyncReadyCallback         end_callback;
> +    gpointer                    end_callback_data;
> +    GError                     *error;
> +    GCancellable               *cancellable;
>      goffset                     total_sent;
>      goffset                     transfer_size;
>  } FileTransferOperation;
> @@ -1838,9 +1842,7 @@ static void file_xfer_close_cb(GObject      *object,
>          }
>      }
>  
> -    /* Notify to user that files have been transferred or something error
> -       happened. */
> -    task = g_task_new(self->channel,
> +    task = g_task_new(self,
>                        self->cancellable,
>                        self->callback,
>                        self->user_data);
> @@ -1919,6 +1921,42 @@ static void
> file_xfer_flush_callback(SpiceFileTransferTask *xfer_task,
>      file_xfer_flush_async(main_channel, cancellable,
> file_xfer_data_flushed_cb, xfer_task);
>  }
>  
> +static void file_xfer_end_callback(GObject *source_object,
> +                                   GAsyncResult *res,
> +                                   gpointer user_data)
> +{
> +    GTask *task;
> +    FileTransferOperation *xfer_op;
> +
> +    task = G_TASK(res);
> +    if (!g_task_had_error(task))
> +        /* SpiceFileTransferTask and FileTransferOperation are freed on
> +         * file_transfer_operation_task_finished */
> +        return;

In general, a GAsyncReadyCallback is expected to call a _finish() function to
get the error status. Since this is all internal, we can poke around at the
implementation of the asynchronous task to find the error, but I think I'd
prefer to provide a _finish() function to follow the conventions.

> +
> +    xfer_op = user_data;
> +
> +    if (xfer_op->error != NULL)
> +        return;
> +
> +    /* Get the GError from SpiceFileTransferTask so we can properly return to
> +     * the application when the FileTransferOperation ends */
> +    g_task_propagate_boolean(task, &xfer_op->error);

since file_xfer_end_callback() is called once for each file in the operation,
the above line will overwrite xfer_op->error each time it is called. So we'll
only report the error status of the last file that finished. We probably don't
want to overwrite an earlier error with a later success.

> +
> +    /* User can cancel a FileTransfer without cancelling the whole
> +     * operation. For that, spice_main_file_copy_async must be called
> +     * without GCancellabe */
> +    if (g_error_matches(xfer_op->error, G_IO_ERROR, G_IO_ERROR_CANCELLED) &&
> +            xfer_op->cancellable == NULL) {
> +        SpiceFileTransferTask *xfer_task;
> +
> +        xfer_task = SPICE_FILE_TRANSFER_TASK(source_object);
> +        spice_debug("file-transfer %u was cancelled",
> +                    spice_file_transfer_task_get_id(xfer_task));
> +        g_clear_error(&xfer_op->error);
> +    }
> +}
> +
>  /* main context */
>  static void file_xfer_read_cb(GObject *source_object,
>                                GAsyncResult *res,
> @@ -3108,10 +3146,24 @@ static void
> file_transfer_operation_end(FileTransferOperation *xfer_op)
>      g_return_if_fail(xfer_op != NULL);
>      spice_debug("Freeing file-transfer-operation %p", xfer_op);
>  
> +    if (xfer_op->end_callback) {
> +        GTask *task = g_task_new(xfer_op->channel,
> +                                 xfer_op->cancellable,
> +                                 xfer_op->end_callback,
> +                                 xfer_op->end_callback_data);
> +
> +        if (xfer_op->error != NULL) {
> +            g_task_return_error(task, xfer_op->error);
> +        } else {
> +            g_task_return_boolean(task, TRUE);
> +        }
> +    }
> +

If FileTransferOperation owned a GTask* member, we wouldn't have to store these
'cancellable', 'end_callback', and 'end_callback_data' fields separately. I know
that it's done elsewhere in the code, but it seems a bit odd to create a GTask
right as the task is done and essentially use it only to trigger the callback.
Also, the cancellable doesn't do much good if the GTask only lives long enough
to be created and return ;)


>      /* SpiceFileTransferTask itself is freed after it emits "finish" */
>      if (xfer_op->tasks != NULL)
>          g_list_free(xfer_op->tasks);
>  
> +    g_clear_object (&xfer_op->cancellable);
>      g_free(xfer_op);
>  }
>  
> @@ -3225,7 +3277,11 @@ static void task_finished(SpiceFileTransferTask *task,
>   * files, please connect to the #SpiceMainChannel::new-file-transfer signal.
>   *
>   * When the operation is finished, callback will be called. You can then call
> - * spice_main_file_copy_finish() to get the result of the operation.
> + * spice_main_file_copy_finish() to get the result of the operation. Note
> that
> + * before release 0.32 the callback was called for each file in multiple file
> + * transfer. This behavior was changed for the same reason as the
> + * progress_callback (above). If you need to monitor the ending of individual
> + * files, you can connect to "finished" signal from each
> SpiceFileTransferTask.
>   *
>   **/
>  void spice_main_file_copy_async(SpiceMainChannel *channel,
> @@ -3259,15 +3315,19 @@ void spice_main_file_copy_async(SpiceMainChannel
> *channel,
>      xfer_op = g_new0(FileTransferOperation, 1);
>      xfer_op->progress_callback = progress_callback;
>      xfer_op->progress_callback_data = progress_callback_data;
> +    xfer_op->end_callback = callback;
> +    xfer_op->end_callback_data = user_data;
>      xfer_op->channel = channel;
> +    xfer_op->error = NULL;
> +    xfer_op->cancellable = (cancellable != NULL) ? g_object_ref(cancellable)
> : NULL;
>      xfer_op->tasks = spice_file_transfer_task_create_tasks(channel,
>                                                             sources,
>                                                             flags,
>                                                             cancellable,
>                                                             file_xfer_flush_ca
> llback,
>                                                             xfer_op,
> -                                                           callback,
> -                                                           user_data);
> +                                                           file_xfer_end_call
> back,
> +                                                           xfer_op);
>      spice_debug("New file-transfer-operation %p", xfer_op);
>      for (it = xfer_op->tasks; it != NULL; it = it->next) {
>          SpiceFileTransferTask *task = SPICE_FILE_TRANSFER_TASK(it->data);


I assume the following file was accidentally committed?

Reviewed-by: Jonathon Jongsma <jjongsma@xxxxxxxxxx>


> diff --git a/src/tmp-introspect325cwcm0/SpiceClientGtk-3.0.c b/src/tmp-
> introspect325cwcm0/SpiceClientGtk-3.0.c
> new file mode 100644
> index 0000000..765abbf
> --- /dev/null
> +++ b/src/tmp-introspect325cwcm0/SpiceClientGtk-3.0.c
> @@ -0,0 +1,628 @@
> +/* This file is generated, do not edit */
> +#include <glib.h>
> +#include <string.h>
> +#include <stdlib.h>
> +
> +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
> + * GObject introspection: Dump introspection data
> + *
> + * Copyright (C) 2008 Colin Walters <walters@xxxxxxxxxx>
> + *
> + * This library is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU Lesser General Public
> + * License as published by the Free Software Foundation; either
> + * version 2 of the License, or (at your option) any later version.
> + *
> + * This library is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
> + * Lesser General Public License for more details.
> + *
> + * You should have received a copy of the GNU Lesser General Public
> + * License along with this library; if not, write to the
> + * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
> + * Boston, MA 02111-1307, USA.
> + */
> +
> +#include <stdlib.h>
> +
> +#include <glib.h>
> +#include <glib-object.h>
> +#include <gio/gio.h>
> +
> +/* This file is both compiled into libgirepository.so, and installed
> + * on the filesystem.  But for the dumper, we want to avoid linking
> + * to libgirepository; see
> + * https://bugzilla.gnome.org/show_bug.cgi?id=630342
> + */
> +#ifdef G_IREPOSITORY_COMPILATION
> +#include "config.h"
> +#include "girepository.h"
> +#endif
> +
> +#include <string.h>
> +
> +static void
> +escaped_printf (GOutputStream *out, const char *fmt, ...) G_GNUC_PRINTF (2,
> 3);
> +
> +static void
> +escaped_printf (GOutputStream *out, const char *fmt, ...)
> +{
> +  char *str;
> +  va_list args;
> +  gsize written;
> +  GError *error = NULL;
> +
> +  va_start (args, fmt);
> +
> +  str = g_markup_vprintf_escaped (fmt, args);
> +  if (!g_output_stream_write_all (out, str, strlen (str), &written, NULL,
> &error))
> +    {
> +      g_critical ("failed to write to iochannel: %s", error->message);
> +      g_clear_error (&error);
> +    }
> +  g_free (str);
> +
> +  va_end (args);
> +}
> +
> +static void
> +goutput_write (GOutputStream *out, const char *str)
> +{
> +  gsize written;
> +  GError *error = NULL;
> +  if (!g_output_stream_write_all (out, str, strlen (str), &written, NULL,
> &error))
> +    {
> +      g_critical ("failed to write to iochannel: %s", error->message);
> +      g_clear_error (&error);
> +    }
> +}
> +
> +typedef GType (*GetTypeFunc)(void);
> +typedef GQuark (*ErrorQuarkFunc)(void);
> +
> +static GType
> +invoke_get_type (GModule *self, const char *symbol, GError **error)
> +{
> +  GetTypeFunc sym;
> +  GType ret;
> +
> +  if (!g_module_symbol (self, symbol, (void**)&sym))
> +    {
> +      g_set_error (error,
> +		   G_IO_ERROR,
> +		   G_IO_ERROR_FAILED,
> +		   "Failed to find symbol '%s'", symbol);
> +      return G_TYPE_INVALID;
> +    }
> +
> +  ret = sym ();
> +  if (ret == G_TYPE_INVALID)
> +    {
> +      g_set_error (error,
> +		   G_IO_ERROR,
> +		   G_IO_ERROR_FAILED,
> +		   "Function '%s' returned G_TYPE_INVALID", symbol);
> +    }
> +  return ret;
> +}
> +
> +static GQuark
> +invoke_error_quark (GModule *self, const char *symbol, GError **error)
> +{
> +  ErrorQuarkFunc sym;
> +
> +  if (!g_module_symbol (self, symbol, (void**)&sym))
> +    {
> +      g_set_error (error,
> +		   G_IO_ERROR,
> +		   G_IO_ERROR_FAILED,
> +		   "Failed to find symbol '%s'", symbol);
> +      return G_TYPE_INVALID;
> +    }
> +
> +  return sym ();
> +}
> +
> +static void
> +dump_properties (GType type, GOutputStream *out)
> +{
> +  guint i;
> +  guint n_properties;
> +  GParamSpec **props;
> +
> +  if (G_TYPE_FUNDAMENTAL (type) == G_TYPE_OBJECT)
> +    {
> +      GObjectClass *klass;
> +      klass = g_type_class_ref (type);
> +      props = g_object_class_list_properties (klass, &n_properties);
> +    }
> +  else
> +    {
> +      void *klass;
> +      klass = g_type_default_interface_ref (type);
> +      props = g_object_interface_list_properties (klass, &n_properties);
> +    }
> +
> +  for (i = 0; i < n_properties; i++)
> +    {
> +      GParamSpec *prop;
> +
> +      prop = props[i];
> +      if (prop->owner_type != type)
> +	continue;
> +
> +      escaped_printf (out, "    <property name=\"%s\" type=\"%s\"
> flags=\"%d\"/>\n",
> +		      prop->name, g_type_name (prop->value_type), prop-
> >flags);
> +    }
> +  g_free (props);
> +}
> +
> +static void
> +dump_signals (GType type, GOutputStream *out)
> +{
> +  guint i;
> +  guint n_sigs;
> +  guint *sig_ids;
> +
> +  sig_ids = g_signal_list_ids (type, &n_sigs);
> +  for (i = 0; i < n_sigs; i++)
> +    {
> +      guint sigid;
> +      GSignalQuery query;
> +      guint j;
> +
> +      sigid = sig_ids[i];
> +      g_signal_query (sigid, &query);
> +
> +      escaped_printf (out, "    <signal name=\"%s\" return=\"%s\"",
> +		      query.signal_name, g_type_name (query.return_type));
> +
> +      if (query.signal_flags & G_SIGNAL_RUN_FIRST)
> +        escaped_printf (out, " when=\"first\"");
> +      else if (query.signal_flags & G_SIGNAL_RUN_LAST)
> +        escaped_printf (out, " when=\"last\"");
> +      else if (query.signal_flags & G_SIGNAL_RUN_CLEANUP)
> +        escaped_printf (out, " when=\"cleanup\"");
> +#if GLIB_CHECK_VERSION(2, 29, 15)
> +      else if (query.signal_flags & G_SIGNAL_MUST_COLLECT)
> +        escaped_printf (out, " when=\"must-collect\"");
> +#endif
> +      if (query.signal_flags & G_SIGNAL_NO_RECURSE)
> +        escaped_printf (out, " no-recurse=\"1\"");
> +
> +      if (query.signal_flags & G_SIGNAL_DETAILED)
> +        escaped_printf (out, " detailed=\"1\"");
> +
> +      if (query.signal_flags & G_SIGNAL_ACTION)
> +        escaped_printf (out, " action=\"1\"");
> +
> +      if (query.signal_flags & G_SIGNAL_NO_HOOKS)
> +        escaped_printf (out, " no-hooks=\"1\"");
> +
> +      goutput_write (out, ">\n");
> +
> +      for (j = 0; j < query.n_params; j++)
> +	{
> +	  escaped_printf (out, "      <param type=\"%s\"/>\n",
> +			  g_type_name (query.param_types[j]));
> +	}
> +      goutput_write (out, "    </signal>\n");
> +    }
> +  g_free (sig_ids);
> +}
> +
> +static void
> +dump_object_type (GType type, const char *symbol, GOutputStream *out)
> +{
> +  guint n_interfaces;
> +  guint i;
> +  GType *interfaces;
> +
> +  escaped_printf (out, "  <class name=\"%s\" get-type=\"%s\"",
> +		  g_type_name (type), symbol);
> +  if (type != G_TYPE_OBJECT)
> +    {
> +      GString *parent_str;
> +      GType parent;
> +      gboolean first = TRUE;
> +
> +      parent = g_type_parent (type);
> +      parent_str = g_string_new ("");
> +      while (parent != G_TYPE_INVALID)
> +        {
> +          if (first)
> +            first = FALSE;
> +          else
> +            g_string_append_c (parent_str, ',');
> +          g_string_append (parent_str, g_type_name (parent));
> +          parent = g_type_parent (parent);
> +        }
> +
> +      escaped_printf (out, " parents=\"%s\"", parent_str->str);
> +
> +      g_string_free (parent_str, TRUE);
> +    }
> +
> +  if (G_TYPE_IS_ABSTRACT (type))
> +    escaped_printf (out, " abstract=\"1\"");
> +  goutput_write (out, ">\n");
> +
> +  interfaces = g_type_interfaces (type, &n_interfaces);
> +  for (i = 0; i < n_interfaces; i++)
> +    {
> +      GType itype = interfaces[i];
> +      escaped_printf (out, "    <implements name=\"%s\"/>\n",
> +		      g_type_name (itype));
> +    }
> +  g_free (interfaces);
> +
> +  dump_properties (type, out);
> +  dump_signals (type, out);
> +  goutput_write (out, "  </class>\n");
> +}
> +
> +static void
> +dump_interface_type (GType type, const char *symbol, GOutputStream *out)
> +{
> +  guint n_interfaces;
> +  guint i;
> +  GType *interfaces;
> +
> +  escaped_printf (out, "  <interface name=\"%s\" get-type=\"%s\">\n",
> +		  g_type_name (type), symbol);
> +
> +  interfaces = g_type_interface_prerequisites (type, &n_interfaces);
> +  for (i = 0; i < n_interfaces; i++)
> +    {
> +      GType itype = interfaces[i];
> +      if (itype == G_TYPE_OBJECT)
> +	{
> +	  /* Treat this as implicit for now; in theory GInterfaces are
> +	   * supported on things like GstMiniObject, but right now
> +	   * the introspection system only supports GObject.
> +	   * http://bugzilla.gnome.org/show_bug.cgi?id=559706
> +	   */
> +	  continue;
> +	}
> +      escaped_printf (out, "    <prerequisite name=\"%s\"/>\n",
> +		      g_type_name (itype));
> +    }
> +  g_free (interfaces);
> +
> +  dump_properties (type, out);
> +  dump_signals (type, out);
> +  goutput_write (out, "  </interface>\n");
> +}
> +
> +static void
> +dump_boxed_type (GType type, const char *symbol, GOutputStream *out)
> +{
> +  escaped_printf (out, "  <boxed name=\"%s\" get-type=\"%s\"/>\n",
> +		  g_type_name (type), symbol);
> +}
> +
> +static void
> +dump_flags_type (GType type, const char *symbol, GOutputStream *out)
> +{
> +  guint i;
> +  GFlagsClass *klass;
> +
> +  klass = g_type_class_ref (type);
> +  escaped_printf (out, "  <flags name=\"%s\" get-type=\"%s\">\n",
> +		  g_type_name (type), symbol);
> +
> +  for (i = 0; i < klass->n_values; i++)
> +    {
> +      GFlagsValue *value = &(klass->values[i]);
> +
> +      escaped_printf (out, "    <member name=\"%s\" nick=\"%s\"
> value=\"%d\"/>\n",
> +		      value->value_name, value->value_nick, value->value);
> +    }
> +  goutput_write (out, "  </flags>\n");
> +}
> +
> +static void
> +dump_enum_type (GType type, const char *symbol, GOutputStream *out)
> +{
> +  guint i;
> +  GEnumClass *klass;
> +
> +  klass = g_type_class_ref (type);
> +  escaped_printf (out, "  <enum name=\"%s\" get-type=\"%s\">\n",
> +		  g_type_name (type), symbol);
> +
> +  for (i = 0; i < klass->n_values; i++)
> +    {
> +      GEnumValue *value = &(klass->values[i]);
> +
> +      escaped_printf (out, "    <member name=\"%s\" nick=\"%s\"
> value=\"%d\"/>\n",
> +		      value->value_name, value->value_nick, value->value);
> +    }
> +  goutput_write (out, "  </enum>");
> +}
> +
> +static void
> +dump_fundamental_type (GType type, const char *symbol, GOutputStream *out)
> +{
> +  guint n_interfaces;
> +  guint i;
> +  GType *interfaces;
> +  GString *parent_str;
> +  GType parent;
> +  gboolean first = TRUE;
> +
> +
> +  escaped_printf (out, "  <fundamental name=\"%s\" get-type=\"%s\"",
> +		  g_type_name (type), symbol);
> +
> +  if (G_TYPE_IS_ABSTRACT (type))
> +    escaped_printf (out, " abstract=\"1\"");
> +
> +  if (G_TYPE_IS_INSTANTIATABLE (type))
> +    escaped_printf (out, " instantiatable=\"1\"");
> +
> +  parent = g_type_parent (type);
> +  parent_str = g_string_new ("");
> +  while (parent != G_TYPE_INVALID)
> +    {
> +      if (first)
> +        first = FALSE;
> +      else
> +        g_string_append_c (parent_str, ',');
> +      if (!g_type_name (parent))
> +        break;
> +      g_string_append (parent_str, g_type_name (parent));
> +      parent = g_type_parent (parent);
> +    }
> +
> +  if (parent_str->len > 0)
> +    escaped_printf (out, " parents=\"%s\"", parent_str->str);
> +  g_string_free (parent_str, TRUE);
> +
> +  goutput_write (out, ">\n");
> +
> +  interfaces = g_type_interfaces (type, &n_interfaces);
> +  for (i = 0; i < n_interfaces; i++)
> +    {
> +      GType itype = interfaces[i];
> +      escaped_printf (out, "    <implements name=\"%s\"/>\n",
> +		      g_type_name (itype));
> +    }
> +  g_free (interfaces);
> +  goutput_write (out, "  </fundamental>\n");
> +}
> +
> +static void
> +dump_type (GType type, const char *symbol, GOutputStream *out)
> +{
> +  switch (g_type_fundamental (type))
> +    {
> +    case G_TYPE_OBJECT:
> +      dump_object_type (type, symbol, out);
> +      break;
> +    case G_TYPE_INTERFACE:
> +      dump_interface_type (type, symbol, out);
> +      break;
> +    case G_TYPE_BOXED:
> +      dump_boxed_type (type, symbol, out);
> +      break;
> +    case G_TYPE_FLAGS:
> +      dump_flags_type (type, symbol, out);
> +      break;
> +    case G_TYPE_ENUM:
> +      dump_enum_type (type, symbol, out);
> +      break;
> +    case G_TYPE_POINTER:
> +      /* GValue, etc.  Just skip them. */
> +      break;
> +    default:
> +      dump_fundamental_type (type, symbol, out);
> +      break;
> +    }
> +}
> +
> +static void
> +dump_error_quark (GQuark quark, const char *symbol, GOutputStream *out)
> +{
> +  escaped_printf (out, "  <error-quark function=\"%s\" domain=\"%s\"/>\n",
> +		  symbol, g_quark_to_string (quark));
> +}
> +
> +/**
> + * g_irepository_dump:
> + * @arg: Comma-separated pair of input and output filenames
> + * @error: a %GError
> + *
> + * Argument specified is a comma-separated pair of filenames; i.e. of
> + * the form "input.txt,output.xml".  The input file should be a
> + * UTF-8 Unix-line-ending text file, with each line containing either
> + * "get-type:" followed by the name of a GType _get_type function, or
> + * "error-quark:" followed by the name of an error quark function.  No
> + * extra whitespace is allowed.
> + *
> + * The output file should already exist, but be empty.  This function will
> + * overwrite its contents.
> + *
> + * Returns: %TRUE on success, %FALSE on error
> + */
> +#ifndef G_IREPOSITORY_COMPILATION
> +static gboolean
> +dump_irepository (const char *arg, GError **error) G_GNUC_UNUSED;
> +static gboolean
> +dump_irepository (const char *arg, GError **error)
> +#else
> +gboolean
> +g_irepository_dump (const char *arg, GError **error)
> +#endif
> +{
> +  GHashTable *output_types;
> +  char **args;
> +  GFile *input_file;
> +  GFile *output_file;
> +  GFileInputStream *input;
> +  GFileOutputStream *output;
> +  GDataInputStream *in;
> +  GModule *self;
> +  gboolean caught_error = FALSE;
> +
> +  self = g_module_open (NULL, 0);
> +  if (!self)
> +    {
> +      g_set_error (error,
> +		   G_IO_ERROR,
> +		   G_IO_ERROR_FAILED,
> +		   "failed to open self: %s",
> +		   g_module_error ());
> +      return FALSE;
> +    }
> +
> +  args = g_strsplit (arg, ",", 2);
> +
> +  input_file = g_file_new_for_path (args[0]);
> +  output_file = g_file_new_for_path (args[1]);
> +
> +  g_strfreev (args);
> +
> +  input = g_file_read (input_file, NULL, error);
> +  g_object_unref (input_file);
> +
> +  if (input == NULL)
> +    {
> +      g_object_unref (output_file);
> +      return FALSE;
> +    }
> +
> +  output = g_file_replace (output_file, NULL, FALSE, 0, NULL, error);
> +  g_object_unref (output_file);
> +
> +  if (output == NULL)
> +    {
> +      g_input_stream_close (G_INPUT_STREAM (input), NULL, NULL);
> +      return FALSE;
> +    }
> +
> +  goutput_write (G_OUTPUT_STREAM (output), "<?xml version=\"1.0\"?>\n");
> +  goutput_write (G_OUTPUT_STREAM (output), "<dump>\n");
> +
> +  output_types = g_hash_table_new (NULL, NULL);
> +
> +  in = g_data_input_stream_new (G_INPUT_STREAM (input));
> +  g_object_unref (input);
> +
> +  while (TRUE)
> +    {
> +      gsize len;
> +      char *line = g_data_input_stream_read_line (in, &len, NULL, NULL);
> +      const char *function;
> +
> +      if (line == NULL || *line == '\0')
> +        {
> +          g_free (line);
> +          break;
> +        }
> +
> +      g_strchomp (line);
> +
> +      if (strncmp (line, "get-type:", strlen ("get-type:")) == 0)
> +        {
> +          GType type;
> +
> +          function = line + strlen ("get-type:");
> +
> +          type = invoke_get_type (self, function, error);
> +
> +          if (type == G_TYPE_INVALID)
> +            {
> +              g_printerr ("Invalid GType function: '%s'\n", function);
> +              caught_error = TRUE;
> +              g_free (line);
> +              break;
> +            }
> +
> +          if (g_hash_table_lookup (output_types, (gpointer) type))
> +            goto next;
> +          g_hash_table_insert (output_types, (gpointer) type, (gpointer)
> type);
> +
> +          dump_type (type, function, G_OUTPUT_STREAM (output));
> +        }
> +      else if (strncmp (line, "error-quark:", strlen ("error-quark:")) == 0)
> +        {
> +          GQuark quark;
> +          function = line + strlen ("error-quark:");
> +          quark = invoke_error_quark (self, function, error);
> +
> +          if (quark == 0)
> +            {
> +              g_printerr ("Invalid error quark function: '%s'\n", function);
> +              caught_error = TRUE;
> +              g_free (line);
> +              break;
> +            }
> +
> +          dump_error_quark (quark, function, G_OUTPUT_STREAM (output));
> +        }
> +
> +
> +    next:
> +      g_free (line);
> +    }
> +
> +  g_hash_table_destroy (output_types);
> +
> +  goutput_write (G_OUTPUT_STREAM (output), "</dump>\n");
> +
> +  {
> +    GError **ioerror;
> +    /* Avoid overwriting an earlier set error */
> +    if (caught_error)
> +      ioerror = NULL;
> +    else
> +      ioerror = error;
> +    if (!g_input_stream_close (G_INPUT_STREAM (in), NULL, ioerror))
> +      return FALSE;
> +    if (!g_output_stream_close (G_OUTPUT_STREAM (output), NULL, ioerror))
> +      return FALSE;
> +  }
> +
> +  return !caught_error;
> +}
> +
> +
> +int
> +main(int argc, char **argv)
> +{
> +  GError *error = NULL;
> +  const char *introspect_dump_prefix = "--introspect-dump=";
> +
> +#if !GLIB_CHECK_VERSION(2,35,0)
> +  g_type_init ();
> +#endif
> +
> +  
> +
> +  if (argc != 2 || !g_str_has_prefix (argv[1], introspect_dump_prefix))
> +    {
> +      g_printerr ("Usage: %s --introspect-dump=input,output", argv[0]);
> +      exit (1);
> +    }
> +
> +  if (!dump_irepository (argv[1] + strlen(introspect_dump_prefix), &error))
> +    {
> +      g_printerr ("%s\n", error->message);
> +      exit (1);
> +    }
> +  exit (0);
> +}
> +extern GType spice_grab_sequence_get_type(void);
> +extern GType spice_gtk_session_get_type(void);
> +extern GType spice_display_key_event_get_type(void);
> +extern GType spice_display_get_type(void);
> +extern GType spice_usb_device_widget_get_type(void);
> +GType (*GI_GET_TYPE_FUNCS_[])(void) = {
> +  spice_grab_sequence_get_type,
> +  spice_gtk_session_get_type,
> +  spice_display_key_event_get_type,
> +  spice_display_get_type,
> +  spice_usb_device_widget_get_type
> +};
_______________________________________________
Spice-devel mailing list
Spice-devel@xxxxxxxxxxxxxxxxxxxxx
https://lists.freedesktop.org/mailman/listinfo/spice-devel




[Index of Archives]     [Linux ARM Kernel]     [Linux ARM]     [Linux Omap]     [Fedora ARM]     [IETF Annouce]     [Security]     [Bugtraq]     [Linux]     [Linux OMAP]     [Linux MIPS]     [ECOS]     [Asterisk Internet PBX]     [Linux API]     [Monitors]