我们从一个实际的例子看看:
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
1. Signal Node生成
2. callback指定
3. c Marshal指定
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); }
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);