[gst-devel] GObject-related changes to code

Erik Walthinsen omega at temple-baptist.com
Mon Jun 25 05:27:44 CEST 2001


There are a few somewhat-significant changes to keep in mind when changing
existing code or writing new code now that GObject is the base object
model for GStreamer (listed in no particular order):


1) GTK_OBJECT is now G_OBJECT
Basic thing to remember is that the first pass of conversion is just
removing the 'tk' from everything...


2) The CAST and TYPE macros have changed
They've been renamed to be a little less cryptic, though longer.  Refer to
any existing plugin in CVS for a reference.


3) Class type definitions are different
The GtkTypeInfo struct has been renamed GInfo (gasp!) and restructured.
The new definition is:

struct _GTypeInfo
{
  guint16                class_size;

  GBaseInitFunc          base_init;
  GBaseFinalizeFunc      base_finalize;

  GClassInitFunc         class_init;
  GClassFinalizeFunc     class_finalize;
  gconstpointer          class_data;

  guint16                instance_size;
  guint16                n_preallocs;
  GInstanceInitFunc      instance_init;

  const GTypeValueTable *value_table;
};

Here's a side-by-side comparison:

object_info = {			object_info = {
  "Object",			  sizeof(ObjectClass),
  sizeof (Object),		  object_base_class_init,
  sizeof (ObjectClass),		  NULL, <base_finalize>
  object_class_init,		  object_class_init,
  object_init,			  NULL, <class_finalize>
  NULL, <set_arg>		  NULL, <class_data>
  NULL, <get_arg>		  sizeof(Object),
  object_base_class_init,	  <prealloc count>
};				  object_init,
				};

I've left them out so things fit, but note the different function
prototype typedefs in the struct typedef.  The function to actually
register the type has changed as well:

object_type = g_type_register_static(<base>_TYPE, "Object", &object_info, 0);

Not sure exactly why the name moved out of the struct, but...  Need to
give that function the parent class type, typically GST_TYPE_ELEMENT, and
the 0 is for flags, none of which should be relevant to the majority of
elements.

Also, gtk_type_register_enum() is now g_enum_register_static(), for those
who register their own enums to aid with the editor and such.


4) set_arg and get_arg are not set_property and get_property
The GtkArg system has been redesigned to be a bit more sane, and more
complex:

4a) Properties are now specified with GParamSpecs
A GParamSpec is typically created inline from one of several functions by
the name of g_param_spec_<TYPE>, which take three strings, range, default,
and flags:

  g_object_class_install_property (G_OBJECT_CLASS(klass), REAL_ARG_ACTIVE,
    g_param_spec_boolean("active","Active","Whether the pad is active.",
                         TRUE,G_PARAM_READWRITE));

The first string is the official name of the argument.  The second is a
nickname, and the third is a "blurb" for use in things like Glade and
gsteditor.  I suspect you'd have a label with the nickname, and a tooltip
over that with the blurb.

Refer to the GObject docmentation (link below) for the various param_spec
constructors and their prototypes.

4b) [sg]et_arg are now [sg]et_property
The new names of these functions reflect their new signatures and what
they do.  They now look like:

object_set_property (GObject *object, guint prop_id,
                     const GValue *value, GParamSpec *pspec);
object_get_property (GObject *object, guint prop_id,
                     GValue *value, GParamSpec *pspec);

Note the lack of a 'const' in the get_property definition: that's because
you actually need to write to the *value....  This is different from the
old signatures, which used a GtkArg.  This is because GValue is now a more
complete way of packaging up any given type.  The prop_id is separated
out, as is the GParamSpec.

4c) G_VALUE_<type> goes away
The G_VALUE_ macros have gone away, in favor of:

  <type> g_value_get_<type> (GValue *value);
  g_value_set_<type> (GValue *value, <type>);

Beware however that these are currently complete function calls, which is
something I intend to rectify with patches to gobject.  In the meantime,
treat them as relatively heavy operations. ;-(

4d) New default: case for properties
If you want to be fully correct in property handling, you need to add the
following code to the default: case of your switch (prop_id):

    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
      break;


5) Various signal functions have changed
There are a number of changes to the signatures of the signal-related
functions, none of which are too hard to deal with.

5a) gtk_signal_new() is now g_signal_newc()
You'll need to do some slight re-arranging, and adding a couple new args:

  gst_object_signals[PARENT_SET] =
    g_signal_newc("parent_set", G_TYPE_FROM_CLASS(klass), G_SIGNAL_RUN_LAST,
                  G_STRUCT_OFFSET (GstObjectClass, parent_set), NULL, NULL,
                  g_cclosure_marshal_VOID__OBJECT,G_TYPE_NONE,1,
                  G_TYPE_OBJECT);

The differences are:
  a) GTK_RUN_LAST -> G_SIGNAL_RUN_LAST, and swapped with the object type
  b) use G_TYPE_FROM_CLASS(klass) instead of gtkobject_class->type
  c) GTK_SIGNAL_OFFSET -> G_STRUCT_OFFSET
  d) two new args that can/should default to NULL after the offset
  e) changes to the marshalling and typing (see 5b)

