#!/usr/bin/perl -w # # 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. # # # This gets information about object heirarchies and signals # by compiling a small C program. CFLAGS and LDFLAGS must be # set appropriately before running this script. # # NOTE: the lookup_signal_srg_names() function contains the argument names of # standard GTK signal handlers. This may need to be updated for new # GTK signals or Gnome widget signals. use Getopt::Long; # Options # name of documentation module my $MODULE; my $OUTPUT_DIR; %optctl = (module => \$MODULE, types => \$TYPES_FILE, nogtkinit => \$NO_GTK_INIT); GetOptions(\%optctl, "module=s", "types:s", "nogtkinit"); $TYPES_FILE = $TYPES_FILE ? $TYPES_FILE : "$MODULE.types"; open TYPES, $TYPES_FILE || die "Cannot open $TYPES_FILE: $!\n"; open OUTPUT, ">$MODULE-scan.c" || die "Cannot open $MODULE-scan.c: $!\n"; # write a C program to scan the types $includes = ""; @types = (); for () { if (/^#include/) { $includes .= $_; } elsif (/^%/) { next; } elsif (/^\s*$/) { next; } else { chomp; push @types, $_; } } $ntypes = @types + 1; print OUTPUT < #include $includes GtkType object_types[$ntypes]; GstElement *elements[$ntypes]; gint _typenr = 0; gboolean get_plugins () { gint i = 0; EOT for (@types) { print OUTPUT " gst_plugin_load(\"$_\");\n"; print OUTPUT " elements[i] = gst_elementfactory_make(\"$_\", \"dummy\");\n"; print OUTPUT " object_types[i] = GTK_OBJECT_TYPE(GTK_OBJECT(elements[i]));\n"; print OUTPUT " i++; \n"; } print OUTPUT " object_types[i] = 0;\n"; print OUTPUT <nsignals == 0) return; object_class_name = gtk_type_name (object_type); for (sig = 0; sig < class->nsignals; sig++) { if (!class->signals[sig]) { /*g_print ("Signal slot [%u] is empty\n", sig);*/ continue; } output_widget_signal (fp, object_type, object_class_name, class->signals[sig]); } } /* This outputs one signal. */ static void output_widget_signal (FILE *fp, GtkType object_type, gchar *object_name, guint signal_id) { GtkSignalQuery *query_info; gchar *ret_type, *pos, *type_name, *arg_name, *object_arg, *object_arg_start; gboolean is_pointer; gchar ret_type_buffer[1024], buffer[1024]; gint i, param; gchar **arg_names; gint param_num, widget_num, event_num, callback_num; gint *arg_num; gchar signal_name[128]; /* g_print ("Object: %s Type: %i Signal: %u\n", object_name, object_type, signal_id);*/ param_num = 1; widget_num = event_num = callback_num = 0; query_info = gtk_signal_query (signal_id); if (query_info == NULL) { g_warning ("Couldn't query signal"); return; } /* Output the return type and function name. */ ret_type = get_type_name (query_info->return_val, &is_pointer); sprintf (ret_type_buffer, "%s%s", ret_type, is_pointer ? "*" : ""); /* 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); 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_start = pos; sprintf (pos, "*%s\n", object_arg); pos += strlen (pos); g_strdown (object_arg_start); if (!strcmp (object_arg_start, "widget")) widget_num++; /* Convert signal name to use underscores rather than dashes '-'. */ strcpy (signal_name, query_info->signal_name); for (i = 0; signal_name[i]; i++) { if (signal_name[i] == '-') signal_name[i] = '_'; } /* Output the signal parameters. */ arg_names = lookup_signal_arg_names (object_name, signal_name); for (param = 0; param < query_info->nparams; param++) { if (arg_names) { sprintf (pos, "%s\n", arg_names[param]); pos += strlen (pos); } else { type_name = get_type_name (query_info->params[param], &is_pointer); /* Most arguments to the callback are called "arg1", "arg2", etc. GdkWidgets are called "widget", "widget2", ... GdkEvents are called "event", "event2", ... GtkCallbacks are called "callback", "callback2", ... */ if (!strcmp (type_name, "GtkWidget")) { arg_name = "widget"; arg_num = &widget_num; } else if (!strcmp (type_name, "GdkEvent")) { type_name = get_gdk_event (signal_name); arg_name = "event"; arg_num = &event_num; is_pointer = TRUE; } 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) *arg_num += 1; } } fprintf (fp, "\n%s::%s\n%s\n%s\n\n", object_name, query_info->signal_name, ret_type_buffer, buffer); g_free (query_info); } static gchar * get_type_name (GtkType type, gboolean * is_pointer) { static gchar *GbTypeNames[] = { "char", "gchar", "bool", "gboolean", "int", "gint", "uint", "guint", "long", "glong", "ulong", "gulong", "float", "gfloat", "double", "gdouble", "string", "gchar", "enum", "gint", "flags", "gint", "boxed", "gpointer", "foreign", "gpointer", "callback", "GtkCallback", /* ?? */ "args", "gpointer", "pointer", "gpointer", "signal", "gpointer", "c_callback", "GtkCallback", /* ?? */ NULL }; GtkType parent_type; gchar *type_name, *parent_type_name; gint i; *is_pointer = FALSE; type_name = gtk_type_name (type); for (i = 0; GbTypeNames[i]; i += 2) { if (!strcmp (type_name, GbTypeNames[i])) { if (!strcmp (type_name, "string")) *is_pointer = TRUE; return GbTypeNames[i + 1]; } } for (;;) { parent_type = gtk_type_parent (type); if (parent_type == 0) break; type = parent_type; } parent_type_name = gtk_type_name (type); /*g_print ("Parent type name: %s\n", parent_type_name);*/ if (!strcmp (parent_type_name, "GtkObject") || !strcmp (parent_type_name, "boxed") || !strcmp (parent_type_name, "GtkBoxed")) *is_pointer = TRUE; return type_name; } static gchar * get_gdk_event (const gchar * signal_name) { static gchar *GbGDKEvents[] = { "button_press_event", "GdkEventButton", "button_release_event", "GdkEventButton", "motion_notify_event", "GdkEventMotion", "delete_event", "GdkEvent", "destroy_event", "GdkEvent", "expose_event", "GdkEventExpose", "key_press_event", "GdkEventKey", "key_release_event", "GdkEventKey", "enter_notify_event", "GdkEventCrossing", "leave_notify_event", "GdkEventCrossing", "configure_event", "GdkEventConfigure", "focus_in_event", "GdkEventFocus", "focus_out_event", "GdkEventFocus", "map_event", "GdkEvent", "unmap_event", "GdkEvent", "property_notify_event", "GdkEventProperty", "selection_clear_event", "GdkEventSelection", "selection_request_event", "GdkEventSelection", "selection_notify_event", "GdkEventSelection", "proximity_in_event", "GdkEventProximity", "proximity_out_event", "GdkEventProximity", "drag_begin_event", "GdkEventDragBegin", "drag_request_event", "GdkEventDragRequest", "drag_end_event", "GdkEventDragRequest", "drop_enter_event", "GdkEventDropEnter", "drop_leave_event", "GdkEventDropLeave", "drop_data_available_event", "GdkEventDropDataAvailable", "other_event", "GdkEventOther", "client_event", "GdkEventClient", "no_expose_event", "GdkEventNoExpose", NULL }; gint i; for (i = 0; GbGDKEvents[i]; i += 2) { if (!strcmp (signal_name, GbGDKEvents[i])) return GbGDKEvents[i + 1]; } return "GdkEvent"; } /* This returns argument names to use for some known GTK signals. It is passed a widget name, e.g. 'GtkCList' and a signal name, e.g. 'select_row' and it returns a pointer to an array of argument types and names. */ static gchar ** lookup_signal_arg_names (gchar * type, const gchar * signal_name) { /* Each arg array starts with the object type name and the signal name, and then signal arguments follow. */ static gchar *GbArgTable[][16] = { {"GtkCList", "select_row", "gint row", "gint column", "GdkEventButton *event"}, {"GtkCList", "unselect_row", "gint row", "gint column", "GdkEventButton *event"}, {"GtkCList", "click_column", "gint column"}, {"GtkCList", "resize_column", "gint column", "gint width"}, {"GtkCList", "extend_selection", "GtkScrollType scroll_type", "gfloat position", "gboolean auto_start_selection"}, {"GtkCList", "scroll_vertical", "GtkScrollType scroll_type", "gfloat position"}, {"GtkCList", "scroll_horizontal", "GtkScrollType scroll_type", "gfloat position"}, {"GtkContainer", "focus", "GtkDirectionType direction"}, {"GtkCTree", "tree_select_row", "GList *node", "gint column"}, {"GtkCTree", "tree_unselect_row", "GList *node", "gint column"}, {"GtkCTree", "tree_expand", "GList *node"}, {"GtkCTree", "tree_collapse", "GList *node"}, {"GtkCTree", "tree_move", "GList *node", "GList *new_parent", "GList *new_sibling"}, {"GtkCTree", "change_focus_row_expansion", "GtkCTreeExpansionType expansion"}, {"GtkEditable", "insert_text", "gchar *new_text", "gint new_text_length", "gint *position"}, {"GtkEditable", "delete_text", "gint start_pos", "gint end_pos"}, {"GtkEditable", "set_editable", "gboolean is_editable"}, {"GtkEditable", "move_cursor", "gint x", "gint y"}, {"GtkEditable", "move_word", "gint num_words"}, {"GtkEditable", "move_page", "gint x", "gint y"}, {"GtkEditable", "move_to_row", "gint row"}, {"GtkEditable", "move_to_column", "gint column"}, {"GtkEditable", "kill_char", "gint direction"}, {"GtkEditable", "kill_word", "gint direction"}, {"GtkEditable", "kill_line", "gint direction"}, {"GtkInputDialog", "enable_device", "gint deviceid"}, {"GtkInputDialog", "disable_device", "gint deviceid"}, {"GtkListItem", "extend_selection", "GtkScrollType scroll_type", "gfloat position", "gboolean auto_start_selection"}, {"GtkListItem", "scroll_vertical", "GtkScrollType scroll_type", "gfloat position"}, {"GtkListItem", "scroll_horizontal", "GtkScrollType scroll_type", "gfloat position"}, {"GtkMenuShell", "move_current", "GtkMenuDirectionType direction"}, {"GtkMenuShell", "activate_current", "gboolean force_hide"}, {"GtkNotebook", "switch_page", "GtkNotebookPage *page", "gint page_num"}, {"GtkStatusbar", "text_pushed", "guint context_id", "gchar *text"}, {"GtkStatusbar", "text_popped", "guint context_id", "gchar *text"}, {"GtkTipsQuery", "widget_entered", "GtkWidget *widget", "gchar *tip_text", "gchar *tip_private"}, {"GtkTipsQuery", "widget_selected", "GtkWidget *widget", "gchar *tip_text", "gchar *tip_private", "GdkEventButton *event"}, {"GtkToolbar", "orientation_changed", "GtkOrientation orientation"}, {"GtkToolbar", "style_changed", "GtkToolbarStyle style"}, {"GtkWidget", "draw", "GdkRectangle *area"}, {"GtkWidget", "size_request", "GtkRequisition *requisition"}, {"GtkWidget", "size_allocate", "GtkAllocation *allocation"}, {"GtkWidget", "state_changed", "GtkStateType state"}, {"GtkWidget", "style_set", "GtkStyle *previous_style"}, {"GtkWidget", "install_accelerator", "gchar *signal_name", "gchar key", "gint modifiers"}, {"GtkWidget", "add_accelerator", "guint accel_signal_id", "GtkAccelGroup *accel_group", "guint accel_key", "GdkModifierType accel_mods", "GtkAccelFlags accel_flags"}, {"GtkWidget", "parent_set", "GtkObject *old_parent"}, {"GtkWidget", "remove_accelerator", "GtkAccelGroup *accel_group", "guint accel_key", "GdkModifierType accel_mods"}, {"GtkWidget", "debug_msg", "gchar *message"}, {"GtkWindow", "move_resize", "gint *x", "gint *y", "gint width", "gint height"}, {"GtkWindow", "set_focus", "GtkWidget *widget"}, {"GtkWidget", "selection_get", "GtkSelectionData *data", "guint info", "guint time"}, {"GtkWidget", "selection_received", "GtkSelectionData *data", "guint time"}, {"GtkWidget", "drag_begin", "GdkDragContext *drag_context"}, {"GtkWidget", "drag_end", "GdkDragContext *drag_context"}, {"GtkWidget", "drag_data_delete", "GdkDragContext *drag_context"}, {"GtkWidget", "drag_leave", "GdkDragContext *drag_context", "guint time"}, {"GtkWidget", "drag_motion", "GdkDragContext *drag_context", "gint x", "gint y", "guint time"}, {"GtkWidget", "drag_drop", "GdkDragContext *drag_context", "gint x", "gint y", "guint time"}, {"GtkWidget", "drag_data_get", "GdkDragContext *drag_context", "GtkSelectionData *data", "guint info", "guint time"}, {"GtkWidget", "drag_data_received", "GdkDragContext *drag_context", "gint x", "gint y", "GtkSelectionData *data", "guint info", "guint time"}, {NULL} }; gint i; for (i = 0; GbArgTable[i][0]; i++) { if (!strcmp (type, GbArgTable[i][0]) && !strcmp (signal_name, GbArgTable[i][1])) return &GbArgTable[i][2]; } return NULL; } /* This outputs the hierarchy of all widgets which have been initialized, i.e. by calling their XXX_get_type() initialization function. */ static void output_widget_hierarchy () { FILE *fp; fp = fopen (hierarchy_filename, "w"); if (fp == NULL) { g_warning ("Couldn't open output file: %s", hierarchy_filename); return; } output_hierarchy (fp, GTK_TYPE_OBJECT, 0); fclose (fp); } /* This is called recursively to output the hierarchy of a widget. */ static void output_hierarchy (FILE *fp, GtkType type, gint level) { GList *list; guint i; if (!type) return; for (i = 0; i < level; i++) fprintf (fp, " "); fprintf (fp, gtk_type_name (type)); fprintf (fp, "\n"); list = gtk_type_children_types (type); while (list) { GtkType child = (GtkType) list->data; output_hierarchy (fp, child, level + 1); list = list->next; } } static void output_args () { FILE *fp; gint i; fp = fopen (args_filename, "w"); if (fp == NULL) { g_warning ("Couldn't open output file: %s", args_filename); return; } for (i = 0; object_types[i]; i++) output_widget_args (fp, object_types[i]); fclose (fp); } static void output_widget_args (FILE *fp, GtkType object_type) { GtkObjectClass *class; gchar *object_class_name; GtkArg *args; guint32 *arg_flags; guint n_args; gint arg; gchar flags[16], *pos; class = gtk_type_class (object_type); if (!class) return; object_class_name = gtk_type_name (object_type); args = gtk_object_query_args (class->type, &arg_flags, &n_args); for (arg = 0; arg < n_args; arg++) { pos = flags; /* We use one-character flags for simplicity. */ if (arg_flags[arg] & GTK_ARG_READABLE) *pos++ = 'r'; if (arg_flags[arg] & GTK_ARG_WRITABLE) *pos++ = 'w'; if (arg_flags[arg] & GTK_ARG_CONSTRUCT) *pos++ = 'x'; if (arg_flags[arg] & GTK_ARG_CONSTRUCT_ONLY) *pos++ = 'X'; if (arg_flags[arg] & GTK_ARG_CHILD_ARG) *pos++ = 'c'; *pos = '\0'; fprintf (fp, "\n%s\n%s\n%s\n", args[arg].name, gtk_type_name (args[arg].type), flags); if (GTK_FUNDAMENTAL_TYPE(args[arg].type) == GTK_TYPE_ENUM) { GtkEnumValue *values; gint i=0; values = gtk_type_enum_get_values(args[arg].type); while (values[i].value_name) { fprintf (fp, "\n%d\n%s\n\n", values[i].value, values[i].value_nick); i++; } } fprintf(fp, "\n\n"); } g_free (args); g_free (arg_flags); } static void output_pads () { FILE *fp; gint i; fp = fopen (pads_filename, "w"); if (fp == NULL) { g_warning ("Couldn't open output file: %s", pads_filename); return; } for (i = 0; elements[i]; i++) output_widget_pads (fp, elements[i]); fclose (fp); } static void output_widget_pads (FILE *fp, GstElement *element) { GtkObjectClass *class; GstElementFactory *factory; GList *pads; class = GTK_OBJECT(element)->klass; if (!class || !GST_IS_ELEMENT_CLASS(class)) return; pads = gst_element_get_pad_list(element); factory = GST_ELEMENT_CLASS(class)->elementfactory; if (!factory) return; while (pads) { GstPad *pad = (GstPad *)pads->data; GstType *type; //type = gst_type_find_by_id(pad->type); type = gst_type_find_by_id(1); fprintf (fp, "\n%s::%s\n", gtk_type_name(factory->type), gst_object_get_name (GST_OBJECT (pad))); if (type) { fprintf(fp, "%s\n", type->mime); if (type->exts) fprintf(fp, "%s\n", type->exts); } fprintf(fp, "\n\n"); pads = g_list_next(pads); } } 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} : ""; $command = "$CC $CFLAGS -c -o $MODULE-scan.o $MODULE-scan.c && $LD -o $MODULE-scan $MODULE-scan.o $LDFLAGS"; system($command) == 0 or die "Compilation of scanner failed\n"; system("./$MODULE-scan") == 0 or die "Scan failed\n"; unlink "./$MODULE-scan.c", "./$MODULE-scan",