spice-gtk,是红帽的一个远程连接的一个项目,这是一个c语言实现的面相对象的项目,采用的是gobject面相对象的方法,所以读懂spice-gtk的代码首先得了解一下gobject的语法。
spice_channel_init(SpiceChannel *channel)首先初始化初始化的时候将priv结构体中的一些变量初始化,公有变量SpiceChannel结构体,typedef转换的变量,其实是_SpiceChannel, 在spice-types.h中转换,存在于spice-channel.h中。SpiceChannelPrivate类似其实是_SpiceChannelPrivate存在于spice-channel-priv.h中类似于c++中的private变量这边是gobject编程的一种安全机制。
struct _SpiceChannel
{
GObject parent;
SpiceChannelPrivate *priv;
/* Do not add fields to this struct */
};
struct _SpiceChannelPrivate {
/* swapped on migration */
SSL_CTX *ctx;
SSL *ssl;
SpiceOpenSSLVerify *sslverify;
GSocket *sock;
GSocketConnection *conn;
GInputStream *in;
GOutputStream *out;
。
。
。
下面变量未显示
};
其实spice-channel类初始化的时候还会调用一个spice_channel_class_init(SpiceChannelClass *kclass)
SpiceChannelClass这个类和上面的类似其实是_SpiceChannelClass这是在spice-type.h中转换的
实际上结构体是这样的都是一些函数指针,在class_init中会将这些函数指针真正的指向具体的函数,在spicechannelclass结构体中也会有一个spicechannelclassprivate变量,也是存在于spice-channel-priv.h中如下所示:
struct _SpiceChannelClass
{
GObjectClass parent_class;
/*< public >*/
/* signals, main context */
void (*channel_event)(SpiceChannel *channel, SpiceChannelEvent event);
void (*open_fd)(SpiceChannel *channel, int with_tls);
/*< private >*/
/* virtual methods, coroutine context */
void (*handle_msg)(SpiceChannel *channel, SpiceMsgIn *msg);
void (*channel_up)(SpiceChannel *channel);
void (*iterate_write)(SpiceChannel *channel);
void (*iterate_read)(SpiceChannel *channel);
/*< private >*/
/* virtual method, any context */
gpointer deprecated;
void (*channel_reset)(SpiceChannel *channel, gboolean migrating);
void (*channel_reset_capabilities)(SpiceChannel *channel);
/*< private >*/
/* virtual methods, coroutine context */
void (*channel_send_migration_handshake)(SpiceChannel *channel);
SpiceChannelClassPrivate *priv;
/*
* If adding fields to this struct, remove corresponding
* amount of padding to avoid changing overall struct size
*/
gchar _spice_reserved[SPICE_RESERVED_PADDING - 2 * sizeof(void *)];
};
struct _SpiceChannelClassPrivate
{
GArray *handlers;
};
这里面的有一个数组handlers接下来我们分析这个handlers的作用:
首先分析一下客户端接收包的时候数据包走的流程,刚开始接收是在spice_channel_iterate_read这个函数中有一个调用很重要:
spice_channel_recv_msg(channel, (handler_msg_in)SPICE_CHANNEL_GET_CLASS(channel)->handle_msg, NULL);
第二个参数其实传递的是spicechannelclass中的一个函数指针,而这个函数指针很明确在class_init中指向的函数就是spice_chanel_handle_msg,因此我们要分析这个函数:
static void spice_channel_handle_msg(SpiceChannel *channel, SpiceMsgIn *msg)
{
SpiceChannelClass *klass = SPICE_CHANNEL_GET_CLASS(channel);
int type = spice_msg_in_type(msg);
spice_msg_handler handler; //这边只是一个函数指针,具体的指向哪里还不知道
g_return_if_fail(type < klass->priv->handlers->len);
if (type > SPICE_MSG_BASE_LAST && channel->priv->disable_channel_msg)
return;
/*这个函数的第一个参数其实就是上面大黑字定义的一个garray,g_array_index函数的作用就是在这个数组中寻找到匹配这个type的函数,这样上面的函数指针就有明确指向的定义了*/
handler = g_array_index(klass->priv->handlers, spice_msg_handler, type);//通过channel-display最底下的channel_set_handlers函数中去匹配
g_return_if_fail(handler != NULL);
handler(channel, msg);//这个handle其实已经是channel-display中的处理视频包的相关函数了
}
g_array_index会去查找garray这个包type和处理函数的对应表,而这个表存在每个模块的最后,因为包有视频的包,音频的包,鼠标操作的包键盘操作的包各种各样,这边给出一个视频包处理的文件,channel-display.c 处理网络层收到的包中关于视频的包,匹配表如下所示:
//本函数存在channel-display.c最后
static void channel_set_handlers(SpiceChannelClass *klass)
{
static const spice_msg_handler handlers[] = {
[ SPICE_MSG_DISPLAY_MODE ] = display_handle_mode,
[ SPICE_MSG_DISPLAY_MARK ] = display_handle_mark,
[ SPICE_MSG_DISPLAY_RESET ] = display_handle_reset,
[ SPICE_MSG_DISPLAY_COPY_BITS ] = display_handle_copy_bits,
[ SPICE_MSG_DISPLAY_INVAL_LIST ] = display_handle_inv_list,
[ SPICE_MSG_DISPLAY_INVAL_ALL_PIXMAPS ] = display_handle_inv_pixmap_all,
[ SPICE_MSG_DISPLAY_INVAL_PALETTE ] = display_handle_inv_palette,
[ SPICE_MSG_DISPLAY_INVAL_ALL_PALETTES ] = display_handle_inv_palette_all,
[ SPICE_MSG_DISPLAY_STREAM_CREATE ] = display_handle_stream_create,
[ SPICE_MSG_DISPLAY_STREAM_DATA ] = display_handle_stream_data,
[ SPICE_MSG_DISPLAY_STREAM_CLIP ] = display_handle_stream_clip,
[ SPICE_MSG_DISPLAY_STREAM_DESTROY ] = display_handle_stream_destroy,
[ SPICE_MSG_DISPLAY_STREAM_DESTROY_ALL ] = display_handle_stream_destroy_all,
[ SPICE_MSG_DISPLAY_STREAM_DATA_SIZED ] = display_handle_stream_data,
[ SPICE_MSG_DISPLAY_STREAM_ACTIVATE_REPORT ] = display_handle_stream_activate_report,
[ SPICE_MSG_DISPLAY_DRAW_FILL ] = display_handle_draw_fill,
[ SPICE_MSG_DISPLAY_DRAW_OPAQUE ] = display_handle_draw_opaque,
[ SPICE_MSG_DISPLAY_DRAW_COPY ] = display_handle_draw_copy,
[ SPICE_MSG_DISPLAY_DRAW_BLEND ] = display_handle_draw_blend,
[ SPICE_MSG_DISPLAY_DRAW_BLACKNESS ] = display_handle_draw_blackness,
[ SPICE_MSG_DISPLAY_DRAW_WHITENESS ] = display_handle_draw_whiteness,
[ SPICE_MSG_DISPLAY_DRAW_INVERS ] = display_handle_draw_invers,
[ SPICE_MSG_DISPLAY_DRAW_ROP3 ] = display_handle_draw_rop3,
[ SPICE_MSG_DISPLAY_DRAW_STROKE ] = display_handle_draw_stroke,
[ SPICE_MSG_DISPLAY_DRAW_TEXT ] = display_handle_draw_text,
[ SPICE_MSG_DISPLAY_DRAW_TRANSPARENT ] = display_handle_draw_transparent,
[ SPICE_MSG_DISPLAY_DRAW_ALPHA_BLEND ] = display_handle_draw_alpha_blend,
[ SPICE_MSG_DISPLAY_DRAW_COMPOSITE ] = display_handle_draw_composite,
[ SPICE_MSG_DISPLAY_SURFACE_CREATE ] = display_handle_surface_create,
[ SPICE_MSG_DISPLAY_SURFACE_DESTROY ] = display_handle_surface_destroy,
[ SPICE_MSG_DISPLAY_MONITORS_CONFIG ] = display_handle_monitors_config,
#ifdef G_OS_UNIX
[ SPICE_MSG_DISPLAY_GL_SCANOUT_UNIX ] = display_handle_gl_scanout_unix,
#endif
[ SPICE_MSG_DISPLAY_GL_DRAW ] = display_handle_gl_draw,
};
spice_channel_set_handlers(klass, handlers, G_N_ELEMENTS(handlers));
}