/* GTK - The GIMP Toolkit
 * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.	 See the GNU
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public
 * License along with this library; if not, write to the
 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 * Boston, MA 02111-1307, USA.
 */
#include <stdarg.h>
#include <string.h>
#include <stdio.h>
#include "gtksignal.h"
#include "gtkargcollector.c"


#define	SIGNAL_BLOCK_SIZE		(100)
#define	HANDLER_BLOCK_SIZE		(200)
#define	EMISSION_BLOCK_SIZE		(100)
#define	DISCONNECT_INFO_BLOCK_SIZE	(64)
#define MAX_SIGNAL_PARAMS		(31)

enum
{
  EMISSION_CONTINUE,
  EMISSION_RESTART,
  EMISSION_DONE
};

#define GTK_RUN_TYPE(x)	 ((x) & GTK_RUN_MASK)


typedef struct _GtkSignal		GtkSignal;
typedef struct _GtkSignalHash		GtkSignalHash;
typedef struct _GtkHandler		GtkHandler;
typedef struct _GtkHandlerInfo		GtkHandlerInfo;
typedef struct _GtkEmission		GtkEmission;
typedef union  _GtkEmissionAllocator	GtkEmissionAllocator;
typedef struct _GtkDisconnectInfo	GtkDisconnectInfo;

typedef void (*GtkSignalMarshaller0) (GtkObject *object,
				      gpointer	 data);

struct _GtkSignal
{
  guint		      signal_id;
  GtkType	      object_type;
  gchar		     *name;
  guint		      function_offset;
  GtkSignalRunType    signal_flags;
  GtkSignalMarshaller marshaller;
  GtkType	      return_val;
  GtkType	     *params;
  guint		      nparams;
};

struct _GtkSignalHash
{
  GtkType object_type;
  GQuark  quark;
  guint	  signal_id;
};

struct _GtkHandler
{
  guint		   id;
  guint		   blocked : 20;
  guint		   object_signal : 1;
  guint		   after : 1;
  guint		   no_marshal : 1;
  guint16	   ref_count;
  guint16	   signal_id;
  GtkSignalFunc	   func;
  gpointer	   func_data;
  GtkSignalDestroy destroy_func;
  GtkHandler	  *prev;
  GtkHandler	  *next;
};

struct _GtkHandlerInfo
{
  GtkObject	     *object;
  GtkSignalMarshaller marshaller;
  GtkArg	     *params;
  GtkType	     *param_types;
  GtkType	      return_val;
  GtkSignalRunType    signal_flags;
  guint		      nparams;
  guint		      signal_id;
};

struct _GtkEmission
{
  GtkObject *object;
  guint	     signal_id;
};

union _GtkEmissionAllocator
{
  GtkEmissionAllocator *next;
  GtkEmission		emission;
};

struct _GtkDisconnectInfo
{
  GtkObject	*object1;
  guint		 disconnect_handler1;
  guint		 signal_handler;
  GtkObject	*object2;
  guint		 disconnect_handler2;
};


static guint	    gtk_signal_hash	       (gconstpointer h);
static gint	    gtk_signal_compare	       (gconstpointer h1,
						gconstpointer h2);
static GtkHandler*  gtk_signal_handler_new     (void);
static void	    gtk_signal_handler_ref     (GtkHandler    *handler);
static void	    gtk_signal_handler_unref   (GtkHandler    *handler,
						GtkObject     *object);
static void	    gtk_signal_handler_insert  (GtkObject     *object,
						GtkHandler    *handler);
static void	    gtk_signal_real_emit       (GtkObject     *object,
						GtkSignal     *signal,
						GtkArg	      *params);
static GtkHandler*  gtk_signal_get_handlers    (GtkObject     *object,
						guint	       signal_type);
static guint	    gtk_signal_connect_by_type (GtkObject     *object,
						guint	       signal_id,
						GtkSignalFunc  func,
						gpointer       func_data,
						GtkSignalDestroy destroy_func,
						gint	       object_signal,
						gint	       after,
						gint	       no_marshal);
static guint	    gtk_alive_disconnecter     (GtkDisconnectInfo *info);
static GtkEmission* gtk_emission_new	       (void);
static void	    gtk_emission_add	       (GList	      **emissions,
						GtkObject      *object,
						guint		signal_type);
static void	    gtk_emission_remove	       (GList	      **emissions,
						GtkObject      *object,
						guint		signal_type);
static gint	    gtk_emission_check	       (GList	       *emissions,
						GtkObject      *object,
						guint		signal_type);
static gint	    gtk_handlers_run	       (GtkHandler     *handlers,
						GtkHandlerInfo *info,
						gint		after);
static gboolean	    gtk_signal_collect_params  (GtkArg	       *params,
						guint		nparams,
						GtkType	       *param_types,
						GtkType		return_type,
						va_list		var_args);

#define LOOKUP_SIGNAL_ID(signal_id)	( \
  signal_id > 0 && signal_id < gtk_n_signals ? \
    (GtkSignal*) gtk_signals + signal_id : \
    (GtkSignal*) 0 \
)


static GtkSignalMarshal global_marshaller = NULL;
static GtkSignalDestroy global_destroy_notify = NULL;

static guint	   		 gtk_handler_id = 1;
static guint	   		 handler_quark = 0;
static GHashTable  		*gtk_signal_hash_table = NULL;
static GtkSignal   		*gtk_signals = NULL;
static guint	   		 gtk_n_signals = 0;
static GMemChunk   		*gtk_signal_hash_mem_chunk = NULL;
static GMemChunk   		*gtk_disconnect_info_mem_chunk = NULL;
static GtkHandler  		*gtk_handler_free_list = NULL;
static GtkEmissionAllocator	*gtk_emission_free_list = NULL;



static GList *current_emissions = NULL;
static GList *stop_emissions = NULL;
static GList *restart_emissions = NULL;

static GtkSignal*
gtk_signal_next_and_invalidate (void)
{
  static guint gtk_n_free_signals = 0;
  register GtkSignal *signal;
  register guint new_signal_id;
  
  /* don't keep *any* GtkSignal pointers across invokation of this function!!!
   */
  
  if (gtk_n_free_signals == 0)
    {
      register guint i;
      register guint size;
      
      /* nearest pow
       */
      size = gtk_n_signals + SIGNAL_BLOCK_SIZE;
      size *= sizeof (GtkSignal);
      i = 1;
      while (i < size)
	i <<= 1;
      size = i;
      
      gtk_signals = g_realloc (gtk_signals, size);
      
      gtk_n_free_signals = size / sizeof (GtkSignal) - gtk_n_signals;
      
      memset (gtk_signals + gtk_n_signals, 0, gtk_n_free_signals * sizeof (GtkSignal));
    }
  
  new_signal_id = gtk_n_signals++;
  gtk_n_free_signals--;
  
  signal = LOOKUP_SIGNAL_ID (new_signal_id);
  if (signal)
    signal->signal_id = new_signal_id;
  
  return signal;
}

