Pidgin的后台库分为很多功能独立的模块,帐户模块,连接模块,会话模块,配置模块等等,每个模块有可能需要知道其他模块的状态,这种需求如果依赖glib来实现,那就是使用gobject信号,每个模块注册自己感兴趣模块的信号,但是pidgin自己实现了模块间通知的机制,虽然简单,但是足够好用。
pidgin的模块间通知实现在libpurple目录下的signals.c文件中,主要的函数是:purple_signal_register, purple_signal_connect, purple_signal_disconnect, purple_signal_emit.看名字是不是和gobject那些很象?每个模块通过purple_signal_register建立自己信号,通过purple_signal_emit来发出信号,调用信号上连接的处理函数;其他模块通过purple_signal_connect和purple_signal_disconnect来连接,断开信号处理函数。
首先来看purple_signal_register:这个函数用来建立模块实例的一个指定信号
gulong
purple_signal_register(void *instance, const char *signal,
PurpleSignalMarshalFunc marshal,
PurpleValue *ret_value, int num_values, ...)
{
PurpleInstanceData *instance_data;
PurpleSignalData *signal_data;
va_list args;
g_return_val_if_fail(instance != NULL, 0);
g_return_val_if_fail(signal != NULL, 0);
g_return_val_if_fail(marshal != NULL, 0);
instance_data =
(PurpleInstanceData *)g_hash_table_lookup(instance_table, instance);
if (instance_data == NULL)
{
instance_data = g_new0(PurpleInstanceData, 1);
instance_data->instance = instance;
instance_data->next_signal_id = 1;
instance_data->signals =
g_hash_table_new_full(g_str_hash, g_str_equal, g_free,
(GDestroyNotify)destroy_signal_data);
g_hash_table_insert(instance_table, instance, instance_data);
}
/* 每个模块可以有多个实例,每个实例都有自己单独的信号列表 */
signal_data = g_new0(PurpleSignalData, 1);
signal_data->id = instance_data->next_signal_id;
signal_data->marshal = marshal;
signal_data->next_handler_id = 1;
signal_data->ret_value = ret_value;
signal_data->num_values = num_values;
/* 根据传递参数生成一个信号实例 */
if (num_values > 0)
{
int i;
signal_data->values = g_new0(PurpleValue *, num_values);
va_start(args, num_values);
for (i = 0; i < num_values; i++)
signal_data->values[i] = va_arg(args, PurpleValue *);
va_end(args);
}
/*上面在signal_data->values中记录回调函数参数类型, 主要是为在dbus上发送信号使用,这里不用管它 */
g_hash_table_insert(instance_data->signals,
g_strdup(signal), signal_data);
/* 将信号实例加入模块实例的信号列表 */
instance_data->next_signal_id++;
instance_data->signal_count++;
return signal_data->id;
}
再来看其他模块用来连接指定信号的函数purple_signal_connect,在purple_signal_connect是调用signal_connect_common来实现的:
static gulong
signal_connect_common(void *instance, const char *signal, void *handle,
PurpleCallback func, void *data, int priority, gboolean use_vargs)
{
PurpleInstanceData *instance_data;
PurpleSignalData *signal_data;
PurpleSignalHandlerData *handler_data;
g_return_val_if_fail(instance != NULL, 0);
g_return_val_if_fail(signal != NULL, 0);
g_return_val_if_fail(handle != NULL, 0);
g_return_val_if_fail(func != NULL, 0);
/* Get the instance data */
instance_data =
(PurpleInstanceData *)g_hash_table_lookup(instance_table, instance);
/* 在purple模块实例hash表里找到要连接信号的模块实例指针 */
if (instance_data == NULL)
{
purple_debug_warning("signals", "Something tried to register a callback "
"for the '%s' signal, but we do not have any signals "
"registered with the given handle/n", signal);
g_return_val_if_reached(0);
}
/* Get the signal data */
signal_data =
(PurpleSignalData *)g_hash_table_lookup(instance_data->signals, signal);
if (signal_data == NULL)
{
purple_debug(PURPLE_DEBUG_ERROR, "signals",
"Signal data for %s not found!/n", signal);
return 0;
}
/* 查找该模块实例的信号实例指针 */
/* Create the signal handler data */
handler_data = g_new0(PurpleSignalHandlerData, 1);
handler_data->id = signal_data->next_handler_id;
handler_data->cb = func;
handler_data->handle = handle;
handler_data->data = data;
handler_data->use_vargs = use_vargs;
handler_data->priority = priority;
/* 创建信号处理handler实例 */
signal_data->handlers = g_list_insert_sorted(signal_data->handlers, handler_data, (GCompareFunc)handler_priority);
signal_data->handler_count++;
signal_data->next_handler_id++;
/* 将信号处理handler加入信号实例的处理handler链表 */
return handler_data->id;
}
当模块中有状态改变,需要通知其他关心这个事件的其他模块,这时候,就轮到purple_signal_emit登场了:
void
purple_signal_emit(void *instance, const char *signal, ...)
{
va_list args;
g_return_if_fail(instance != NULL);
g_return_if_fail(signal != NULL);
va_start(args, signal);
purple_signal_emit_vargs(instance, signal, args);
va_end(args);
}
void
purple_signal_emit_vargs(void *instance, const char *signal, va_list args)
{
PurpleInstanceData *instance_data;
PurpleSignalData *signal_data;
PurpleSignalHandlerData *handler_data;
GList *l, *l_next;
va_list tmp;
g_return_if_fail(instance != NULL);
g_return_if_fail(signal != NULL);
instance_data =
(PurpleInstanceData *)g_hash_table_lookup(instance_table, instance);
g_return_if_fail(instance_data != NULL);
/* 在purple模块实例hash表里找到要发送信号的模块实例指针 */
signal_data =
(PurpleSignalData *)g_hash_table_lookup(instance_data->signals, signal);
if (signal_data == NULL)
{
purple_debug(PURPLE_DEBUG_ERROR, "signals",
"Signal data for %s not found!/n", signal);
return;
}
/* 找到要发送信号的实例的指针 */
for (l = signal_data->handlers; l != NULL; l = l_next)
{
l_next = l->next;
handler_data = (PurpleSignalHandlerData *)l->data;
/* This is necessary because a va_list may only be
* evaluated once */
G_VA_COPY(tmp, args);
if (handler_data->use_vargs)
{
((void (*)(va_list, void *))handler_data->cb)(tmp,
handler_data->data);
}
else
{
signal_data->marshal(handler_data->cb, tmp,
handler_data->data, NULL);
}
/* 依次调用信号处理handler链表中的处理函数 */
va_end(tmp);
}
#ifdef HAVE_DBUS
purple_dbus_signal_emit_purple(signal, signal_data->num_values,
signal_data->values, args);
#endif /* HAVE_DBUS */
/* 在dbus上广播信号,这里用到了上面记录的signal_data->values,这个是根据每个参数的类型,将参数通过dbus_message_iter_append_basic 打包进dbus message中 */
ang
}
上面的signal_data->marshal实际上就是对不同类型参数组合的回调函数,通过va_arg取purple_signal_emit_vargs压入栈中的参数来调用其他模块注册的信号处理函数,举两个例子:
void
purple_marshal_VOID__INT(PurpleCallback cb, va_list args, void *data,
void **return_val)
{
gint arg1 = va_arg(args, gint);
((void (*)(gint, void *))cb)(arg1, data);
}
void
purple_marshal_POINTER__POINTER_POINTER(PurpleCallback cb, va_list args, void *data,
void **return_val)
{
gpointer ret_val;
void *arg1 = va_arg(args, void *);
void *arg2 = va_arg(args, void *);
ret_val = ((gpointer (*)(void *, void *, void *))cb)(arg1, arg2, data);
if (return_val != NULL)
*return_val = ret_val;
PIDGIN模块间通知的实现方式
最新推荐文章于 2024-08-18 22:19:19 发布