5b) New marshalling setup
There's a new marshalling namespace, which is related to "closures", which
still mystify me.  Basically, you just replace "gtk_marshal" with
"g_cclosure_marshal".  However, the current list of marshallers built in
this namespace is quite short, as it is only sufficient for the basic
GObject stuff.  Therefore we have had to add gstmarshal.[ch], which need
to be set up to be generated sources.  Those marshallers are:

gst_marshal_VOID__OBJECT_POINTER
gst_marshal_VOID__INT_INT

More will likely be added as more elements are added.  It might be worth
lobbying to have a lot of these common ones added to gobject, or at least
the set that was in Gtk+-1.2.

5c) Signal emission changes
There's a minor change in g_signal_emit: a new field that's a GQuark,
between the signal ID and the arguments:

g_signal_emit (G_OBJECT (object), gst_object_signals[PARENT_SET], 0, parent);

Not sure what it's for, but setting it to zero hasn't had any side
effects.  It's probably useful, but I haven't checked into how yet.

5d) Signal emission non-changes
You'll find that in the code in HEAD, we use g_signal_connectc() with an
extra gboolean argument set to FALSE, instead of gtk_signal_connect().
Since we put that in, a new convenience macro by the name of
g_signal_connect() has been added to gobject CVS, which has the exact same
signal as gtk_signal_connect().  So the reality is that you really don't
have to do anything for this one....  It's a freebie ;-)


For the most part, everything else is the same.  Change your apps to use
g_... to access things, and all will be good.

Attached are two scripts, one is the perl that does the work, the other is
a shell script that will take a list of files on the commandline (yes, it
could be done better....).  Running ./gtk2gobject *.[ch] should do the
vast majority of the conversion work.  Beware that it's not perfect, and
assumes a very Gtk+/GStreamer code-style, but it's a good starting point.
If anyone has patches to make it better, I'll maintain an official version
on a webpage I'll be creating shortly to house all the stuff related to
GtkObject -> GObject.

TTYL,
    Omega

      Erik Walthinsen <omega at temple-baptist.com> - System Administrator
        __
       /  \                GStreamer - The only way to stream!
      |    | M E G A        ***** http://gstreamer.net/ *****
      _\  /_
-------------- next part --------------
#!/bin/sh
for n in $*;do
  cat $n | /home/omega/gst.gobject1/gtk2gobject.pl | /home/omega/gst.gobject1/gtk2gobject.pl > /tmp/$$
  cat /tmp/$$ > $n
  rm -f /tmp/$$
done
-------------- next part --------------
#!/usr/bin/perl

my $objname;