void
gtk_signal_init (void)
{
  if (!handler_quark)
    {
      GtkSignal *zero;
      
      zero = gtk_signal_next_and_invalidate ();
      g_assert (zero == NULL);
      
      handler_quark = g_quark_from_static_string ("gtk-signal-handlers");
      
      gtk_signal_hash_mem_chunk =
	g_mem_chunk_new ("GtkSignalHash mem chunk",
			 sizeof (GtkSignalHash),
			 sizeof (GtkSignalHash) * SIGNAL_BLOCK_SIZE,
			 G_ALLOC_ONLY);
      gtk_disconnect_info_mem_chunk =
	g_mem_chunk_new ("GtkDisconnectInfo mem chunk",
			 sizeof (GtkDisconnectInfo),
			 sizeof (GtkDisconnectInfo) * DISCONNECT_INFO_BLOCK_SIZE,
			 G_ALLOC_AND_FREE);
      gtk_handler_free_list = NULL;
      gtk_emission_free_list = NULL;
      
      gtk_signal_hash_table = g_hash_table_new (gtk_signal_hash,
						gtk_signal_compare);
    }
}

guint
gtk_signal_newv (const gchar	     *r_name,
		 GtkSignalRunType     signal_flags,
		 GtkType	      object_type,
		 guint		      function_offset,
		 GtkSignalMarshaller  marshaller,
		 GtkType	      return_val,
		 guint		      nparams,
		 GtkType	     *params)
{
  GtkSignal *signal;
  GtkSignalHash *hash;
  GQuark quark;
  guint i;
  gchar *name;
  
  g_return_val_if_fail (r_name != NULL, 0);
  g_return_val_if_fail (marshaller != NULL, 0);
  g_return_val_if_fail (nparams < MAX_SIGNAL_PARAMS, 0);
  if (nparams)
    g_return_val_if_fail (params != NULL, 0);
  
  if (!handler_quark)
    gtk_signal_init ();


  name = g_strdup (r_name);
  g_strdelimit (name, NULL, '_');

  quark = gtk_signal_lookup (name, object_type);
  if (quark)
    {
      g_warning ("gtk_signal_newv(): signal \"%s\" already exists in the `%s' class ancestry\n",
		 r_name,
		 gtk_type_name (object_type));
      g_free (name);
      return 0;
    }
  
  if (return_val != GTK_TYPE_NONE &&
      (signal_flags & GTK_RUN_BOTH) == GTK_RUN_FIRST)
    {
      g_warning ("gtk_signal_newv(): signal \"%s\" with return value `%s' excludes GTK_RUN_LAST",
		 name, gtk_type_name (return_val));
      g_free (name);
      return 0;
    }
  
  signal = gtk_signal_next_and_invalidate ();
  
  /* signal->signal_id already set */
  
  signal->object_type = object_type;
  signal->name = name;
  signal->function_offset = function_offset;
  signal->signal_flags = signal_flags;
  signal->marshaller = marshaller;
  signal->return_val = return_val;
  signal->nparams = nparams;
  
  if (nparams > 0)
    {
      signal->params = g_new (GtkType, nparams);
      
      for (i = 0; i < nparams; i++)
	signal->params[i] = params[i];
    }
  else
    signal->params = NULL;

  /* insert "signal_name" into hash table
   */
  hash = g_chunk_new (GtkSignalHash, gtk_signal_hash_mem_chunk);
  hash->object_type = object_type;
  hash->quark = g_quark_from_string (signal->name);
  hash->signal_id = signal->signal_id;
  g_hash_table_insert (gtk_signal_hash_table, hash, GUINT_TO_POINTER (hash->signal_id));

  /* insert "signal-name" into hash table
   */
  g_strdelimit (signal->name, NULL, '-');
  quark = g_quark_from_static_string (signal->name);
  if (quark != hash->quark)
    {
      hash = g_chunk_new (GtkSignalHash, gtk_signal_hash_mem_chunk);
      hash->object_type = object_type;
      hash->quark = quark;
      hash->signal_id = signal->signal_id;
      g_hash_table_insert (gtk_signal_hash_table, hash, GUINT_TO_POINTER (hash->signal_id));
    }
  
  return signal->signal_id;
}

guint
gtk_signal_new (const gchar	    *name,
		GtkSignalRunType     signal_flags,
		GtkType		     object_type,
		guint		     function_offset,
		GtkSignalMarshaller  marshaller,
		GtkType		     return_val,
		guint		     nparams,
		...)
{
  GtkType *params;
  guint i;
  va_list args;
  guint signal_id;
  
  g_return_val_if_fail (nparams < MAX_SIGNAL_PARAMS, 0);
  
  if (nparams > 0)
    {
      params = g_new (GtkType, nparams);
      
      va_start (args, nparams);
      
      for (i = 0; i < nparams; i++)
	params[i] = va_arg (args, GtkType);
      
      va_end (args);
    }
  else
    params = NULL;
  
  signal_id = gtk_signal_newv (name,
			       signal_flags,
			       object_type,
			       function_offset,
			       marshaller,
			       return_val,
			       nparams,
			       params);
  
  g_free (params);
  
  return signal_id;
}

guint
gtk_signal_lookup (const gchar *name,
		   GtkType	object_type)
{
  GtkSignalHash hash;
  
  g_return_val_if_fail (name != NULL, 0);
  g_return_val_if_fail (gtk_type_is_a (object_type, GTK_TYPE_OBJECT), 0);
  
  hash.quark = g_quark_try_string (name);
  if (hash.quark)
    {
      while (object_type)
	{
	  guint signal_id;
	  
	  hash.object_type = object_type;
	  
	  signal_id = GPOINTER_TO_UINT (g_hash_table_lookup (gtk_signal_hash_table, &hash));
	  if (signal_id)
	    return signal_id;
	  
	  object_type = gtk_type_parent (object_type);
	}
    }
  
  return 0;
}

GtkSignalQuery*
gtk_signal_query (guint signal_id)
{
  GtkSignalQuery *query;
  GtkSignal *signal;
  
  g_return_val_if_fail (signal_id >= 1, NULL);
  
  signal = LOOKUP_SIGNAL_ID (signal_id);
  if (signal)
    {
      query = g_new (GtkSignalQuery, 1);
      
      query->object_type = signal->object_type;
      query->signal_id = signal_id;
      query->signal_name = signal->name;
      query->is_user_signal = signal->function_offset == 0;
      query->signal_flags = signal->signal_flags;
      query->return_val = signal->return_val;
      query->nparams = signal->nparams;
      query->params = signal->params;
    }
  else
    query = NULL;
  
  return query;
}

