spice-gtk,是紅帽的一個遠程連接的一個項目,這是一個c語言實現的面相對象的項目,採用的是gobject面相對象的方法,所以讀懂spice-gtk的代碼首先得了解一下gobject的語法。
spice-channel網絡層接收所有服務端的包
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));
}