Re: Exporting the Gtk+ object system to interpreters [was:no_marshall signals and GtkTypeInfo]



On 7 Dec 1998, Marius Vollmer wrote:

> I'm currently hacking (rather unfocused) on exporting the Gtk+ object
> system to Guile Scheme.  For that I need a few hooks into the Gtk+
> signal and type system.  I thought that the stuff that I committed is
> innocent enough to just go in without much justification.  Obviously I
> was wrong and I want to apologize for not following the `Dienstweg' ;-)

i've added a gtk_type_query() interface (similar to gtk_signal_query)
as you may have noticed, please tell me if that fullfills your needs.

> One of the problems are the class functions, I think.  With "class
> function" I'm referring to the functions that are directly pointed to
> from the class structures of a type, like gtk_button_draw.  I want to
> be able to use interpreted functions for these class functions, altho
> I'm not sure if I need them.  Therefore, I want to add this function
> to Gtk+:
> 
>   void gtk_signal_set_class_function_full (GtkType            type,
> 	    				   const gchar       *signal,
> 		    			   GtkSignalFunc      func,
> 					   GtkCallbackMarshal marshal,
> 					   gpointer           data,
> 					   GtkDestroyNotify   destroy_func);
> 
> It would do some magic so that one is able to use the established
> "full" interface for setting class functions, including ones with a
> custom marshaller and destroy notification.  This can be done with an
> overhead of two flag tests per signal invocation when the above
> function has not been called on a type (which will be the ususal case)
> and two additional words in GtkObjectClass.
> 
> The above function will cover replacing the default handler for
> existing signals.  It should also be possible to add new signals to
> new types.
> 
> The problem with that is to provide default marshal functions for
> arbitrary new signals.  So I settled for allowing for signals without
> default marshallers.  You can only connect handlers to such signals
> that have their own custom marshaller.

hm, there's actually a bunch of things that needs to be adressed to
get the signaling behaviour you require.
1) you need to create signals that don't provide normal marshallers
   at all (i.e. the default handlers and possibly connected handlers
   will always be invoked with unmarshalled parameters: guint n_params,
   GtkArg *params, etc).
2) you need a mechanism to specify a default-handler with gpointer *data
   arg that is not retrived as a function pointer through the normal class
   offset, and which gets invoked with unmarshalled parameters.
3) you need a mechanism to eventually chain the default handler of the
   parent class (e.g. if you create a new container type in scheme, you'd
   probably want to chain the GtkContainer::add handler of the parent
   class from within your scheme-default-handler for GtkContainer::add).
   for GtkObject::destroy this is even mandatory.

so if i understood your (and keneth) needs correctly, something like
the following should be implemented:

typedef enum                    /*< flags >*/
{
  GTK_RUN_FIRST         = 1 << 0,
  GTK_RUN_LAST          = 1 << 1,
  GTK_RUN_BOTH          = (GTK_RUN_FIRST | GTK_RUN_LAST),
  GTK_RUN_NO_RECURSE    = 1 << 2,
  GTK_RUN_ACTION        = 1 << 3,
  GTK_RUN_NO_HOOKS      = 1 << 4,
+ GTK_RUN_UNMARSHALLED  = 1 << 5
} GtkSignalRunType;

typedef void (*GtkUnmarshalledFunc) (GtkObject   *object,
				     GtkType	  instance_type,
  				     guint	  signal_id,
				     guint        n_params,
				     GtkArg      *params,
				     gpointer	  data);

/* this function is just an alias for gtk_object_class_user_signal_newv()
 * with signal_flags|=GTK_RUN_UNMARSHALLED and marshaller=NULL. it is used
 * to create new signals for classes that are not derived in C.
 */
guint gtk_object_class_unmarshalled_signal_newv (GtkObjectClass  *klass,
                                                 const gchar     *name,
                                                 GtkSignalRunType signal_flags,
                                                 GtkType          return_val,
                                                 guint            n_params,
                                                 GtkType         *params);

/* setting an unmarshalled default handler automatically sets the corrsponding
 * class function to NULL, and thus can only be done once per klass
 * and signal.
 * (we therefore don't need a GtkDestroyNotify function for the data arg).
 * this can be used to override inherited default handlers
 * in derived classes and to specify default handlers for newly created
 * unmarshalled signals.
 */
void  gtk_object_class_set_unmarshalled_handler (GtkObjectClass     *klass,
                                                 guint               signal_id,
                                                 GtkUnmarshalledFunc default_handler,
                                                 gpointer            data);

/* function to be used internally from within the signaling system,
 * and from unmarshalled default handlers to chain the parent class'
 * default handler. GTK_TYPE_OBJECT (object) is required to
 * be derived from instance_type.
 */
void gtk_object_invoke_default_handler (GtkObject *object,
				        GtkType	   instance_type,
	                                guint      signal_id,
	                                GtkArg    *params);


the default handlers will be invoked as:
void
interp_default_handler (GtkObject      *object,
		        GtkType	        instance_type,
			guint           signal_id,
			guint           n_params,
                        GtkArg         *params,
                        gpointer        data)
{
  /* the GtkArg params[] array conforms to the GtkType*params types, specified
   * for gtk_object_class_unmarshalled_signal_newv(). a possible return
   * value is stored in params[n_params].
   */
 
  /* chaining of the parent class' handler (e.g. required for ::destroy) */
  gtk_object_invoke_default_handler (object,
                                     gtk_type_parent (instance_type),
                                     signal_id,
                                     params);
}

the instance_type parameter cares about proper chaining of default handlers.
e.g. a scheme type SchemePrompt could derive from SchemeInput which would
in turn derive from GtkEntry, and both of them would override
GtkEditable::insert_text:
gtk_object_class_set_unmarshalled_handler (gtk_type_class ("SchemeInput"),
                                           gtk_signal_lookup ("insert_text",
                                                              GTK_TYPE_EDITABLE),
                                           scheme_input_default_handler_insert_text,
                                           data);
gtk_object_class_set_unmarshalled_handler (gtk_type_class ("SchemePrompt"),
                                           gtk_signal_lookup ("insert_text",
                                                              GTK_TYPE_EDITABLE),
                                           scheme_prompt_default_handler_insert_text,
                                           data);
upon emission of the signal:
gtk_signal_emitv (GTK_OBJECT (scheme_prompt),
                  gtk_signal_lookup ("insert_text", GTK_TYPE_EDITABLE),
                  &params);
the call sequence will be as follows:
void
scheme_prompt_default_handler_insert_text (GtkObject *object,
                                           GtkType    instance_type,
                                           ...)
{
  /* GTK_OBJECT_TYPE (object) is gtk_type_from_name ("SchemePrompt"),
   * instance_type is gtk_type_from_name ("SchemePrompt").
   * the next call invokes scheme_input_default_handler_insert_text()
   */
  gtk_object_invoke_default_handler (object,
                                     gtk_type_parent (instance_type),
                                     signal_id,
                                     params);
}
void
scheme_input_default_handler_insert_text (GtkObject *object,
                                          GtkType    instance_type,
                                          ...)
{
  /* GTK_OBJECT_TYPE (object) is gtk_type_from_name ("SchemePrompt"),
   * instance_type is gtk_type_from_name ("SchemeInput").
   * the next call invokes gtk_entry_insert_text()
   */
  gtk_object_invoke_default_handler (object,
                                     gtk_type_parent (instance_type),
                                     signal_id,
                                     params);
}
void
gtk_entry_insert_text (GtkObject* object,
                       ...)
{
  /* GTK_OBJECT_TYPE (object) is gtk_type_from_name ("SchemePrompt"),
   * instance_type is not passed in, but would be GTK_TYPE_ENTRY.
   * from hereon, further chaining is done is through the usual
   * GTK_EDITABLE_CLASS (parent_class)->insert_text (...);
   */
}


i'm passing in the signal_id to GtkDefaultHandler, because it's required
to chain the parent class' default handler, and i'm also assuming you'd
most likely want to use the same function to marshall the distinct
default handlers for different kinds of newly created interpreter signals.

C functions that want to connect to unmarshalled signals (though that behaviour
is questionable, it should be taken care of) will be invoked with the
same signature as the default handler, i.e. that of GtkUnmarshalledFunc.
instance_type will always be GTK_TYPE_OBJECT (object) in such cases (or
we may define another GtkUnmarshalledHandler type for them, having the
signature of GtkUnmarshalledFunc without the instance_type arg).

> 
> - Marius
> 

---
ciaoTJ



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