gchar*
gtk_signal_name (guint signal_id)
{
  GtkSignal *signal;
  
  g_return_val_if_fail (signal_id >= 1, NULL);
  
  signal = LOOKUP_SIGNAL_ID (signal_id);
  if (signal)
    return signal->name;
  
  return NULL;
}

void
gtk_signal_emitv (GtkObject           *object,
		  guint                signal_id,
		  GtkArg              *params)
{
  GtkSignal *signal;

  g_return_if_fail (object != NULL);
  g_return_if_fail (signal_id >= 1);
  
  signal = LOOKUP_SIGNAL_ID (signal_id);
  g_return_if_fail (signal != NULL);
  g_return_if_fail (gtk_type_is_a (GTK_OBJECT_TYPE (object), signal->object_type));

  if (signal->nparams > 0)
    g_return_if_fail (params != NULL);

  gtk_signal_real_emit (object, signal, params);
}

void
gtk_signal_emit (GtkObject *object,
		 guint	    signal_id,
		 ...)
{
  GtkSignal *signal;
  va_list    args;
  GtkArg     params[MAX_SIGNAL_PARAMS + 1];
  gboolean   abort;

  g_return_if_fail (object != NULL);
  g_return_if_fail (signal_id >= 1);
  
  signal = LOOKUP_SIGNAL_ID (signal_id);
  g_return_if_fail (signal != NULL);
  g_return_if_fail (gtk_type_is_a (GTK_OBJECT_TYPE (object), signal->object_type));

  va_start (args, signal_id);
  abort = gtk_signal_collect_params (params,
				     signal->nparams,
				     signal->params,
				     signal->return_val,
				     args);
  va_end (args);

  if (!abort)
    gtk_signal_real_emit (object, signal, params);
}

void
gtk_signal_emitv_by_name (GtkObject           *object,
			  const gchar         *name,
			  GtkArg              *params)
{
  guint signal_id;
  
  g_return_if_fail (object != NULL);
  g_return_if_fail (name != NULL);
  g_return_if_fail (params != NULL);
  
  signal_id = gtk_signal_lookup (name, GTK_OBJECT_TYPE (object));
  
  if (signal_id >= 1)
    {
      GtkSignal *signal;
      
      signal = LOOKUP_SIGNAL_ID (signal_id);
      g_return_if_fail (signal != NULL);
      g_return_if_fail (gtk_type_is_a (GTK_OBJECT_TYPE (object), signal->object_type));

      gtk_signal_real_emit (object, signal, params);
    }
  else
    {
      g_warning ("gtk_signal_emitv_by_name(): could not find signal \"%s\" in the `%s' class ancestry",
		 name,
		 gtk_type_name (GTK_OBJECT_TYPE (object)));
    }
}

void
gtk_signal_emit_by_name (GtkObject	 *object,
			 const gchar	 *name,
			 ...)
{
  guint signal_id;
  
  g_return_if_fail (object != NULL);
  g_return_if_fail (name != NULL);
  
  signal_id = gtk_signal_lookup (name, GTK_OBJECT_TYPE (object));
  
  if (signal_id >= 1)
    {
      GtkSignal *signal;
      GtkArg     params[MAX_SIGNAL_PARAMS + 1];
      va_list    args;
      gboolean   abort;
      
      signal = LOOKUP_SIGNAL_ID (signal_id);
      g_return_if_fail (signal != NULL);
      g_return_if_fail (gtk_type_is_a (GTK_OBJECT_TYPE (object), signal->object_type));

      va_start (args, name);
      abort = gtk_signal_collect_params (params,
					 signal->nparams,
					 signal->params,
					 signal->return_val,
					 args);
      va_end (args);

      if (!abort)
	gtk_signal_real_emit (object, signal, params);
    }
  else
    {
      g_warning ("gtk_signal_emit_by_name(): could not find signal \"%s\" in the `%s' class ancestry",
		 name,
		 gtk_type_name (GTK_OBJECT_TYPE (object)));
    }
}

void
gtk_signal_emit_stop (GtkObject *object,
		      guint	  signal_id)
{
  g_return_if_fail (object != NULL);
  g_return_if_fail (signal_id >= 1);
  
  if (gtk_emission_check (current_emissions, object, signal_id))
    gtk_emission_add (&stop_emissions, object, signal_id);
  else
    g_warning ("gtk_signal_emit_stop(): no current emission (%u) for object `%s'",
	       signal_id,
	       gtk_type_name (GTK_OBJECT_TYPE (object)));
}

void
gtk_signal_emit_stop_by_name (GtkObject	      *object,
			      const gchar     *name)
{
  guint signal_id;
  
  g_return_if_fail (object != NULL);
  g_return_if_fail (name != NULL);
  
  signal_id = gtk_signal_lookup (name, GTK_OBJECT_TYPE (object));
  if (signal_id)
    gtk_signal_emit_stop (object, signal_id);
  else
    g_warning ("gtk_signal_emit_stop_by_name(): could not find signal \"%s\" in the `%s' class ancestry",
	       name,
	       gtk_type_name (GTK_OBJECT_TYPE (object)));
}

guint
gtk_signal_n_emissions (GtkObject  *object,
			guint	    signal_id)
{
  GList *list;
  guint n;
  
  g_return_val_if_fail (object != NULL, 0);
  g_return_val_if_fail (GTK_IS_OBJECT (object), 0);
  
  n = 0;
  for (list = current_emissions; list; list = list->next)
    {
      GtkEmission *emission;
      
      emission = list->data;
      
      if ((emission->object == object) &&
	  (emission->signal_id == signal_id))
	n++;
    }
  
  return n;
}

guint
gtk_signal_n_emissions_by_name (GtkObject	*object,
				const gchar	*name)
{
  guint signal_id;
  guint n;
  
  g_return_val_if_fail (object != NULL, 0);
  g_return_val_if_fail (GTK_IS_OBJECT (object), 0);
  g_return_val_if_fail (name != NULL, 0);
  
  signal_id = gtk_signal_lookup (name, GTK_OBJECT_TYPE (object));
  if (signal_id)
    n = gtk_signal_n_emissions (object, signal_id);
  else
    {
      g_warning ("gtk_signal_n_emissions_by_name(): could not find signal \"%s\" in the `%s' class ancestry",
		 name,
		 gtk_type_name (GTK_OBJECT_TYPE (object)));
      n = 0;
    }
  
  return n;
}

