gsignal 1.new/2.connect/3.emit

os posted @ 2013年8月21日 14:32 in GObject , 1444 阅读

gobjec相关学习文章的list.

 

我们从一个实际的例子看看:

New Signal:

1. New一个signal

在类初始化时,先指定了成员method:

klass->show = gtk_widget_real_show;

 然后定义信号:

  widget_signals[SHOW] =
    g_signal_new (I_("show"),
		  G_TYPE_FROM_CLASS (gobject_class),
		  G_SIGNAL_RUN_FIRST,
		  G_STRUCT_OFFSET (GtkWidgetClass, show),
		  NULL, NULL,
		  _gtk_marshal_VOID__VOID,
		  G_TYPE_NONE, 0);

这里注意:G_STRUCT_OFFSET( GtkWidgetClass, show)

这个是靠地址偏移,找到GtkWidgetClass的成员函数指针的地址

 

2. 往下看:

guint
g_signal_new (const gchar	 *signal_name,
	      GType		  itype,
	      GSignalFlags	  signal_flags,
	      guint               class_offset,
	      GSignalAccumulator  accumulator,
	      gpointer		  accu_data,
	      GSignalCMarshaller  c_marshaller,
	      GType		  return_type,
	      guint		  n_params,
	      ...)
{
  va_list args;
  guint signal_id;

  g_return_val_if_fail (signal_name != NULL, 0);
  
  va_start (args, n_params);

  signal_id = g_signal_new_valist (signal_name, itype, signal_flags,
                                   class_offset ? g_signal_type_cclosure_new (itype, class_offset) : NULL,
				   accumulator, accu_data, c_marshaller,
                                   return_type, n_params, args);

  va_end (args);

一般情况下,class_offset不是0, 所以会调用:g_signal_type_cclosure_new (itype, class_offset)

GClosure*
g_signal_type_cclosure_new (GType    itype,
			    guint    struct_offset)
{
  GClosure *closure;
  
  g_return_val_if_fail (G_TYPE_IS_CLASSED (itype) || G_TYPE_IS_INTERFACE (itype), NULL);
  g_return_val_if_fail (struct_offset >= sizeof (GTypeClass), NULL);
  
  closure = g_closure_new_simple (sizeof (GClosure), (gpointer) itype);
  if (G_TYPE_IS_INTERFACE (itype))
    g_closure_set_meta_marshal (closure, GUINT_TO_POINTER (struct_offset), g_type_iface_meta_marshal);
  else
    g_closure_set_meta_marshal (closure, GUINT_TO_POINTER (struct_offset), g_type_class_meta_marshal);
  
  return closure;
}

GUINT_TO_POINTER (struct_offset), 这个就是函数指针:指的就是 GtkWidgetClass的Method:(*show)

这个(*show)作为回调函数的。

往下看:

void
g_closure_set_meta_marshal (GClosure       *closure,
			    gpointer        marshal_data,
			    GClosureMarshal meta_marshal)
{
  GClosureNotifyData *notifiers;

  g_return_if_fail (closure != NULL);
  g_return_if_fail (meta_marshal != NULL);
  g_return_if_fail (closure->is_invalid == FALSE);
  g_return_if_fail (closure->in_marshal == FALSE);
  g_return_if_fail (closure->meta_marshal == 0);

  notifiers = closure->notifiers;
  closure->notifiers = g_renew (GClosureNotifyData, NULL, CLOSURE_N_NOTIFIERS (closure) + 1);
  if (notifiers)
    {
      /* usually the meta marshal will be setup right after creation, so the
       * g_memmove() should be rare-case scenario
       */
      g_memmove (closure->notifiers + 1, notifiers, CLOSURE_N_NOTIFIERS (closure) * sizeof (notifiers[0]));
      g_free (notifiers);
    }
  closure->notifiers[0].data = marshal_data;
  closure->notifiers[0].notify = (GClosureNotify) meta_marshal;
  SET (closure, meta_marshal, 1);
}

这里的marshal_data, 就是上面的:GUINT_TO_POINTER (struct_offset), 就是用户指定的callback了。

同时,这个callback作为最后一个参数,传入:g_type_iface_meta_marshal

static void
g_type_class_meta_marshal (GClosure       *closure,
			   GValue /*out*/ *return_value,
			   guint           n_param_values,
			   const GValue   *param_values,
			   gpointer        invocation_hint,
			   gpointer        marshal_data)
{
  GTypeClass *class;
  gpointer callback;
  /* GType itype = (GType) closure->data; */
  guint offset = GPOINTER_TO_UINT (marshal_data);
  
  class = G_TYPE_INSTANCE_GET_CLASS (g_value_peek_pointer (param_values + 0), itype, GTypeClass);
  callback = G_STRUCT_MEMBER (gpointer, class, offset);
  if (callback)
    closure->marshal (closure,
		      return_value,
		      n_param_values, param_values,
		      invocation_hint,
		      callback);
}

在这个函数里面,首先提取callback.

如果有callback的化,后面会把callback做为最后一个参数,传入到marshal里面;

比如:_gtk_marshal_VOID__VOID == g_cclosure_marshal_VOID__VOID

void
g_cclosure_marshal_VOID__VOID (GClosure     *closure,
                               GValue       *return_value G_GNUC_UNUSED,
                               guint         n_param_values,
                               const GValue *param_values,
                               gpointer      invocation_hint G_GNUC_UNUSED,
                               gpointer      marshal_data)
{
  typedef void (*GMarshalFunc_VOID__VOID) (gpointer     data1,
                                           gpointer     data2);
  register GMarshalFunc_VOID__VOID callback;
  register GCClosure *cc = (GCClosure*) closure;
  register gpointer data1, data2;

  g_return_if_fail (n_param_values == 1);

  if (G_CCLOSURE_SWAP_DATA (closure))
    {
      data1 = closure->data;
      data2 = g_value_peek_pointer (param_values + 0);
    }
  else
    {
      data1 = g_value_peek_pointer (param_values + 0);
      data2 = closure->data;
    }
  callback = (GMarshalFunc_VOID__VOID) (marshal_data ? marshal_data : cc->callback);

  callback (data1,
            data2);
}

最后真正调用的这是前面指定的callback: klass->show = gtk_widget_real_show;

 

上边把user callback同closure绑定之后,g_signal_new_valist会调用g_signal_newv

guint
g_signal_newv (const gchar       *signal_name,
               GType              itype,
               GSignalFlags       signal_flags,
               GClosure          *class_closure,
               GSignalAccumulator accumulator,
	       gpointer		  accu_data,
               GSignalCMarshaller c_marshaller,
               GType		  return_type,
               guint              n_params,
               GType		 *param_types)
{
  gchar *name;
  guint signal_id, i;
  SignalNode *node;
  
  g_return_val_if_fail (signal_name != NULL, 0);
  g_return_val_if_fail (G_TYPE_IS_INSTANTIATABLE (itype) || G_TYPE_IS_INTERFACE (itype), 0);
  if (n_params)
    g_return_val_if_fail (param_types != NULL, 0);
  g_return_val_if_fail ((return_type & G_SIGNAL_TYPE_STATIC_SCOPE) == 0, 0);
  if (return_type == (G_TYPE_NONE & ~G_SIGNAL_TYPE_STATIC_SCOPE))
    g_return_val_if_fail (accumulator == NULL, 0);
  if (!accumulator)
    g_return_val_if_fail (accu_data == NULL, 0);

  name = g_strdup (signal_name);
  g_strdelimit (name, G_STR_DELIMITERS ":^", '_');  /* FIXME do character checks like for types */
  
  SIGNAL_LOCK ();
  
  signal_id = signal_id_lookup (g_quark_try_string (name), itype);
  node = LOOKUP_SIGNAL_NODE (signal_id);

  /* setup permanent portion of signal node */
  if (!node)
    {
      SignalKey key;
      
      signal_id = g_n_signal_nodes++;
      node = g_new (SignalNode, 1);
      node->signal_id = signal_id;
      g_signal_nodes = g_renew (SignalNode*, g_signal_nodes, g_n_signal_nodes);
      g_signal_nodes[signal_id] = node;
      node->itype = itype;
      node->name = name;
      key.itype = itype;
      key.quark = g_quark_from_string (node->name);
      key.signal_id = signal_id;
      g_signal_key_bsa = g_bsearch_array_insert (g_signal_key_bsa, &g_signal_key_bconfig, &key);
      g_strdelimit (name, "_", '-');
      node->name = g_intern_string (name);
      key.quark = g_quark_from_string (name);
      g_signal_key_bsa = g_bsearch_array_insert (g_signal_key_bsa, &g_signal_key_bconfig, &key);

      TRACE(GOBJECT_SIGNAL_NEW(signal_id, name, itype));
    }
  node->destroyed = FALSE;
  node->test_class_offset = 0;

  /* setup reinitializable portion */
  node->flags = signal_flags & G_SIGNAL_FLAGS_MASK;
  node->n_params = n_params;
  node->param_types = g_memdup (param_types, sizeof (GType) * n_params);
  node->return_type = return_type;
  node->class_closure_bsa = NULL;
  if (accumulator)
    {
      node->accumulator = g_new (SignalAccumulator, 1);
      node->accumulator->func = accumulator;
      node->accumulator->data = accu_data;
    }
  else
    node->accumulator = NULL;
  node->c_marshaller = c_marshaller;
  node->emission_hooks = NULL;
  if (class_closure)
    signal_add_class_closure (node, 0, class_closure);
  else if (G_TYPE_IS_INSTANTIATABLE (itype) && return_type == G_TYPE_NONE)
    {
      /* optimize NOP emissions */
      node->test_class_offset = TEST_CLASS_MAGIC;
    }
  SIGNAL_UNLOCK ();

  g_free (name);

  return signal_id;
}

上面这个函数完成:

1. 如果是新建的Signal, 则把它放入SignalNode数组中:g_signal_nodes[signal_id]

2. 并且放入用于查询的二分数组中:g_signal_key_bsa

3. 因为user指定了自己的回调,所以signal_add_class_closure()把class closure也放入

    到相关的二分查找数组中:node->class_closure_bsa

4. 指定c_marshal, 这是:_gtk_marshal_VOID__VOID

 

New signal的小结:

1. Signal Node生成

2. callback指定

3. c Marshal指定

 

Signal Connect:

gsignal.c提供了3个相关的API:

gulong	 g_signal_connect_closure_by_id	      (gpointer		  instance,
					       guint		  signal_id,
					       GQuark		  detail,
					       GClosure		 *closure,
					       gboolean		  after);
gulong	 g_signal_connect_closure	      (gpointer		  instance,
					       const gchar       *detailed_signal,
					       GClosure		 *closure,
					       gboolean		  after);
gulong	 g_signal_connect_data		      (gpointer		  instance,
					       const gchar	 *detailed_signal,
					       GCallback	  c_handler,
					       gpointer		  data,
					       GClosureNotify	  destroy_data,
					       GConnectFlags	  connect_flags);

用的最多的是g_signal_connect_data, 直接用它的也有,不过大多是用经过包装后的:

gtype.h:

#define g_signal_connect(instance, detailed_signal, c_handler, data) \
    g_signal_connect_data ((instance), (detailed_signal), (c_handler), (data), NULL, (GConnectFlags) 0)
#define g_signal_connect_after(instance, detailed_signal, c_handler, data) \
    g_signal_connect_data ((instance), (detailed_signal), (c_handler), (data), NULL, G_CONNECT_AFTER)
#define g_signal_connect_swapped(instance, detailed_signal, c_handler, data) \
    g_signal_connect_data ((instance), (detailed_signal), (c_handler), (data), NULL, G_CONNECT_SWAPPED)

以及:

gobject.c:

gulong
g_signal_connect_object (gpointer      instance,
			 const gchar  *detailed_signal,
			 GCallback     c_handler,
			 gpointer      gobject,
			 GConnectFlags connect_flags)

这个函数其实是封装:g_signal_connect_closure/g_signal_connect_data

gulong
g_signal_connect_object (gpointer      instance,
			 const gchar  *detailed_signal,
			 GCallback     c_handler,
			 gpointer      gobject,
			 GConnectFlags connect_flags)
{
  g_return_val_if_fail (G_TYPE_CHECK_INSTANCE (instance), 0);
  g_return_val_if_fail (detailed_signal != NULL, 0);
  g_return_val_if_fail (c_handler != NULL, 0);

  if (gobject)
    {
      GClosure *closure;

      g_return_val_if_fail (G_IS_OBJECT (gobject), 0);

      closure = ((connect_flags & G_CONNECT_SWAPPED) ? g_cclosure_new_object_swap : g_cclosure_new_object) (c_handler, gobject);

      return g_signal_connect_closure (instance, detailed_signal, closure, connect_flags & G_CONNECT_AFTER);
    }
  else
    return g_signal_connect_data (instance, detailed_signal, c_handler, NULL, NULL, connect_flags);
}

 

 

 Signal Emit: 

void
gtk_widget_show (GtkWidget *widget)
{
  g_return_if_fail (GTK_IS_WIDGET (widget));

  if (!gtk_widget_get_visible (widget))
    {
      g_object_ref (widget);
      gtk_widget_push_verify_invariants (widget);

      if (!gtk_widget_is_toplevel (widget))
        gtk_widget_queue_resize (widget);

      /* see comment in set_parent() for why this should and can be
       * conditional
       */
      if (widget->priv->need_compute_expand ||
          widget->priv->computed_hexpand ||
          widget->priv->computed_vexpand)
        {
          if (widget->priv->parent != NULL)
            gtk_widget_queue_compute_expand (widget->priv->parent);
        }

      g_signal_emit (widget, widget_signals[SHOW], 0);
      g_object_notify (G_OBJECT (widget), "visible");

      gtk_widget_pop_verify_invariants (widget);
      g_object_unref (widget);
    }
}

 

void
g_signal_emit (gpointer instance,
	       guint    signal_id,
	       GQuark   detail,
	       ...)
{
  va_list var_args;

  va_start (var_args, detail);
  g_signal_emit_valist (instance, signal_id, detail, var_args);
  va_end (var_args);
}

 

static gboolean
signal_emit_unlocked_R (SignalNode   *node,
			GQuark	      detail,
			gpointer      instance,
			GValue	     *emission_return,
			const GValue *instance_and_params)
{
  SignalAccumulator *accumulator;
  Emission emission;
  GClosure *class_closure;
  HandlerList *hlist;
  Handler *handler_list = NULL;
  GValue *return_accu, accu = { 0, };
  guint signal_id;
  gulong max_sequential_handler_number;
  gboolean return_value_altered = FALSE;

  emission.instance = instance;
  emission.ihint.signal_id = node->signal_id;
  emission.ihint.detail = detail;
  emission.ihint.run_type = 0;
  emission.state = 0;
  emission.chain_type = G_TYPE_NONE;
  emission_push ((node->flags & G_SIGNAL_NO_RECURSE) ? &g_restart_emissions : &g_recursive_emissions, &emission);
  class_closure = signal_lookup_closure (node, instance);
  
 EMIT_RESTART:
  
  if (handler_list)
    handler_unref_R (signal_id, instance, handler_list);
  max_sequential_handler_number = g_handler_sequential_number;
  hlist = handler_list_lookup (signal_id, instance);
  handler_list = hlist ? hlist->handlers : NULL;
  if (handler_list)
    handler_ref (handler_list);
  
  emission.ihint.run_type = G_SIGNAL_RUN_FIRST;
  
  if ((node->flags & G_SIGNAL_RUN_FIRST) && class_closure)
    {
      emission.state = EMISSION_RUN;

      emission.chain_type = G_TYPE_FROM_INSTANCE (instance);
      SIGNAL_UNLOCK ();
      g_closure_invoke (class_closure,
			return_accu,
			node->n_params + 1,
			instance_and_params,
			&emission.ihint);
      if (!accumulate (&emission.ihint, emission_return, &accu, accumulator) &&
	  emission.state == EMISSION_RUN)
	emission.state = EMISSION_STOP;
      SIGNAL_LOCK ();
      emission.chain_type = G_TYPE_NONE;
      return_value_altered = TRUE;
      
      if (emission.state == EMISSION_STOP)
	goto EMIT_CLEANUP;
      else if (emission.state == EMISSION_RESTART)
	goto EMIT_RESTART;
    }
  

上面做如下的动作:

1)做一个emmission, 收集好signal的相关信息,并且压入list中,临时保存信息

2)找到和这个signal相关联的closure (callback)

