GSignal convenience functions



It seems that one of the things that is holding up use of GObject is that
it is more difficult to use the GSignal functions than the old GtkSignal
ones.  To try and get things moving a bit, I put together a patch that
adds g_signal_new, g_signal_emit, g_signal_emitv and
g_signal_connect_cclosure functions that make signals more convenient to
use from C.

I have done a little testing of these functions, and they seem to work
well.  One bit of ugly code is putting the instance variable in the GValue
array.  At the moment, I misuse the collect_value function for the type
(if it is available) to set the value, or just set it as a G_TYPE_POINTER
value.  This could probably be cleaned up a bit more.

We may want to change the name of g_signal_connect_cclosure
though.  Alternatively, with a few #defines, we could provide APIs more
similar to gtk_signal_connect, _after, _object and _object_after in terms 
of connect_cclosure.

There are probably a few problems with this patch, but it is probably a
good start for these functions.

James.
Index: gsignal.c
===================================================================
RCS file: /cvs/gnome/glib/gobject/gsignal.c,v
retrieving revision 1.13
diff -u -r1.13 gsignal.c
--- gsignal.c	2000/11/29 12:34:14	1.13
+++ gsignal.c	2000/12/07 13:05:56
@@ -24,6 +24,8 @@
 #include        "gsignal.h"
 
 #include        "gbsearcharray.h"
+#include        "gvaluetypes.h"
+#include        "gvaluecollector.h"
 
 
 /* pre allocation configurations
@@ -965,6 +967,56 @@
   return signal_id;
 }
 
+guint
+g_signal_new (const gchar	 *signal_name,
+	      GType		  itype,
+	      GSignalFlags	  signal_flags,
+	      guint		  function_offset,
+	      GSignalAccumulator  accumulator,
+	      GSignalCMarshaller  c_marshaller,
+	      GType		  return_type,
+	      guint		  n_params,
+	      ...)
+{
+  GClosure *class_closure = NULL;
+  GType *params;
+  guint i;
+  va_list args;
+  guint signal_id;
+
+  if (function_offset != 0)
+    class_closure = g_signal_type_closure_new (itype, function_offset);
+
+  if (n_params > 0)
+    {
+      params = g_new (GType, n_params);
+
+      va_start (args, n_params);
+
+      for (i = 0; i < n_params; i++)
+	params[i] = va_arg (args, GType);
+
+      va_end (args);
+    }
+  else
+    params = NULL;
+
+  signal_id = g_signal_newv (signal_name,
+			     itype,
+			     signal_flags,
+			     class_closure,
+			     accumulator,
+			     c_marshaller,
+			     return_type,
+			     n_params,
+			     params);
+
+  g_free (params);
+
+  return signal_id;
+}
+
+
 static void
 signal_destroy_R (SignalNode *signal_node)
 {
@@ -1047,6 +1099,29 @@
   return handler_id;
 }
 
+guint
+g_signal_connect_cclosure (gpointer           instance,
+			   gchar             *signal,
+			   GQuark             detail,
+			   GCallback          func,
+			   gpointer           user_data,
+			   gboolean           after,
+			   gboolean           swap)
+{
+  GClosure *closure;
+
+  if (swap)
+    closure = g_cclosure_new_swap (func, user_data, NULL);
+  else
+    closure = g_cclosure_new (func, user_data, NULL);
+
+  return g_signal_connect_closure_by_id (instance,
+					 g_signal_lookup (signal, G_TYPE_FROM_INSTANCE (instance)),
+					 detail,
+					 closure,
+					 after);
+}
+
 void
 g_signal_handler_block (gpointer instance,
                         guint    handler_id)
@@ -1408,6 +1483,171 @@
   
   G_UNLOCK (g_signal_mutex);
 }
+
+static gboolean
+g_signal_collect_params (GValue      *instance_and_params,
+			 guint        n_params,
+			 GType        itype,
+			 const GType *param_types,
+			 gpointer     instance,
+			 va_list      var_args)
+{
+  GTypeValueTable *value_table;
+  register GValue *last_param;
+  register gboolean failed = FALSE;
+
+  /* set instance member.  Misuse the collect_value method if
+   * available. Otherwise, just use G_TYPE_POINTER. */
+  value_table = g_type_value_table_peek (itype);
+  if (value_table->collect_value)
+    {
+      GType collect_type;
+      GTypeCValue collect_value;
+      gchar *error;
+
+      g_value_init (instance_and_params, itype);
+      collect_value.v_pointer = instance;
+      error = (value_table->collect_value) (instance_and_params, 0,
+					    &collect_type, &collect_value);
+      if (error)
+	{
+	  g_warning("g_signal_collect_params(): %s", error);
+	  g_free(error);
+	  failed = TRUE;
+	}
+    }
+  else
+    {
+      /* not perfect, but will work with the generated marshalers */
+      g_value_init (instance_and_params, G_TYPE_POINTER);
+      g_value_set_pointer (instance_and_params, instance);
+    }
+
+  instance_and_params++;
+  for (last_param = instance_and_params + n_params;
+       instance_and_params < last_param; instance_and_params++)
+    {
+      gchar *error;
+
+      g_value_init (instance_and_params, *(param_types++));
+      G_VALUE_COLLECT (instance_and_params, var_args, &error);
+      if (error)
+	{
+	  failed = TRUE;
+	  g_warning ("g_signal_collect_params(): %s", error);
+	  g_free (error);
+	}
+    }
+
+  return failed;
+}
+
+void
+g_signal_emit (gpointer		  instance,
+	       guint		  signal_id,
+	       GQuark		  detail,
+	       ...)
+{
+  GValue *instance_and_params;
+  GValue return_value = { 0, };
+  GSignalQuery query;
+  gboolean abort = FALSE;
+  va_list var_args;
+  guint i;
+
+  g_signal_query (signal_id, &query);
+  g_return_if_fail (query.signal_id != 0);
+
+  instance_and_params = g_new0 (GValue, query.n_params + 1);
+
+  va_start (var_args, detail);
+  abort = g_signal_collect_params (instance_and_params,
+				   query.n_params,
+				   query.itype,
+				   query.param_types,
+				   instance,
+				   var_args);
+  if (!abort)
+    {
+      if (query.return_type != G_TYPE_NONE)
+	g_value_init (&return_value, query.return_type);
+
+      g_signal_emitv (instance_and_params, query.signal_id,
+		      detail, &return_value);
+
+      if (query.return_type != G_TYPE_NONE)
+	{
+	  gchar *error;
+	  G_VALUE_LCOPY (&return_value, var_args, &error);
+	  if (error)
+	    {
+	      g_warning("g_signal_emit(): %s", error);
+	      g_free(error);
+	    }
+	  g_value_unset (&return_value);
+	}
+    }
+  va_end (var_args);
+
+  for (i = 0; i <= query.n_params; i++)
+    g_value_unset (instance_and_params + i);
+  g_free (instance_and_params);
+}
+
+void
+g_signal_emit_by_name (gpointer		  instance,
+		       gchar		 *name,
+		       GQuark		  detail,
+		       ...)
+{
+  GValue *instance_and_params;
+  GValue return_value = { 0, };
+  GSignalQuery query;
+  gboolean abort = FALSE;
+  va_list var_args;
+  guint i;
+
+  g_signal_query (g_signal_lookup (name, G_TYPE_FROM_INSTANCE(instance)),
+		  &query);
+  g_return_if_fail (query.signal_id != 0);
+
+  instance_and_params = g_new0 (GValue, query.n_params + 1);
+
+  va_start (var_args, detail);
+  abort = g_signal_collect_params (instance_and_params,
+				   query.n_params,
+				   query.itype,
+				   query.param_types,
+				   instance,
+				   var_args);
+  if (!abort)
+    {
+      if (query.return_type != G_TYPE_NONE)
+	g_value_init (&return_value, query.return_type);
+
+      g_signal_emitv (instance_and_params, query.signal_id,
+		      detail, &return_value);
+
+      if (query.return_type != G_TYPE_NONE)
+	{
+	  gchar *error;
+
+	  G_VALUE_LCOPY (&return_value, var_args, &error);
+	  if (error)
+	    {
+	      g_warning("g_signal_emit(): %s", error);
+	      g_free(error);
+	    }
+	  g_value_unset (&return_value);
+	}
+    }
+  va_end (var_args);
+
+  for (i = 0; i <= query.n_params; i++)
+    g_value_unset (instance_and_params + i);
+  g_free (instance_and_params);
+}
+
 
 static void
 signal_emit_R (SignalNode   *node,
Index: gsignal.h
===================================================================
RCS file: /cvs/gnome/glib/gobject/gsignal.h,v
retrieving revision 1.7
diff -u -r1.7 gsignal.h
--- gsignal.h	2000/11/05 05:07:26	1.7
+++ gsignal.h	2000/12/07 13:05:56
@@ -95,10 +95,27 @@
 					       GType		  return_type,
 					       guint		  n_params,
 					       GType		 *param_types);