guint
gtk_signal_connect (GtkObject	  *object,
		    const gchar	  *name,
		    GtkSignalFunc  func,
		    gpointer	   func_data)
{
  guint signal_id;
  
  g_return_val_if_fail (object != NULL, 0);
  g_return_val_if_fail (GTK_IS_OBJECT (object), 0);
  
  signal_id = gtk_signal_lookup (name, GTK_OBJECT_TYPE (object));
  if (!signal_id)
    {
      g_warning ("gtk_signal_connect(): could not find signal \"%s\" in the `%s' class ancestry",
		 name,
		 gtk_type_name (GTK_OBJECT_TYPE (object)));
      return 0;
    }
  
  return gtk_signal_connect_by_type (object, signal_id, 
				     func, func_data, NULL,
				     FALSE, FALSE, FALSE);
}

guint
gtk_signal_connect_after (GtkObject	*object,
			  const gchar	*name,
			  GtkSignalFunc	 func,
			  gpointer	 func_data)
{
  guint signal_id;
  
  g_return_val_if_fail (object != NULL, 0);
  
  signal_id = gtk_signal_lookup (name, GTK_OBJECT_TYPE (object));
  if (!signal_id)
    {
      g_warning ("gtk_signal_connect_after(): could not find signal \"%s\" in the `%s' class ancestry",
		 name,
		 gtk_type_name (GTK_OBJECT_TYPE (object)));
      return 0;
    }
  
  return gtk_signal_connect_by_type (object, signal_id, 
				     func, func_data, NULL,
				     FALSE, TRUE, FALSE);
}

guint	
gtk_signal_connect_full (GtkObject	     *object,
			 const gchar	     *name,
			 GtkSignalFunc	      func,
			 GtkCallbackMarshal   marshal,
			 gpointer	      func_data,
			 GtkDestroyNotify     destroy_func,
			 gint		      object_signal,
			 gint		      after)
{
  guint signal_id;
  
  g_return_val_if_fail (object != NULL, 0);
  
  signal_id = gtk_signal_lookup (name, GTK_OBJECT_TYPE (object));
  if (!signal_id)
    {
      g_warning ("gtk_signal_connect_full(): could not find signal \"%s\" in the `%s' class ancestry",
		 name,
		 gtk_type_name (GTK_OBJECT_TYPE (object)));
      return 0;
    }
  
  if (marshal)
    return gtk_signal_connect_by_type (object, signal_id, (GtkSignalFunc) marshal, 
				       func_data, destroy_func, 
				       object_signal, after, TRUE);
  else
    return gtk_signal_connect_by_type (object, signal_id, func, 
				       func_data, destroy_func, 
				       object_signal, after, FALSE);
}

guint
gtk_signal_connect_interp (GtkObject	     *object,
			   const gchar	     *name,
			   GtkCallbackMarshal func,
			   gpointer	      func_data,
			   GtkDestroyNotify   destroy_func,
			   gint		      after)
{
  return gtk_signal_connect_full (object, name, NULL, func,
				  func_data, destroy_func, FALSE, after);
}

guint
gtk_signal_connect_object (GtkObject	 *object,
			   const gchar	 *name,
			   GtkSignalFunc  func,
			   GtkObject	 *slot_object)
{
  guint signal_id;
  
  g_return_val_if_fail (object != NULL, 0);
  /* slot_object needs to be treated as ordinary pointer
   */
  
  signal_id = gtk_signal_lookup (name, GTK_OBJECT_TYPE (object));
  if (!signal_id)
    {
      g_warning ("gtk_signal_connect_object(): could not find signal \"%s\" in the `%s' class ancestry",
		 name,
		 gtk_type_name (GTK_OBJECT_TYPE (object)));
      return 0;
    }
  
  return gtk_signal_connect_by_type (object, signal_id, 
				     func, slot_object, NULL,
				     TRUE, FALSE, FALSE);
}

guint
gtk_signal_connect_object_after (GtkObject     *object,
				 const gchar   *name,
				 GtkSignalFunc	func,
				 GtkObject     *slot_object)
{
  guint signal_id;
  
  g_return_val_if_fail (object != NULL, 0);
  
  signal_id = gtk_signal_lookup (name, GTK_OBJECT_TYPE (object));
  if (!signal_id)
    {
      g_warning ("gtk_signal_connect_object_after(): could not find signal \"%s\" in the `%s' class ancestry",
		 name,
		 gtk_type_name (GTK_OBJECT_TYPE (object)));
      return 0;
    }
  
  return gtk_signal_connect_by_type (object, signal_id, 
				     func, slot_object, NULL,
				     TRUE, TRUE, FALSE);
}

void
gtk_signal_connect_while_alive (GtkObject	 *object,
				const gchar	 *signal,
				GtkSignalFunc	  func,
				gpointer	  func_data,
				GtkObject	 *alive_object)
{
  GtkDisconnectInfo *info;
  
  g_return_if_fail (object != NULL);
  g_return_if_fail (GTK_IS_OBJECT (object));
  g_return_if_fail (signal != NULL);
  g_return_if_fail (func != NULL);
  g_return_if_fail (alive_object != NULL);
  g_return_if_fail (GTK_IS_OBJECT (alive_object));
  
  info = g_chunk_new (GtkDisconnectInfo, gtk_disconnect_info_mem_chunk);
  info->object1 = object;
  info->object2 = alive_object;
  
  info->signal_handler = gtk_signal_connect (object, signal, func, func_data);
  info->disconnect_handler1 =
    gtk_signal_connect_object (info->object1,
			       "destroy",
			       GTK_SIGNAL_FUNC (gtk_alive_disconnecter),
			       (GtkObject*) info);
  info->disconnect_handler2 =
    gtk_signal_connect_object (info->object2,
			       "destroy",
			       GTK_SIGNAL_FUNC (gtk_alive_disconnecter),
			       (GtkObject*) info);
}

void
gtk_signal_connect_object_while_alive (GtkObject	*object,
				       const gchar	*signal,
				       GtkSignalFunc	 func,
				       GtkObject	*alive_object)
{
  GtkDisconnectInfo *info;
  
  g_return_if_fail (object != NULL);
  g_return_if_fail (GTK_IS_OBJECT (object));
  g_return_if_fail (signal != NULL);
  g_return_if_fail (func != NULL);
  g_return_if_fail (alive_object != NULL);
  g_return_if_fail (GTK_IS_OBJECT (alive_object));
  
  info = g_chunk_new (GtkDisconnectInfo, gtk_disconnect_info_mem_chunk);
  info->object1 = object;
  info->object2 = alive_object;
  
  info->signal_handler = gtk_signal_connect_object (object, signal, func, alive_object);
  info->disconnect_handler1 =
    gtk_signal_connect_object (info->object1,
			       "destroy",
			       GTK_SIGNAL_FUNC (gtk_alive_disconnecter),
			       (GtkObject*) info);
  info->disconnect_handler2 =
    gtk_signal_connect_object (info->object2,
			       "destroy",
			       GTK_SIGNAL_FUNC (gtk_alive_disconnecter),
			       (GtkObject*) info);
}