while (<>) {
  chomp;

# long list of seds to do.
  s/GtkObject/GObject/g;
  s/gtkobject/gobject/g;
  s/GTK_OBJECT/G_OBJECT/g;
  s/GTK_CHECK_CAST/G_TYPE_CHECK_INSTANCE_CAST/g;
  s/GTK_CHECK_TYPE/G_TYPE_CHECK_INSTANCE_TYPE/g;
  s/GTK_CHECK_CLASS_CAST/G_TYPE_CHECK_CLASS_CAST/g;
  s/GTK_CHECK_CLASS_TYPE/G_TYPE_CHECK_CLASS_TYPE/g;
  s/GTK_TYPE_/G_TYPE_/g;
  s/GTK_VALUE_/G_VALUE_/g;
  s/GtkType/GType/g;
  s/gtk_type_unique/g_type_register_static/g;
  s/get_arg/get_property/g;
  s/set_arg/set_property/g;
  s/gtk\/gtk.h/glib-object.h/g;
  s/(.*set.*)GObject \*object, *GtkArg \*arg, *guint id/\1GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec/g;
  s/(.*get.*)GObject \*object, *GtkArg \*arg, *guint id/\1GObject *object, guint prop_id, GValue *value, GParamSpec *pspec/g;
  s/GtkSignalFunc/gpointer/g;
  s/gtk_signal_new/g_signal_newc/g;
  s/(g_signal_newc.*)G_OBJECT_TYPE\(klass\)/\1G_TYPE_FROM_CLASS\(klass\)/g;
  s/GTK_SIGNAL_OFFSET/G_STRUCT_OFFSET/g;
  s/(G_STRUCT_OFFSET\s*\([^)]*\)),.*$/\1, NULL, NULL,/g;
  s/gtk_marshal/g_cclosure_marshal/g;
  s/marshal_NONE/marshal_VOID/g;
  s/__NONE/__VOID/g;
  s/GTK_OBJECT\s*\(([a-z]*)\)->klass/G_OBJECT_GET_CLASS(\1)/g;
  s/GTK_RUN_([A-Z]*), *gobject_class->type/G_OBJECT_TYPE(klass), G_SIGNAL_RUN_\1/g;
  s/gtk_signal_emit(.*)],/g_signal_emit(\1,NULL)/g;
  s/GtkClassInitFunc/GClassInitFunc/g;
  s/GObjectInitFunc/GInstanceInitFunc/g;
  s/G_TYPE_VOID/G_TYPE_NONE/g;
  s/gtk_type_class/g_type_class_ref/g;
  s/gtk_type_register_enum/g_enum_register_static/g;
  s/GtkEnumValue/GEnumValue/g;
  s/switch\(id\)/switch (prop_id)/g;
  s/switch \(id\)/switch (prop_id)/g;
  s/G_OBJECT_TYPE\(g[tk]*object_class\)/G_TYPE_FROM_CLASS(klass)/g;

  next if /gtk_object_class_add_signals/;
  next if /^\s*LAST_SIGNAL\);$/;

  if (/^\s*\/\//) {
    print "$_\n";
    next;
  }

  if (/GTypeInfo/) {
    my (@elements);
    print "$_\n";
    $_ = <>;
    chomp;
    if (/sizeof/) {
      print;next;
    } else {
      s/^\s*(.*),$/\1/;
      push @elements, $_;
    }
    while (<>) {
      chomp;
      if (/};/) { last; }
      s/^\s*(.*),$/\1/;
      push @elements, $_;
    }
    print "      " . $elements[2] . ",\n";
    print "      NULL,\n";
    print "      NULL,\n";
    print "      " . $elements[3] . ",\n";
    print "      NULL,\n";
    print "      NULL,\n";
    print "      " . $elements[1] . ",\n";
    print "      0,\n";
    print "      " . $elements[4] . ",\n";
    $objname = $elements[0];
  } elsif (/g_type_register_static/) {
    s/([^"]),\s*\&/\1, $objname, &/;
    s/([^0])\);/\1, 0);/;
  } elsif (/gtk_object_add_arg_type/) {
    s/^.*\(//;
    while (/,\s*$/) {
      my $nextline = <>;
      chomp $nextline;
      $nextline =~ s/^\s*//;
      $_ .= $nextline;
    }
    s/,\s*/,/g;
    s/\);//;

    my @args = split ",";
    $args[0] =~ s/[A-Z]\w*:://;
    $args[2] =~ s/GTK_ARG_/G_PARAM_/;
    print "  g_object_class_install_property(G_OBJECT_CLASS(klass), $args[3],\n";
    if ($args[1] =~ /G_TYPE_INT/) {
      print "    g_param_spec_int($args[0],$args[0],$args[0],\n";
      print "                     G_MININT,G_MAXINT,0,$args[2])); // CHECKME\n";
    } elsif ($args[1] =~ /G_TYPE_UINT/) {
      print "    g_param_spec_uint($args[0],$args[0],$args[0],\n";
      print "                     0,G_MAXUINT,0,$args[2])); // CHECKME\n";
    } elsif ($args[1] =~ /G_TYPE_LONG/) {
      print "    g_param_spec_long($args[0],$args[0],$args[0],\n";
      print "                     G_MINLONG,G_MAXLONG,0,$args[2])); // CHECKME\n";
    } elsif ($args[1] =~ /G_TYPE_ULONG/) {
      print "    g_param_spec_ulong($args[0],$args[0],$args[0],\n";
      print "                       0,G_MAXULONG,0,$args[2])); // CHECKME\n";
    } elsif ($args[1] =~ /G_TYPE_FLOAT/) {
      print "    g_param_spec_float($args[0],$args[0],$args[0],\n";
      print "                       G_MINFLOAT,G_MAXFLOAT,0,$args[2])); // CHECKME\n";
    } elsif ($args[1] =~ /G_TYPE_DOUBLE/) {
      print "    g_param_spec_double($args[0],$args[0],$args[0],\n";
      print "                        G_MINDOUBLE,G_MAXDOUBLE,0,$args[2])); // CHECKME\n";
    } elsif ($args[1] =~ /G_TYPE_BOOL/) {
      print "    g_param_spec_boolean($args[0],$args[0],$args[0],\n";
      print "                         TRUE,$args[2])); // CHECKME\n";
    } elsif ($args[1] =~ /G_TYPE_STRING/) {
      print "    g_param_spec_string($args[0],$args[0],$args[0],\n";
      print "                        NULL, $args[2])); // CHECKME\n";
    } elsif ($args[1] =~ /G_TYPE_POINTER/) {
      print "    g_param_spec_string($args[0],$args[0],$args[0],\n";
      print "                        NULL, $args[2])); // CHECKME\n";
    } else {
      print "    g_param_spec_enum($args[0],$args[0],$args[0],\n";
      print "                      $args[1],0,$args[2])); // CHECKME!\n";
    }

    next;
  } elsif (/G_VALUE_/) {
    s/BOOL/BOOLEAN/;
    my $type = $_;
    $type =~ s/^.*G_VALUE_([A-Z]*).*$/\1/;
    $type = lc($type);
    s/G_VALUE_[A-Z]*\s*\(\*arg\)/g_value_get_$type (value)/g;
  }


  s/g_value_get_([a-z]*) \(value\)\s=\s*(.*);/g_value_set_\1 (value, \2);/;
  s/arg->type = G_TYPE_INVALID/G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec)/;

  print "$_\n";
}


More information about the gstreamer-devel mailing list