| #!/usr/bin/env perl |
| # -*- cperl -*- |
| # |
| # gtk-doc - GTK DocBook documentation generator. |
| # Copyright (C) 1998 Damon Chaplin |
| # |
| # This program is free software; you can redistribute it and/or modify |
| # it under the terms of the GNU General Public License as published by |
| # the Free Software Foundation; either version 2 of the License, or |
| # (at your option) any later version. |
| # |
| # This program 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 General Public License for more details. |
| # |
| # You should have received a copy of the GNU General Public License |
| # along with this program; if not, write to the Free Software |
| # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. |
| # |
| |
| # |
| # This gets information about object hierarchies and signals |
| # by compiling a small C program. CFLAGS and LDFLAGS must be |
| # set appropriately before running this script. |
| # |
| |
| use Getopt::Long; |
| |
| # Options |
| |
| # name of documentation module |
| my $MODULE; |
| my $OUTPUT_DIR; |
| my $INSPECT_DIR; |
| my $VERBOSE; |
| my $PRINT_VERSION; |
| my $PRINT_HELP; |
| my $TYPE_INIT_FUNC="g_type_init ()"; |
| |
| # --nogtkinit is deprecated, as it is the default now anyway. |
| %optctl = (module => \$MODULE, |
| source => \$SOURCE, |
| types => \$TYPES_FILE, |
| nogtkinit => \$NO_GTK_INIT, |
| 'type-init-func' => \$TYPE_INIT_FUNC, |
| 'output-dir' => \$OUTPUT_DIR, |
| 'inspect-dir' => \$INSPECT_DIR, |
| 'verbose' => \$VERBOSE, |
| 'version' => \$PRINT_VERSION, |
| 'help' => \$PRINT_HELP); |
| |
| GetOptions(\%optctl, "module=s", "source=s", "types:s", "output-dir:s", "inspect-dir:s", "nogtkinit", "type-init-func:s", "verbose", "version", "help"); |
| |
| if ($NO_GTK_INIT) { |
| # Do nothing. This just avoids a warning. |
| # the option is not used anymore |
| } |
| |
| if ($PRINT_VERSION) { |
| print "1.5\n"; |
| exit 0; |
| } |
| |
| if (!$MODULE) { |
| $PRINT_HELP = 1; |
| } |
| |
| if ($PRINT_HELP) { |
| print <<EOF; |
| gstdoc-scangobj version 1.5 - introspect gstreamer-plugins |
| |
| --module=MODULE_NAME Name of the doc module being parsed |
| --source=SOURCE_NAME Name of the source module for plugins |
| --types=FILE The name of the file to store the types in |
| --type-init-func=FUNC The init function to call instead of g_type_init() |
| --output-dir=DIRNAME The directory where the results are stored |
| --inspect-dir=DIRNAME The directory where the plugin inspect data is stored |
| --verbose Print extra output while processing |
| --version Print the version of this program |
| --help Print this help |
| EOF |
| exit 0; |
| } |
| |
| $OUTPUT_DIR = $OUTPUT_DIR ? $OUTPUT_DIR : "."; |
| |
| $TYPES_FILE = $TYPES_FILE ? $TYPES_FILE : "$OUTPUT_DIR/$MODULE.types"; |
| |
| open (TYPES, $TYPES_FILE) || die "Cannot open $TYPES_FILE: $!\n"; |
| open (OUTPUT, ">$MODULE-scan.c") || die "Cannot open $MODULE-scan.c: $!\n"; |
| |
| my $old_signals_filename = "$OUTPUT_DIR/$MODULE.signals"; |
| my $new_signals_filename = "$OUTPUT_DIR/$MODULE.signals.new"; |
| my $old_hierarchy_filename = "$OUTPUT_DIR/$MODULE.hierarchy"; |
| my $new_hierarchy_filename = "$OUTPUT_DIR/$MODULE.hierarchy.new"; |
| my $old_interfaces_filename = "$OUTPUT_DIR/$MODULE.interfaces"; |
| my $new_interfaces_filename = "$OUTPUT_DIR/$MODULE.interfaces.new"; |
| my $old_prerequisites_filename = "$OUTPUT_DIR/$MODULE.prerequisites"; |
| my $new_prerequisites_filename = "$OUTPUT_DIR/$MODULE.prerequisites.new"; |
| my $old_args_filename = "$OUTPUT_DIR/$MODULE.args"; |
| my $new_args_filename = "$OUTPUT_DIR/$MODULE.args.new"; |
| my $old_sections_filename = "$OUTPUT_DIR/$MODULE-sections"; |
| my $new_sections_filename = "$OUTPUT_DIR/$MODULE-sections.new"; |
| |
| my $debug_log="g_message"; |
| if (!defined($VERBOSE) or $VERBOSE eq "0") { |
| $debug_log="//$debug_log"; |
| } |
| |
| # write a C program to scan the types |
| |
| $includes = ""; |
| @types = (); |
| @impl_types = (); |
| |
| for (<TYPES>) { |
| if (/^#include/) { |
| $includes .= $_; |
| } elsif (/^%/) { |
| next; |
| } elsif (/^\s*$/) { |
| next; |
| } elsif (/^type:(.*)$/) { |
| $t = $1; |
| chomp $t; |
| push @impl_types, $t; |
| } else { |
| chomp; |
| push @types, $_; |
| } |
| } |
| |
| $ntypes = @types + @impl_types + 1; |
| |
| print OUTPUT <<EOT; |
| |
| /* file generated by common/gstdoc-scangobj */ |
| |
| #include <string.h> |
| #include <stdlib.h> |
| #include <stdio.h> |
| #include <errno.h> |
| |
| #include <gst/gst.h> |
| EOT |
| |
| if ($includes) { |
| print OUTPUT $includes; |
| } else { |
| for (@types) { |
| print OUTPUT "extern GType $_ (void);\n"; |
| } |
| } |
| |
| print OUTPUT <<EOT; |
| |
| #ifdef GTK_IS_WIDGET_CLASS |
| #include <gtk/gtkversion.h> |
| #endif |
| |
| static GType *object_types = NULL; |
| |
| static GString *xmlstr = NULL; |
| |
| static const gchar* |
| xmlprint (gint indent, const gchar *tag, const gchar *data) |
| { |
| const gchar indent_str[] = " "; |
| |
| /* reset */ |
| g_string_truncate (xmlstr, 0); |
| g_string_append_len (xmlstr, indent_str, MIN (indent, strlen (indent_str))); |
| g_string_append_printf (xmlstr, "<%s>", tag); |
| |
| if (data) { |
| gchar *s; |
| |
| s = g_markup_escape_text (data, -1); |
| g_string_append (xmlstr, s); |
| g_free (s); |
| } |
| |
| g_string_append_printf (xmlstr, "</%s>\\n", tag); |
| return xmlstr->str; |
| } |
| |
| static gint |
| gst_feature_sort_compare (gconstpointer a, gconstpointer b) |
| { |
| const gchar *name_a = gst_plugin_feature_get_name ((GstPluginFeature *) a); |
| const gchar *name_b = gst_plugin_feature_get_name ((GstPluginFeature *) b); |
| return strcmp (name_a, name_b); |
| } |
| |
| static gint |
| static_pad_template_compare (gconstpointer a, gconstpointer b) |
| { |
| GstStaticPadTemplate *spt_a = (GstStaticPadTemplate *) a; |
| GstStaticPadTemplate *spt_b = (GstStaticPadTemplate *) b; |
| |
| /* we want SINK before SRC (enum is UNKNOWN, SRC, SINK) */ |
| if (spt_a->direction != spt_b->direction) |
| return spt_b->direction - spt_a->direction; |
| |
| /* we want ALWAYS first, SOMETIMES second, REQUEST last |
| * (enum is ALWAYS, SOMETIMES, REQUEST) */ |
| if (spt_a->presence != spt_b->presence) |
| return spt_a->presence - spt_b->presence; |
| |
| return strcmp (spt_a->name_template, spt_b->name_template); |
| } |
| |
| static GType * |
| get_object_types (void) |
| { |
| gpointer g_object_class; |
| GList *plugins = NULL; |
| GList *factories = NULL; |
| GList *l; |
| GstElementFactory *factory = NULL; |
| GType type; |
| gint i = 0; |
| gboolean reinspect; |
| |
| /* get a list of features from plugins in our source module */ |
| plugins = gst_registry_get_plugin_list (gst_registry_get ()); |
| |
| xmlstr = g_string_new (""); |
| |
| reinspect = !g_file_test ("scanobj-build.stamp", G_FILE_TEST_EXISTS); |
| |
| while (plugins) { |
| GList *features; |
| GstPlugin *plugin; |
| const gchar *source; |
| FILE *inspect = NULL; |
| gchar *inspect_name; |
| |
| plugin = (GstPlugin *) (plugins->data); |
| plugins = g_list_next (plugins); |
| source = gst_plugin_get_source (plugin); |
| if (!source || strcmp (source, "$SOURCE") != 0) { |
| continue; |
| } |
| |
| /* skip static coreelements plugin with pipeline and bin element factory */ |
| if (gst_plugin_get_filename (plugin) == NULL) |
| continue; |
| |
| $debug_log ("plugin: %s source: %s", gst_plugin_get_name (plugin), source); |
| |
| if (reinspect) { |
| gchar *basename; |
| |
| inspect_name = g_strdup_printf ("$INSPECT_DIR" G_DIR_SEPARATOR_S "plugin-%s.xml", |
| gst_plugin_get_name (plugin)); |
| inspect = fopen (inspect_name, "w"); |
| if (inspect == NULL) { |
| g_error ("Could not open %s for writing: %s\\n", inspect_name, |
| g_strerror (errno)); |
| } |
| g_free (inspect_name); |
| |
| basename = g_path_get_basename (gst_plugin_get_filename (plugin)); |
| |
| /* output plugin data */ |
| fputs ("<plugin>\\n",inspect); |
| fputs (xmlprint(2, "name", gst_plugin_get_name (plugin)),inspect); |
| fputs (xmlprint(2, "description", gst_plugin_get_description (plugin)),inspect); |
| fputs (xmlprint(2, "filename", gst_plugin_get_filename (plugin)),inspect); |
| fputs (xmlprint(2, "basename", basename),inspect); |
| fputs (xmlprint(2, "version", gst_plugin_get_version (plugin)),inspect); |
| fputs (xmlprint(2, "license", gst_plugin_get_license (plugin)),inspect); |
| fputs (xmlprint(2, "source", gst_plugin_get_source (plugin)),inspect); |
| fputs (xmlprint(2, "package", gst_plugin_get_package (plugin)),inspect); |
| fputs (xmlprint(2, "origin", gst_plugin_get_origin (plugin)),inspect); |
| fputs (" <elements>\\n", inspect); |
| |
| g_free (basename); |
| } |
| |
| features = |
| gst_registry_get_feature_list_by_plugin (gst_registry_get (), |
| gst_plugin_get_name (plugin)); |
| |
| /* sort factories by feature->name */ |
| features = g_list_sort (features, gst_feature_sort_compare); |
| |
| while (features) { |
| GstPluginFeature *feature; |
| feature = GST_PLUGIN_FEATURE (features->data); |
| feature = gst_plugin_feature_load (feature); |
| if (!feature) { |
| g_warning ("Could not load plugin feature %s", |
| gst_plugin_feature_get_name (feature)); |
| } |
| factories = g_list_prepend (factories, feature); |
| |
| if (GST_IS_ELEMENT_FACTORY (feature)) { |
| const gchar *pad_dir[] = { "unknown","source","sink" }; |
| const gchar *pad_pres[] = { "always","sometimes","request" }; |
| GList *pads, *pad; |
| |
| $debug_log (" feature: %s", gst_plugin_feature_get_name (feature)); |
| |
| factory = GST_ELEMENT_FACTORY (feature); |
| |
| if (reinspect) { |
| /* output element data */ |
| fputs (" <element>\\n", inspect); |
| fputs (xmlprint(6, "name", gst_plugin_feature_get_name (feature)),inspect); |
| fputs (xmlprint(6, "longname", gst_element_factory_get_metadata (factory, GST_ELEMENT_METADATA_LONGNAME)),inspect); |
| fputs (xmlprint(6, "class", gst_element_factory_get_metadata (factory, GST_ELEMENT_METADATA_KLASS)),inspect); |
| fputs (xmlprint(6, "description", gst_element_factory_get_metadata (factory, GST_ELEMENT_METADATA_DESCRIPTION)),inspect); |
| fputs (xmlprint(6, "author", gst_element_factory_get_metadata (factory, GST_ELEMENT_METADATA_AUTHOR)),inspect); |
| fputs (" <pads>\\n", inspect); |
| |
| /* output pad-template data */ |
| pads = g_list_copy ((GList *) gst_element_factory_get_static_pad_templates (factory)); |
| pads = g_list_sort (pads, static_pad_template_compare); |
| for (pad = pads; pad != NULL; pad = pad->next) { |
| GstStaticPadTemplate *pt = pad->data; |
| |
| fputs (" <caps>\\n", inspect); |
| fputs (xmlprint(10, "name", pt->name_template),inspect); |
| fputs (xmlprint(10, "direction", pad_dir[pt->direction]),inspect); |
| fputs (xmlprint(10, "presence", pad_pres[pt->presence]),inspect); |
| fputs (xmlprint(10, "details", pt->static_caps.string),inspect); |
| fputs (" </caps>\\n", inspect); |
| } |
| g_list_free (pads); |
| fputs (" </pads>\\n </element>\\n", inspect); |
| } |
| } else if (GST_IS_TRACER_FACTORY (feature)) { |
| $debug_log (" feature: %s", gst_plugin_feature_get_name (feature)); |
| |
| if (reinspect) { |
| /* output element data */ |
| fputs (" <tracer>\\n", inspect); |
| fputs (xmlprint(6, "name", gst_plugin_feature_get_name (feature)),inspect); |
| |
| fputs (" </tracer>\\n", inspect); |
| } |
| } |
| features = g_list_next (features); |
| } |
| |
| if (reinspect) { |
| fputs (" </elements>\\n</plugin>", inspect); |
| fclose (inspect); |
| } |
| } |
| |
| g_string_free (xmlstr, TRUE); |
| |
| $debug_log ("number of element factories: %d", g_list_length (factories)); |
| |
| /* allocate the object_types array to hold them */ |
| object_types = g_new0 (GType, g_list_length (factories)+$ntypes+1); |
| |
| l = factories; |
| i = 0; |
| |
| /* fill it */ |
| while (l) { |
| const gchar *name = NULL; |
| type = 0; |
| if (GST_IS_ELEMENT_FACTORY (l->data)) { |
| factory = GST_ELEMENT_FACTORY (l->data); |
| type = gst_element_factory_get_element_type (factory); |
| name = gst_element_factory_get_metadata (factory, GST_ELEMENT_METADATA_LONGNAME); |
| } else if (GST_IS_TRACER_FACTORY (l->data)) { |
| GstTracerFactory *t = GST_TRACER_FACTORY (l->data); |
| type = gst_tracer_factory_get_tracer_type (t); |
| name = gst_plugin_feature_get_name (GST_PLUGIN_FEATURE (t)); |
| } |
| if (type != 0) { |
| $debug_log ("adding type for factory %s", name); |
| object_types[i++] = type; |
| } else { |
| g_message ("type info for factory %s not found", name); |
| } |
| l = g_list_next (l); |
| } |
| |
| EOT |
| |
| # get_type functions: |
| for (@types) { |
| print OUTPUT <<EOT; |
| type = $_ (); |
| if (type == 0) { |
| g_message ("$_ () didn't return a valid type"); |
| } |
| else { |
| object_types[i++] = type; |
| } |
| EOT |
| } |
| |
| # Implicit types retrieved from GLib: |
| for (@impl_types) { |
| print OUTPUT <<EOT; |
| type = g_type_from_name ("$_"); |
| if (type == 0) { |
| g_message ("Implicit type $_ not found"); |
| } |
| else { |
| object_types[i++] = type; |
| } |
| EOT |
| } |
| |
| print OUTPUT <<EOT; |
| |
| object_types[i] = 0; |
| |
| /* reference the GObjectClass to initialize the param spec pool |
| * potentially needed by interfaces. See http://bugs.gnome.org/571820 */ |
| g_object_class = g_type_class_ref (G_TYPE_OBJECT); |
| |
| /* Need to make sure all the types are loaded in and initialize |
| * their signals and properties. |
| */ |
| for (i=0; object_types[i]; i++) |
| { |
| if (G_TYPE_IS_CLASSED (object_types[i])) |
| g_type_class_ref (object_types[i]); |
| if (G_TYPE_IS_INTERFACE (object_types[i])) |
| g_type_default_interface_ref (object_types[i]); |
| } |
| |
| g_type_class_unref (g_object_class); |
| |
| return object_types; |
| } |
| |
| /* |
| * This uses GObject type functions to output signal prototypes and the object |
| * hierarchy. |
| */ |
| |
| /* The output files */ |
| const gchar *signals_filename = "$new_signals_filename"; |
| const gchar *hierarchy_filename = "$new_hierarchy_filename"; |
| const gchar *interfaces_filename = "$new_interfaces_filename"; |
| const gchar *prerequisites_filename = "$new_prerequisites_filename"; |
| const gchar *args_filename = "$new_args_filename"; |
| const gchar *sections_filename = "$new_sections_filename"; |
| |
| |
| static void output_signals (void); |
| static void output_object_signals (FILE *fp, GType object_type); |
| static void output_object_signal (FILE *fp, const gchar *object_class_name, |
| guint signal_id); |
| static const gchar * get_type_name (GType type, gboolean * is_pointer); |
| static void output_object_hierarchy (void); |
| static void output_hierarchy (FILE *fp, GType type, guint level); |
| |
| static void output_object_interfaces (void); |
| static void output_interfaces (FILE *fp, GType type); |
| |
| static void output_interface_prerequisites (void); |
| static void output_prerequisites (FILE *fp, GType type); |
| |
| static void output_args (void); |
| static void output_object_args (FILE *fp, GType object_type); |
| |
| static void output_sections (void); |
| static void output_object_section (FILE *fp, GType object_type); |
| |
| |
| int |
| main (G_GNUC_UNUSED int argc, G_GNUC_UNUSED char *argv[]) |
| { |
| $TYPE_INIT_FUNC; |
| |
| get_object_types (); |
| |
| output_signals (); |
| output_object_hierarchy (); |
| output_object_interfaces (); |
| output_interface_prerequisites (); |
| output_args (); |
| |
| output_sections (); |
| |
| return 0; |
| } |
| |
| |
| static void |
| output_signals (void) |
| { |
| FILE *fp; |
| gint i; |
| |
| fp = fopen (signals_filename, "w"); |
| if (fp == NULL) |
| { |
| g_warning ("Couldn't open output file: %s : %s", signals_filename, g_strerror(errno)); |
| return; |
| } |
| |
| for (i = 0; object_types[i]; i++) |
| output_object_signals (fp, object_types[i]); |
| |
| fclose (fp); |
| } |
| |
| static gint |
| compare_signals (const void *a, const void *b) |
| { |
| const guint *signal_a = a; |
| const guint *signal_b = b; |
| |
| return strcmp (g_signal_name (*signal_a), g_signal_name (*signal_b)); |
| } |
| |
| /* This outputs all the signals of one object. */ |
| static void |
| output_object_signals (FILE *fp, GType object_type) |
| { |
| const gchar *object_class_name; |
| guint *signals, n_signals; |
| guint sig; |
| |
| if (G_TYPE_IS_INSTANTIATABLE (object_type) || |
| G_TYPE_IS_INTERFACE (object_type)) |
| { |
| |
| object_class_name = g_type_name (object_type); |
| |
| signals = g_signal_list_ids (object_type, &n_signals); |
| qsort (signals, n_signals, sizeof (guint), compare_signals); |
| |
| for (sig = 0; sig < n_signals; sig++) |
| { |
| output_object_signal (fp, object_class_name, signals[sig]); |
| } |
| g_free (signals); |
| } |
| } |
| |
| |
| /* This outputs one signal. */ |
| static void |
| output_object_signal (FILE *fp, |
| const gchar *object_name, |
| guint signal_id) |
| { |
| GSignalQuery query_info; |
| const gchar *type_name, *ret_type, *object_arg, *arg_name; |
| gchar *pos, *object_arg_lower; |
| gboolean is_pointer; |
| gchar buffer[1024]; |
| guint i, param; |
| gint param_num, widget_num, event_num, callback_num; |
| gint *arg_num; |
| gchar signal_name[128]; |
| gchar flags[16]; |
| |
| $debug_log ("Object: %s Signal: %u", object_name, signal_id); |
| |
| param_num = 1; |
| widget_num = event_num = callback_num = 0; |
| |
| g_signal_query (signal_id, &query_info); |
| |
| /* Output the signal object type and the argument name. We assume the |
| type is a pointer - I think that is OK. We remove "Gtk" or "Gnome" and |
| convert to lower case for the argument name. */ |
| pos = buffer; |
| sprintf (pos, "%s ", object_name); |
| pos += strlen (pos); |
| |
| /* Try to come up with a sensible variable name for the first arg |
| * It chops off 2 know prefixes :/ and makes the name lowercase |
| * It should replace lowercase -> uppercase with '_' |
| * GFileMonitor -> file_monitor |
| * GIOExtensionPoint -> extension_point |
| * GtkTreeView -> tree_view |
| * if 2nd char is upper case too |
| * search for first lower case and go back one char |
| * else |
| * search for next upper case |
| */ |
| if (!strncmp (object_name, "Gtk", 3)) |
| object_arg = object_name + 3; |
| else if (!strncmp (object_name, "Gnome", 5)) |
| object_arg = object_name + 5; |
| else |
| object_arg = object_name; |
| |
| object_arg_lower = g_ascii_strdown (object_arg, -1); |
| sprintf (pos, "*%s\\n", object_arg_lower); |
| pos += strlen (pos); |
| if (!strncmp (object_arg_lower, "widget", 6)) |
| widget_num = 2; |
| g_free(object_arg_lower); |
| |
| /* Convert signal name to use underscores rather than dashes '-'. */ |
| strncpy (signal_name, query_info.signal_name, 127); |
| signal_name[127] = '\\0'; |
| for (i = 0; signal_name[i]; i++) |
| { |
| if (signal_name[i] == '-') |
| signal_name[i] = '_'; |
| } |
| |
| /* Output the signal parameters. */ |
| for (param = 0; param < query_info.n_params; param++) |
| { |
| type_name = get_type_name (query_info.param_types[param] & ~G_SIGNAL_TYPE_STATIC_SCOPE, &is_pointer); |
| |
| /* Most arguments to the callback are called "arg1", "arg2", etc. |
| GtkWidgets are called "widget", "widget2", ... |
| GtkCallbacks are called "callback", "callback2", ... */ |
| if (!strcmp (type_name, "GtkWidget")) |
| { |
| arg_name = "widget"; |
| arg_num = &widget_num; |
| } |
| else if (!strcmp (type_name, "GtkCallback") |
| || !strcmp (type_name, "GtkCCallback")) |
| { |
| arg_name = "callback"; |
| arg_num = &callback_num; |
| } |
| else |
| { |
| arg_name = "arg"; |
| arg_num = ¶m_num; |
| } |
| sprintf (pos, "%s ", type_name); |
| pos += strlen (pos); |
| |
| if (!arg_num || *arg_num == 0) |
| sprintf (pos, "%s%s\\n", is_pointer ? "*" : " ", arg_name); |
| else |
| sprintf (pos, "%s%s%i\\n", is_pointer ? "*" : " ", arg_name, |
| *arg_num); |
| pos += strlen (pos); |
| |
| if (arg_num) |
| { |
| if (*arg_num == 0) |
| *arg_num = 2; |
| else |
| *arg_num += 1; |
| } |
| } |
| |
| pos = flags; |
| /* We use one-character flags for simplicity. */ |
| if (query_info.signal_flags & G_SIGNAL_RUN_FIRST) |
| *pos++ = 'f'; |
| if (query_info.signal_flags & G_SIGNAL_RUN_LAST) |
| *pos++ = 'l'; |
| if (query_info.signal_flags & G_SIGNAL_RUN_CLEANUP) |
| *pos++ = 'c'; |
| if (query_info.signal_flags & G_SIGNAL_NO_RECURSE) |
| *pos++ = 'r'; |
| if (query_info.signal_flags & G_SIGNAL_DETAILED) |
| *pos++ = 'd'; |
| if (query_info.signal_flags & G_SIGNAL_ACTION) |
| *pos++ = 'a'; |
| if (query_info.signal_flags & G_SIGNAL_NO_HOOKS) |
| *pos++ = 'h'; |
| *pos = 0; |
| |
| /* Output the return type and function name. */ |
| ret_type = get_type_name (query_info.return_type & ~G_SIGNAL_TYPE_STATIC_SCOPE, &is_pointer); |
| |
| fprintf (fp, |
| "<SIGNAL>\\n<NAME>%s::%s</NAME>\\n<RETURNS>%s%s</RETURNS>\\n<FLAGS>%s</FLAGS>\\n%s</SIGNAL>\\n\\n", |
| object_name, query_info.signal_name, ret_type, is_pointer ? "*" : "", flags, buffer); |
| } |
| |
| |
| /* Returns the type name to use for a signal argument or return value, given |
| the GtkType from the signal info. It also sets is_pointer to TRUE if the |
| argument needs a '*' since it is a pointer. */ |
| static const gchar * |
| get_type_name (GType type, gboolean * is_pointer) |
| { |
| const gchar *type_name; |
| |
| *is_pointer = FALSE; |
| type_name = g_type_name (type); |
| |
| switch (type) { |
| case G_TYPE_NONE: |
| case G_TYPE_CHAR: |
| case G_TYPE_UCHAR: |
| case G_TYPE_BOOLEAN: |
| case G_TYPE_INT: |
| case G_TYPE_UINT: |
| case G_TYPE_LONG: |
| case G_TYPE_ULONG: |
| case G_TYPE_FLOAT: |
| case G_TYPE_DOUBLE: |
| case G_TYPE_POINTER: |
| /* These all have normal C type names so they are OK. */ |
| return type_name; |
| |
| case G_TYPE_STRING: |
| /* A GtkString is really a gchar*. */ |
| *is_pointer = TRUE; |
| return "gchar"; |
| |
| case G_TYPE_ENUM: |
| case G_TYPE_FLAGS: |
| /* We use a gint for both of these. Hopefully a subtype with a decent |
| name will be registered and used instead, as GTK+ does itself. */ |
| return "gint"; |
| |
| case G_TYPE_BOXED: |
| /* The boxed type shouldn't be used itself, only subtypes. Though we |
| return 'gpointer' just in case. */ |
| return "gpointer"; |
| |
| case G_TYPE_PARAM: |
| /* A GParam is really a GParamSpec*. */ |
| *is_pointer = TRUE; |
| return "GParamSpec"; |
| |
| #if GLIB_CHECK_VERSION (2, 25, 9) |
| case G_TYPE_VARIANT: |
| *is_pointer = TRUE; |
| return "GVariant"; |
| #endif |
| |
| default: |
| break; |
| } |
| |
| /* For all GObject subclasses we can use the class name with a "*", |
| e.g. 'GtkWidget *'. */ |
| if (g_type_is_a (type, G_TYPE_OBJECT)) |
| *is_pointer = TRUE; |
| |
| /* Also catch non GObject root types */ |
| if (G_TYPE_IS_CLASSED (type)) |
| *is_pointer = TRUE; |
| |
| /* All boxed subtypes will be pointers as well. */ |
| /* Exception: GStrv */ |
| if (g_type_is_a (type, G_TYPE_BOXED) && |
| !g_type_is_a (type, G_TYPE_STRV)) |
| *is_pointer = TRUE; |
| |
| /* All pointer subtypes will be pointers as well. */ |
| if (g_type_is_a (type, G_TYPE_POINTER)) |
| *is_pointer = TRUE; |
| |
| /* But enums are not */ |
| if (g_type_is_a (type, G_TYPE_ENUM) || |
| g_type_is_a (type, G_TYPE_FLAGS)) |
| *is_pointer = FALSE; |
| |
| return type_name; |
| } |
| |
| |
| /* This outputs the hierarchy of all objects which have been initialized, |
| i.e. by calling their XXX_get_type() initialization function. */ |
| static void |
| output_object_hierarchy (void) |
| { |
| FILE *fp; |
| gint i,j; |
| GType root, type; |
| GType root_types[$ntypes] = { G_TYPE_INVALID, }; |
| |
| fp = fopen (hierarchy_filename, "w"); |
| if (fp == NULL) |
| { |
| g_warning ("Couldn't open output file: %s : %s", hierarchy_filename, g_strerror(errno)); |
| return; |
| } |
| output_hierarchy (fp, G_TYPE_OBJECT, 0); |
| output_hierarchy (fp, G_TYPE_INTERFACE, 0); |
| |
| for (i=0; object_types[i]; i++) { |
| root = object_types[i]; |
| while ((type = g_type_parent (root))) { |
| root = type; |
| } |
| if ((root != G_TYPE_OBJECT) && (root != G_TYPE_INTERFACE)) { |
| for (j=0; root_types[j]; j++) { |
| if (root == root_types[j]) { |
| root = G_TYPE_INVALID; break; |
| } |
| } |
| if(root) { |
| root_types[j] = root; |
| output_hierarchy (fp, root, 0); |
| } |
| } |
| } |
| |
| fclose (fp); |
| } |
| |
| static int |
| compare_types (const void *a, const void *b) |
| { |
| const char *na = g_type_name (*((GType *)a)); |
| const char *nb = g_type_name (*((GType *)b)); |
| |
| return g_strcmp0 (na, nb); |
| } |
| |
| |
| /* This is called recursively to output the hierarchy of a object. */ |
| static void |
| output_hierarchy (FILE *fp, |
| GType type, |
| guint level) |
| { |
| guint i; |
| GType *children; |
| guint n_children; |
| |
| if (!type) |
| return; |
| |
| for (i = 0; i < level; i++) |
| fprintf (fp, " "); |
| fprintf (fp, "%s\\n", g_type_name (type)); |
| |
| children = g_type_children (type, &n_children); |
| qsort (children, n_children, sizeof (GType), compare_types); |
| |
| |
| for (i=0; i < n_children; i++) |
| output_hierarchy (fp, children[i], level + 1); |
| |
| g_free (children); |
| } |
| |
| static void output_object_interfaces (void) |
| { |
| guint i; |
| FILE *fp; |
| |
| fp = fopen (interfaces_filename, "w"); |
| if (fp == NULL) |
| { |
| g_warning ("Couldn't open output file: %s : %s", interfaces_filename, g_strerror(errno)); |
| return; |
| } |
| output_interfaces (fp, G_TYPE_OBJECT); |
| |
| for (i = 0; object_types[i]; i++) |
| { |
| if (!g_type_parent (object_types[i]) && |
| (object_types[i] != G_TYPE_OBJECT) && |
| G_TYPE_IS_INSTANTIATABLE (object_types[i])) |
| { |
| output_interfaces (fp, object_types[i]); |
| } |
| } |
| fclose (fp); |
| } |
| |
| static void |
| output_interfaces (FILE *fp, |
| GType type) |
| { |
| guint i; |
| GType *children, *interfaces; |
| guint n_children, n_interfaces; |
| |
| if (!type) |
| return; |
| |
| interfaces = g_type_interfaces (type, &n_interfaces); |
| |
| if (n_interfaces > 0) |
| { |
| fprintf (fp, "%s", g_type_name (type)); |
| for (i=0; i < n_interfaces; i++) |
| fprintf (fp, " %s", g_type_name (interfaces[i])); |
| fprintf (fp, "\\n"); |
| } |
| g_free (interfaces); |
| |
| children = g_type_children (type, &n_children); |
| |
| for (i=0; i < n_children; i++) |
| output_interfaces (fp, children[i]); |
| |
| g_free (children); |
| } |
| |
| static void output_interface_prerequisites (void) |
| { |
| FILE *fp; |
| |
| fp = fopen (prerequisites_filename, "w"); |
| if (fp == NULL) |
| { |
| g_warning ("Couldn't open output file: %s : %s", prerequisites_filename, g_strerror(errno)); |
| return; |
| } |
| output_prerequisites (fp, G_TYPE_INTERFACE); |
| fclose (fp); |
| } |
| |
| static void |
| output_prerequisites (FILE *fp, |
| GType type) |
| { |
| #if GLIB_CHECK_VERSION(2,1,0) |
| guint i; |
| GType *children, *prerequisites; |
| guint n_children, n_prerequisites; |
| |
| if (!type) |
| return; |
| |
| prerequisites = g_type_interface_prerequisites (type, &n_prerequisites); |
| |
| if (n_prerequisites > 0) |
| { |
| fprintf (fp, "%s", g_type_name (type)); |
| for (i=0; i < n_prerequisites; i++) |
| fprintf (fp, " %s", g_type_name (prerequisites[i])); |
| fprintf (fp, "\\n"); |
| } |
| g_free (prerequisites); |
| |
| children = g_type_children (type, &n_children); |
| |
| for (i=0; i < n_children; i++) |
| output_prerequisites (fp, children[i]); |
| |
| g_free (children); |
| #endif |
| } |
| |
| static void |
| output_args (void) |
| { |
| FILE *fp; |
| gint i; |
| |
| fp = fopen (args_filename, "w"); |
| if (fp == NULL) |
| { |
| g_warning ("Couldn't open output file: %s : %s", args_filename, g_strerror(errno)); |
| return; |
| } |
| |
| for (i = 0; object_types[i]; i++) { |
| output_object_args (fp, object_types[i]); |
| } |
| |
| fclose (fp); |
| } |
| |
| static gint |
| compare_param_specs (const void *a, const void *b) |
| { |
| GParamSpec *spec_a = *(GParamSpec **)a; |
| GParamSpec *spec_b = *(GParamSpec **)b; |
| |
| return strcmp (g_param_spec_get_name (spec_a), g_param_spec_get_name (spec_b)); |
| } |
| |
| /* Its common to have unsigned properties restricted |
| * to the signed range. Therefore we make this look |
| * a bit nicer by spelling out the max constants. |
| */ |
| |
| /* Don't use "==" with floats, it might trigger a gcc warning. */ |
| #define GTKDOC_COMPARE_FLOAT(x, y) (x <= y && x >= y) |
| |
| static gchar* |
| describe_double_constant (gdouble value) |
| { |
| gchar *desc; |
| |
| if (GTKDOC_COMPARE_FLOAT (value, G_MAXDOUBLE)) |
| desc = g_strdup ("G_MAXDOUBLE"); |
| else if (GTKDOC_COMPARE_FLOAT (value, G_MINDOUBLE)) |
| desc = g_strdup ("G_MINDOUBLE"); |
| else if (GTKDOC_COMPARE_FLOAT (value, -G_MAXDOUBLE)) |
| desc = g_strdup ("-G_MAXDOUBLE"); |
| else if (GTKDOC_COMPARE_FLOAT (value, G_MAXFLOAT)) |
| desc = g_strdup ("G_MAXFLOAT"); |
| else if (GTKDOC_COMPARE_FLOAT (value, G_MINFLOAT)) |
| desc = g_strdup ("G_MINFLOAT"); |
| else if (GTKDOC_COMPARE_FLOAT (value, -G_MAXFLOAT)) |
| desc = g_strdup ("-G_MAXFLOAT"); |
| else{ |
| /* make sure floats are output with a decimal dot irrespective of |
| * current locale. Use formatd since we want human-readable numbers |
| * and do not need the exact same bit representation when deserialising */ |
| desc = g_malloc0 (G_ASCII_DTOSTR_BUF_SIZE); |
| g_ascii_formatd (desc, G_ASCII_DTOSTR_BUF_SIZE, "%g", value); |
| } |
| |
| return desc; |
| } |
| |
| static gchar* |
| describe_signed_constant (gsize size, gint64 value) |
| { |
| gchar *desc = NULL; |
| |
| switch (size) { |
| case 8: |
| if (value == G_MAXINT64) |
| desc = g_strdup ("G_MAXINT64"); |
| else if (value == G_MININT64) |
| desc = g_strdup ("G_MININT64"); |
| /* fall through */ |
| case 4: |
| if (sizeof (int) == 4) { |
| if (value == G_MAXINT) |
| desc = g_strdup ("G_MAXINT"); |
| else if (value == G_MININT) |
| desc = g_strdup ("G_MININT"); |
| else if (value == (gint64)G_MAXUINT) |
| desc = g_strdup ("G_MAXUINT"); |
| } |
| if (value == G_MAXLONG) |
| desc = g_strdup ("G_MAXLONG"); |
| else if (value == G_MINLONG) |
| desc = g_strdup ("G_MINLONG"); |
| else if (value == (gint64)G_MAXULONG) |
| desc = g_strdup ("G_MAXULONG"); |
| /* fall through */ |
| case 2: |
| if (sizeof (int) == 2) { |
| if (value == G_MAXINT) |
| desc = g_strdup ("G_MAXINT"); |
| else if (value == G_MININT) |
| desc = g_strdup ("G_MININT"); |
| else if (value == (gint64)G_MAXUINT) |
| desc = g_strdup ("G_MAXUINT"); |
| } |
| break; |
| default: |
| break; |
| } |
| if (!desc) |
| desc = g_strdup_printf ("%" G_GINT64_FORMAT, value); |
| |
| return desc; |
| } |
| |
| static gchar* |
| describe_unsigned_constant (gsize size, guint64 value) |
| { |
| gchar *desc = NULL; |
| |
| switch (size) { |
| case 8: |
| if (value == G_MAXINT64) |
| desc = g_strdup ("G_MAXINT64"); |
| else if (value == G_MAXUINT64) |
| desc = g_strdup ("G_MAXUINT64"); |
| /* fall through */ |
| case 4: |
| if (sizeof (int) == 4) { |
| if (value == (guint64)G_MAXINT) |
| desc = g_strdup ("G_MAXINT"); |
| else if (value == G_MAXUINT) |
| desc = g_strdup ("G_MAXUINT"); |
| } |
| if (value == (guint64)G_MAXLONG) |
| desc = g_strdup ("G_MAXLONG"); |
| else if (value == G_MAXULONG) |
| desc = g_strdup ("G_MAXULONG"); |
| /* fall through */ |
| case 2: |
| if (sizeof (int) == 2) { |
| if (value == (guint64)G_MAXINT) |
| desc = g_strdup ("G_MAXINT"); |
| else if (value == G_MAXUINT) |
| desc = g_strdup ("G_MAXUINT"); |
| } |
| break; |
| default: |
| break; |
| } |
| if (!desc) |
| desc = g_strdup_printf ("%" G_GUINT64_FORMAT, value); |
| |
| return desc; |
| } |
| |
| static gchar* |
| describe_type (GParamSpec *spec) |
| { |
| gchar *desc; |
| gchar *lower; |
| gchar *upper; |
| |
| if (G_IS_PARAM_SPEC_CHAR (spec)) |
| { |
| GParamSpecChar *pspec = G_PARAM_SPEC_CHAR (spec); |
| |
| lower = describe_signed_constant (sizeof(gchar), pspec->minimum); |
| upper = describe_signed_constant (sizeof(gchar), pspec->maximum); |
| if (pspec->minimum == G_MININT8 && pspec->maximum == G_MAXINT8) |
| desc = g_strdup (""); |
| else if (pspec->minimum == G_MININT8) |
| desc = g_strdup_printf ("<= %s", upper); |
| else if (pspec->maximum == G_MAXINT8) |
| desc = g_strdup_printf (">= %s", lower); |
| else |
| desc = g_strdup_printf ("[%s,%s]", lower, upper); |
| g_free (lower); |
| g_free (upper); |
| } |
| else if (G_IS_PARAM_SPEC_UCHAR (spec)) |
| { |
| GParamSpecUChar *pspec = G_PARAM_SPEC_UCHAR (spec); |
| |
| lower = describe_unsigned_constant (sizeof(guchar), pspec->minimum); |
| upper = describe_unsigned_constant (sizeof(guchar), pspec->maximum); |
| if (pspec->minimum == 0 && pspec->maximum == G_MAXUINT8) |
| desc = g_strdup (""); |
| else if (pspec->minimum == 0) |
| desc = g_strdup_printf ("<= %s", upper); |
| else if (pspec->maximum == G_MAXUINT8) |
| desc = g_strdup_printf (">= %s", lower); |
| else |
| desc = g_strdup_printf ("[%s,%s]", lower, upper); |
| g_free (lower); |
| g_free (upper); |
| } |
| else if (G_IS_PARAM_SPEC_INT (spec)) |
| { |
| GParamSpecInt *pspec = G_PARAM_SPEC_INT (spec); |
| |
| lower = describe_signed_constant (sizeof(gint), pspec->minimum); |
| upper = describe_signed_constant (sizeof(gint), pspec->maximum); |
| if (pspec->minimum == G_MININT && pspec->maximum == G_MAXINT) |
| desc = g_strdup (""); |
| else if (pspec->minimum == G_MININT) |
| desc = g_strdup_printf ("<= %s", upper); |
| else if (pspec->maximum == G_MAXINT) |
| desc = g_strdup_printf (">= %s", lower); |
| else |
| desc = g_strdup_printf ("[%s,%s]", lower, upper); |
| g_free (lower); |
| g_free (upper); |
| } |
| else if (G_IS_PARAM_SPEC_UINT (spec)) |
| { |
| GParamSpecUInt *pspec = G_PARAM_SPEC_UINT (spec); |
| |
| lower = describe_unsigned_constant (sizeof(guint), pspec->minimum); |
| upper = describe_unsigned_constant (sizeof(guint), pspec->maximum); |
| if (pspec->minimum == 0 && pspec->maximum == G_MAXUINT) |
| desc = g_strdup (""); |
| else if (pspec->minimum == 0) |
| desc = g_strdup_printf ("<= %s", upper); |
| else if (pspec->maximum == G_MAXUINT) |
| desc = g_strdup_printf (">= %s", lower); |
| else |
| desc = g_strdup_printf ("[%s,%s]", lower, upper); |
| g_free (lower); |
| g_free (upper); |
| } |
| else if (G_IS_PARAM_SPEC_LONG (spec)) |
| { |
| GParamSpecLong *pspec = G_PARAM_SPEC_LONG (spec); |
| |
| lower = describe_signed_constant (sizeof(glong), pspec->minimum); |
| upper = describe_signed_constant (sizeof(glong), pspec->maximum); |
| if (pspec->minimum == G_MINLONG && pspec->maximum == G_MAXLONG) |
| desc = g_strdup (""); |
| else if (pspec->minimum == G_MINLONG) |
| desc = g_strdup_printf ("<= %s", upper); |
| else if (pspec->maximum == G_MAXLONG) |
| desc = g_strdup_printf (">= %s", lower); |
| else |
| desc = g_strdup_printf ("[%s,%s]", lower, upper); |
| g_free (lower); |
| g_free (upper); |
| } |
| else if (G_IS_PARAM_SPEC_ULONG (spec)) |
| { |
| GParamSpecULong *pspec = G_PARAM_SPEC_ULONG (spec); |
| |
| lower = describe_unsigned_constant (sizeof(gulong), pspec->minimum); |
| upper = describe_unsigned_constant (sizeof(gulong), pspec->maximum); |
| if (pspec->minimum == 0 && pspec->maximum == G_MAXULONG) |
| desc = g_strdup (""); |
| else if (pspec->minimum == 0) |
| desc = g_strdup_printf ("<= %s", upper); |
| else if (pspec->maximum == G_MAXULONG) |
| desc = g_strdup_printf (">= %s", lower); |
| else |
| desc = g_strdup_printf ("[%s,%s]", lower, upper); |
| g_free (lower); |
| g_free (upper); |
| } |
| else if (G_IS_PARAM_SPEC_INT64 (spec)) |
| { |
| GParamSpecInt64 *pspec = G_PARAM_SPEC_INT64 (spec); |
| |
| lower = describe_signed_constant (sizeof(gint64), pspec->minimum); |
| upper = describe_signed_constant (sizeof(gint64), pspec->maximum); |
| if (pspec->minimum == G_MININT64 && pspec->maximum == G_MAXINT64) |
| desc = g_strdup (""); |
| else if (pspec->minimum == G_MININT64) |
| desc = g_strdup_printf ("<= %s", upper); |
| else if (pspec->maximum == G_MAXINT64) |
| desc = g_strdup_printf (">= %s", lower); |
| else |
| desc = g_strdup_printf ("[%s,%s]", lower, upper); |
| g_free (lower); |
| g_free (upper); |
| } |
| else if (G_IS_PARAM_SPEC_UINT64 (spec)) |
| { |
| GParamSpecUInt64 *pspec = G_PARAM_SPEC_UINT64 (spec); |
| |
| lower = describe_unsigned_constant (sizeof(guint64), pspec->minimum); |
| upper = describe_unsigned_constant (sizeof(guint64), pspec->maximum); |
| if (pspec->minimum == 0 && pspec->maximum == G_MAXUINT64) |
| desc = g_strdup (""); |
| else if (pspec->minimum == 0) |
| desc = g_strdup_printf ("<= %s", upper); |
| else if (pspec->maximum == G_MAXUINT64) |
| desc = g_strdup_printf (">= %s", lower); |
| else |
| desc = g_strdup_printf ("[%s,%s]", lower, upper); |
| g_free (lower); |
| g_free (upper); |
| } |
| else if (G_IS_PARAM_SPEC_FLOAT (spec)) |
| { |
| GParamSpecFloat *pspec = G_PARAM_SPEC_FLOAT (spec); |
| |
| lower = describe_double_constant (pspec->minimum); |
| upper = describe_double_constant (pspec->maximum); |
| if (GTKDOC_COMPARE_FLOAT (pspec->minimum, -G_MAXFLOAT)) |
| { |
| if (GTKDOC_COMPARE_FLOAT (pspec->maximum, G_MAXFLOAT)) |
| desc = g_strdup (""); |
| else |
| desc = g_strdup_printf ("<= %s", upper); |
| } |
| else if (GTKDOC_COMPARE_FLOAT (pspec->maximum, G_MAXFLOAT)) |
| desc = g_strdup_printf (">= %s", lower); |
| else |
| desc = g_strdup_printf ("[%s,%s]", lower, upper); |
| g_free (lower); |
| g_free (upper); |
| } |
| else if (G_IS_PARAM_SPEC_DOUBLE (spec)) |
| { |
| GParamSpecDouble *pspec = G_PARAM_SPEC_DOUBLE (spec); |
| |
| lower = describe_double_constant (pspec->minimum); |
| upper = describe_double_constant (pspec->maximum); |
| if (GTKDOC_COMPARE_FLOAT (pspec->minimum, -G_MAXDOUBLE)) |
| { |
| if (GTKDOC_COMPARE_FLOAT (pspec->maximum, G_MAXDOUBLE)) |
| desc = g_strdup (""); |
| else |
| desc = g_strdup_printf ("<= %s", upper); |
| } |
| else if (GTKDOC_COMPARE_FLOAT (pspec->maximum, G_MAXDOUBLE)) |
| desc = g_strdup_printf (">= %s", lower); |
| else |
| desc = g_strdup_printf ("[%s,%s]", lower, upper); |
| g_free (lower); |
| g_free (upper); |
| } |
| #if GLIB_CHECK_VERSION (2, 12, 0) |
| else if (G_IS_PARAM_SPEC_GTYPE (spec)) |
| { |
| GParamSpecGType *pspec = G_PARAM_SPEC_GTYPE (spec); |
| gboolean is_pointer; |
| |
| desc = g_strdup (get_type_name (pspec->is_a_type, &is_pointer)); |
| } |
| #endif |
| #if GLIB_CHECK_VERSION (2, 25, 9) |
| else if (G_IS_PARAM_SPEC_VARIANT (spec)) |
| { |
| GParamSpecVariant *pspec = G_PARAM_SPEC_VARIANT (spec); |
| gchar *variant_type; |
| |
| variant_type = g_variant_type_dup_string (pspec->type); |
| desc = g_strdup_printf ("GVariant<%s>", variant_type); |
| g_free (variant_type); |
| } |
| #endif |
| else |
| { |
| desc = g_strdup (""); |
| } |
| |
| return desc; |
| } |
| |
| static gchar* |
| describe_default (GParamSpec *spec) |
| { |
| gchar *desc; |
| |
| if (G_IS_PARAM_SPEC_CHAR (spec)) |
| { |
| GParamSpecChar *pspec = G_PARAM_SPEC_CHAR (spec); |
| |
| desc = g_strdup_printf ("%d", pspec->default_value); |
| } |
| else if (G_IS_PARAM_SPEC_UCHAR (spec)) |
| { |
| GParamSpecUChar *pspec = G_PARAM_SPEC_UCHAR (spec); |
| |
| desc = g_strdup_printf ("%u", pspec->default_value); |
| } |
| else if (G_IS_PARAM_SPEC_BOOLEAN (spec)) |
| { |
| GParamSpecBoolean *pspec = G_PARAM_SPEC_BOOLEAN (spec); |
| |
| desc = g_strdup_printf ("%s", pspec->default_value ? "TRUE" : "FALSE"); |
| } |
| else if (G_IS_PARAM_SPEC_INT (spec)) |
| { |
| GParamSpecInt *pspec = G_PARAM_SPEC_INT (spec); |
| |
| desc = g_strdup_printf ("%d", pspec->default_value); |
| } |
| else if (G_IS_PARAM_SPEC_UINT (spec)) |
| { |
| GParamSpecUInt *pspec = G_PARAM_SPEC_UINT (spec); |
| |
| desc = g_strdup_printf ("%u", pspec->default_value); |
| } |
| else if (G_IS_PARAM_SPEC_LONG (spec)) |
| { |
| GParamSpecLong *pspec = G_PARAM_SPEC_LONG (spec); |
| |
| desc = g_strdup_printf ("%ld", pspec->default_value); |
| } |
| else if (G_IS_PARAM_SPEC_LONG (spec)) |
| { |
| GParamSpecULong *pspec = G_PARAM_SPEC_ULONG (spec); |
| |
| desc = g_strdup_printf ("%lu", pspec->default_value); |
| } |
| else if (G_IS_PARAM_SPEC_INT64 (spec)) |
| { |
| GParamSpecInt64 *pspec = G_PARAM_SPEC_INT64 (spec); |
| |
| desc = g_strdup_printf ("%" G_GINT64_FORMAT, pspec->default_value); |
| } |
| else if (G_IS_PARAM_SPEC_UINT64 (spec)) |
| { |
| GParamSpecUInt64 *pspec = G_PARAM_SPEC_UINT64 (spec); |
| |
| desc = g_strdup_printf ("%" G_GUINT64_FORMAT, pspec->default_value); |
| } |
| else if (G_IS_PARAM_SPEC_UNICHAR (spec)) |
| { |
| GParamSpecUnichar *pspec = G_PARAM_SPEC_UNICHAR (spec); |
| |
| if (g_unichar_isprint (pspec->default_value)) |
| desc = g_strdup_printf ("'%c'", pspec->default_value); |
| else |
| desc = g_strdup_printf ("%u", pspec->default_value); |
| } |
| else if (G_IS_PARAM_SPEC_ENUM (spec)) |
| { |
| GParamSpecEnum *pspec = G_PARAM_SPEC_ENUM (spec); |
| |
| GEnumValue *value = g_enum_get_value (pspec->enum_class, pspec->default_value); |
| if (value) |
| desc = g_strdup_printf ("%s", value->value_name); |
| else |
| desc = g_strdup_printf ("%d", pspec->default_value); |
| } |
| else if (G_IS_PARAM_SPEC_FLAGS (spec)) |
| { |
| GParamSpecFlags *pspec = G_PARAM_SPEC_FLAGS (spec); |
| guint default_value; |
| GString *acc; |
| |
| default_value = pspec->default_value; |
| acc = g_string_new (""); |
| |
| while (default_value) |
| { |
| GFlagsValue *value = g_flags_get_first_value (pspec->flags_class, default_value); |
| |
| if (!value) |
| break; |
| |
| if (acc->len > 0) |
| g_string_append (acc, "|"); |
| g_string_append (acc, value->value_name); |
| |
| default_value &= ~value->value; |
| } |
| |
| if (default_value == 0) |
| desc = g_string_free (acc, FALSE); |
| else |
| { |
| desc = g_strdup_printf ("%d", pspec->default_value); |
| g_string_free (acc, TRUE); |
| } |
| } |
| else if (G_IS_PARAM_SPEC_FLOAT (spec)) |
| { |
| GParamSpecFloat *pspec = G_PARAM_SPEC_FLOAT (spec); |
| |
| /* make sure floats are output with a decimal dot irrespective of |
| * current locale. Use formatd since we want human-readable numbers |
| * and do not need the exact same bit representation when deserialising */ |
| desc = g_malloc0 (G_ASCII_DTOSTR_BUF_SIZE); |
| g_ascii_formatd (desc, G_ASCII_DTOSTR_BUF_SIZE, "%g", |
| pspec->default_value); |
| } |
| else if (G_IS_PARAM_SPEC_DOUBLE (spec)) |
| { |
| GParamSpecDouble *pspec = G_PARAM_SPEC_DOUBLE (spec); |
| |
| /* make sure floats are output with a decimal dot irrespective of |
| * current locale. Use formatd since we want human-readable numbers |
| * and do not need the exact same bit representation when deserialising */ |
| desc = g_malloc0 (G_ASCII_DTOSTR_BUF_SIZE); |
| g_ascii_formatd (desc, G_ASCII_DTOSTR_BUF_SIZE, "%g", |
| pspec->default_value); |
| } |
| else if (G_IS_PARAM_SPEC_STRING (spec)) |
| { |
| GParamSpecString *pspec = G_PARAM_SPEC_STRING (spec); |
| |
| if (pspec->default_value) |
| { |
| gchar *esc = g_strescape (pspec->default_value, NULL); |
| |
| desc = g_strdup_printf ("\\"%s\\"", esc); |
| |
| g_free (esc); |
| } |
| else |
| desc = g_strdup_printf ("NULL"); |
| } |
| else |
| { |
| desc = g_strdup (""); |
| } |
| |
| return desc; |
| } |
| |
| |
| static void |
| output_object_args (FILE *fp, GType object_type) |
| { |
| gpointer class; |
| const gchar *object_class_name; |
| guint arg; |
| gchar flags[16], *pos; |
| GParamSpec **properties; |
| guint n_properties; |
| gboolean child_prop; |
| gboolean style_prop; |
| gboolean is_pointer; |
| const gchar *type_name; |
| gchar *type_desc; |
| gchar *default_value; |
| |
| if (G_TYPE_IS_OBJECT (object_type)) |
| { |
| class = g_type_class_peek (object_type); |
| if (!class) |
| return; |
| |
| properties = g_object_class_list_properties (class, &n_properties); |
| } |
| #if GLIB_MAJOR_VERSION > 2 || (GLIB_MAJOR_VERSION == 2 && GLIB_MINOR_VERSION >= 3) |
| else if (G_TYPE_IS_INTERFACE (object_type)) |
| { |
| class = g_type_default_interface_ref (object_type); |
| |
| if (!class) |
| return; |
| |
| properties = g_object_interface_list_properties (class, &n_properties); |
| } |
| #endif |
| else |
| return; |
| |
| object_class_name = g_type_name (object_type); |
| |
| child_prop = FALSE; |
| style_prop = FALSE; |
| |
| while (TRUE) { |
| qsort (properties, n_properties, sizeof (GParamSpec *), compare_param_specs); |
| for (arg = 0; arg < n_properties; arg++) |
| { |
| GParamSpec *spec = properties[arg]; |
| const gchar *nick, *blurb, *dot; |
| |
| if (spec->owner_type != object_type) |
| continue; |
| |
| pos = flags; |
| /* We use one-character flags for simplicity. */ |
| if (child_prop && !style_prop) |
| *pos++ = 'c'; |
| if (style_prop) |
| *pos++ = 's'; |
| if (spec->flags & G_PARAM_READABLE) |
| *pos++ = 'r'; |
| if (spec->flags & G_PARAM_WRITABLE) |
| *pos++ = 'w'; |
| if (spec->flags & G_PARAM_CONSTRUCT) |
| *pos++ = 'x'; |
| if (spec->flags & G_PARAM_CONSTRUCT_ONLY) |
| *pos++ = 'X'; |
| *pos = 0; |
| |
| nick = g_param_spec_get_nick (spec); |
| blurb = g_param_spec_get_blurb (spec); |
| |
| dot = ""; |
| if (blurb) { |
| int str_len = strlen (blurb); |
| if (str_len > 0 && blurb[str_len - 1] != '.') |
| dot = "."; |
| } |
| |
| type_desc = describe_type (spec); |
| default_value = describe_default (spec); |
| type_name = get_type_name (spec->value_type, &is_pointer); |
| fprintf (fp, "<ARG>\\n<NAME>%s::%s</NAME>\\n<TYPE>%s%s</TYPE>\\n<RANGE>%s</RANGE>\\n<FLAGS>%s</FLAGS>\\n<NICK>%s</NICK>\\n<BLURB>%s%s</BLURB>\\n<DEFAULT>%s</DEFAULT>\\n</ARG>\\n\\n", |
| object_class_name, g_param_spec_get_name (spec), type_name, is_pointer ? "*" : "", type_desc, flags, nick ? nick : "(null)", blurb ? blurb : "(null)", dot, default_value); |
| g_free (type_desc); |
| g_free (default_value); |
| } |
| |
| g_free (properties); |
| |
| #ifdef GTK_IS_CONTAINER_CLASS |
| if (!child_prop && GTK_IS_CONTAINER_CLASS (class)) { |
| properties = gtk_container_class_list_child_properties (class, &n_properties); |
| child_prop = TRUE; |
| continue; |
| } |
| #endif |
| |
| #ifdef GTK_IS_CELL_AREA_CLASS |
| if (!child_prop && GTK_IS_CELL_AREA_CLASS (class)) { |
| properties = gtk_cell_area_class_list_cell_properties (class, &n_properties); |
| child_prop = TRUE; |
| continue; |
| } |
| #endif |
| |
| #ifdef GTK_IS_WIDGET_CLASS |
| #if GTK_CHECK_VERSION(2,1,0) |
| if (!style_prop && GTK_IS_WIDGET_CLASS (class)) { |
| properties = gtk_widget_class_list_style_properties (GTK_WIDGET_CLASS (class), &n_properties); |
| style_prop = TRUE; |
| continue; |
| } |
| #endif |
| #endif |
| |
| break; |
| } |
| } |
| |
| static void |
| output_sections (void) |
| { |
| FILE *fp; |
| gint i; |
| |
| fp = fopen (sections_filename, "w"); |
| if (fp == NULL) |
| { |
| g_warning ("Couldn't open output file: %s : %s", sections_filename, g_strerror(errno)); |
| return; |
| } |
| |
| for (i = 0; object_types[i]; i++) { } |
| qsort (object_types, i, sizeof (GType), compare_types); |
| |
| for (i = 0; object_types[i]; i++) { |
| output_object_section (fp, object_types[i]); |
| } |
| |
| fclose (fp); |
| } |
| |
| static gboolean |
| find_by_type (GstPluginFeature *f, gpointer data) { |
| return (GST_IS_ELEMENT_FACTORY(f) && |
| ((GType)data == gst_element_factory_get_element_type (GST_ELEMENT_FACTORY(f)))); |
| } |
| |
| static void |
| output_object_section (FILE *fp, GType object_type) |
| { |
| /* e.g. GstFakeSink */ |
| const gchar *tn = g_type_name (object_type); |
| const gchar *cct = &tn[3]; /* cut 'Gst' */ |
| gchar *title, *lct, *uct; |
| gint i, j, l = strlen(cct); |
| gpointer class; |
| GParamSpec **properties; |
| guint n_properties; |
| const gchar *ptn; |
| gchar *ptns; |
| GString *strbuf = g_string_new (NULL); |
| GList *fl; |
| GstPluginFeature *f = NULL; |
| gboolean need_unserscore = TRUE, have_abbrev = FALSE; |
| |
| fl = gst_registry_feature_filter (gst_registry_get(), find_by_type, TRUE, |
| (gpointer)object_type); |
| if (fl) { |
| f = fl->data; |
| g_list_free(fl); |
| } |
| if (f) { |
| title = g_strdup (gst_plugin_feature_get_name(f)); |
| g_object_unref (f); |
| } else { |
| title = g_ascii_strdown(cct, -1); |
| } |
| |
| /* turn CamelCase into '_' separated all lower, resulting string is atmost |
| * twice as long, special casing for abbevs like GstTCPClientSink */ |
| lct = g_malloc(2*l); |
| for (i = 0, j = 0; i < l; i++) { |
| if (g_ascii_isupper (cct[i])) { |
| if (need_unserscore) { |
| if (i > 0) { |
| lct[j++] = '_'; |
| } |
| } else { |
| have_abbrev = TRUE; |
| } |
| lct[j++] = g_ascii_tolower(cct[i]); |
| need_unserscore = FALSE; |
| } else { |
| if (have_abbrev) { |
| lct[j] = lct[j-1]; |
| lct[j-1] = '_'; |
| j++; |
| have_abbrev = FALSE; |
| } |
| lct[j++] = cct[i]; |
| need_unserscore = TRUE; |
| } |
| } |
| lct[j] = '\\0'; |
| uct = g_ascii_strup(lct, -1); |
| |
| /* scan properties and find local enums */ |
| class = g_type_class_peek (object_type); |
| properties = g_object_class_list_properties (class, &n_properties); |
| qsort (properties, n_properties, sizeof (GParamSpec *), compare_param_specs); |
| for (i = 0; i < n_properties; i++) { |
| GParamSpec *spec = properties[i]; |
| if (!(G_IS_PARAM_SPEC_ENUM (spec) || G_IS_PARAM_SPEC_FLAGS (spec))) { |
| continue; |
| } |
| ptn = g_type_name(spec->value_type); |
| // does it start with tn? |
| if (strncmp(tn, ptn, strlen(tn))) { |
| continue; |
| } |
| g_string_append_c(strbuf, '\\n'); |
| g_string_append(strbuf, ptn); |
| } |
| ptns = g_string_free (strbuf, FALSE); |
| |
| /* later we can remove the SUBSECTION Standart/Private, since we only need to |
| * highlight what is public API */ |
| fprintf (fp, "<SECTION>\\n" |
| "<FILE>element-%s</FILE>\\n" |
| "<TITLE>%s</TITLE>\\n" |
| "Gst%s%s\\n" |
| "<SUBSECTION Standard>\\n" |
| "Gst%sClass\\n" |
| "GST_%s\\n" |
| "GST_%s_CAST\\n" |
| "GST_IS_%s\\n" |
| "GST_%s_CLASS\\n" |
| "GST_IS_%s_CLASS\\n" |
| "GST_TYPE_%s\\n" |
| "<SUBSECTION Private>\\n" |
| "gst_%s_get_type\\n" |
| "</SECTION>\\n\\n", |
| title, title, cct, ptns, |
| cct, uct, uct, uct, uct, uct, uct, lct); |
| g_free (title); |
| g_free (lct); |
| g_free (uct); |
| g_free (ptns); |
| } |
| |
| EOT |
| |
| close OUTPUT; |
| |
| # Compile and run our file |
| |
| $CC = $ENV{CC} ? $ENV{CC} : "gcc"; |
| $LD = $ENV{LD} ? $ENV{LD} : $CC; |
| $CFLAGS = $ENV{CFLAGS} ? "$ENV{CFLAGS}" : ""; |
| $LDFLAGS = $ENV{LDFLAGS} ? $ENV{LDFLAGS} : ""; |
| |
| my $o_file; |
| if ($CC =~ /libtool/) { |
| $o_file = "$MODULE-scan.lo" |
| } else { |
| $o_file = "$MODULE-scan.o" |
| } |
| |
| my $stdout=""; |
| if (!defined($VERBOSE) or $VERBOSE eq "0") { |
| $stdout=">/dev/null"; |
| } |
| |
| # Compiling scanner |
| $command = "$CC $stdout $CFLAGS -c -o $o_file $MODULE-scan.c"; |
| system("($command)") == 0 or die "Compilation of scanner failed: $!\n"; |
| |
| # Linking scanner |
| $command = "$LD $stdout -o $MODULE-scan $o_file $LDFLAGS"; |
| system($command) == 0 or die "Linking of scanner failed: $!\n"; |
| |
| # Running scanner $MODULE-scan "; |
| system("sh -c ./$MODULE-scan") == 0 or die "Scan failed: $!\n"; |
| |
| if (!defined($ENV{"GTK_DOC_KEEP_INTERMEDIATE"})) { |
| unlink "./$MODULE-scan.c", "./$MODULE-scan.o", "./$MODULE-scan.lo", "./$MODULE-scan"; |
| } |
| |
| # Copied from gtk-doc 1db161bd708cdfb88b362ea0b5d047034d9c3272 |
| ############################################################################# |
| # Function : UpdateFileIfChanged |
| # Description : Compares the old version of the file with the new version and |
| # if the file has changed it moves the new version into the old |
| # versions place. This is used so we only change files if |
| # needed, so we can do proper dependency tracking and we don't |
| # needlessly check files into version control systems that haven't |
| # changed. |
| # It returns 0 if the file hasn't changed, and 1 if it has. |
| # Arguments : $old_file - the pathname of the old file. |
| # $new_file - the pathname of the new version of the file. |
| # $make_backup - 1 if a backup of the old file should be kept. |
| # It will have the .bak suffix added to the file name. |
| ############################################################################# |
| |
| sub UpdateFileIfChanged { |
| my ($old_file, $new_file, $make_backup) = @_; |
| |
| #@TRACE@("Comparing $old_file with $new_file..."); |
| |
| # If the old file doesn't exist we want this to default to 1. |
| my $exit_code = 1; |
| |
| if (-e $old_file) { |
| `cmp -s "$old_file" "$new_file"`; |
| $exit_code = $? >> 8; |
| #@TRACE@(" cmp exit code: $exit_code ($?)"); |
| } |
| |
| if ($exit_code > 1) { |
| die "Error running 'cmp $old_file $new_file'"; |
| } |
| |
| if ($exit_code == 1) { |
| #@TRACE@(" files changed - replacing old version with new version."); |
| if ($make_backup && -e $old_file) { |
| rename ($old_file, "$old_file.bak") |
| || die "Can't move $old_file to $old_file.bak: $!"; |
| } |
| rename ($new_file, $old_file) |
| || die "Can't move $new_file to $old_file: $!"; |
| |
| return 1; |
| } else { |
| #@TRACE@(" files the same - deleting new version."); |
| unlink ("$new_file") |
| || die "Can't delete file: $new_file: $!"; |
| |
| return 0; |
| } |
| } |
| |
| &UpdateFileIfChanged ($old_hierarchy_filename, $new_hierarchy_filename, 0); |
| # we will merge these in scangobj-merge.py |
| #&UpdateFileIfChanged ($old_interfaces_filename, $new_interfaces_filename, 0); |
| #&UpdateFileIfChanged ($old_prerequisites_filename, $new_prerequisites_filename, 0); |
| #&UpdateFileIfChanged ($old_signals_filename, $new_signals_filename, 0); |
| #&UpdateFileIfChanged ($old_args_filename, $new_args_filename, 0); |
| #&UpdateFileIfChanged ($old_sections_filename, $new_sections_filename, 0); |