void
gtk_signal_disconnect (GtkObject *object,
		       guint	  handler_id)
{
  GtkHandler *handler;
  
  g_return_if_fail (object != NULL);
  g_return_if_fail (handler_id > 0);
  
  handler = gtk_object_get_data_by_id (object, handler_quark);
  
  while (handler)
    {
      if (handler->id == handler_id)
	{
	  handler->id = 0;
	  handler->blocked += 1;
	  gtk_signal_handler_unref (handler, object);
	  return;
	}
      handler = handler->next;
    }
  
  g_warning ("gtk_signal_disconnect(): could not find handler (%u)", handler_id);
}

void
gtk_signal_disconnect_by_func (GtkObject     *object,
			       GtkSignalFunc  func,
			       gpointer	      data)
{
  GtkHandler *handler;
  gint found_one;
  
  g_return_if_fail (object != NULL);
  g_return_if_fail (func != NULL);
  
  found_one = FALSE;
  handler = gtk_object_get_data_by_id (object, handler_quark);
  
  while (handler)
    {
      GtkHandler *handler_next;
      
      handler_next = handler->next;
      if ((handler->id > 0) &&
	  (handler->func == func) &&
	  (handler->func_data == data))
	{
	  found_one = TRUE;
	  handler->id = 0;
	  handler->blocked += 1;
	  gtk_signal_handler_unref (handler, object);
	}
      handler = handler_next;
    }
  
  if (!found_one)
    g_warning ("gtk_signal_disconnect_by_func(): could not find handler (0x%0lX) containing data (0x%0lX)", (long) func, (long) data);
}

void
gtk_signal_disconnect_by_data (GtkObject *object,
			       gpointer	  data)
{
  GtkHandler *handler;
  gint found_one;
  
  g_return_if_fail (object != NULL);
  
  found_one = FALSE;
  handler = gtk_object_get_data_by_id (object, handler_quark);
  
  while (handler)
    {
      GtkHandler *handler_next;
      
      handler_next = handler->next;
      if ((handler->id > 0) &&
	  (handler->func_data == data))
	{
	  found_one = TRUE;
	  handler->id = 0;
	  handler->blocked += 1;
	  gtk_signal_handler_unref (handler, object);
	}
      handler = handler_next;
    }
  
  if (!found_one)
    g_warning ("gtk_signal_disconnect_by_data(): could not find handler containing data (0x%0lX)", (long) data);
}

void
gtk_signal_handler_block (GtkObject *object,
			  guint	     handler_id)
{
  GtkHandler *handler;
  
  g_return_if_fail (object != NULL);
  g_return_if_fail (handler_id > 0);
  
  handler = gtk_object_get_data_by_id (object, handler_quark);
  
  while (handler)
    {
      if (handler->id == handler_id)
	{
	  handler->blocked += 1;
	  return;
	}
      handler = handler->next;
    }
  
  g_warning ("gtk_signal_handler_block(): could not find handler (%u)", handler_id);
}

void
gtk_signal_handler_block_by_func (GtkObject	*object,
				  GtkSignalFunc	 func,
				  gpointer	 data)
{
  GtkHandler *handler;
  gint found_one;
  
  g_return_if_fail (object != NULL);
  g_return_if_fail (func != NULL);
  
  found_one = FALSE;
  handler = gtk_object_get_data_by_id (object, handler_quark);
  
  while (handler)
    {
      if ((handler->id > 0) &&
	  (handler->func == func) &&
	  (handler->func_data == data))
	{
	  found_one = TRUE;
	  handler->blocked += 1;
	}
      handler = handler->next;
    }
  
  if (!found_one)
    g_warning ("gtk_signal_handler_block_by_func(): could not find handler (0x%0lX) containing data (0x%0lX)", (long) func, (long) data);
}

void
gtk_signal_handler_block_by_data (GtkObject *object,
				  gpointer   data)
{
  GtkHandler *handler;
  gint found_one;
  
  g_return_if_fail (object != NULL);
  
  found_one = FALSE;
  handler = gtk_object_get_data_by_id (object, handler_quark);
  
  while (handler)
    {
      if ((handler->id > 0) &&
	  (handler->func_data == data))
	{
	  found_one = TRUE;
	  handler->blocked += 1;
	}
      handler = handler->next;
    }
  
  if (!found_one)
    g_warning ("gtk_signal_handler_block_by_data(): could not find handler containing data (0x%0lX)", (long) data);
}

void
gtk_signal_handler_unblock (GtkObject *object,
			    guint      handler_id)
{
  GtkHandler *handler;
  
  g_return_if_fail (object != NULL);
  g_return_if_fail (handler_id > 0);
  
  handler = gtk_object_get_data_by_id (object, handler_quark);
  
  while (handler)
    {
      if (handler->id == handler_id)
	{
	  if (handler->blocked > 0)
	    handler->blocked -= 1;
	  else
	    g_warning ("gtk_signal_handler_unblock(): handler (%u) is not blocked", handler_id);
	  return;
	}
      handler = handler->next;
    }
  
  g_warning ("gtk_signal_handler_unblock(): could not find handler (%u)", handler_id);
}

void
gtk_signal_handler_unblock_by_func (GtkObject	  *object,
				    GtkSignalFunc  func,
				    gpointer	   data)
{
  GtkHandler *handler;
  gint found_one;
  
  g_return_if_fail (object != NULL);
  g_return_if_fail (func != NULL);
  
  found_one = FALSE;
  handler = gtk_object_get_data_by_id (object, handler_quark);
  
  while (handler)
    {
      if ((handler->id > 0) &&
	  (handler->func == func) &&
	  (handler->func_data == data) &&
	  (handler->blocked > 0))
	{
	  handler->blocked -= 1;
	  found_one = TRUE;
	}
      handler = handler->next;
    }
  
  if (!found_one)
    g_warning ("gtk_signal_handler_unblock_by_func(): could not find blocked handler (0x%0lX) containing data (0x%0lX)", (long) func, (long) data);
}

void
gtk_signal_handler_unblock_by_data (GtkObject *object,
				    gpointer   data)
{
  GtkHandler *handler;
  gint found_one;
  
  g_return_if_fail (object != NULL);
  
  found_one = FALSE;
  handler = gtk_object_get_data_by_id (object, handler_quark);
  
  while (handler)
    {
      if ((handler->id > 0) &&
	  (handler->func_data == data) &&
	  (handler->blocked > 0))
	{
	  handler->blocked -= 1;
	  found_one = TRUE;
	}
      handler = handler->next;
    }
  
  if (!found_one)
    g_warning ("gtk_signal_handler_unblock_by_data(): could not find blocked handler containing data (0x%0lX)", (long) data);
}