3)  根据signal id, 找到hander list

4)  如果有closure, 则调用g_closure_invoke(), 启动callback流程

  if ((node->flags & G_SIGNAL_RUN_FIRST) && class_closure)
    {
      emission.state = EMISSION_RUN;

      emission.chain_type = G_TYPE_FROM_INSTANCE (instance);
      SIGNAL_UNLOCK ();
      g_closure_invoke (class_closure,
			return_accu,
			node->n_params + 1,
			instance_and_params,
			&emission.ihint);

 

   4.1) 调用closure_invoke_notifier() //保护参数

   4.2) 执行marshal(), 其中在marshal中调用 user callback

          因为参数传入时,用GValue形式传入,在marshal中:

          (1)需要把参数由Gvalue --> C 参数形式

          (2)调用user callback

          (3)把返回的值再包装成GValue形式带回

5. 如果通过g_signal_connect_xxx() 注册了自己的回调,会调用handler_list

  if (handler_list)
    {
      Handler *handler = handler_list;
     
      emission.state = EMISSION_RUN;
      handler_ref (handler);
      do
 {
   Handler *tmp;
  
   if (handler->after)
     {
       handler_unref_R (signal_id, instance, handler_list);
       handler_list = handler;
       break;
     }
   else if (!handler->block_count && (!handler->detail || handler->detail == detail) &&
     handler->sequential_number < max_sequential_handler_number)
     {
       SIGNAL_UNLOCK ();
       g_closure_invoke (handler->closure,
    return_accu,
    node->n_params + 1,
    instance_and_params,
    &emission.ihint);

 

gobjec相关学习文章的list.

 

 

 


登录 *


loading captcha image...
(输入验证码)
or Ctrl+Enter