+guint g_signal_new			      (const gchar	 *signal_name,
+					       GType		  itype,
+					       GSignalFlags	   signal_flags,
+					       guint		  function_offset,
+					       GSignalAccumulator accumulator,
+					       GSignalCMarshaller c_marshaller,
+					       GType		  return_type,
+					       guint		  n_params,
+					       ...);
 void	g_signal_emitv			      (const GValue	 *instance_and_params,
 					       guint		  signal_id,
 					       GQuark		  detail,
 					       GValue		 *return_value);
+void	g_signal_emit			      (gpointer		  instance,
+					       guint		  signal_id,
+					       GQuark		  detail,
+					       ...);
+void	g_signal_emit_by_name		      (gpointer		  instance,
+					       gchar		 *signal,
+					       GQuark		  detail,
+					       ...);
 guint	g_signal_lookup			      (const gchar	 *name,
 					       GType		  itype);
 gchar*	g_signal_name			      (guint		  signal_id);
@@ -127,6 +144,13 @@
 					       GQuark		  detail,
 					       GClosure		 *closure,
 					       gboolean		  after);
+guint    g_signal_connect_cclosure            (gpointer           instance,
+					       gchar             *name,
+					       GQuark             detail,
+					       GCallback          func,
+					       gpointer           user_data,
+					       gboolean           after,
+					       gboolean           swap);
 void	 g_signal_handler_block		      (gpointer		  instance,
 					       guint		  handler_id);
 void	 g_signal_handler_unblock	      (gpointer		  instance,


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