void
gtk_signal_handlers_destroy (GtkObject *object)
{
  GtkHandler *handler;
  
  /* we make the "optimization" of destroying the first handler in the last
   * place, since we don't want gtk_signal_handler_unref() to reset the objects
   * handler_key data on each removal
   */
  
  handler = gtk_object_get_data_by_id (object, handler_quark);
  if (handler)
    {
      handler = handler->next;
      while (handler)
	{
	  GtkHandler *next;
	  
	  next = handler->next;
	  gtk_signal_handler_unref (handler, object);
	  handler = next;
	}
      handler = gtk_object_get_data_by_id (object, handler_quark);
      gtk_signal_handler_unref (handler, object);
    }
}

void
gtk_signal_default_marshaller (GtkObject      *object,
			       GtkSignalFunc   func,
			       gpointer	       func_data,
			       GtkArg	      *params)
{
  GtkSignalMarshaller0 rfunc;
  
  rfunc = (GtkSignalMarshaller0) func;
  
  (* rfunc) (object, func_data);
}

void
gtk_signal_set_funcs (GtkSignalMarshal marshal_func,
		      GtkSignalDestroy destroy_func)
{
  global_marshaller = marshal_func;
  global_destroy_notify = destroy_func;
}

static guint
gtk_signal_hash (gconstpointer h)
{
  register const GtkSignalHash *hash = h;
  
  return hash->object_type ^ hash->quark;
}

static gint
gtk_signal_compare (gconstpointer h1,
		    gconstpointer h2)
{
  register const GtkSignalHash *hash1 = h1;
  register const GtkSignalHash *hash2 = h2;
  
  return (hash1->quark == hash2->quark &&
	  hash1->object_type == hash2->object_type);
}

static guint
gtk_alive_disconnecter (GtkDisconnectInfo *info)
{
  g_return_val_if_fail (info != NULL, 0);
  
  gtk_signal_disconnect (info->object1, info->disconnect_handler1);
  gtk_signal_disconnect (info->object1, info->signal_handler);
  gtk_signal_disconnect (info->object2, info->disconnect_handler2);
  
  g_mem_chunk_free (gtk_disconnect_info_mem_chunk, info);
  
  return 0;
}

static GtkHandler*
gtk_signal_handler_new (void)
{
  GtkHandler *handler;

  if (!gtk_handler_free_list)
    {
      GtkHandler *handler_block;
      guint i;

      handler_block = g_new0 (GtkHandler, HANDLER_BLOCK_SIZE);
      for (i = 1; i < HANDLER_BLOCK_SIZE; i++)
	{
	  (handler_block + i)->next = gtk_handler_free_list;
	  gtk_handler_free_list = (handler_block + i);
	}
      
      handler = handler_block;
    }
  else
    {
      handler = gtk_handler_free_list;
      gtk_handler_free_list = handler->next;
    }
  
  handler->id = 0;
  handler->blocked = 0;
  handler->signal_id = 0;
  handler->object_signal = FALSE;
  handler->after = FALSE;
  handler->no_marshal = FALSE;
  handler->ref_count = 1;
  handler->func = NULL;
  handler->func_data = NULL;
  handler->destroy_func = NULL;
  handler->prev = NULL;
  handler->next = NULL;
  
  return handler;
}

static void
gtk_signal_handler_ref (GtkHandler *handler)
{
  handler->ref_count += 1;
}

static void
gtk_signal_handler_unref (GtkHandler *handler,
			  GtkObject  *object)
{
  if (!handler->ref_count)
    {
      /* FIXME: i wanna get removed somewhen */
      g_warning ("gtk_signal_handler_unref(): handler with ref_count==0!");
      return;
    }
  
  handler->ref_count -= 1;
  
  if (handler->ref_count == 0)
    {
      if (handler->destroy_func)
	(* handler->destroy_func) (handler->func_data);
      else if (!handler->func && global_destroy_notify)
	(* global_destroy_notify) (handler->func_data);
      
      if (handler->prev)
	handler->prev->next = handler->next;
      else if (handler->next)
	gtk_object_set_data_by_id (object, handler_quark, handler->next);
      else
	{
	  GTK_OBJECT_UNSET_FLAGS (object, GTK_CONNECTED);
	  gtk_object_set_data_by_id (object, handler_quark, NULL);
	}
      if (handler->next)
	handler->next->prev = handler->prev;
      
      handler->next = gtk_handler_free_list;
      gtk_handler_free_list = handler;
    }
}

static void
gtk_signal_handler_insert (GtkObject  *object,
			   GtkHandler *handler)
{
  GtkHandler *tmp;
  
  /* FIXME: remove */ g_assert (handler->next == NULL);
  /* FIXME: remove */ g_assert (handler->prev == NULL);
  
  tmp = gtk_object_get_data_by_id (object, handler_quark);
  if (!tmp)
    {
      GTK_OBJECT_SET_FLAGS (object, GTK_CONNECTED);
      gtk_object_set_data_by_id (object, handler_quark, handler);
    }
  else
    while (tmp)
      {
	if (tmp->signal_id < handler->signal_id)
	  {
	    if (tmp->prev)
	      {
		tmp->prev->next = handler;
		handler->prev = tmp->prev;
	      }
	    else
	      gtk_object_set_data_by_id (object, handler_quark, handler);
	    tmp->prev = handler;
	    handler->next = tmp;
	    break;
	  }
	
	if (!tmp->next)
	  {
	    tmp->next = handler;
	    handler->prev = tmp;
	    break;
	  }
	tmp = tmp->next;
      }
}

static GtkObject *gtk_trace_signal_object = NULL;

static void
gtk_signal_real_emit (GtkObject *object,
		      GtkSignal *signal,
		      GtkArg	*params)
{
  GtkHandler	*handlers;
  GtkHandlerInfo info;
  guchar       **signal_func_offset;
  register guint signal_id = signal->signal_id;

#ifdef  G_ENABLE_DEBUG
  if (gtk_debug_flags & GTK_DEBUG_SIGNALS ||
      object == gtk_trace_signal_object)
    fprintf (stdout, "trace: signal_emit(\"%s\") for %s:%p\n",
	     signal->name,
	     gtk_type_name (GTK_OBJECT_TYPE (object)),
	     object);
#endif  /* G_ENABLE_DEBUG */
  
  if ((signal->signal_flags & GTK_RUN_NO_RECURSE) &&
      gtk_emission_check (current_emissions, object, signal_id))
    {
      gtk_emission_add (&restart_emissions, object, signal_id);
      return;
    }
  
  gtk_object_ref (object);

  gtk_emission_add (&current_emissions, object, signal_id);
  
 emission_restart:
  if (GTK_RUN_TYPE (signal->signal_flags) != GTK_RUN_LAST && signal->function_offset != 0)
    {
      signal_func_offset = (guchar**) ((guchar*) object->klass +
				       signal->function_offset);
      if (*signal_func_offset)
	(* signal->marshaller) (object, (GtkSignalFunc) *signal_func_offset,
				NULL, params);
    }
  
  if (GTK_OBJECT_CONNECTED (object))
    {
      handlers = gtk_signal_get_handlers (object, signal_id);
      if (handlers)
	{
	  info.object = object;
	  info.marshaller = signal->marshaller;
	  info.params = params;
	  info.param_types = signal->params;
	  info.return_val = signal->return_val;
	  info.nparams = signal->nparams;
	  info.signal_flags = signal->signal_flags;
	  info.signal_id = signal_id;

	  switch (gtk_handlers_run (handlers, &info, FALSE))
	    {
	    case  EMISSION_CONTINUE:
	      break;
	    case  EMISSION_RESTART:
	      goto emission_restart;
	    case  EMISSION_DONE:
	      goto emission_done;
	    }
	}
      else
	info.object = NULL;
    }
  else
    info.object = NULL;
  
  if (GTK_RUN_TYPE (signal->signal_flags) != GTK_RUN_FIRST	&& signal->function_offset != 0)
    {
      signal_func_offset = (guchar**) ((guchar*) object->klass +
				       signal->function_offset);
      if (*signal_func_offset)
	(* signal->marshaller) (object, (GtkSignalFunc) *signal_func_offset,
				NULL, params);
    }
  
  if (GTK_OBJECT_CONNECTED (object))
    {
      handlers = gtk_signal_get_handlers (object, signal_id);
      if (handlers)
	{
	  if (!info.object)
	    {
	      info.object = object;
	      info.marshaller = signal->marshaller;
	      info.params = params;
	      info.param_types = signal->params;
	      info.return_val = signal->return_val;
	      info.nparams = signal->nparams;
	      info.signal_flags = signal->signal_flags;
	      info.signal_id = signal_id;
	    }
	  switch (gtk_handlers_run (handlers, &info, TRUE))
	    {
	    case  EMISSION_CONTINUE:
	      break;
	    case  EMISSION_RESTART:
	      goto emission_restart;
	    case  EMISSION_DONE:
	      goto emission_done;
	    }
	}
    }
  
 emission_done:
  
  gtk_emission_remove (&current_emissions, object, signal_id);
  
  if (signal->signal_flags & GTK_RUN_NO_RECURSE)
    gtk_emission_remove (&restart_emissions, object, signal_id);
  
  gtk_object_unref (object);
}

static GtkHandler*
gtk_signal_get_handlers (GtkObject *object,
			 guint	    signal_id)
{
  GtkHandler *handlers;
  
  handlers = gtk_object_get_data_by_id (object, handler_quark);
  
  while (handlers)
    {
      if (handlers->signal_id == signal_id)
	return handlers;
      handlers = handlers->next;
    }
  
  return NULL;
}

guint
gtk_signal_handler_pending (GtkObject		*object,
			    guint		 signal_id,
			    gboolean		 may_be_blocked)
{
  GtkHandler *handlers;
  guint handler_id;
  
  g_return_val_if_fail (object != NULL, 0);
  g_return_val_if_fail (signal_id >= 1, 0);

  if (GTK_OBJECT_CONNECTED (object))
    handlers = gtk_signal_get_handlers (object, signal_id);
  else
    return 0;
  
  handler_id = 0;
  while (handlers && handlers->signal_id == signal_id)
    {
      if (handlers->id > 0 &&
	  (may_be_blocked || handlers->blocked == FALSE))
	{
	  handler_id = handlers->id;
	  break;
	}
      
      handlers = handlers->next;
    }
  
  return handler_id;
}

guint
gtk_signal_handler_pending_by_func (GtkObject           *object,
				    guint                signal_id,
				    gboolean             may_be_blocked,
				    GtkSignalFunc        func,
				    gpointer             data)
{
  GtkHandler *handlers;
  guint handler_id;
  
  g_return_val_if_fail (object != NULL, 0);
  g_return_val_if_fail (func != NULL, 0);
  g_return_val_if_fail (signal_id >= 1, 0);

  if (GTK_OBJECT_CONNECTED (object))
    handlers = gtk_signal_get_handlers (object, signal_id);
  else
    return 0;
  
  handler_id = 0;
  while (handlers && handlers->signal_id == signal_id)
    {
      if (handlers->id > 0 &&
	  handlers->func == func &&
	  handlers->func_data == data &&
	  (may_be_blocked || handlers->blocked == FALSE))
	{
	  handler_id = handlers->id;
	  break;
	}
      
      handlers = handlers->next;
    }
  
  return handler_id;
}

static guint
gtk_signal_connect_by_type (GtkObject	    *object,
			    guint	     signal_id,
			    GtkSignalFunc    func,
			    gpointer	     func_data,
			    GtkSignalDestroy destroy_func,
			    gint	     object_signal,
			    gint	     after,
			    gint	     no_marshal)
{
  GtkObjectClass *class;
  GtkHandler *handler;
  gint found_it;
  
  g_return_val_if_fail (object != NULL, 0);
  g_return_val_if_fail (object->klass != NULL, 0);
  
  /* Search through the signals for this object and make
   *  sure the one we are adding is valid. We need to perform
   *  the lookup on the objects parents as well. If it isn't
   *  valid then issue a warning and return.
   * As of now (1998-05-27) this lookup shouldn't be neccessarry
   *  anymore since gtk_signal_lookup() has been reworked to only
   *  return correct signal ids per class-branch.
   */
  found_it = FALSE;
  class = object->klass;
  while (class)
    {
      GtkType parent;
      guint *object_signals;
      guint nsignals;
      guint i;
      
      object_signals = class->signals;
      nsignals = class->nsignals;
      
      for (i = 0; i < nsignals; i++)
	if (object_signals[i] == signal_id)
	  {
	    found_it = TRUE;
	    break;
	  }
      
      parent = gtk_type_parent (class->type);
      if (parent)
	class = gtk_type_class (parent);
      else
	class = NULL;
    }
  
  if (!found_it)
    {
      g_warning ("gtk_signal_connect_by_type(): could not find signal id (%u) in the `%s' class ancestry",
		 signal_id,
		 gtk_type_name (object->klass->type));
      return 0;
    }
  
  handler = gtk_signal_handler_new ();
  handler->id = gtk_handler_id++;
  handler->signal_id = signal_id;
  handler->object_signal = object_signal;
  handler->func = func;
  handler->func_data = func_data;
  handler->destroy_func = destroy_func;
  handler->after = after != FALSE;
  handler->no_marshal = no_marshal;
  
  gtk_signal_handler_insert (object, handler);
  return handler->id;
}

static GtkEmission*
gtk_emission_new (void)
{
  GtkEmission *emission;
  
  if (!gtk_emission_free_list)
    {
      GtkEmissionAllocator *emission_block;
      guint i;

      emission_block = g_new0 (GtkEmissionAllocator, EMISSION_BLOCK_SIZE);
      for (i = 1; i < EMISSION_BLOCK_SIZE; i++)
	{
	  (emission_block + i)->next = gtk_emission_free_list;
	  gtk_emission_free_list = (emission_block + i);
	}

      emission = &emission_block->emission;
    }
  else
    {
      emission = &gtk_emission_free_list->emission;
      gtk_emission_free_list = gtk_emission_free_list->next;
    }

  emission->object = NULL;
  emission->signal_id = 0;
  
  return emission;
}

static void
gtk_emission_add (GList	    **emissions,
		  GtkObject  *object,
		  guint	      signal_id)
{
  GtkEmission *emission;
  
  g_return_if_fail (emissions != NULL);
  g_return_if_fail (object != NULL);
  
  emission = gtk_emission_new ();
  emission->object = object;
  emission->signal_id = signal_id;
  
  *emissions = g_list_prepend (*emissions, emission);
}

static void
gtk_emission_remove (GList     **emissions,
		     GtkObject	*object,
		     guint	 signal_id)
{
  GList *tmp;
  
  g_return_if_fail (emissions != NULL);
  
  tmp = *emissions;
  while (tmp)
    {
      GtkEmissionAllocator *ea;
      
      ea = tmp->data;
      
      if ((ea->emission.object == object) &&
	  (ea->emission.signal_id == signal_id))
	{
	  *emissions = g_list_remove_link (*emissions, tmp);
	  g_list_free (tmp);

	  ea->next = gtk_emission_free_list;
	  gtk_emission_free_list = ea;
	  break;
	}
      
      tmp = tmp->next;
    }
}

static gint
gtk_emission_check (GList     *emissions,
		    GtkObject *object,
		    guint      signal_id)
{
  GtkEmission *emission;
  GList *tmp;
  
  tmp = emissions;
  while (tmp)
    {
      emission = tmp->data;
      tmp = tmp->next;
      
      if ((emission->object == object) &&
	  (emission->signal_id == signal_id))
	return TRUE;
    }
  return FALSE;
}

static gint
gtk_handlers_run (GtkHandler	 *handlers,
		  GtkHandlerInfo *info,
		  gint		  after)
{
  while (handlers && handlers->signal_id == info->signal_id)
    {
      GtkHandler *handlers_next;
      
      gtk_signal_handler_ref (handlers);
      
      if (handlers->blocked == 0 && (handlers->after == after))
	{
	  if (handlers->func)
	    {
	      if (handlers->no_marshal)
		(* (GtkCallbackMarshal) handlers->func) (info->object,
							 handlers->func_data,
							 info->nparams,
							 info->params);
	      else if (handlers->object_signal)
		(* info->marshaller) ((GtkObject*) handlers->func_data, /* don't GTK_OBJECT() cast */
				      handlers->func,
				      handlers->func_data,
				      info->params);
	      else
		(* info->marshaller) (info->object,
				      handlers->func,
				      handlers->func_data,
				      info->params);
	    }
	  else if (global_marshaller)
	    (* global_marshaller) (info->object,
				   handlers->func_data,
				   info->nparams,
				   info->params,
				   info->param_types,
				   info->return_val);
	  
	  if (gtk_emission_check (stop_emissions, info->object,
				  info->signal_id))
	    {
	      gtk_emission_remove (&stop_emissions, info->object,
				   info->signal_id);
	      
	      if (info->signal_flags & GTK_RUN_NO_RECURSE)
		gtk_emission_remove (&restart_emissions, info->object,
				     info->signal_id);
	      gtk_signal_handler_unref (handlers, info->object);
	      return EMISSION_DONE;
	    }
	  else if ((info->signal_flags & GTK_RUN_NO_RECURSE) &&
		   gtk_emission_check (restart_emissions, info->object,
				       info->signal_id))
	    {
	      gtk_emission_remove (&restart_emissions, info->object,
				   info->signal_id);
	      gtk_signal_handler_unref (handlers, info->object);
	      return EMISSION_RESTART;
	    }
	}
      
      handlers_next = handlers->next;
      gtk_signal_handler_unref (handlers, info->object);
      handlers = handlers_next;
    }
  
  return EMISSION_CONTINUE;
}

static gboolean
gtk_signal_collect_params (GtkArg	       *params,
			   guint		n_params,
			   GtkType	       *param_types,
			   GtkType		return_type,
			   va_list		var_args)
{
  register GtkArg *last_param;
  register gboolean failed = FALSE;

  for (last_param = params + n_params; params < last_param; params++)
    {
      register gchar *error;

      params->type = *(param_types++);
      params->name = NULL;
      error = gtk_arg_collect_value (GTK_FUNDAMENTAL_TYPE (params->type),
				     params,
				     &var_args);
      if (error)
	{
	  failed = TRUE;
	  g_warning ("gtk_signal_collect_params(): %s", error);
	  g_free (error);
	}
    }

  params->type = return_type;
  params->name = NULL;

  return_type = GTK_FUNDAMENTAL_TYPE (return_type);
  if (return_type != GTK_TYPE_NONE)
    {
      if ((return_type >= GTK_TYPE_CHAR &&
	   return_type <= GTK_TYPE_BOXED) ||
	  (return_type == GTK_TYPE_POINTER) ||
	  (return_type == GTK_TYPE_OBJECT))
	{
	  GTK_VALUE_POINTER (*params) = va_arg (var_args, gpointer);
	  
	  if (GTK_VALUE_POINTER (*params) == NULL)
	    {
	      failed = TRUE;
	      g_warning ("gtk_signal_collect_params(): invalid NULL pointer for return argument type `%s'",
			 gtk_type_name (params->type));
	    }
	}
      else
	{
	  failed = TRUE;
	  g_warning ("gtk_signal_collect_params(): unsupported return argument type `%s'",
		     gtk_type_name (params->type));
	}
    }
  else
    GTK_VALUE_POINTER (*params) = NULL;

  return failed;
}